/* hjemmesidedrift — shared stylesheet. System fonts only, no external requests. */
:root {
  --bg: #ffffff;
  --bg-soft: #f3f5f8;
  --text: #17202a;
  --muted: #4b5563;
  --line: #d7dce3;
  --accent: #0b5cad;
  --accent-text: #ffffff;
  --accent-soft: #e6f0fb;
  --ok: #146c43;
  --err: #b42318;
  --radius: 8px;
  --gutter: 16px;
  /* Two container widths, both fluid, both expressed in rem/vw so the marketing guard's
     "no px width above 328" rule holds at every viewport.

     --max is the PROSE measure. It grows only gently (44rem -> 48rem), because the type
     ladder below grows faster than it does: body text goes 17px -> 21px between 1100 and
     2560, so a column that grew at the same rate would keep the same over-long line. Line
     length in CHARACTERS therefore gets shorter as the screen gets bigger, which is the
     direction it should move.

     --max-wide is for the blocks that have more than one thing to place: the forside hero
     beside its proof screenshot, the two plan cards on /priser/, the comparison rows, the
     three example cards on /eksempler/. Those get the room, prose does not. */
  --max: clamp(44rem, 30rem + 12vw, 48rem);
  --max-wide: clamp(46rem, 20rem + 48vw, 84rem);
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #121417;
    --bg-soft: #1c2026;
    --text: #e9ecef;
    --muted: #aab2bd;
    --line: #333a44;
    --accent: #8ec1ff;
    --accent-text: #0b1a2c;
    --accent-soft: #1a2a3f;
    --ok: #6fd39a;
    --err: #ff8a80;
  }
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* The base of the type ladder. Flat 17px up to about 1100px of viewport, then a straight
     line to 21px at 2560, ceiling 21.6px. Every other size below is anchored to the same
     line so the whole page grows together rather than in steps: the preferred term is
     0.874rem + 0.274vw, i.e. exactly 17px at 1100 and exactly 21px at 2560. Keeping a rem
     term in the preferred value means a reader who raises their browser's default font
     size still gets a bigger page, which a pure-vw ladder would take away from them. */
  font-size: clamp(1.0625rem, 0.874rem + 0.274vw, 1.35rem);
  line-height: 1.55;
  overflow-x: hidden;
}
img, svg, video { max-width: 100%; height: auto; }
table { border-collapse: collapse; width: 100%; max-width: 100%; }
th, td { text-align: left; vertical-align: top; padding: .5rem .5rem .5rem 0; border-bottom: 1px solid var(--line); }
th { font-weight: 600; width: 30%; }
pre, code { white-space: pre-wrap; overflow-wrap: anywhere; }
a { color: var(--accent); }
a:hover { text-decoration-thickness: 2px; }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* The shared container. The header bar and the footer always take the wide measure, so the
   brand, the menu and the footer links keep the same left and right edge on every page.
   <main> is narrow by default — most of the 34 pages are one column of prose, and a wide
   <main> around a narrow column would only push that column to one side — and widens only
   on the pages that actually contain a multi-column block. The :has() test names those
   blocks rather than the pages, so a page that grows a price table or a card grid gets the
   room the day it does, without a per-page class. */
.wrap { width: 100%; max-width: var(--max-wide); margin: 0 auto; padding-left: var(--gutter); padding-right: var(--gutter); }
main.wrap { max-width: var(--max); }
main.wrap:has(.hero-shot, .prices, .plans, .cmp, .vs-cols) { max-width: var(--max-wide); }
/* …and inside a wide <main>, prose still stops at the readable measure. Only the blocks
   that lay something out — grids of cards, comparison rows, the hero — use the full width.
   These are caps, not widths: inside a 350px card they change nothing, and on the pages
   where <main> is already the prose measure they change nothing either.
   The cap is LEFT-aligned, not centred. Centring it was measurably wrong: on /priser/ the
   capped <h1> then started 91px right of the .plans grid at 1280, 234px right at 1920 and
   256px right at 2560, so "Priser" and every section heading hung a quarter of the content
   width to the right of the cards they introduce, and the offset grew with the screen.
   Left-aligning gives the page ONE left edge — heading, lead, cards and comparison rows all
   start on it — and the leftover width sits on the right where the eye does not track it. */
main p, main ul, main ol, main dl, main h1, main h2, main h3, main h4,
main blockquote, main details, main table, main figcaption {
  max-width: var(--max);
}
/* Exception: .cmp is a <ul> that is a layout, not a list of sentences. */
main .cmp { max-width: none; }
.skip { position: absolute; left: -999px; top: 0; background: var(--accent); color: var(--accent-text); padding: .5rem 1rem; z-index: 10; }
.skip:focus { left: var(--gutter); }

/* header */
.site-header { border-bottom: 1px solid var(--line); background: var(--bg); }
.bar { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem; padding-top: .75rem; padding-bottom: .75rem; }
.brand { font-weight: 700; text-decoration: none; color: var(--text); font-size: clamp(1.125rem, 1.02rem + 0.32vw, 1.6rem); margin-right: auto; }
/* flex: none on the items: once the type grows, the menu plus the button is close to the
   width of the bar, and the default flex-shrink squeezed the "Gratis udkast" button until
   its label spilled out of its own blue box. The items keep their natural width and wrap
   to a second line instead. */
.site-header nav { display: flex; flex-wrap: wrap; gap: .25rem .9rem; align-items: center; }
.site-header nav > * { flex: none; }
/* :not(.btn) because `.site-header nav a` outranks both `.btn` and `.btn-small` on
   specificity, so the plain-link padding of `.25rem 0` was winning on the "Gratis udkast"
   button too: it computed padding-left/right 0px at every width and the blue box sat flush
   against its own label. Excluding .btn here lets the button keep its own padding instead
   of needing a more specific rule to undo this one. */
.site-header nav a:not(.btn) { text-decoration: none; color: var(--text); padding: .25rem 0; }
.site-header nav a[aria-current="page"] { text-decoration: underline; text-underline-offset: .2em; }

/* main */
main { padding-top: 1.5rem; padding-bottom: 3rem; }
/* The rest of the type ladder. Every clamp() starts at the size this page used to have at
   360 (so nothing on a phone changes by a pixel) and climbs to a ceiling that is reached
   above 2560, so a 2K and a 4K screen do not render identically. The old 40em step rules
   for h1 and .hero h1 are gone: they were a second system pulling against this one, and
   the ladder passes through roughly the same sizes at 768 that the steps used to jump to. */
h1 { font-size: clamp(1.85rem, 1.5rem + 1.35vw, 3.35rem); line-height: 1.15; margin: 0 0 .75rem; letter-spacing: -.01em; }
h2 { font-size: clamp(1.35rem, 1.18rem + 0.62vw, 2.15rem); line-height: 1.25; margin: 2rem 0 .6rem; }
h3 { font-size: clamp(1.1rem, 1rem + 0.36vw, 1.7rem); margin: 1.4rem 0 .4rem; }
p { margin: 0 0 1rem; }
.lead { font-size: clamp(1.15rem, 1.05rem + 0.33vw, 1.7rem); color: var(--muted); }
section { margin-bottom: 1.5rem; }
ul, ol { padding-left: 1.3rem; }
li { margin-bottom: .45rem; }

.btn {
  display: inline-block;
  background: var(--accent);
  color: var(--accent-text);
  border: 0;
  border-radius: var(--radius);
  padding: .8rem 1.3rem;
  font: inherit;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}
.btn:hover { filter: brightness(1.08); }
.btn-small { padding: .45rem .8rem; font-size: clamp(.95rem, .87rem + 0.24vw, 1.25rem); }
.cta { margin-top: 1.5rem; }
.cta-block { background: var(--bg-soft); border-radius: var(--radius); padding: 1.25rem var(--gutter); }
.cta-block h2 { margin-top: 0; }

/* hero */
.hero { padding-bottom: .5rem; }
.hero h1 { font-size: clamp(2rem, 1.6rem + 1.75vw, 3.4rem); }
.prices { display: grid; grid-template-columns: 1fr; gap: .75rem; margin: 1.25rem 0; }
.price { border: 1px solid var(--line); border-radius: var(--radius); padding: .9rem 1rem; display: grid; }
.price .plan { font-size: .9rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.price .amount { font-size: clamp(1.5rem, 1.3rem + 0.7vw, 2.5rem); font-weight: 700; }
.price .note, .plan-card .note { font-size: .9rem; color: var(--muted); font-weight: 400; }
.three { list-style: none; padding: 0; }
.three li { padding-left: 1.4rem; position: relative; }
.three li::before, .checks li::before { content: "\2713"; position: absolute; left: 0; color: var(--ok); font-weight: 700; }
.checks { list-style: none; padding: 0; }
.checks li { padding-left: 1.4rem; position: relative; }

/* steps */
.steps { counter-reset: step; list-style: none; padding: 0; }
.steps li { counter-increment: step; position: relative; padding-left: 2.4rem; margin-bottom: .9rem; }
.steps li::before {
  content: counter(step);
  position: absolute; left: 0; top: .1rem;
  width: 1.7rem; height: 1.7rem; border-radius: 50%;
  background: var(--accent); color: var(--accent-text);
  font-weight: 700; font-size: .95rem;
  display: flex; align-items: center; justify-content: center;
}
.steps.big li { margin-bottom: 1.5rem; }
.steps.big h2 { margin: 0 0 .4rem; font-size: 1.2rem; }

/* plans */
.plans { display: grid; grid-template-columns: 1fr; gap: 1rem; margin: 1.25rem 0; }
.plan-card { border: 1px solid var(--line); border-radius: var(--radius); padding: 1.1rem var(--gutter); background: var(--bg-soft); }
.plan-card h2 { margin-top: 0; }
.plan-card .amount { font-size: clamp(1.6rem, 1.38rem + 0.75vw, 2.7rem); font-weight: 700; margin-bottom: .75rem; }
.plan-note { font-size: .97rem; }
/* Capability status on a plan bullet. A line that is not built yet must not wear the
   green check the rest of the list uses, so the ::before marker is swapped for a dash
   in the muted colour and the label spells the status out in words. Tokens only. */
.kap-status { font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  padding: 1px 7px; border-radius: 999px; border: 1px solid var(--line); background: var(--bg); color: var(--muted); white-space: nowrap; }
li[data-status="klar"] > .kap-status { color: var(--ok); border-color: var(--ok); }
.checks li[data-status="under-udvikling"]::before { content: "\2013"; color: var(--muted); }

/* faq */
details { border-top: 1px solid var(--line); padding: .6rem 0; }
details:last-of-type { border-bottom: 1px solid var(--line); }
summary { cursor: pointer; font-weight: 600; }
details p { margin: .6rem 0 .3rem; }

/* form */
.form { max-width: 34rem; }
.field { margin-bottom: 1.1rem; }
.field label { display: block; font-weight: 600; margin-bottom: .3rem; }
.field .opt { font-weight: 400; color: var(--muted); }
.field input, .field select, .field textarea {
  width: 100%;
  max-width: 100%;
  font: inherit;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .65rem .75rem;
}
.field textarea { resize: vertical; min-height: 7rem; }
.field.check { display: grid; grid-template-columns: auto 1fr; gap: .6rem; align-items: start; }
.field.check input { width: 1.25rem; height: 1.25rem; margin-top: .2rem; }
.field.check label { font-weight: 400; margin: 0; }
.hint { color: var(--muted); font-size: .95rem; margin: .3rem 0 0; }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.status:empty { display: none; }
.status { padding: .6rem .8rem; border-radius: var(--radius); background: var(--bg-soft); }
.status.error { color: var(--err); border: 1px solid var(--err); }
.thanks { background: var(--accent-soft); border-radius: var(--radius); padding: 1rem var(--gutter); }
.thanks h2 { margin-top: 0; }
.big-mail { font-size: 1.3rem; font-weight: 600; overflow-wrap: anywhere; }
/* eksempler — screenshots of the three demonstration sites */
.shot { display: block; width: 100%; border: 1px solid var(--line); border-radius: var(--radius); }

/* /eksempler/#kort-vs-fuld — the three-field draft beside the full one. Stacked below
   48em: two 1280-wide shots side by side on a 360 phone would be two illegible
   thumbnails, and the captions are the point. From 48em two equal columns, each
   minmax(0, 1fr) so a full-width image can never push the grid past the viewport. */
.vs-cols { display: grid; gap: 1.5rem; margin: 1.25rem 0; }
.vs { margin: 0; }
.vs figcaption { margin-top: .6rem; color: var(--muted); font-size: .95rem; }
@media (min-width: 48em) {
  .vs-cols { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

/* forside — the same example screenshot as proof, below the price line and the CTA */
.hero-shot { margin: 1.75rem 0 0; }
.hero-shot img { display: block; width: 100%; border: 1px solid var(--line); border-radius: var(--radius); }
.hero-shot figcaption { margin-top: .6rem; color: var(--muted); font-size: .95rem; }

/* From 62em the hero stops being a stack and becomes headline-and-offer on the left with
   the proof screenshot beside it, so the one image that answers "can these people build a
   website" is above the fold on a laptop and on a 2K monitor instead of below it.
   The headline, the lead and the three bullets share column 1 with the figure beside them
   in column 2; the price row and the call to action then run the FULL width underneath,
   because three price boxes squeezed into half the hero broke "1.195 kr/md" over two
   lines. The 0.92fr image column keeps the 1280-wide screenshot below its natural size at
   every viewport this rule applies to, so it is never upscaled. */
@media (min-width: 62em) {
  .hero {
    display: grid;
    grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr);
    column-gap: clamp(2rem, 2.5vw, 3.5rem);
    align-items: start;
  }
  .hero > * { grid-column: 1; margin-left: 0; margin-right: 0; }
  .hero > .hero-shot { grid-column: 2; grid-row: 1 / span 3; margin-top: .4rem; }
  .hero > .prices, .hero > .cta { grid-column: 1 / -1; }
}

/* /udkast/ — the form with the proof of what arrives beside it. Below 48em the two
   columns stack FORM FIRST, deliberately: the first field and the line that says it
   costs nothing must be reachable on a 390 phone without scrolling past a screenshot.
   From 48em the proof sits in a fixed 17rem column on the right, which is roughly the
   CSS width of the 390-wide phone shot it holds, so the shot is never upscaled. */
.udkast-cols { display: grid; gap: 1.75rem; align-items: start; }
.udkast-proof { margin: 0; }
.udkast-proof h2 { margin-top: 0; font-size: 1.15rem; }
.proof-shot { margin: 0; }
.proof-shot img { display: block; width: 100%; max-width: 20rem; border: 1px solid var(--line); border-radius: var(--radius); }
.proof-shot figcaption { margin-top: .6rem; color: var(--muted); font-size: .95rem; }
.proof-steps { margin-top: 1.1rem; font-size: .97rem; }
.proof-steps li { margin-bottom: .6rem; }
.proof-more { margin: 0; font-size: .95rem; }
@media (min-width: 48em) {
  .udkast-cols { grid-template-columns: minmax(0, 1fr) minmax(0, 17rem); gap: 2rem; }
}

/* /priser/ — the price comparison. Cards at EVERY width, never a table: the
   36-month table on /vaerktoejer/hvad-koster-en-hjemmeside/ collapses to one word
   per cell at 360, and this must not repeat that shape. Below 40em each row is a
   plain stacked card; from 40em the category heading moves into a left column. */
.cmp { list-style: none; padding: 0; margin: 1.25rem 0; display: grid; gap: .9rem; }
.cmp-row { border: 1px solid var(--line); border-radius: var(--radius); padding: .9rem 1rem; margin: 0; }
.cmp-row h3 { margin: 0 0 .5rem; font-size: 1.05rem; }
.cmp-row p { margin: 0 0 .6rem; }
.cmp-row p:last-child { margin-bottom: 0; }
.cmp-price { font-weight: 600; }
.cmp-src { color: var(--muted); font-size: .92rem; }
.cmp-src a { overflow-wrap: anywhere; }
.cmp-us { background: var(--accent-soft); border-color: var(--accent); }
.cmp-foot { color: var(--muted); font-size: .95rem; }

/* .cmp-summary — the one-screen summary that sits ABOVE the evidence cards (T-DKWHY.7).
   Measured before this block landed, /priser/ was 9,580px tall at 390 (10.6 phone
   screens) and the sourced comparison sat at the bottom of it, so the page's strongest
   asset was the part a phone buyer was least likely to reach. The summary repeats
   nothing new: every cell is a verbatim substring of the card it links to, and a guard
   in test/price-comparison.test.cjs fails the build the day one of them drifts.
   It carries .stack as well, so it uses the SAME stacked-rows-with-labels shape the
   underdatabehandler table on /databehandleraftale/ uses below 40em — one responsive
   table pattern on this site, not two. Layout-only rules here: the narrow prose cap
   that main table gets would squeeze six columns into one word per cell, and the
   global th width: 30% means nothing across six of them. Our own row gets no colour,
   no border and no weight the other five do not get — .cmp-us stops at the card. */
main .cmp-summary { max-width: none; }
.cmp-summary { margin: 1.25rem 0 1.5rem; font-size: .95rem; }
.cmp-summary caption { text-align: left; color: var(--muted); font-size: .92rem; padding-bottom: .6rem; }
.cmp-summary th { width: auto; color: var(--muted); font-size: .92rem; }
.cmp-summary th, .cmp-summary td { padding: .5rem .7rem .5rem 0; }
.cmp-summary th:last-child, .cmp-summary td:last-child { padding-right: 0; }
.cmp-summary td:first-child { font-weight: 600; }
/* The Kilde column is deliberately NOT allowed to break mid-word: with overflow-wrap
   anywhere its min-content width collapsed to one character, auto table layout handed it
   almost nothing, and "webministeriet.dk" rendered as "webmini steriet.d k". Keeping the
   host whole makes the column ask for the width it actually needs. */
.cmp-summary td:last-child { color: var(--muted); }

/* table.stack — opt-in responsive table, used by the underdatabehandler table in
   punkt 6 of /databehandleraftale/. That table is three columns wide and one cell
   is an 85-character description of the hosting supplier, so its minimum width at
   360 was 383px: the databehandleraftale was the one page of 34 that scrolled
   sideways on a phone, and it is the document a buyer is asked to accept before
   paying. Below 40em each row becomes a block and each cell a full-width line with
   its own column label (copied verbatim into data-label) above the value, so no
   information from the header row is lost. From 40em up it is an ordinary table
   again. The class is opt-in on purpose: the two-column table higher up the same
   page already fits at 360 and must keep its shape, as must every other table. */
@media (max-width: 39.99em) {
  table.stack, table.stack tbody, table.stack tr, table.stack td { display: block; width: auto; }
  /* The header row's labels move into the cells below, so the row itself is dead
     weight at this width. */
  table.stack tr:first-child { display: none; }
  table.stack tr { border-bottom: 1px solid var(--line); padding: .45rem 0; }
  table.stack td { border-bottom: 0; padding: .2rem 0; }
  table.stack td::before {
    content: attr(data-label);
    display: block;
    font-weight: 600;
    font-size: .92rem;
    color: var(--muted);
  }
}
/* The same stacked pattern, tuned twice for the six-column summary on /priser/.
   (1) It runs one breakpoint further out. Measured with tools/check-overflow.cjs, that
   table's min-content width is 812px — six columns, and the widest single word in them is
   the host "webministeriet.dk" — so below 54em (864px, i.e. 812 plus the two 16px gutters)
   the ordinary table shape puts a sideways scrollbar on /priser/: measured scrollWidth 702
   at a 640px viewport before the Kilde column was allowed its full width, 812 after. Only
   this one table is named, so the underdatabehandler table keeps the 40em step T-DKBIG.2
   chose for it.
   (2) Its label sits INLINE with the value instead of on its own line. The
   databehandleraftale cells are 85-character paragraphs, where a label on its own line
   costs nothing; these cells are two to eight words, and a block label doubled the stacked
   summary's height — measured 2,645px at 390, against 1,4xx with the label inline. Same
   mechanism (data-label -> ::before), same information, half the scrolling. */
@media (max-width: 53.99em) {
  table.cmp-summary, table.cmp-summary tbody, table.cmp-summary tr, table.cmp-summary td { display: block; width: auto; }
  table.cmp-summary tr:first-child { display: none; }
  /* The caption has to be told to be a block too. Left as display: table-caption inside a
     display: block table it gets an anonymous table box of its own and shrinks to
     min-content: measured, the intro line rendered as a 90px-wide ribbon of single words
     470px tall at 390. The underdatabehandler table has no caption, so the pattern had
     never met this. */
  table.cmp-summary caption { display: block; }
  table.cmp-summary tr { border-bottom: 1px solid var(--line); padding: .5rem 0; }
  table.cmp-summary td { border-bottom: 0; padding: .12rem 0; }
  table.cmp-summary td::before {
    content: attr(data-label);
    display: inline;
    padding-right: .4rem;
    font-weight: 600;
    font-size: .92rem;
    color: var(--muted);
  }
  /* the row's own name leads the block, so a stacked row still reads as one thing. It
     keeps its "Udbyder" label like every other cell — a labelled row means all six. */
  table.cmp-summary td:first-child { padding-bottom: .15rem; }
}

[hidden] { display: none !important; }

/* footer */
.site-footer { border-top: 1px solid var(--line); background: var(--bg-soft); padding: 1.5rem 0 2rem; color: var(--muted); font-size: .95rem; }
.site-footer nav { display: flex; flex-wrap: wrap; gap: .3rem 1rem; }
.site-footer a { color: var(--text); }

@media (min-width: 40em) {
  .prices { grid-template-columns: 1fr 1fr 1fr; }
  .plans { grid-template-columns: 1fr 1fr; }
  .cmp-row { display: grid; grid-template-columns: 12rem 1fr; column-gap: 1.25rem; align-items: start; }
  .cmp-row h3 { margin: 0; }
}

/* The gutter grows with the viewport. The base 16px declaration in :root stays exactly as
   it is — it is what a 360 phone gets, and a guard pins it — and these are overrides on
   top of it. Breakpoints are in em because the marketing guard fails the stylesheet on any
   width, min-width or max-width above 328px. */
@media (min-width: 64em) { :root { --gutter: 1.5rem; } }
@media (min-width: 100em) { :root { --gutter: 2rem; } }

/* /priser/ — with room to spare, the category label column can hold its longer headings on
   one line instead of two. */
@media (min-width: 75em) {
  .cmp-row { grid-template-columns: 15rem 1fr; column-gap: 2rem; padding: 1.1rem 1.4rem; }
}

/* /eksempler/ — the three example sites, three-up once there is room for three legible
   1280-wide screenshots side by side. There is no wrapper element around the three
   <section>s to turn into a grid, and the page is generated markup this task does not own,
   so <main> itself becomes the grid and everything that is NOT one of the three example
   sections spans all three columns. The :has() test is "a section whose screenshot is a
   linked <img class="shot"> directly in a paragraph", which is exactly the shape of those
   three sections and not of the two-shot comparison figure above them.
   row-gap stays 0 so the existing margins keep the page's vertical rhythm; the only fix
   needed is the first heading of each section, whose 2rem top margin no longer collapses
   out of its section once the section is a grid item. */
@media (min-width: 64em) {
  main.wrap:has(> section > p > a > img.shot) {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: clamp(1.25rem, 2vw, 2.5rem);
    row-gap: 0;
  }
  /* width:100% so a full-width row is the width of the grid, not shrink-to-fit around its
     own text; the prose cap above then trims it back from the right, keeping the same left
     edge as the three cards below. */
  main.wrap:has(> section > p > a > img.shot) > * { grid-column: 1 / -1; width: 100%; }
  main.wrap:has(> section > p > a > img.shot) > section > h2:first-child { margin-top: 0; }
  /* The three cards share one grid row, so they are all as tall as the tallest. Letting the
     description paragraph absorb that slack puts the three screenshots — identical 4:3
     files in identical columns, so identical heights — and the three buttons on the same
     two lines, instead of at three different heights set by how long the prose happens to
     be. */
  main.wrap:has(> section > p > a > img.shot) > section:has(> p > a > img.shot) {
    grid-column: auto;
    margin-bottom: 2rem;
    display: flex;
    flex-direction: column;
  }
  main.wrap:has(> section > p > a > img.shot) > section:has(> p > a > img.shot) > p:first-of-type {
    flex: 1 0 auto;
  }
}
