/* upgreat.css — additions on top of Source's screen.css.
 *
 * Loaded after screen.css in default.hbs, so it can lean on Source's
 * variables (--color-*, --font-*, --container-*) and override where the
 * marketing pages need something Source doesn't have.
 *
 * Deliberately hand-written and unbuilt: this theme has no gulp/postcss
 * step, because the site it serves lives about ten weeks. Editing this
 * file IS editing the stylesheet.
 *
 * Scope discipline: nothing here restyles Source's post or archive
 * templates. The article template is the one part of Source we keep
 * verbatim — it's the hardest thing to get right and it's already right.
 *
 * No new typeface and no new palette by design. The visual identity
 * arrives from Hartstikke in August and lands on the October site; this
 * one borrows Source's Inter/JetBrains Mono and adds hierarchy instead.
 */

:root {
    /* ~68 characters at the body size. The brief asks for a reading
       measure around 70; 68ch lands there for Inter's average advance. */
    --u-measure: 68ch;

    /* Section rhythm. Two steps, used unevenly on purpose — a page where
       every gap is identical reads as a template. */
    --u-section-y: clamp(4.5rem, 9vw, 9rem);
    --u-section-y-tight: clamp(3rem, 5vw, 4.5rem);

    /* Body text that has to stay legible. Source's --color-secondary-text
       (black at 55%) clears 4.5:1 on white but only just, so it's reserved
       for captions here; running prose gets this instead. */
    --u-ink-soft: rgb(0 0 0 / 0.68);

    --u-rule: 1px solid var(--color-border);
}

/* ─── Landing shell ──────────────────────────────────────────────────
   Every homepage section is a full-width band with a centred inner
   column, so a section can change its background without the content
   column moving. */

.u-landing {
    /* Source's .gh-main adds top padding meant for article headers. */
    padding-top: 0;
}

.u-band {
    padding-inline: var(--container-gap);
}

.u-band-inner {
    margin-inline: auto;
    max-width: var(--container-width);
    padding-block: var(--u-section-y);
}

.u-band + .u-band > .u-band-inner {
    border-top: var(--u-rule);
}

/* A band that carries its own background owns its separation; it must
   not also draw a rule. */
.u-band--inverted + .u-band > .u-band-inner,
.u-band--inverted > .u-band-inner {
    border-top: 0;
}

.u-band--inverted {
    background-color: var(--color-darker-gray);
    color: var(--color-white);
}

/* ─── Hero ───────────────────────────────────────────────────────────
   Two columns when there's a photograph to show, one when there isn't.
   That single rule is what keeps the imageless state looking chosen
   rather than unfinished: no empty rectangle, no placeholder, the type
   simply takes the width it needs.

   The headline, the excerpt and the page content are siblings in this
   box (see landing.hbs), so the imageless hero can float the headline
   left and let both hero paragraphs run down the right column. Floats
   rather than a grid on purpose: everything after the hero is editor
   content, and a grid would make every paragraph, card and heading on
   the page a grid item. A float leaves normal flow alone and only
   shortens the line boxes beside it. */

.u-hero-inner::after {
    content: "";
    display: block;
    clear: both;
}

@media (min-width: 900px) {
    .u-hero--with-figure .u-hero-inner {
        display: grid;
        /* Asymmetric on purpose: the words are the argument, the
           photograph is the evidence. */
        grid-template-columns: 1.15fr 1fr;
        column-gap: clamp(2.5rem, 5vw, 5rem);
        align-items: start;
    }

    /* A column can carry the photograph or the prose, not both, so with
       a photograph the content goes back under the whole hero. */
    .u-hero--with-figure .u-hero-inner > .gh-content {
        grid-column: 1 / -1;
        margin-top: clamp(2.4rem, 4vw, 3.6rem);
    }
}

/* Without a photograph the hero has a whole column of nothing to the
   right of it, which is exactly what "unfinished" looks like. So the
   imageless variant sets both hero paragraphs beside the headline
   instead: the same two-column rhythm, carried by type. Drop a still
   from the Ghent footage into the page's feature image and it
   reverts to headline-plus-evidence with no code change.

   The two paragraphs sit on the headline's LAST line, not its first.
   That is the whole point of the composition and the reason this is a
   grid rather than the float it used to be: floats align to the top of
   the line box they start in, and no amount of margin bottom-aligns a
   two-paragraph column against a headline whose height changes with the
   language (five lines in English, six in Dutch) and with the width.

   How the bottom alignment is done, since it is not obvious. The
   headline spans all three rows; the right column occupies rows two and
   three; row one is `1fr` and holds nothing. In a grid whose own height
   is decided by its content, a flexible row grows to absorb whatever a
   spanning item needs beyond the tracks it crosses — so row one becomes
   exactly the headline's overhang, and the right column is pushed to the
   headline's baseline. When the right column is the taller of the two,
   row one collapses to zero and they simply align at the top. */
@media (min-width: 1000px) {
    .u-hero:not(.u-hero--with-figure) .u-hero-inner {
        display: grid;
        /* Twelve columns rather than the two this composition needs,
           because the band has to hold two different splits: the hero's
           headline-and-evidence, and the loop figure beside its sentence
           further down (see the 1200px block below). Two named columns
           would serve the first and leave the second nowhere to sit.
           Everything that is not one of those five blocks spans all
           twelve and never has to know the grid is here. */
        grid-template-columns: repeat(12, 1fr);
        column-gap: 1.2%;
        grid-template-rows: 1fr auto auto;

        /* Dense, and it is load-bearing. The loop figure takes the right
           half of its row and the sentence explaining it takes the left,
           but the sentence comes AFTER the figure in the content — and
           sparse packing never moves the cursor backwards, so it would
           land on the row below and the pairing would be lost. Dense is
           safe here precisely because everything else spans the full
           twelve: a full-width block cannot backfill into a half-empty
           row, so nothing else can be reordered by it. */
        grid-auto-flow: row dense;
    }

    /* The clearfix is for the float this replaced. Left in place it would
       be a grid item of its own, and an empty fourth row. */
    .u-hero:not(.u-hero--with-figure) .u-hero-inner::after {
        display: none;
    }

    /* `display: contents` is what keeps the editor out of this. The
       content wrapper stops generating a box, so the blocks Sven writes
       become grid items directly — which is the only way the hero's
       second paragraph, an ordinary paragraph in the editor, can sit in
       the right-hand column while everything after it runs full width.
       Inherited type (font-size, letter-spacing) still reaches them,
       because inheritance does not care about display. */
    .u-hero:not(.u-hero--with-figure) .u-hero-inner > .gh-content {
        display: contents;
    }

    /* Full width is the default for every block on the page, named once
       here so that adding a section to the editor needs no CSS at all.
       Only the three pieces of the hero below opt out. */
    .u-hero:not(.u-hero--with-figure) .u-hero-inner > *,
    .u-hero:not(.u-hero--with-figure) .u-hero-inner > .gh-content > * {
        grid-column: 1 / -1;
    }

    .u-hero:not(.u-hero--with-figure) .u-hero-title {
        grid-column: 1 / 8;
        grid-row: 1 / 4;
        max-width: none;
    }

    .u-hero:not(.u-hero--with-figure) .u-hero-sub {
        grid-column: 9 / -1;
        grid-row: 2;
        margin-top: 0;
        max-width: none;
    }

    /* The hero's second paragraph — the first block in the editor.
       Written with the same `:not([class])` as the section rule further
       down, which it has to outrank rather than tie with: equal
       specificity would hand the argument to whichever rule is lower in
       the file. */
    .u-hero:not(.u-hero--with-figure) .u-hero-inner > .gh-content > p:not([class]):first-child {
        grid-column: 9 / -1;
        grid-row: 3;
        margin-top: 2rem;
        max-width: none;
    }

    /* First thing under the hero, and the block that has to clear the
       headline's column. Auto-placement already puts it on the fourth
       row for that reason; this is only the air above it. */
    .u-hero .gh-content > ul.u-pillars {
        margin-top: clamp(3rem, 5vw, 4.5rem);
    }
}

.u-hero-title {
    font-family: var(--gh-font-heading, var(--font-sans));
    font-size: clamp(3.2rem, 1.8rem + 4.4vw, 5.8rem);
    font-weight: 700;
    letter-spacing: -0.028em;
    line-height: 1.08;
    text-wrap: balance;
    max-width: 22ch;
}

.u-hero-sub {
    margin-top: clamp(2.4rem, 3vw, 3.2rem);
    max-width: 52ch;
    font-size: clamp(1.7rem, 1.55rem + 0.4vw, 1.9rem);
    line-height: 1.55;
    color: var(--u-ink-soft);
    text-wrap: pretty;
}

/* Pillars. Three claims, separated by rules rather than boxed into
   cards — a card here would imply three products. */
.u-pillars {
    margin-top: 0;
    display: grid;
    gap: 0;
    list-style: none;
    padding: 0;
}

.u-pillars > li {
    padding-block: 1.6rem;
    border-top: var(--u-rule);
    font-size: 1.6rem;
    line-height: 1.5;
    color: var(--u-ink-soft);
}

.u-pillars > li:last-child {
    border-bottom: var(--u-rule);
}

/* The first pillar's top rule doubles as the line that closes the hero's
   paragraph, so without a gap the prose sits on it. The pillars keep
   `margin-top: 0` for the case where they open the band on their own
   (the Dutch page today), and take the gap only when prose precedes
   them. Ghost inlines HTML-card markup as siblings of the editor's
   paragraphs, which is what makes the adjacency selector work at all. */
.gh-content > p + ul.u-pillars {
    margin-top: clamp(1.8rem, 3vw, 2.6rem);
}

/* Three claims, three columns, one rule across the top of each. Stacked
   they read as a list of features; side by side they read as the shape of
   the argument — and they use the width the imageless hero leaves over
   instead of trailing down the left edge. */
@media (min-width: 900px) {
    .u-pillars {
        grid-template-columns: repeat(3, 1fr);
        column-gap: clamp(2.4rem, 4vw, 4.8rem);
    }

    .u-pillars > li,
    .u-pillars > li:last-child {
        border-bottom: 0;
        padding-bottom: 0;
    }
}

/* Source puts 8px between consecutive list items, which is right for a
   bulleted list in an article and wrong for three columns: in a grid that
   margin is vertical offset, not vertical rhythm, so the second and third
   pillars — and the rules above them — sat 8px below the first. Written
   without the `+` so it also covers the stacked state, where the rules
   above each item are the separation and the margin only widened the
   first gap. */
.gh-content ul.u-pillars > li {
    margin-top: 0;
}

.u-pillars b,
.u-pillars strong {
    color: var(--color-darker-gray);
    font-weight: 600;
}

.u-actions {
    margin-top: clamp(2.4rem, 4vw, 3.2rem);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1.2rem 2.4rem;
}

.u-button {
    display: inline-flex;
    align-items: center;
    gap: 0.8rem;
    padding: 1.2rem 2.2rem;
    background-color: var(--color-darker-gray);
    color: var(--color-white);
    border-radius: 4px;
    font-size: 1.6rem;
    font-weight: 500;
    text-decoration: none;
    transition: opacity 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}

.u-button:hover {
    opacity: 0.82;
}

.u-link-arrow {
    font-size: 1.6rem;
    font-weight: 500;
    color: var(--color-darker-gray);
    text-decoration: none;
    border-bottom: 1px solid var(--color-dark-border);
    padding-bottom: 0.2rem;
    transition: border-color 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}

.u-link-arrow:hover {
    border-color: currentColor;
}

/* Trust bar. One quiet line; the claims it carries are checkable, which
   is the only reason it's allowed to be this small. */
.u-trustbar {
    margin-top: clamp(2.4rem, 4vw, 3.2rem);
    font-size: 1.4rem;
    line-height: 1.6;
    color: var(--color-secondary-text);
}

.u-figure {
    margin: 0;
}

.u-figure img {
    width: 100%;
    border-radius: 4px;
}

/* Captions are not decoration here. "Ghent, 48 GPUs in immersion
   cooling, July 2026." is what turns a photograph into evidence, so the
   caption gets real typographic treatment rather than being an
   afterthought. */
.u-figure figcaption {
    margin-top: 1.2rem;
    font-family: var(--font-mono);
    font-size: 1.3rem;
    line-height: 1.5;
    color: var(--color-secondary-text);
}

/* ─── Sections inside the editor content ─────────────────────────────
   Sections two through four of the homepage are authored in the Ghost
   editor, so their headings arrive as plain <h2> from ordinary heading
   cards — no classes, nothing for Sven to remember. The rule and the
   large top margin here are what turn a flat run of blocks into paced
   sections, which is the whole difference between "sober" and
   "unfinished" on a page this sparse. */

.u-sections .gh-content > h2 {
    margin-top: var(--u-section-y);
    padding-top: var(--u-section-y);
    border-top: var(--u-rule);
}

.u-sections .gh-content > h2:first-child {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
}

.u-sections .gh-content > h2,
.u-section-title {
    font-size: clamp(2.4rem, 1.9rem + 1.5vw, 3.4rem);
    font-weight: 650;
    letter-spacing: -0.02em;
    line-height: 1.15;
    text-wrap: balance;
    max-width: 30ch;
}

/* The paragraph directly under a section heading is its subtitle. */
.u-sections .gh-content > h2 + p {
    margin-top: 1.2rem;
    max-width: 54ch;
    font-size: 1.7rem;
    line-height: 1.55;
    color: var(--u-ink-soft);
}

/* Any other unclassed paragraph in a section is a closing line: bigger,
   because on this page those lines carry the argument.
   `:not([class])` matters — Ghost inlines HTML-card markup straight into
   .gh-content, so .u-trustbar, .u-actions and .u-loop are direct children
   too, and without the guard this rule would outrank their own styling on
   specificity alone. */
.u-sections .gh-content > p:not([class]) {
    margin-top: clamp(2.4rem, 4vw, 3.6rem);
    max-width: var(--u-measure);
    font-size: clamp(1.7rem, 1.6rem + 0.4vw, 2rem);
    line-height: 1.55;
    text-wrap: pretty;
}

/* The hero's second paragraph is page content — the first block in the
   editor — so it lands here rather than in the hero template. Stacked, it
   follows the excerpt directly and needs a gap of its own; from 1000px up
   it moves into the hero's right column and the rule above resets this. */
.u-sections .gh-content > p:not([class]):first-child {
    margin-top: clamp(3.2rem, 5vw, 5rem);
}

/* Blocks that arrive as HTML cards sit outside the h2 rhythm above, so
   they carry their own separation. */
.u-sections .gh-content > .u-teaser-block {
    margin-top: var(--u-section-y);
    padding-top: var(--u-section-y);
    border-top: var(--u-rule);
}

/* Unless a heading already opened the section, in which case the heading
   drew the rule and paid the padding — the block is that section's
   content, not a section of its own. */
.u-sections .gh-content > h2 + .u-teaser-block {
    margin-top: 1.2rem;
    padding-top: 0;
    border-top: 0;
}

/* Source styles links inside .gh-content with the accent colour, which
   is now near-black — the same near-black the primary button is filled
   with. Without this the button reads as an empty rectangle. */
.u-sections .gh-content a.u-button,
.gh-content a.u-button {
    color: var(--color-white);
    text-decoration: none;
}

.gh-content a.u-link-arrow {
    text-decoration: none;
}

/* Source indents lists. The pillars are separated by rules, not by
   markers, so the indent is just a misalignment against the headline
   above them. */
.gh-content ul.u-pillars {
    padding-left: 0;
    list-style: none;
}

.gh-content ul.u-pillars > li::marker {
    content: none;
}

/* ─── Section headings ───────────────────────────────────────────────
   No tracked-uppercase eyebrow above each section. The heading and its
   one-line subtitle carry the hierarchy. */

.u-section-title {
    font-size: clamp(2.4rem, 1.9rem + 1.5vw, 3.4rem);
    font-weight: 650;
    letter-spacing: -0.02em;
    line-height: 1.15;
    text-wrap: balance;
    max-width: 30ch;
}

.u-section-sub {
    margin-top: 1.2rem;
    max-width: 54ch;
    font-size: 1.7rem;
    line-height: 1.55;
    color: var(--u-ink-soft);
}

.u-section-note {
    margin-top: clamp(2.4rem, 4vw, 3.6rem);
    max-width: var(--u-measure);
    font-size: clamp(1.7rem, 1.6rem + 0.4vw, 2rem);
    line-height: 1.55;
    text-wrap: pretty;
}

/* ─── Facts: what's actually running ─────────────────────────────────
   Almost a table, because that's what it is. The numbers are the point,
   so they get size and tabular figures; everything around them stays
   small. No icons — an icon here would make it read as a brochure. */

.u-sites {
    margin-top: clamp(3rem, 5vw, 4rem);
    display: grid;
    gap: 0;
}

.u-site {
    display: grid;
    gap: 0.8rem 3.2rem;
    padding-block: clamp(2rem, 3vw, 2.8rem);
    border-top: var(--u-rule);
}

.u-site:last-of-type {
    border-bottom: var(--u-rule);
}

/* Tops, not baselines. A shared baseline is the correct alignment for two
   runs of text at similar sizes; at 46px against 17px it puts the whole
   figure above the line it belongs to, and every row reads as a number
   that floated up out of its own sentence. Aligning the boxes instead
   lands the cap heights within about a pixel of each other, because the
   figure's tight leading and the body's loose leading nearly cancel. */
@media (min-width: 720px) {
    .u-site {
        grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);
        align-items: start;
    }
}

.u-site-figure {
    font-size: clamp(3.2rem, 2.4rem + 2.4vw, 4.6rem);
    font-weight: 600;
    letter-spacing: -0.03em;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.u-site-figure span {
    display: block;
    margin-top: 0.6rem;
    font-size: 1.4rem;
    font-weight: 500;
    letter-spacing: 0;
    color: var(--color-secondary-text);
}

.u-site-body {
    font-size: 1.7rem;
    line-height: 1.6;
    color: var(--u-ink-soft);
    max-width: 56ch;
}

.u-site-body strong {
    display: block;
    margin-bottom: 0.4rem;
    color: var(--color-darker-gray);
    font-weight: 600;
}

/* A site that isn't running yet sits in the same table as the two that
   are, because the count is real and the plan is real. It reads quieter
   so nobody mistakes it for capacity they can rent today — the figure
   keeps its size, the row loses its full contrast. */
.u-site--planned {
    color: var(--color-secondary-text);
}

.u-site--planned .u-site-figure,
.u-site--planned .u-site-body,
.u-site--planned .u-site-body strong {
    color: inherit;
}

/* ─── The closed loop ────────────────────────────────────────────────
   Two states of one figure. Without JavaScript it is an ordered list of
   the four steps with the caption under it, which says the same thing in
   less space. With JavaScript, assets/js/loop.js draws the ring in WebGL
   and moves those same <li> elements over the canvas — the text is never
   duplicated, so it stays editable in Ghost and readable by a screen
   reader either way.

   Everything inside is sized in `em`, and loop.js sets the figure's
   font-size from its rendered width. One number scales the whole drawing
   instead of a dozen clamps drifting apart. */

.u-loop-figure {
    margin: clamp(2.8rem, 4vw, 3.6rem) 0 0;
    max-width: 60rem;
}

/* Wide enough, the drawing moves right and the sentence that explains it
   sits beside it instead of under it — the claim and the evidence for it
   on one line of sight.
   1200px and not the 1000px the hero uses: the figure is 48% of the
   column, and loop.js clamps its type at 11px, so below about a 500px
   figure the labels stop fitting inside their rings. At 1200px the
   figure is ~530px, with room to spare. Narrower than that, the drawing
   keeps the full column and the sentence goes back underneath. */
@media (min-width: 1200px) {
    .u-sections .gh-content > .u-loop-figure {
        float: right;
        width: 48%;
        max-width: none;
        margin-left: 4%;
    }

    /* Centred against a square whose height is 48% of this element's own
       containing block — which is what a percentage margin resolves
       against, so one number tracks the drawing at every width. */
    .u-sections .gh-content > .u-loop-figure + p {
        margin-top: calc(24% - 3rem);
        max-width: 42ch;
    }

    /* The same composition, said in grid terms, for the homepage — where
       the hero band above turned this content into grid items and a
       float on a grid item is ignored outright. Without these two rules
       the drawing goes full width and the sentence drops underneath it,
       which is the layout the float existed to prevent.

       Kept as an override rather than a replacement because the float
       above still runs on every OTHER page that uses .u-sections, and on
       this one below 1000px where the hero is not a grid at all. */
    .u-hero:not(.u-hero--with-figure) .u-hero-inner > .gh-content > .u-loop-figure {
        grid-column: 7 / -1;
        float: none;
        width: auto;
        margin-left: 0;
    }

    .u-hero:not(.u-hero--with-figure) .u-hero-inner > .gh-content > .u-loop-figure + p {
        grid-column: 1 / 6;
        /* Grid centres it against the drawing's row; the percentage
           margin the float needed would now be measured against the
           wrong box and simply push it down. */
        align-self: center;
        margin-top: 0;
        max-width: none;
    }
}

/* ── The no-JavaScript state, which is also the pre-upgrade state ── */

/* Scoped through .gh-content on purpose: Source styles `.gh-content ol`,
   which outranks a bare class, so an unscoped rule loses the indent
   argument. Same reason .u-pillars is written as `.gh-content ul.u-pillars`
   further up. */
.gh-content ol.u-loop-steps,
.u-loop-steps {
    margin: 0;
    padding: 0;
    list-style: none;
    counter-reset: u-loop;
    display: grid;
    gap: 1.2rem;
}

.u-loop-steps > li {
    counter-increment: u-loop;
    display: flex;
    gap: 1.2rem;
    align-items: baseline;
    font-size: 1.6rem;
    line-height: 1.4;
    color: var(--color-darker-gray);
}

.u-loop-steps > li::before {
    content: counter(u-loop);
    font-family: var(--font-mono);
    font-size: 1.3rem;
    color: var(--color-secondary-text);
}

.u-loop-caption {
    margin-top: 1.6rem;
    font-size: 1.5rem;
    line-height: 1.45;
}

.u-loop-caption strong {
    display: block;
    font-weight: 650;
    letter-spacing: -0.01em;
}

.u-loop-caption span {
    color: var(--u-ink-soft);
}

/* ── The upgraded state ──
   `is-live` is set by the script, so none of this applies until the
   canvas actually exists. */

.u-loop-figure.is-live {
    position: relative;
    /* The square the ring is drawn in. */
    aspect-ratio: 1;
}

.u-loop-stage {
    position: absolute;
    inset: 0;
}

.u-loop-stage canvas {
    display: block;
    width: 100%;
    height: 100%;
}

.is-live .u-loop-steps {
    display: block;
    margin: 0;
    gap: 0;
}

/* Positioned by the script at the projected centre of each ring. The
   width is capped so a long label wraps inside its circle rather than
   spilling across the drawing — 8.4em against a ring about 12em across. */
.is-live .u-loop-steps > li {
    position: absolute;
    display: block;
    width: 8.4em;
    margin: 0;
    transform: translate(-50%, -50%);
    text-align: center;
    font-size: 1em;
    font-weight: 600;
    line-height: 1.16;
    letter-spacing: -0.005em;
    text-transform: uppercase;
    text-wrap: balance;
    /* The canvas is behind; nothing here should eat a pointer event, or
       the parallax stops following the cursor over the labels. */
    pointer-events: none;
}

.is-live .u-loop-steps > li::before {
    display: block;
    margin-bottom: 0.35em;
    font-family: var(--font-sans);
    font-size: 0.86em;
    font-weight: 500;
    letter-spacing: 0;
    color: var(--color-darker-gray);
    text-transform: none;
}

/* Two of the four labels are long enough to need the smaller size the
   original drawing used for them. Set by the script rather than guessed
   at here — see loop.js. */
.is-live .u-loop-steps > li.is-long {
    font-size: 0.84em;
}

/* Wide enough for "ONE CLOSED LOOP" and its subtitle on one line each,
   and no wider: the inner edges of the two side rings are about 17.8em
   apart, so there is room to spare but not much. The Dutch title is
   longer and takes two lines, which is what the original drawing does
   too. */
.is-live .u-loop-caption {
    position: absolute;
    width: 14.4em;
    margin: 0;
    transform: translate(-50%, -50%);
    text-align: center;
    pointer-events: none;
}

.is-live .u-loop-caption strong {
    font-size: 1.2em;
    line-height: 1.1;
    text-transform: uppercase;
    text-wrap: balance;
}

.is-live .u-loop-caption span {
    display: block;
    margin-top: 0.4em;
    font-size: 0.92em;
    line-height: 1.3;
}

/* Kept for the mono step-row, which other pages may still use. */
.u-loop {
    margin-top: clamp(2.4rem, 4vw, 3.2rem);
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.8rem 1.4rem;
    font-family: var(--font-mono);
    font-size: 1.4rem;
    line-height: 1.6;
    color: var(--u-ink-soft);
}

.u-loop-step {
    white-space: nowrap;
}

.u-loop-arrow {
    color: var(--color-mid-gray);
}

/* ─── The audiences ──────────────────────────────────────────────────
   Equal weight, equal height, no visual hierarchy between them — the
   brief is explicit about that. Rules instead of boxes keeps them from
   reading as pricing tiers.

   Two of them until the August revision, three now. Two columns went to
   three rather than to a 2 + 1 arrangement, because a panel on a row of
   its own is a claim about importance, and the third audience is the one
   we are least ready for. */

.u-panels {
    margin-top: clamp(3rem, 5vw, 4rem);
    display: grid;
    gap: clamp(2.4rem, 4vw, 4rem);
}

/* Two columns from 800px, three only from 1100 — three columns of this
   copy at 800px leaves about 24 characters to the line, which is a
   newspaper column, not a panel. The odd panel out sits alone on the
   second row in between, and at that width it is the third one, which is
   the right one to have there. */
@media (min-width: 800px) {
    .u-panels {
        grid-template-columns: 1fr 1fr;
    }
}

/* Counted by the browser, not written down here, and that is deliberate.
   `repeat(3, 1fr)` would be correct only for as long as there are exactly
   three panels — and the panels live in the editor, while this file
   deploys separately. The two went out of step once already: the theme
   landed on a site whose content still had two panels, and they sat in
   two thirds of the band with an empty column beside them.

   auto-fit lays down as many tracks as fit and collapses the ones no
   panel claims, so two panels make two columns and three make three. The
   count can change in Ghost without anyone touching CSS, which is the
   arrangement the rest of this site already assumes. */
@media (min-width: 1100px) {
    .u-panels {
        grid-template-columns: repeat(auto-fit, minmax(24rem, 1fr));
        gap: clamp(2.4rem, 3vw, 3.2rem);
    }
}

.u-panel {
    padding-top: 2.4rem;
    border-top: 2px solid var(--color-darker-gray);
}

/* ─── The audience we are not ready for yet ──────────────────────────
   Regulated enterprises, until the certificates land. It is on the page
   because the conversation starts months before the contract does, and
   it is greyed because pretending otherwise is the failure mode this
   whole site is written against.

   Everything here is a step down rather than a different treatment: a
   hairline instead of the 2px rule, the heading in the soft ink the body
   already uses, no colour anywhere. The panel reads as the same object,
   one state earlier. It must not read as disabled — the CTA in it is
   live and we want it clicked. */
.u-panel--wip {
    border-top-color: var(--color-border);
    border-top-width: 1px;
}

.u-panel--wip h3 {
    color: var(--u-ink-soft);
}

/* The label the greying needs in order to mean something. Grey on its
   own is ambiguous — it could be a rendering accident, or a panel
   nobody finished writing. This says which. */
.u-wip {
    /* A block that shrinks to its text, rather than an inline-block. The
       pill sits above the heading and has to push it down, and vertical
       margins on an inline-level box do not do that. */
    display: block;
    width: fit-content;
    margin-bottom: 1.2rem;
    padding: 0.4rem 1.1rem;
    border: var(--u-rule);
    border-radius: 999px;
    font-size: 1.2rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    line-height: 1.2;
    color: var(--u-ink-soft);
    /* The pill is not a link and not a control; nothing about it should
       invite a click or break across two lines. */
    white-space: nowrap;
}

/* The closing sentence of the third panel: what "work in progress"
   actually means, in dates rather than adjectives. Set apart from the
   CTA above it so the two are not read as one instruction. */
.u-panel .u-panel-note {
    margin-top: 1.2rem;
    font-size: 1.5rem;
    color: var(--color-secondary-text);
}

.u-panel h3 {
    font-size: 1.9rem;
    font-weight: 650;
    letter-spacing: -0.01em;
    line-height: 1.3;
}

.u-panel p {
    margin-top: 1.2rem;
    font-size: 1.6rem;
    line-height: 1.65;
    color: var(--u-ink-soft);
    text-wrap: pretty;
}

/* The line each panel ends on: a link that names the next step, then the
   detail in plain text after it. Written as `.u-panel p.u-panel-cta` so it
   outranks the rule above rather than depending on source order.

   Deliberately not .u-link-arrow — that class drops the underline, and
   with a sentence continuing after the link there would be nothing left
   to show where the link stops. The arrow is inside the link text for the
   same reason. */
.u-panel p.u-panel-cta {
    margin-top: 1.6rem;
}

.u-panel-cta a {
    font-weight: 500;
    color: var(--color-darker-gray);
}

/* ─── Pricing teaser ─────────────────────────────────────────────────
   Small and quiet: it announces a page, it isn't one. */

.u-teaser .u-band-inner {
    padding-block: var(--u-section-y-tight);
}

.u-teaser-title {
    font-size: clamp(1.9rem, 1.8rem + 0.5vw, 2.2rem);
    font-weight: 600;
    letter-spacing: -0.015em;
    max-width: 34ch;
}

.u-teaser-body {
    margin-top: 1.2rem;
    max-width: var(--u-measure);
    font-size: 1.6rem;
    line-height: 1.65;
    color: var(--u-ink-soft);
}

.u-teaser .u-link-arrow {
    display: inline-block;
    margin-top: 1.6rem;
}

/* ─── Signup band ────────────────────────────────────────────────────
   The one conversion action on the site, and the only place a solid
   colour field appears. The inversion is what "clearly separated" means
   without adding ornament. */

.u-signup .u-band-inner {
    max-width: 68rem;
    text-align: left;
}

.u-signup-title {
    font-size: clamp(2.4rem, 2rem + 1.4vw, 3.2rem);
    font-weight: 650;
    letter-spacing: -0.02em;
    line-height: 1.15;
    text-wrap: balance;
}

.u-signup-sub {
    margin-top: 1.2rem;
    max-width: 52ch;
    font-size: 1.7rem;
    /* Light type on dark reads lighter, so it gets extra leading. */
    line-height: 1.65;
    color: rgb(255 255 255 / 0.72);
}

.u-form {
    margin-top: 2.8rem;
    display: grid;
    gap: 1.2rem;
}

@media (min-width: 640px) {
    .u-form {
        /* The choice field needs the room, not the email field: its
           longest option ("I'm evaluating for a regulated organisation")
           is three times the length of a placeholder address, and a
           native select truncates rather than wraps. */
        grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr) auto;
        align-items: stretch;
    }
}

.u-field {
    display: grid;
    gap: 0.6rem;
}

.u-field > label {
    font-size: 1.3rem;
    font-weight: 500;
    color: rgb(255 255 255 / 0.72);
}

.u-input,
.u-select {
    width: 100%;
    padding: 1.2rem 1.4rem;
    font-size: 1.6rem;
    color: var(--color-white);
    background-color: rgb(255 255 255 / 0.08);
    border: 1px solid rgb(255 255 255 / 0.24);
    border-radius: 4px;
}

/* Placeholder contrast is held to the same bar as body text. */
.u-input::placeholder {
    color: rgb(255 255 255 / 0.62);
}

.u-input:focus-visible,
.u-select:focus-visible,
.u-submit:focus-visible {
    outline: 2px solid var(--color-white);
    outline-offset: 2px;
}

.u-select {
    /* Native select on a dark field renders its options with the system
         palette; force the popup to stay readable. */
    color-scheme: dark;
}

.u-submit {
    align-self: end;
    padding: 1.2rem 2.2rem;
    font-size: 1.6rem;
    font-weight: 550;
    color: var(--color-darker-gray);
    background-color: var(--color-white);
    border: 0;
    border-radius: 4px;
    cursor: pointer;
    transition: opacity 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}

.u-submit:hover {
    opacity: 0.86;
}

.u-form-note {
    margin-top: 1.6rem;
    max-width: 56ch;
    font-size: 1.4rem;
    line-height: 1.6;
    color: rgb(255 255 255 / 0.72);
}

.u-form-feedback {
    margin-top: 1.2rem;
    font-size: 1.4rem;
    line-height: 1.6;
    min-height: 1lh;
}

/* Ghost's members script toggles these classes on the form element. */
.u-form-wrap .u-form-feedback[data-members-error]:not(:empty) {
    color: #ff9d9d;
}

.u-form-wrap.success .u-form-sent {
    display: block;
}

.u-form-sent {
    display: none;
    margin-top: 1.2rem;
    font-size: 1.5rem;
    line-height: 1.6;
    color: var(--color-white);
}

/* ─── Reading page (/pricing) ────────────────────────────────────────
   One column, generous leading, capped measure. It's an article that
   happens not to be in the feed.

   Left-anchored, not centred, and that distinction is the whole of this
   block. Until the August revision the inner column was `--u-measure`
   wide with `margin-inline: auto`, which put a 68-character column in
   the middle of a 1200-pixel container — every line of the page floating
   in the centre of the screen while the homepage next door starts hard
   against the left edge. Two pages, two different left margins, and the
   reader crossing between them sees the site shift under them.

   So the container is the same width as a homepage band, and the reading
   measure moves down onto the prose itself. Same 68 characters, same
   comfort, but they start where every other line on the site starts. The
   blocks that are not prose — the drivers, the tier table, the callout —
   take the full width, which is what makes them read as structure rather
   than as more paragraphs. */

.u-read {
    padding-inline: var(--container-gap);
}

.u-read-inner {
    margin-inline: auto;
    max-width: var(--container-width);
    padding-block: clamp(4rem, 7vw, 7rem);
}

.u-read-title {
    font-size: clamp(3rem, 2.2rem + 2.6vw, 4.4rem);
    font-weight: 700;
    letter-spacing: -0.026em;
    line-height: 1.1;
    text-wrap: balance;
}

.u-read-standfirst {
    margin-top: 2rem;
    font-size: clamp(1.8rem, 1.7rem + 0.5vw, 2.1rem);
    line-height: 1.6;
    color: var(--u-ink-soft);
    text-wrap: pretty;
}

/* Ghost renders page content into .gh-content; inside a reading page it
   gets tighter measure and more air between blocks than Source's
   default. */
.u-read .gh-content {
    margin-top: clamp(3rem, 5vw, 4.5rem);
    font-size: 1.8rem;
    line-height: 1.7;
}

/* Prose carries the measure now that the container no longer does. The
   `:not([class])` guard is the same one the homepage sections use: Ghost
   inlines HTML-card markup straight into .gh-content, so the drivers,
   the tier table and the callout are direct children here too, and they
   are meant to be full width. Anything Sven writes as an ordinary
   paragraph or list gets the 68 characters; anything that arrived as a
   card lays itself out. */
.u-read .gh-content > * {
    max-width: none;
}

.u-read .gh-content > p:not([class]),
.u-read .gh-content > ul:not([class]),
.u-read .gh-content > ol:not([class]),
.u-read .gh-content > h2,
.u-read .gh-content > h3 {
    max-width: var(--u-measure);
}

.u-read .gh-content h2 {
    margin-top: clamp(3.6rem, 6vw, 5.2rem);
    font-size: 2.2rem;
    font-weight: 650;
    letter-spacing: -0.015em;
    line-height: 1.3;
}

.u-read .gh-content h3 {
    margin-top: 3rem;
    font-size: 1.9rem;
    font-weight: 600;
}

.u-read .gh-content p,
.u-read .gh-content ul,
.u-read .gh-content ol {
    margin-top: 1.6rem;
    text-wrap: pretty;
}

/* The headline is part of the hero, not of the prose, so it keeps a
   display measure of its own rather than the reading one. */
.u-read-title {
    max-width: 18ch;
}

/* The line above the headline, not below it: on /pricing the excerpt is
   "How we think about pricing", which names the subject before the
   headline makes its argument. Below the headline it would be a
   standfirst and would have to carry new information; above it, it is a
   label and is allowed to repeat the nav item it came from. */
.u-read-eyebrow {
    margin-bottom: clamp(1.6rem, 2.5vw, 2.4rem);
    font-size: 1.6rem;
    font-weight: 600;
    letter-spacing: -0.005em;
    color: var(--color-secondary-text);
}

/* The hero's action row and trust line, which are the homepage's and are
   deliberately identical to it — the two pages open the same way, so the
   reader crossing between them meets one button, not two designs of one.
   Named here only to restore the air that `.u-read .gh-content p` would
   otherwise flatten to a paragraph gap. */
.u-read .gh-content > .u-actions {
    margin-top: clamp(2.8rem, 4vw, 3.6rem);
}

.u-read .gh-content > .u-trustbar {
    margin-top: clamp(2.8rem, 4vw, 3.6rem);
}

.u-read .gh-content hr {
    margin-block: clamp(3.2rem, 5vw, 4.4rem);
    border: 0;
    border-top: var(--u-rule);
}

/* ─── What drives the cost ───────────────────────────────────────────
   Four numbered drivers, each a rule, a number and a body. The number is
   the point: "four things you control" is a promise the layout has to
   keep, and a reader who can count them believes it in a way no
   paragraph achieves.

   Numbered by hand in the markup rather than by a counter, because these
   arrive as an HTML card from the editor and a counter would renumber
   silently if a block were ever moved. A wrong number that Sven can see
   in the editor beats a right one he cannot. */

.u-drivers {
    margin-top: clamp(3rem, 5vw, 4rem);
}

.u-driver {
    display: grid;
    gap: 0.8rem 2.4rem;
    padding-block: clamp(2.4rem, 4vw, 3.4rem);
    border-top: var(--u-rule);
}

@media (min-width: 720px) {
    .u-driver {
        grid-template-columns: 5.6rem minmax(0, 1fr);
    }

    .u-driver > .u-driver-body {
        grid-column: 2;
    }
}

.u-driver-num {
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1.6;
    color: var(--color-darker-gray);
    /* The four numbers are a column; proportional digits would make it a
       ragged one. */
    font-variant-numeric: tabular-nums;
}

/* Written long, as `.u-read .gh-content …`, and not as the shorter
   `.u-driver h3` it wants to be. The page's own `.u-read .gh-content h3`
   and `… p` rules are two class names deep and would otherwise win on
   specificity, putting prose margins back on a block that is not prose.
   Every rule from here to the callout is long for that one reason. */
.u-read .gh-content .u-driver h3 {
    margin-top: 0;
    font-size: 2rem;
    font-weight: 650;
    letter-spacing: -0.01em;
}

.u-read .gh-content .u-driver p {
    margin-top: 1.2rem;
    max-width: var(--u-measure);
    font-size: 1.65rem;
    line-height: 1.6;
    color: var(--u-ink-soft);
}

.u-read .gh-content .u-driver p + p {
    margin-top: 1.2rem;
}

/* The one block on /pricing that's allowed to look like a table: the
   three ways to buy the same GPU-hour. Authored as an HTML card, and
   since the August revision it lives inside the first driver — timing is
   what it is about, and it was never a subject of its own. */
.u-tiers {
    margin-top: 2.4rem;
    display: grid;
    gap: 0;
}

.u-tier {
    display: grid;
    gap: 0.4rem;
    padding-block: 1.8rem;
    border-top: var(--u-rule);
}

.u-tier:last-child {
    border-bottom: var(--u-rule);
}

@media (min-width: 640px) {
    .u-tier {
        grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
        gap: 0.4rem 2.4rem;
        align-items: baseline;
    }
}

/* One typeface across the whole table, which is a correction. The tier
   names were set in the mono face, back when this block was a standalone
   "three ways to buy" section and the mono read as a spec sheet. Inside a
   driver it stopped reading that way and started reading as a different
   kind of object — code, or a value copied from somewhere. Weight carries
   the name now, exactly as upgreat-drivers_1.html sets it. */
.u-tier-name {
    font-family: var(--gh-font-body, var(--font-sans));
    font-size: 1.55rem;
    font-weight: 700;
    letter-spacing: -0.005em;
    line-height: 1.4;
    color: var(--color-darker-gray);
}

/* The timing under the name — "run it now", "within six hours". It used
   to sit on the same line after a middot, which read as part of the
   name: "Flexible · run it within six hours" is one long label, and the
   three of them no longer scanned as a column of three choices. Dropped
   under the name it is what it always was, the condition attached to the
   choice above it. */
.u-tier-name small {
    display: block;
    margin-top: 0.3rem;
    font-family: var(--font-sans);
    font-size: 1.35rem;
    font-weight: 400;
    color: var(--color-secondary-text);
}

.u-tier-body {
    font-size: 1.6rem;
    line-height: 1.6;
    color: var(--u-ink-soft);
}

/* ─── The closing blocks, alternating ────────────────────────────────
   The three sections that close /pricing step left, right, left down the
   band instead of stacking on one edge. After four numbered drivers and
   a filled callout, three more left-aligned sections read as the page
   having run out of ideas; alternating them puts a rhythm under the last
   screenful and gives the eye a reason to keep going.

   Each block is one HTML card in the editor rather than loose headings
   and paragraphs, and that is the cost of this. Native content cannot be
   alternated: a section here is an h2 followed by some number of p's,
   with nothing in the markup that says where it ends, so no selector can
   catch "this heading and its paragraphs but not the next heading's".
   Wrapping is what makes a section addressable. Text inside them is
   edited normally; it is adding or removing a whole block that needs
   care, same as the panels on the homepage.

   Measure drops from the page's 68 characters to 62. A column that moves
   has to be visibly narrower than the band it moves inside, or the shift
   reads as a mistake rather than a decision. */

.u-read .gh-content > .u-alt {
    margin-top: clamp(3.6rem, 6vw, 5.2rem);
    max-width: 62ch;
}

/* Only once there is room to move into. Below this the blocks are as
   wide as the band and the offset would have nowhere to go — worse, a
   half-applied version reads as broken indentation. */
@media (min-width: 900px) {
    .u-read .gh-content > .u-alt--right {
        margin-left: auto;
    }
}

/* The heading opens its block, so the air belongs to the block above it
   rather than between the heading and the paragraphs it introduces. Both
   rules have to outrank `.u-read .gh-content h2` and `… p`, which are
   two classes deep. */
.u-read .gh-content .u-alt > h2 {
    margin-top: 0;
}

.u-read .gh-content .u-alt > p {
    margin-top: 1.6rem;
    max-width: none;
}

/* ─── Calculator callout ─────────────────────────────────────────────
   The one filled panel on a page made of hairlines, which is the whole
   reason it works — it is the only thing here that is not yet true, and
   it should look like an announcement rather than another driver.

   Same inversion as the signup band, and deliberately NOT a link: there
   is nothing to click yet. A dark panel that does nothing when pressed
   is a broken button; a dark panel with a "Coming soon" badge is a
   notice. The badge is what keeps it the second one. */

.u-calc {
    margin-top: clamp(3.2rem, 5vw, 4.4rem);
    /* Wider than the prose, narrower than the container. At the full
       1200 the panel is mostly empty field with a badge stranded at the
       far right, and the eye reads the gap as a missing element rather
       than as space. */
    max-width: 84rem;
    padding: clamp(2.4rem, 4vw, 3rem);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1.6rem 2.4rem;
    background-color: var(--color-darker-gray);
    color: var(--color-white);
    border-radius: 10px;
}

.u-read .gh-content .u-calc-title {
    margin-top: 0;
    font-size: 1.9rem;
    font-weight: 650;
    letter-spacing: -0.01em;
    line-height: 1.3;
}

.u-read .gh-content .u-calc-body {
    margin-top: 0.6rem;
    max-width: 52ch;
    font-size: 1.55rem;
    line-height: 1.55;
    /* Light on dark reads lighter; same value as the signup band. */
    color: rgb(255 255 255 / 0.72);
}

.u-calc-badge {
    flex: none;
    padding: 0.9rem 1.6rem;
    background-color: var(--color-white);
    color: var(--color-darker-gray);
    border-radius: 999px;
    font-size: 1.3rem;
    font-weight: 600;
    white-space: nowrap;
}

/* Any figure we can't prove yet is labelled as unproven, in the same
   treatment everywhere. Deel D5 of the brief owns the wording; this is
   just its shape. */
.u-pending {
    display: inline-block;
    margin-left: 0.8rem;
    padding: 0.2rem 0.8rem;
    font-family: var(--font-mono);
    font-size: 1.2rem;
    line-height: 1.6;
    color: var(--color-dark-gray);
    background-color: var(--color-lighter-gray);
    border-radius: 3px;
    vertical-align: 0.15em;
}

/* ─── Tables in articles ─────────────────────────────────────────────
   The writing that's planned has figure tables in it, so this has to
   work rather than nearly work.

   Source keeps table cells on one line and puts overflow-x on the table
   element — but a <table> is shrink-to-fit, so the overflow property
   never produces a scroll container and wide tables simply get cut off
   at the column edge. Making the table a block box gives the scrolling
   somewhere to happen. */

.gh-content table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* ─── Article index (/writing) ───────────────────────────────────────
   Source's post list does the work; this only adds the intro above it. */

.u-writing-intro {
    padding-inline: var(--container-gap);
}

.u-writing-intro-inner {
    margin-inline: auto;
    max-width: var(--container-width);
    padding-block: clamp(3.6rem, 6vw, 6rem);
}

.u-writing-intro .u-read-standfirst,
.u-writing-intro-body {
    max-width: var(--u-measure);
}

.u-writing-intro-body {
    margin-top: 2rem;
    font-size: 1.7rem;
    line-height: 1.65;
    color: var(--u-ink-soft);
}

.u-writing-intro-body p + p {
    margin-top: 1.4rem;
}

/* Source insets the feed by two of its sixteen columns, which is right
   for a publication front page and wrong here: every other element on
   the site — the intro directly above it, the signup band, the footer —
   starts at the container edge, so the inset reads as the list being
   slightly out of true rather than as a deliberate reading column. */
.u-feed .gh-container:not(.has-sidebar) :is(.gh-main, .gh-more) {
    grid-column: 1 / -1;
}

/* The feed's own top margin plus the intro's bottom padding is more air
   than the relationship needs; the list belongs to the introduction. */
.u-feed .gh-container {
    margin-top: 0;
}

/* The list has no closing space of its own, so the last article ran flush
   into the dark signup band and read as cut off rather than finished. */
.u-feed {
    padding-bottom: clamp(4rem, 7vw, 7rem);
}

/* The Dutch feed before its first Dutch article. It sits where the cards
   will, aligned with the introduction above it, and it is one line — an
   empty state that fills the space the content will occupy is just a
   second kind of emptiness. */
   Alignment is Source's own .gh-outer/.gh-inner pair in the template
   rather than a guess at the container variables here. */
.u-feed-empty {
    font-size: 1.7rem;
    line-height: 1.55;
    color: var(--u-ink-soft);
}

/* ─── Legal index ────────────────────────────────────────────────────
   Five documents and a line each. The documents themselves render through
   page.hbs and want no help; this page is a list, so the names carry the
   weight and the descriptions stay quiet under them. */

.u-legal-index > h3 {
    margin-top: 3.2rem;
    font-size: 1.9rem;
    font-weight: 650;
    letter-spacing: -0.01em;
}

.u-legal-index > h3 + p {
    margin-top: 0.6rem;
    max-width: var(--u-measure);
    color: var(--u-ink-soft);
}

/* The language link closing each line. Same size as the sentence it sits
   in, so it reads as an aside rather than as a second action. */
.u-legal-index > h3 + p a {
    white-space: nowrap;
}

/* ─── Thank-you page ─────────────────────────────────────────────────
   One screen, three lines. Nothing else belongs here. */

.u-confirm .u-read-inner {
    padding-block: clamp(6rem, 14vh, 12rem);
}

.u-confirm-title {
    font-size: clamp(2.6rem, 2.1rem + 1.8vw, 3.6rem);
    font-weight: 650;
    letter-spacing: -0.02em;
    line-height: 1.15;
    text-wrap: balance;
}

.u-confirm-body {
    margin-top: 1.6rem;
    font-size: 1.8rem;
    line-height: 1.65;
    color: var(--u-ink-soft);
    text-wrap: pretty;
}

/* ─── Footer additions ──────────────────────────────────────────────
   Two lines the brief specifies: the place line, and one sentence that
   names the plainness as a choice. Without the second line a sparse
   site reads as unfinished; with it, it reads as a position. */

/* Source ends its footer at the bar, so the bar carries all 120px of the
   closing space and nothing sits below it. Our two lines do, which left
   a gulf above them and the page stopping flush against the last word.
   The space moves to where it now belongs: a normal gap above the lines,
   real air beneath them. */
.gh-footer-bar {
    margin-bottom: 4.8rem;
}

.u-footer-lines {
    display: grid;
    gap: 0.8rem;
    padding-bottom: clamp(5.6rem, 9vh, 9.6rem);
    font-size: 1.4rem;
    line-height: 1.6;
    color: var(--color-secondary-text);
}

.u-footer-place {
    font-family: var(--font-mono);
}

.u-footer-plain {
    max-width: 62ch;
}

/* Language switch for the one footer line that exists in both. Ghost
   stamps `page-nl` on <body> for the Dutch homepage, which is the only
   Dutch surface on the site — /pricing and /writing stay English. */
.u-lang-nl {
    display: none;
}

body.page-nl .u-lang-en {
    display: none;
}

body.page-nl .u-lang-nl {
    display: block;
}

/* ─── Motion ─────────────────────────────────────────────────────────
   There is none beyond hover and focus transitions, and that is the
   point: a site whose argument is "we publish before we polish" should
   not animate itself on arrival. The transitions that do exist are
   short and ease out. */

@media (prefers-reduced-motion: reduce) {
    * {
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
    }
}
