/* ==========================================================================
   ag0osW3b — components

   The growth file. Every component here reads only semantic tokens, so each
   one works across all four presets in both modes without a preset-specific
   branch. There are no `[data-theme="..."]` selectors in this file, and there
   should never be one: if a component needs to differ per preset, the
   difference belongs in the token contract instead.

   When this file gets unwieldy, split it into components/*.css and add the
   link tags. The cascade layer keeps the order correct either way.
   ========================================================================== */

@layer components {
  /* ---- Header ------------------------------------------------------------*/
  /* Opaque, not translucent. A backdrop blur here contradicted three of the
     four presets outright (console is "zero blur", spec calls a shadow a lie
     about the material, terminal bans glow), and it cost a composited repaint
     on every scroll frame to produce an effect that was invisible behind a 90%
     opaque fill. The border-bottom is what separates the bar from the page. */
  .site-header {
    position: sticky;
    top: 0;
    z-index: 10;
    background: var(--bg);
    border-bottom: var(--border-width) solid var(--border);
  }

  .site-header__inner {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    min-height: 4rem;
    flex-wrap: wrap;
  }

  .brand {
    font-family: var(--font-display);
    font-weight: var(--weight-display);
    letter-spacing: var(--tracking-display);
    color: var(--text);
    margin-right: auto;
  }

  .brand:hover {
    color: var(--accent);
    text-decoration: none;
  }

  .nav {
    display: flex;
    gap: var(--space-4);
    flex-wrap: wrap;
  }

  /* padding-block lifts the hit area off the 24px floor of WCAG 2.2 SC 2.5.8,
     which the bare line box sat exactly on. The header already has a 4rem
     min-height, so this costs no layout. */
  .nav a {
    color: var(--text-muted);
    font-size: var(--text-sm);
    padding-block: var(--space-2);
  }

  .nav a:hover {
    color: var(--text);
    text-decoration: none;
  }

  /* Current page carries weight and a rule, not color alone. */
  .nav a.is-active {
    color: var(--text);
    font-weight: 650;
    box-shadow: inset 0 -2px 0 0 var(--accent);
  }

  /* ---- Theme controls ----------------------------------------------------*/
  /* Revealed only once <html> has the `js` class, which the pre-paint script
     adds. Without JavaScript these controls are absent rather than dead. */
  .theme-controls {
    display: none;
    align-items: center;
    gap: var(--space-3);
  }

  .js .theme-controls {
    display: flex;
  }

  .theme-switcher {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    padding: 3px;
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface);
  }

  .swatch {
    position: relative;
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    padding: 0;
    border: var(--border-width) solid transparent;
    border-radius: var(--radius-sm);
    background: none;
    cursor: pointer;
    color: var(--text);
  }

  .swatch:hover {
    border-color: var(--border-strong);
  }

  /* Selected state carries three signals, none of them hue: a border on the
     button, a ring around the badge, and the check glyph itself. Required by
     the accessibility contract. */
  .swatch[aria-checked="true"] {
    border-color: var(--text);
  }

  .swatch__chip {
    display: block;
    width: 1.2rem;
    height: 1.2rem;
    border-radius: calc(var(--radius-sm) - 1px);
    border: 1px solid color-mix(in oklab, var(--text) 25%, transparent);
    background: linear-gradient(135deg, var(--chip-1) 0 50%, var(--chip-2) 50% 100%);
  }

  /* The badge is drawn in --surface with a --text glyph and a --text ring: a
     pair the contrast test already gates across all eight surfaces, and a ring
     that separates it from any chip colour underneath.

     It used to be a bare glyph in --chip-2, centred on a chip whose lower half
     IS --chip-2 — so half of every tick sat on its own colour at 1:1 and the
     only thing rescuing it was a text-shadow halo, which is the treatment
     terminal's No Glow Rule bans. Moving it off the chip keeps the Paint Chip
     Rule intact: the swatch still shows both of the preset's colours. */
  .swatch[aria-checked="true"]::after {
    content: "✓";
    position: absolute;
    right: 0;
    bottom: 0;
    display: grid;
    place-items: center;
    width: 0.85rem;
    height: 0.85rem;
    border-radius: 50%;
    background: var(--surface);
    color: var(--text);
    box-shadow: 0 0 0 1px var(--text);
    font-size: 0.55rem;
    font-weight: 700;
    line-height: 1;
  }

  .swatch[data-preset="workshop"] .swatch__chip {
    --chip-1: var(--chip-workshop-1);
    --chip-2: var(--chip-workshop-2);
  }

  .swatch[data-preset="console"] .swatch__chip {
    --chip-1: var(--chip-console-1);
    --chip-2: var(--chip-console-2);
  }

  .swatch[data-preset="spec"] .swatch__chip {
    --chip-1: var(--chip-spec-1);
    --chip-2: var(--chip-spec-2);
  }

  .swatch[data-preset="terminal"] .swatch__chip {
    --chip-1: var(--chip-terminal-1);
    --chip-2: var(--chip-terminal-2);
  }

  .mode-toggle {
    display: inline-grid;
    place-items: center;
    width: 2.15rem;
    height: 2.15rem;
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface);
    color: var(--text);
    cursor: pointer;
    font-size: 0.95rem;
    line-height: 1;
  }

  .mode-toggle:hover {
    border-color: var(--border-strong);
  }

  /* ---- Buttons -----------------------------------------------------------*/
  .btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: 0.6rem 1.15rem;
    border: var(--border-width) solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: 650;
    line-height: 1.2;
    cursor: pointer;
  }

  .btn:hover {
    background: var(--surface-hover);
    color: var(--text);
    text-decoration: none;
  }

  /* Press feedback. Transform rather than a layout property, and it inherits
     the preset's easing, so console's press is a hard click and workshop's is
     a soft push. */
  .btn:active {
    transform: translateY(1px);
    transition-duration: var(--dur-fast);
  }

  .btn--primary {
    background: var(--accent-fill);
    border-color: var(--accent-fill);
    color: var(--accent-contrast);
  }

  .btn--primary:hover {
    background: var(--accent-fill-hover);
    border-color: var(--accent-fill-hover);
    color: var(--accent-contrast);
  }

  /* --border-strong, not --border. A ghost button's edge is the only thing
     identifying it as a control, and --border is a divider token that sits at
     1.4-2.0:1 in every preset. The transparent background is what makes this
     read lighter than .btn, not a fainter edge. (SC 1.4.11) */
  .btn--ghost {
    background: transparent;
    border-color: var(--border-strong);
  }

  .btn--sm {
    padding: 0.35rem 0.7rem;
    font-size: var(--text-xs);
  }

  /* ---- Hero --------------------------------------------------------------*/
  .hero {
    padding-block: var(--space-7) var(--space-6);
  }

  .hero__title {
    font-size: calc(var(--text-2xl) * var(--display-scale));
    max-width: calc(var(--hero-measure) * 1.5);
  }

  /* Terminal ends its hero on a blinking block cursor. Every other preset
     resolves --cursor-content to an empty string, so this generates nothing at
     all rather than an invisible box with spacing.

     --cursor-anim is `none` everywhere but terminal. An empty string still
     generates a pseudo-element, so hardcoding the animation here left three of
     the four presets running a perpetual opacity animation on a box that draws
     nothing, which never lets the compositor idle. */
  .hero__title::after {
    content: var(--cursor-content);
    color: var(--accent);
    animation: var(--cursor-anim);
  }

  @keyframes blink {
    50% {
      opacity: 0;
    }
  }

  /* Spec's delight: the hero is dimensioned.
     A drawing states its overall width under the object, ticked at both ends,
     with the figure sitting in a gap in the rule. That is all this is: two
     borders for the ticks, two background segments for the rule, and the
     label as content.

     It spans grid-column 2, so the dimension is the text column itself, and
     the grid lines spec paints behind it make the figure countable. Every
     other preset resolves --dimension-display to none and generates nothing.

     Only true at 65rem and up. Below that the container is narrower than
     twelve whole columns, so the count stops being an integer and a drawing
     that misstates its own dimensions is worse than one that omits them. */
  .hero::after {
    content: var(--dimension-label);
    display: none;
    grid-column: 2;
    height: 0.8rem;
    margin-top: var(--space-7);
    border-left: var(--border-width) solid var(--border-strong);
    border-right: var(--border-width) solid var(--border-strong);
    background-image:
      linear-gradient(var(--border-strong) 0 0),
      linear-gradient(var(--border-strong) 0 0);
    background-size: calc(50% - 5ch) 1px, calc(50% - 5ch) 1px;
    background-position: left center, right center;
    background-repeat: no-repeat;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-label);
    line-height: 0.8rem;
    text-align: center;
    color: var(--text-muted);
    /* Arrives after the last real child, the way an annotation is added once
       the drawing is done. .enter only reaches element children. */
    animation: rise var(--dur-enter) var(--ease) both;
    animation-delay: calc(var(--stagger) * 3);
  }

  @media (min-width: 65rem) {
    .hero::after {
      display: var(--dimension-display);
    }
  }

  /* ---- Content sections --------------------------------------------------
     Identical markup in every preset; the punctuation is entirely tokens.
     Workshop draws no rule and shows no marker, so its sections are separated
     by air alone. Console prints CH.01 over a panel seam, spec numbers them
     over a hairline, terminal prefixes a prompt above a dashed rule. */
  .section {
    counter-increment: section;
    padding-top: var(--space-4);
    border-top: var(--rule-width) var(--rule-style) var(--border);
    /* Named so console's tally can be driven by the section's own pass across
       the viewport rather than by the marker's, which is a few pixels tall and
       would flash rather than hold. Unsupported browsers drop the property. */
    view-timeline-name: --section-view;
  }

  .section__heading {
    font-size: var(--text-xl);
    margin-bottom: var(--space-4);
  }

  /* The marker lives on a real aria-hidden span rather than on the heading's
     own ::before. Generated content is part of the accessible name (accname
     1.2 §4.3.2), so as a pseudo-element it made a screen reader announce
     "CH.01 What I do" in console, "01 What I do" in spec and "$ What I do" in
     terminal: decorative ornament changing heading semantics based on a
     cosmetic preference. The span is rendered unconditionally in every preset,
     so the One Structure Rule still holds; workshop resolves the marker to an
     empty string and the display token to none, and it generates nothing. */
  .section__marker {
    display: var(--heading-marker-display);
    font-family: var(--font-mono);
    font-size: var(--heading-marker-size);
    font-weight: 400;
    letter-spacing: var(--tracking-label);
    text-transform: var(--case-label);
    color: var(--accent);
  }

  @media (max-width: 60rem) {
    .section__marker {
      margin-bottom: var(--space-2);
    }
  }

  .section__marker::before {
    content: var(--heading-marker);
  }

  /* Console's delight: the tally lamp.
     A lamp beside a channel number means the channel is live, so this one is
     lit while its section is on screen and dim once it has passed. The state
     is true rather than decorative, and it is the section's own position that
     drives it.

     Decorative in the accessibility sense (the marker is aria-hidden and the
     section is visible anyway), so hue alone carrying it is fine here.

     Three presets resolve --lamp-display to none, so nothing is generated and
     nothing animates. In console without scroll-driven animation support the
     lamp is simply lit: a panel indicator that does not track, rather than a
     dead one. */
  .section__marker::after {
    content: "";
    display: var(--lamp-display);
    width: 0.5em;
    height: 0.5em;
    margin-left: 0.6em;
    /* --accent-fill, not currentColor. The marker's own colour is the link
       accent, which is darkened in light mode so it can carry text; a lamp
       that inherited it read as a dim square rather than as something lit.
       --accent-fill is the signal role, and it is the same orange the one
       loud control on the panel already uses. */
    background: var(--accent-fill);
  }

  @supports (animation-timeline: view()) {
    .section__marker::after {
      animation-name: tally;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: var(--lamp-timeline);
    }
  }

  /* Held through the middle of the pass and dropped at both ends, so a section
     entering and a section leaving are both dim while the one being read is
     lit. `both` fill is what makes the off-screen ends resolve to the dim
     endpoints instead of the last value. */
  @keyframes tally {
    0%,
    100% {
      opacity: 0.25;
    }

    14%,
    86% {
      opacity: 1;
    }
  }

  /* A scroll-linked fade is still motion the visitor did not ask for. Killing
     the animation outright leaves the lamp at its static value, which is lit:
     the same fallback an unsupporting browser gets. */
  @media (prefers-reduced-motion: reduce) {
    .section__marker::after {
      animation: none;
    }
  }

  .link-more {
    display: inline-block;
    margin-top: var(--space-5);
    font-family: var(--font-display);
    font-weight: 650;
    font-size: var(--text-sm);
  }

  /* ---- The mark ----------------------------------------------------------
     Written as ==phrase== in any body text or in the tagline, and rendered as
     a real <mark>, so the emphasis is in the content rather than in a class
     somebody remembered to add. Every preset draws it with its own instrument
     and every one of them is contrast-tested against --text.

     box-decoration-break is what makes this survive a wrap: without it a
     phrase broken over two lines gets one background sized to the union of
     both fragments, which paints a block across the gap between the lines.

     The negative margin lets the mark overshoot the words slightly, because a
     swipe that stops exactly at the first and last glyph looks like a
     rectangle someone typed rather than a mark someone made. */
  mark {
    padding: 0.08em 0.26em;
    margin-inline: -0.12em;
    background-color: transparent;
    background-image: var(--mark-image);
    border-radius: var(--mark-radius);
    color: inherit;
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
  }

  /* ---- The margin note ---------------------------------------------------
     A person in the margin. The text is content (Section#note, or the
     hero_note setting), and the preset decides the instrument: workshop
     writes it by hand and tilts it off level, console sets it as a panel
     label, spec hangs it under a callout rule, terminal marks it as a
     comment.

     It sits in the rail, which in workshop was otherwise empty: air between
     sections is that preset's grammar, so the margin was the only place left
     where a person could show up. */
  .note {
    font-family: var(--font-hand);
    font-variation-settings: var(--hand-variation);
    font-size: var(--text-sm);
    line-height: 1.35;
    /* Deliberately not --tracking-label. A note is prose, and console tracks
       its labels at 0.09em, which in a 7.5rem margin costs about three
       characters a line. The case still follows the preset. */
    text-transform: var(--case-label);
    color: var(--text-muted);
    text-wrap: pretty;
    padding-top: var(--space-2);
    border-top: var(--note-rule);
    transform: rotate(var(--note-rotate));
  }

  .note::before {
    content: var(--note-prefix);
  }

  /* The leader: a stroke curving out of the note toward what it annotates.
     Two borders and a lopsided radius, which is a quarter of an ellipse and
     reads as drawn rather than plotted. Only workshop resolves --note-leader
     to anything; a panel label, a drawing callout and a terminal comment all
     point at the thing above them without needing a line. */
  .note::after {
    content: "";
    display: var(--note-leader);
    width: 2.6rem;
    height: 1rem;
    margin-top: var(--space-2);
    margin-left: auto;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    border-bottom-right-radius: 90% 75%;
    opacity: 0.55;
    transform: rotate(-7deg);
  }

  /* Folded: the rail is in the flow, so the note sits above what it annotates
     and the leader would point at the wrong thing. */
  @media (max-width: 60rem) {
    .note {
      transform: none;
      margin-bottom: var(--space-2);
    }

    .note::after {
      display: none;
    }
  }

  /* ---- Post list ---------------------------------------------------------
     A rule-separated list, not a card grid. Date sits beside the title on wide
     viewports and above it on narrow ones. */
  /* Spans both tracks when it sits inside a railed section, so each row's own
     rail lines up with the section's rather than being carved out of the text
     column. Ignored when the list is not inside a grid, as on /writing. */
  .post-list {
    list-style: none;
    padding: 0;
    display: grid;
    grid-column: 1 / -1;
  }

  /* The row carries .railed, so the date sits in the margin and every date on
     the page stacks into a scannable column instead of floating at the ragged
     right end of a title. */
  .post-list__item {
    row-gap: var(--space-1);
    padding-block: var(--space-4);
    border-bottom: var(--border-width) solid var(--border);
    min-width: 0;
  }

  .post-list__item:first-child {
    border-top: var(--border-width) solid var(--border);
  }

  /* Workshop's delight: the ink stroke.
     The underline is drawn rather than switched on. It grows from the left at
     the preset's own pace, and because the anchor flips back to the right at
     rest it retracts the way a pen lifts off the page instead of rewinding.

     background-size is a paint-only property, so nothing here is a layout
     animation. The three presets that resolve --ink-stroke to transparent
     paint nothing and keep the plain colour change they have now.

     justify-self is what makes it a stroke rather than a rule. The title is a
     grid item, so it is blockified and its box fills the whole text column:
     the underline ran the full 800px with the words sitting in the first
     third of it. Sizing the item to its content puts the stroke under the
     title, and it also stops the link's hit area from extending across empty
     column for every row in the list.

     The y offset is a fraction of the box rather than its bottom edge, since
     a title inherits the body's leading and the bottom of that line box sits
     well under the descenders. */
  .post-list__title {
    justify-self: start;
    max-width: 100%;
    font-family: var(--font-display);
    font-size: var(--text-lg);
    font-weight: var(--weight-display);
    letter-spacing: var(--tracking-display);
    color: var(--text);
    background-image: linear-gradient(var(--ink-stroke) 0 0);
    background-repeat: no-repeat;
    background-position-x: right;
    background-position-y: calc(100% - 0.38em);
    background-size: 0 1px;
    transition: background-size var(--dur) var(--ease);
  }

  .post-list__title:hover,
  .post-list__title:focus-visible {
    color: var(--accent);
    text-decoration: none;
    background-position-x: left;
    background-size: 100% 1px;
  }

  .post-list__date {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-label);
    color: var(--text-muted);
    white-space: nowrap;
  }

  .post-list__excerpt {
    max-width: 62ch;
    min-width: 0;
    overflow-wrap: break-word;
    color: var(--text-muted);
    font-size: var(--text-sm);
  }

  /* ---- Post --------------------------------------------------------------*/
  .post {
    padding-block: var(--space-7) var(--space-8);
  }

  .post__title {
    font-size: var(--text-2xl);
    max-width: 24ch;
  }

  /* Stacks down the rail rather than running across under the title, so the
     date and tags annotate the post from the margin instead of sitting between
     the reader and the first line of it. */
  .post__meta {
    display: grid;
    justify-items: var(--rail-align);
    gap: var(--space-2);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-label);
    color: var(--text-muted);
  }

  .tag {
    padding: 0.1rem 0.55rem;
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-lg);
  }

  /* Folded back into the flow: a horizontal row again, under the title. */
  @media (max-width: 60rem) {
    .post__meta {
      grid-auto-flow: column;
      justify-content: start;
      justify-items: start;
      gap: var(--space-3);
      margin-top: var(--space-4);
      margin-bottom: var(--space-5);
    }
  }

  .post__footer {
    margin-top: var(--space-8);
    padding-top: var(--space-5);
    border-top: var(--border-width) solid var(--border);
  }

  /* Terminal's delight: the prompt returns.
     Reaching the end of a post is the end of a session, so it comes back
     and waits for the next command. The prompt is --endmark-content and the block after it is
     the same --cursor-content this preset blinks in the hero, which the other
     three resolve to nothing.

     The element is rendered in every preset and collapses to display: none in
     three of them, so the markup stays identical across presets. It is
     aria-hidden in the view: a prompt is punctuation, not content. */
  .post__endmark {
    display: var(--endmark-display);
    margin-top: var(--space-5);
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    color: var(--accent);
  }

  .post__endmark::before {
    content: var(--endmark-content);
  }

  .post__endmark::after {
    content: var(--cursor-content);
    animation: var(--cursor-anim);
  }

  /* ---- Prose (rendered markdown) -----------------------------------------
     overflow-wrap matters more here than anywhere else: the renderer has
     autolink on, so any bare URL an author writes becomes a link, and a
     repository URL measured 522px inside a 310px column before this. */
  .prose {
    max-width: 68ch;
    overflow-wrap: break-word;
  }

  .prose > * + * {
    margin-top: var(--space-4);
  }

  .prose h2 {
    font-size: var(--text-xl);
    margin-top: var(--space-7);
  }

  .prose h3 {
    font-size: var(--text-lg);
    margin-top: var(--space-6);
  }

  .prose a,
  .site-footer__cta a {
    text-decoration: underline;
    text-underline-offset: 0.18em;
  }

  .prose ul,
  .prose ol {
    padding-left: 1.35rem;
  }

  .prose li + li {
    margin-top: var(--space-2);
  }

  .prose blockquote {
    margin-block: var(--space-5);
    padding: var(--space-3) var(--space-5);
    background: var(--bg-subtle);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius);
    color: var(--text-muted);
    font-style: italic;
  }

  .prose code {
    font-size: 0.88em;
    background: var(--bg-subtle);
    border: var(--border-width) solid var(--border);
    padding: 0.1em 0.35em;
    border-radius: var(--radius-sm);
  }

  .prose img {
    border-radius: var(--radius);
  }

  /* Cells wrap, so column count alone is safe; one unbreakable token (an env
     var name, a long identifier) is what bursts the column. The wrapper is
     added by MarkdownRenderer and is focusable so the scroll region is
     reachable from the keyboard. */
  .prose .table-scroll {
    overflow-x: auto;
    max-width: 100%;
  }

  .prose .table-scroll:focus-visible {
    outline: 2px solid var(--focus);
    outline-offset: 2px;
  }

  .prose table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
  }

  .prose th,
  .prose td {
    padding: var(--space-2) var(--space-3);
    border: var(--border-width) solid var(--border);
    text-align: left;
  }

  .prose th {
    background: var(--bg-subtle);
  }

  /* Wide content scrolls inside its own box; the page body never does. */
  .prose pre.highlight {
    background: var(--bg-subtle);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius);
    padding: var(--space-4);
    overflow-x: auto;
    line-height: 1.55;
  }

  .prose pre.highlight code {
    background: none;
    border: 0;
    padding: 0;
    font-size: var(--text-sm);
  }

  /* ---- Syntax highlight (Rouge token classes) ----------------------------
     Lightness comes from --hl-l, which flips with mode; hue and chroma come
     from the preset. That is why these seven rules serve all eight surfaces. */
  .highlight .c, .highlight .c1, .highlight .cm, .highlight .cd {
    color: var(--hl-comment);
    font-style: italic;
  }

  .highlight .k, .highlight .kd, .highlight .kc, .highlight .kn, .highlight .kp, .highlight .kr {
    color: var(--hl-keyword);
  }

  .highlight .s, .highlight .s1, .highlight .s2, .highlight .sb, .highlight .sc,
  .highlight .sd, .highlight .se, .highlight .sh, .highlight .si, .highlight .sx, .highlight .dl {
    color: var(--hl-string);
  }

  .highlight .mi, .highlight .mf, .highlight .mh, .highlight .mo, .highlight .il {
    color: var(--hl-number);
  }

  .highlight .nf, .highlight .nc, .highlight .nn, .highlight .nd,
  .highlight .vi, .highlight .vg, .highlight .vc {
    color: var(--hl-name);
  }

  .highlight .nb, .highlight .bp, .highlight .no {
    color: var(--hl-builtin);
  }

  .highlight .o, .highlight .ow, .highlight .p {
    color: var(--hl-punct);
  }

  /* ---- Flash -------------------------------------------------------------
     The glyph is what keeps notice and alert distinguishable without relying
     on hue. Tokenized: these used to be four hardcoded colors that broke in
     six of the eight surfaces. */
  .flash {
    display: flex;
    gap: var(--space-3);
    max-width: var(--maxw);
    margin: var(--space-4) auto 0;
    padding: var(--space-3) var(--space-4);
    border: var(--border-width) solid;
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
  }

  .flash::before {
    font-weight: 700;
    line-height: inherit;
  }

  .flash--notice {
    background: var(--accent-subtle);
    border-color: var(--accent);
    color: var(--text);
  }

  .flash--notice::before {
    content: "✓";
    color: var(--accent);
  }

  .flash--alert {
    background: var(--danger-bg);
    border-color: var(--danger-border);
    color: var(--text);
  }

  .flash--alert::before {
    content: "!";
    color: var(--danger);
  }

  /* ---- Contact -----------------------------------------------------------*/
  .contact-links {
    list-style: none;
    padding: 0;
    display: flex;
    gap: var(--space-3);
    flex-wrap: wrap;
  }

  /* ---- Footer ------------------------------------------------------------*/
  .site-footer {
    margin-top: var(--space-9);
    padding-block: var(--space-7);
    border-top: var(--border-width) solid var(--border);
    background: var(--bg-subtle);
  }

  .site-footer__inner {
    display: grid;
    gap: var(--space-3);
  }

  .site-footer__cta {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    font-weight: var(--weight-display);
    letter-spacing: var(--tracking-display);
    max-width: 34ch;
  }

  .social {
    display: flex;
    gap: var(--space-5);
    flex-wrap: wrap;
    font-size: var(--text-sm);
  }

  .language-controls {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-5);
  }

  .language-controls button {
    padding: var(--space-2) 0;
    background: none;
    border: 0;
    border-radius: 0;
    font: inherit;
    font-size: var(--text-sm);
    color: var(--text-muted);
    cursor: pointer;
  }

  .language-controls button:hover {
    color: var(--text);
  }

  .language-controls [aria-pressed="true"] {
    color: var(--text);
    font-weight: 650;
    box-shadow: inset 0 -2px 0 0 var(--accent);
  }

  .site-footer__legal {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--text-muted);
  }

  /* ---- Forms -------------------------------------------------------------*/
  .form {
    display: grid;
    gap: var(--space-4);
    max-width: 42rem;
  }

  .field {
    display: grid;
    gap: var(--space-1);
  }

  .field > span {
    font-family: var(--font-display);
    font-weight: 650;
    font-size: var(--text-sm);
  }

  .field input,
  .field textarea,
  .field select {
    font: inherit;
    font-size: var(--text-base);
    color: var(--text);
    background: var(--surface);
    border: var(--border-width) solid var(--border-strong);
    border-radius: var(--radius-sm);
    padding: 0.55rem 0.7rem;
  }

  .field input::placeholder,
  .field textarea::placeholder {
    color: var(--text-faint);
  }

  .field textarea.mono {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    line-height: 1.55;
  }

  .form-actions {
    display: flex;
    gap: var(--space-3);
    align-items: center;
  }

  /* Stays inside the normal content column rather than centering itself, so
     the form lines up with the brand and everything else on the site. */
  .auth {
    padding-block: var(--space-8);
  }

  .auth h1 {
    font-size: var(--text-xl);
    margin-bottom: var(--space-6);
  }

  .auth .form {
    max-width: 26rem;
  }

  @media (max-width: 40rem) {
    .site-header__inner {
      padding-block: var(--space-3);
    }

    .brand {
      margin-right: 0;
      flex: 1 0 100%;
    }

    .nav {
      gap: var(--space-3);
      margin-right: auto;
    }
  }
}
