/* The design system. One stylesheet, plain CSS, no build step, no network.
   Every page in this project loads THIS file first and then at most one small
   page-specific sheet (metrics.css, review.css, demo.css).

   Five rules govern everything below and they are rules rather than taste.

   1. NOTHING IS FETCHED. No CDN, no web font, no remote icon, no `@import` of
      anything that is not in this directory. This is a data-protection
      demonstration for German public administration; a page that phoned a
      third party to look pretty would contradict the product on the product's
      own screen. The type stack is the operating system's, which is also why
      it costs zero bytes and cannot fail to load. The icon set is inline SVG
      in `ui/templates/_icons.html`, drawn on a 24-unit grid in `currentColor`.

   2. CONTRAST IS A FLOOR, NOT A PREFERENCE. Every token pair this file
      actually ships is measured, and the measurements are written down in
      `docs/accessibility-selfcheck.md`: body text at or above 4.5:1, and
      anything that identifies a control - an input border, a button, the focus
      ring - at or above 3:1. A colour is never the only carrier of a meaning;
      where a tone appears, words appear with it.

      The part-16 palette makes that rule visible in the token names. The brand
      sky blue `--brand` measures 2.36:1 on white and therefore MAY NOT CARRY
      TEXT: it is a fill, a rule and a decorative surface, and everything
      textual in the blue family uses `--brand-ink` (6.51:1 on white) or
      `--brand-ink-strong` (8.92:1). The same split exists in the red family
      for the same arithmetic reason - see `--alarm` and `--alarm-text` - and
      in the caution orange part 17 added for the demo ribbon, `--caution` and
      `--caution-text`. All three element colours are held to it by one regex
      over every stylesheet rather than by anybody remembering.

   3. FOCUS IS RESTYLED, NEVER REMOVED. No stylesheet in this project contains
      the rule that switches a focus outline off, and a test asserts that over
      every file in this directory rather than over a list somebody maintains.

   4. DESKTOP-FIRST BY BREAKPOINT, NEVER BY USER AGENT. Nothing here reads a
      user-agent string; the layout is a single column that GAINS columns at
      48rem, 64rem and 80rem. The 320 CSS px discipline is unchanged and still
      tested: wide content scrolls INSIDE its own `.scroll-x` box, the
      two-column definition list drops to one column below 40rem, and no length
      in this file is a fixed pixel value.

   5. NOTHING MOVES ON ITS OWN EXCEPT THE HERO. Part 18 adds micro-transitions
      - a card that lifts under the pointer, a row that tints, a disclosure
      whose marker turns - and every one of them is a response to an input.
      The single self-starting animation in this project is the landing hero,
      which has its own pause control (WCAG 2.2.2) and its own reduced-motion
      still frame in `demo.css`. The blanket query at the bottom of this file
      collapses every duration to nothing for a reader who asked for less.

   THE PART-18 VISUAL SYSTEM, IN ONE PARAGRAPH. Parts 15 to 17 produced a page
   that was correct and read as generic: one shadow on everything, one card
   shape repeated down the page, a type ramp with no display size, a brand
   colour that appeared only as a 3px rule, and a footer that was four grey
   paragraphs. What follows is the same content with a hierarchy: a fluid
   display scale, three elevation levels, a page-top wash and a dark closing
   band so the page has a beginning and an end, cards that are objects rather
   than boxes, numbers that are rendered as numbers, and states that are
   rendered as designed badges. No token changed hue. Five surfaces are new and
   every one of them is in the measured table.

   Layout model, in one paragraph: `body` is full width and paints the canvas,
   the sticky site header spans it, the closing band spans it, and the header's
   inner row, `main` and the footer's inner row share one centred container of
   `--measure`. Every `main > section` is a card on that canvas. */

/* ------------------------------------------------------------------ tokens */

:root {
  color-scheme: light;

  /* Palette (part 16, extended in part 18). The sky blue of the reference, an
     ink that is nearly black, light surfaces, one dark band and two status
     families. Ratios against white in the comments; the full matrix, including
     the tinted surfaces and the band, is in the accessibility self-check. */
  --brand: #4db2ec; /* 2.36:1 - FILLS AND RULES ONLY, never text */
  --brand-ink: #106393; /* 6.51:1 - links and text in the brand family */
  --brand-ink-strong: #0c4e73; /* 8.92:1 - hover, buttons, the focus ring */

  --ink: #222222; /* 15.91:1 */
  --ink-soft: #3f4a52; /* 9.08:1 */
  --muted: #57646d; /* 6.09:1 */

  --ok: #10683c; /* 6.85:1 */
  /* The red family is RESERVED for warning, refusal and alarm and is never
     decorative. Two members, because #dc0000 measures 4.32:1 on the sunken
     surface: it draws rules and edges (a 3:1 floor, comfortably met) and
     `--alarm-text` carries any small text in the family (8.03:1 or better). */
  --alarm: #dc0000; /* 5.19:1 on white, 4.32:1 on the darkest surface */
  --alarm-text: #8f1010; /* 9.34:1 */
  --warn: #7a4a06; /* 7.47:1 */
  /* CAUTION, WHICH IS NOT ALARM. A standing notice that this instance is a
     demonstration is a thing to be aware of, not a thing that went wrong, and
     the red family said "error" to every reader who met it. Same two-member
     split as the red for the same arithmetic reason: `--caution` measures
     4.36:1 on its own tint and 4.24:1 on the canvas, so it draws the ribbon's
     edge and `--caution-text` (7.70:1 on the tint) carries the words. */
  --caution: #b45a00; /* 4.77:1 on white, 4.36:1 on --tint-caution */
  --caution-text: #7a3d00; /* 8.42:1 on white, 7.70:1 on --tint-caution */
  /* THE THIRD FAMILY (part 21), and the fourth element colour. The caseworker
     ground below is a warm amber world, and amber has the same arithmetic the
     sky blue has: `--amber` measures 3.83:1 on white and 3.06:1 on the palest
     amber surface, so it clears the 3:1 an edge needs and CANNOT reach the
     4.5:1 a word needs. Yellow therefore carries surfaces and edges and the
     amber-browns carry every letter - the identical split as `--brand` /
     `--brand-ink` and `--alarm` / `--alarm-text`, held by the same regex over
     every stylesheet rather than by anybody remembering. Declared here, where
     the palette is declared; used by `.ground-casework`. */
  --amber: #b07700; /* 3.83:1 on white - EDGES AND FILLS ONLY, never text */
  --amber-ink: #8a4f0a; /* 6.56:1 on white, 5.24:1 on the deepest amber */
  --amber-ink-strong: #5f3705; /* 10.32:1 on white - hover, buttons, the ring */

  --surface: #ffffff;
  --surface-alt: #f5f7f9;
  --surface-sunken: #eaeaea;
  --canvas: #eef2f5;
  /* PART 18. The top of the page is a shade bluer than the bottom, which is
     what gives a scrolled page a direction without adding an element to any
     template. `--ink` measures 13.50:1 on it and `--muted` 5.17:1, so the wash
     is safe under every piece of text that can reach it. */
  --canvas-top: #e2eef8;

  --tint-brand: #e4f2fb;
  --tint-ok: #e8f3ec;
  --tint-alarm: #fbeaea;
  --tint-caution: #fff3e6; /* --ink measures 14.56:1 on it */
  --tint-sample: #eef2f8;
  /* PART 18. The palest member of the blue family, for large zones - the hero
     panel, a statistic tile, a hovered table row - where `--tint-brand` would
     read as a highlighted state rather than as a surface. Worst ink on it is
     `--muted` at 5.69:1. */
  --tint-brand-soft: #f2f8fd;

  /* PART 18: THE CLOSING BAND. The one dark surface in the project, and it
     exists because a page that ends by fading into its own background has no
     end. White measures 11.96:1 on it, the band's own ink 10.46:1 and its link
     colour 7.86:1; the sky blue finally gets to be visible as a rule at
     5.07:1, which is the first surface in this project where it can be. */
  --band: #0c3a56;
  --band-ink: #e8f1f7; /* 10.46:1 on --band */
  --band-link: #a9d8f2; /* 7.86:1 on --band */
  --band-line: rgba(255, 255, 255, 0.16);
  --band-fill: rgba(255, 255, 255, 0.08);

  /* Two border weights on purpose. `--line` separates things inside a card and
     is decorative; `--line-strong` (4.29:1 on white, 3.56:1 on the sunken
     surface) is what a control boundary is drawn with, because that one has a
     floor. */
  --line: #d8dee3;
  --line-strong: #6f7c85;
  --focus: #0c4e73;

  /* THE GRADIENTS, AND THE ONE RULE THEY OBEY. A gradient under white text is
     only as good as its lightest stop, so every gradient that carries a label
     runs between `--brand-ink` (white at 6.51:1) and `--brand-ink-strong`
     (8.92:1) and never touches `--brand`, which would put white text on 2.36:1
     at one end of the button. `--grad-mark` carries no text of its own; the
     glyph on it is drawn in white and measured against the same two stops. */
  --grad-cta: linear-gradient(135deg, #106393, #0c4e73);
  --grad-mark: linear-gradient(135deg, #106393, #0c4e73);
  /* Decorative only: a hairline accent, never a text bed. */
  --grad-rule: linear-gradient(90deg, #0c4e73, #4db2ec 50%, #0c4e73);
  --grad-panel: linear-gradient(160deg, #f2f8fd, #ffffff 62%);

  /* THE INK ON A FILL, WHICH IS NOT ALWAYS WHITE (part 21). Every component
     that paints `--grad-cta` or `--brand-ink-strong` under a label - the skip
     link, the menu control, a button, the call to action, the wordmark's
     glyph, a completed step - wrote `#ffffff` in place. On the machine ground
     the same components are a LIGHT blue fill, because a dark button on a dark
     card measures 2.30:1 against it and stops being visible as a control; the
     ink on that fill has to be dark. One token, so the inversion is a value
     rather than six overrides. `#ffffff` on the light gradient's palest stop is
     the 6.51:1 this palette has always shipped.
     THE BAND'S OWN WHITE IS NOT THIS TOKEN and stays a literal: the closing
     band is dark on all three grounds, so its wordmark and its focus ring are
     white everywhere and must not follow a ground that inverts. */
  --cta-ink: #ffffff;

  /* The sticky header's translucent fill, so the page visibly passes UNDER it.
     A token because it is a colour and every ground has its own. */
  --header-veil: rgba(255, 255, 255, 0.86);

  /* THE SYNTAX PALETTE, AND WHY IT IS FIVE TOKENS RATHER THAN FIVE RULES
     (part 23). The machine pages show a working copy - dotted paths, their
     values, and the sealed placeholders that replaced every identity value in
     them - and on the machine ground those blocks are coloured the way an
     editor colours them. That colouring is a GROUND'S BUSINESS, so it lives
     where a ground can re-point it, and here it resolves to the page's own ink:
     the citizen and caseworker grounds do not syntax-colour anything, and a
     machine block that ever rendered on one of them is plain monospaced text
     rather than a palette nobody measured against cream paper.

       --code-ink    an inline <code>: a rule id, a part id, a case id
       --code-key    the dotted path on the left of an `=`
       --code-value  what the machine holds on the right of it
       --code-punct  the `=` itself
       --code-seal   a [[PII|KIND|token]] placeholder, so the seal pops

     WHAT IS DELIBERATELY NOT COLOURED, on any ground: prose, tables, headings,
     the letter the counterparty receives, and the free text a visitor typed.
     Colour here means "this is machine text"; spending it on a paragraph would
     say the opposite of what the page is for.

     `--code-ink` IS `currentColor` AND NOT `--ink`, and the difference is a
     pixel diff that caught it. An inline `<code>` is the one member of this set
     that renders on every page in the project, and it renders inside whatever
     is around it - a `.muted` sentence, a table caption, a help line. Pointing
     it at `--ink` would have DARKENED a filename chip on the intake page and a
     configuration path on the caseworker overview, which is a light ground this
     part is not allowed to touch. On the `color` property `currentColor` is
     defined as `inherit`, so this is the byte those pages already shipped. */
  --code-ink: currentColor;
  --code-key: var(--ink);
  --code-value: var(--ink);
  --code-punct: var(--ink);
  --code-seal: var(--ink);

  /* THE TYPE SCALE. Two ramps: a fixed one in `rem` for everything that reads
     as text, and a fluid one for the three sizes that are the page speaking
     rather than the page explaining.

     WHY `clamp()` IS SAFE FOR 1.4.4 (resize text to 200 percent). A font size
     given in `vw` alone ignores the reader's font setting and fails the
     criterion. Every clamp below has a `rem` term inside its preferred value,
     so the middle of the ramp scales with the root size and the whole
     expression moves when a reader zooms text; the `vw` term only decides how
     much of the RANGE a given viewport gets. The minimum of every ramp is a
     size this project already shipped. */
  --text-xs: 0.8125rem;
  --text-sm: 0.9375rem;
  --text-base: 1rem;
  --text-md: 1.0625rem;
  --text-lg: 1.25rem;
  --text-xl: 1.5rem;
  --text-2xl: 1.875rem;
  --text-3xl: 2.375rem;
  /* Fluid: a page title, a hero headline, a statistic. */
  --text-title: clamp(1.875rem, 1.55rem + 1.05vw, 2.75rem);
  --text-hero: clamp(2.125rem, 1.5rem + 2.4vw, 3.75rem);
  --text-stat: clamp(1.75rem, 1.4rem + 1.1vw, 2.5rem);

  /* Spacing scale. One ladder, used everywhere, so vertical rhythm is a
     decision made once rather than per component. */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4.5rem;

  --radius-sm: 6px;
  --radius: 10px;
  --radius-lg: 14px;
  --radius-xl: 20px;
  /* A pill. Deliberately `em` rather than a large pixel value: nothing in this
     project may carry a fixed pixel length, and the reflow test enforces it. */
  --radius-pill: 62em;

  /* THREE ELEVATIONS, NOT ONE (part 18). A single shadow on the header, the
     cards and the popover meant nothing sat above anything. These are tinted
     with the brand's own blue rather than with grey, which is the difference
     between a shadow that looks printed and one that looks lit.

       sm  a card at rest, the header bar
       md  a card under the pointer, the hero panel, a sticky table head
       lg  something that floats over the page: the menu panel */
  --shadow-sm: 0 1px 2px rgba(12, 58, 86, 0.06), 0 1px 3px rgba(12, 58, 86, 0.05);
  --shadow-md: 0 2px 4px rgba(12, 58, 86, 0.06),
    0 10px 24px -8px rgba(12, 58, 86, 0.18);
  --shadow-lg: 0 4px 10px rgba(12, 58, 86, 0.08),
    0 22px 44px -14px rgba(12, 58, 86, 0.28);

  /* One duration and one curve, so every micro-transition in the project feels
     like the same hand. Collapsed to nothing by the reduced-motion query. */
  --ease: cubic-bezier(0.2, 0, 0.1, 1);
  --tempo: 140ms;

  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, "Noto Sans", sans-serif;
  --font-mono: ui-monospace, "Cascadia Mono", "Segoe UI Mono", "Consolas",
    "Liberation Mono", monospace;

  /* The desktop container. 80rem is the part-16 width; the pages breathe into
     real columns at 48rem, 64rem and 80rem rather than growing one long line. */
  --measure: 80rem;
}

/* ------------------------------------------------------ the three grounds */

/* PART 21: THE DEMONSTRATION HAS THREE PHASES AND THEY LOOK LIKE THREE WORLDS.
   The product already names them - `PHASES = ("antrag", "maschine",
   "sachbearbeitung")` in `api/demo.py`, and the step indicator counts a visitor
   through them - and until this part all three rendered on the same sky-blue
   canvas. What follows gives each phase its own ground.

   ONE DESIGN SYSTEM, NOT THREE STYLESHEETS. Every rule below this section is
   written once and reads its colours from tokens; a ground is a body class that
   re-points those tokens and nothing else. No component is duplicated, no page
   sheet is forked, and a rule added to a card is a rule on all three grounds by
   construction. The three grounds are:

     (default)          the citizen: `/`, `/demo/antrag`, `/demo/rundgang`,
                        `/hinweise`, `/inbox`. The part-18 sky-blue canvas,
                        UNCHANGED - `:root` above is this ground, which is why
                        a citizen page carries no class at all.
     .ground-machine    what the machine does inside: `/demo/case/{id}/pipeline`
                        and `/metrics`. Deep blue-slate in the brand's own hue.
     .ground-casework   the caseworker: `/review`, `/review/queue/*`,
                        `/review/case/*`. Warm amber paper.

   THE GROUNDS ARE PRODUCT STYLING, NOT A DEMO FEATURE. `/review*` and
   `/metrics` exist with the demo posture off, and the class on their `<body>`
   is unconditional: theming here is CSS plus a static class, never a byte that
   depends on a flag, so the flag-off byte-identity suite is untouched by it.

   EVERY PAIR ON A NEW GROUND WAS COMPUTED BEFORE IT SHIPPED, and the ratios in
   the comments below are those computations. Part 18 put a focus ring on the
   closing band at 1.34:1 - an indicator nobody could see, found by arithmetic
   and not by looking - and two new grounds are that failure mode multiplied by
   two. The full matrix is in `docs/accessibility-selfcheck.md`. Two pairs broke
   in exactly that way here and both are fixed at the bottom of this file, in
   "the components that a ground breaks": the current step's number, which is
   `--ink` on the sky-blue disc and inverts to 2.05:1 when `--ink` goes light,
   and the primary button, which is a dark gradient that measures 2.30:1
   against a dark card and had to become a light fill with dark ink.

   WHAT A GROUND MAY AND MAY NOT MOVE. A ground re-points the SURFACE ladder,
   the ink ladder, the phase family and the two gradients that carry them. It
   does NOT move the meaning of a semantic hue: green is the good state, red is
   the refusal and orange is the caution on all three grounds, and where those
   tokens change value it is only so a green stays green on a dark surface. The
   caution family's ONLY consumer outside the demo ribbon is `.badge-warn`, and
   the ribbon renders on `/` and `/hinweise`, which are citizen pages. */

/* THE MACHINE GROUND, RE-ANCHORED ON ONE DARK PRO (part 23).

   Part 21 built this ground out of the brand's own navy. It was correct and it
   was this project's invention; the pages it paints are the ones that show what
   the machine did inside, and the surface a reader already associates with
   "machine at work" is an editor. So the ladder is re-anchored on the One Dark
   Pro editor theme, whose neutrals are the chrome of the editor itself:

     #1b1d23  the peek/widget bed        -> --canvas       the page ground
     #21252b  the side bar and panels    -> --canvas-top    the wash
     #282c34  THE EDITOR BACKGROUND      -> --surface       cards, the header
     #2c313a  the hovered list row       -> --surface-alt   articles, notices, pre
     #31363f  a step above it            -> --surface-sunken  <code>, the token

   Nothing about the MECHANISM changes: the ladder is still the light one
   inverted, `--canvas` is still the darkest and `--surface-sunken` the
   lightest, and every component still follows the ground with no
   component-level override. What changed is the values, and every pair below
   was recomputed before it shipped.

   THE FOREGROUND FAMILY IS ANCHORED ON #abb2bf, One Dark's own editor
   foreground, which sits in the middle of the ladder rather than at the top of
   it: in a syntax-coloured buffer #abb2bf is the CALM colour - the one plain
   identifiers and punctuation get - so it is this ground's `--ink-soft` and the
   ladder steps up to a lighter member for headings and body and down to a
   dimmer one for labels.

   This ground is deliberately LOWER in contrast than the one it replaces
   (13.01:1 body text became 10.00:1) because One Dark is a low-glare theme and
   a page that reads like an editor should not shout. Worst text ratio: 4.78:1
   (`--muted` on `--surface-sunken`), against 4.5 - the tightest of the three
   grounds, next to casework's 4.99 and the citizen ground's 5.06 (the syntax
   palette's own dimmest member measures 4.61:1 on that surface, and only in a
   place it never renders). Worst element ratio: 3.22:1 (`--line-strong` on the
   same), against 3.0. */
.ground-machine {
  /* Native controls, the caret and the viewport's scrollbar follow this rather
     than a stylesheet, and the used value is taken from the ROOT element -
     which is why the rule under this block reaches for the root instead. */
  color-scheme: dark;

  --canvas: #1b1d23; /* --ink 12.03:1 */
  --canvas-top: #21252b; /* the wash, one step toward the editor; --ink 10.99:1 */
  --surface: #282c34; /* One Dark's editor bed: cards and the header; --ink 10.00:1 */
  --surface-alt: #2c313a; /* table heads, articles, notices, pre; --ink 9.33:1 */
  --surface-sunken: #31363f; /* <code>, the placeholder token; --ink 8.67:1 */

  --tint-brand: #2d3745; /* --brand-ink-strong 7.62:1 */
  --tint-brand-soft: #2a313c; /* a hovered row, a tinted zone; --ink 9.35:1 */
  --tint-ok: #2b3a31; /* --ok 5.94:1 */
  --tint-alarm: #3d2b2e; /* --alarm-text 6.18:1 */
  --tint-caution: #3d3324; /* --caution-text 7.16:1 */
  --tint-sample: #2a2f39; /* the calmest surface here too (ADR-025) */

  --ink: #d7dae0; /* One Dark Pro's bright foreground; 10.00:1 on --surface */
  --ink-soft: #abb2bf; /* One Dark's editor foreground, verbatim; 6.57:1 */
  --muted: #9ba3b2; /* 5.52:1, and 4.78:1 on the sunken surface */

  /* THE BLUE FAMILY IS ONE DARK'S BLUE. #61afef is the theme's function and
     link colour and it measures 5.93:1 on the editor bed - it would pass as
     text here - but the rule that `--brand` is a fill, a rule and an edge is
     STRUCTURAL rather than a matter of where it happens to measure well, and it
     holds on this ground as on the other two. What the family's text weights
     do is what they did before: on a dark surface the readable member of a blue
     family is a lighter blue. */
  --brand: #61afef; /* One Dark's blue; 5.92:1 on --surface - element only */
  --brand-ink: #79bbf2; /* 6.80:1 - links */
  --brand-ink-strong: #a6d3f7; /* 8.85:1 - hover, buttons, the ring */
  --focus: #a6d3f7; /* 7.68:1 at its worst, on --surface-sunken */

  /* The semantic families are One Dark's own accents, and they keep their
     meanings: the theme's green is the good state, its red is the refusal, its
     orange the caution. Two of them cannot carry a word and say so in the
     split this project has used since part 16 - #e06c75 measures 4.38:1 on the
     editor bed and 3.80:1 on the sunken one, so the red draws edges and the
     lifted #ef9a9f carries the sentence; the orange passes at 5.68:1 but keeps
     its text sibling, because putting `--caution` on text duty is forbidden
     everywhere by one regex and that rule does not bend per ground. One Dark's
     yellow #e5c07b is that sibling, and it is the family the theme itself
     would use. */
  --ok: #98c379; /* One Dark's green; 6.94:1 on --surface */
  --alarm: #e06c75; /* One Dark's red; 4.38:1 on --surface - edges only */
  --alarm-text: #ef9a9f; /* the same red lifted to carry words; 6.53:1 */
  --caution: #d19a66; /* One Dark's orange; 5.68:1 on --surface */
  --caution-text: #e5c07b; /* One Dark's yellow; 8.11:1 on --surface */
  --warn: #e5c07b; /* still reserved, still unused; 8.11:1 if it is ever used */

  --line: #3a404b; /* 1.34:1 on --surface - decorative, like its light twin */
  --line-strong: #7a8496; /* 3.71:1 on --surface, 3.22:1 at its worst */

  /* THE BUTTON HAD TO INVERT. `--grad-cta` runs #106393 to #0c4e73 on the light
     ground and measures 2.30:1 and 1.68:1 against a dark card - a control that
     is not distinguishable from its surroundings, which is exactly what 1.4.11
     forbids. There is no dark blue that fixes it: white text needs the fill
     dark and the dark card needs the fill light, and the two requirements have
     no overlap. So the fill goes light and `--cta-ink` goes dark with it. The
     ink is the ground's own darkest neutral, which is what an editor puts under
     a bright accent. */
  --cta-ink: #1b1d23; /* 10.65:1 and 8.18:1 on the two stops */
  --grad-cta: linear-gradient(135deg, #a6d3f7, #79bbf2);
  --grad-mark: linear-gradient(135deg, #a6d3f7, #79bbf2);
  /* Decorative, and brighter at the ends than the fill so the 3px masthead line
     is a line rather than a smudge: 4.28:1 on a card at its darkest stop. */
  --grad-rule: linear-gradient(90deg, #4a93d4, #61afef 50%, #4a93d4);
  --grad-panel: linear-gradient(160deg, #2c313a, #282c34 62%);

  --header-veil: rgba(40, 44, 52, 0.86);

  /* THE CLOSING BAND, INVERTED SO IT STILL READS AS A FOOTER. On a light page
     the band is the darkest thing on the screen (10.63:1 against the canvas);
     on a dark one the same navy stops being a band. It goes LIGHTER than the
     page instead - the same principle, which is that the closing band is the
     surface that differs most from the page it ends - and on this ground the
     lightest neutral One Dark paints is its selection colour. */
  --band: #3e4451; /* One Dark's selection; 1.73:1 against --canvas */
  --band-ink: #d7dae0; /* 6.97:1 on the band */
  --band-link: #a6d3f7; /* 6.18:1 on the band */

  /* A blue-tinted shadow is invisible on a blue-slate page. Black, and deeper,
     because on a dark ground elevation is carried by the shadow and by the
     surface step rather than by the shadow alone. */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.36), 0 1px 3px rgba(0, 0, 0, 0.3);
  --shadow-md: 0 2px 4px rgba(0, 0, 0, 0.36), 0 10px 24px -8px rgba(0, 0, 0, 0.6);
  --shadow-lg: 0 4px 10px rgba(0, 0, 0, 0.4), 0 22px 44px -14px rgba(0, 0, 0, 0.7);

  /* THE SYNTAX PALETTE. Five tokens, and they carry colour on THIS ground only
     - see the block where they are declared for what they are and what they
     deliberately are not. Every one is a One Dark accent, and the two that
     could not carry a word at their published value are lifted along their own
     hue until they can rather than being used below the floor. Measured on both
     beds a machine token can land on: `pre` is `--surface-alt`, an inline
     `<code>` is `--surface-sunken`. */
  --code-ink: #56b6c2; /* One Dark's cyan; 5.52:1 on --surface-alt, 5.12:1 sunken */
  --code-key: #e2858d; /* #e06c75 lifted; 4.96:1 on --surface-alt, 4.61:1 sunken */
  --code-value: #98c379; /* One Dark's green, verbatim; 6.48:1 / 6.02:1 */
  --code-punct: #9ba3b2; /* the calm foreground, as in the editor; 5.15:1 / 4.78:1 */
  --code-seal: #cd8ee3; /* #c678dd lifted; 5.33:1 on --surface-alt, 4.96:1 sunken */
}

/* The used `color-scheme` is propagated to the viewport from the ROOT element
   only, so a class on `<body>` cannot darken the document's scrollbar on its
   own. This reaches the root from the class that is actually stable in the page
   shell. An engine without `:has()` renders a light scrollbar beside a dark
   page and loses nothing else - the same bargain the invalid-field label makes
   with the same selector further down. */
html:has(> .ground-machine) {
  color-scheme: dark;
}

/* THE CASEWORK GROUND. Warm amber paper: a cream canvas, near-white cards, and
   a family of amber-browns for everything a caseworker reads. The split the
   brief demands is arithmetic and not taste - `--amber` cannot reach 4.5:1 on
   any surface on this ground (3.06:1 at its worst, 3.83:1 on white), so yellow
   draws the edges and the amber-browns carry every word.

   Worst text ratio on this ground: 4.99:1 (`--muted` on `--surface-sunken`),
   against 4.5. Worst element ratio: 3.06:1 (`--amber` on the same), against
   3.0. */
.ground-casework {
  --canvas: #f6ebd6; /* --ink 13.99:1 */
  --canvas-top: #fdf3dc; /* the wash, warmer at the top; --ink 14.97:1 */
  --surface: #fffdf9; /* cards and the header; --ink 16.27:1 */
  --surface-alt: #fbf4e5; /* table heads, articles, notices; --ink 15.09:1 */
  --surface-sunken: #f0e5cd; /* <code>, the language pill; --ink 13.22:1 */

  --tint-brand: #fbe8c1; /* --brand-ink-strong 8.56:1 */
  --tint-brand-soft: #fdf5e3; /* a hovered row, a tinted zone; --ink 15.22:1 */
  /* The sampled case keeps the calmest surface on the page (ADR-025). On this
     ground the calmest thing is a warm grey, not the blue one: a cool patch on
     warm paper reads as a highlight, which is the one thing a random draw must
     never look like. */
  --tint-sample: #f1efe7; /* --ink 14.72:1 */

  /* THE PHASE FAMILY IS THE AMBER FAMILY. Every structural token in the blue
     family points at its amber sibling, which is what makes a link, a legend,
     a badge, a button and a hover state change world together without a single
     component rule being written twice. The element/text split travels with
     them: `--brand` is the member that may not carry text on either ground. */
  --brand: var(--amber); /* #b07700 - 3.77:1 on --surface, element only */
  --brand-ink: var(--amber-ink); /* #8a4f0a - 6.45:1 on --surface */
  --brand-ink-strong: var(--amber-ink-strong); /* #5f3705 - 10.16:1 */
  --focus: var(--amber-ink-strong); /* 8.25:1 at its worst, on the sunken */

  /* Warm ink, because a neutral near-black on cream reads as a screenshot of a
     different site pasted onto this one. */
  --ink: #241e14; /* 16.27:1 on --surface */
  --ink-soft: #4a4032; /* 9.98:1 */
  --muted: #6b5f4c; /* 6.14:1, and 4.99:1 on the sunken surface */

  /* The semantic families are UNCHANGED here, tints included, and that is the
     point rather than an omission. This ground is light, so the light values
     still measure what they measured: `--ok` 6.74:1 on a card, `--alarm-text`
     9.19:1, `--caution-text` 8.28:1. A state that looked one way on the citizen
     pages looks the same way here, which is what "the semantic red keeps its
     meaning on all grounds" has to mean if it means anything. */

  --line: #e6d8bc; /* 1.39:1 on --surface - decorative */
  --line-strong: #7e7053; /* 4.77:1 on --surface, 3.88:1 at its worst */

  --grad-cta: linear-gradient(135deg, #8a4f0a, #5f3705);
  --grad-mark: linear-gradient(135deg, #8a4f0a, #5f3705);
  /* Decorative. The gold mid-stop measures 2.49:1 on a card, which is what the
     sky blue measures on white (2.36:1) in the same role. */
  --grad-rule: linear-gradient(90deg, #5f3705, #cf9a1f 50%, #5f3705);
  --grad-panel: linear-gradient(160deg, #fdf5e3, #fffdf9 62%);

  --header-veil: rgba(255, 253, 249, 0.86);

  /* The closing band, in this world's colour. A navy band under amber paper
     reads as a footer borrowed from another site; this is the same object -
     the darkest surface on the page, 11.55:1 against the canvas - in warm
     brown. Its link is an amber that WOULD fail on paper and passes here at
     9.21:1, which is the same licence `--band-link` has always had: the band
     is its own surface with its own measured ink, exactly as the light ground's
     #a9d8f2 is a sky blue that could not carry text on white either. */
  --band: #3d2a12; /* 11.55:1 against --canvas */
  --band-ink: #f6ecd8; /* 11.64:1 on the band */
  --band-link: #f3cf95; /* 9.21:1 on the band */

  /* Warm shadows, so a card's elevation is the same light source as its page. */
  --shadow-sm: 0 1px 2px rgba(61, 42, 18, 0.07), 0 1px 3px rgba(61, 42, 18, 0.06);
  --shadow-md: 0 2px 4px rgba(61, 42, 18, 0.08),
    0 10px 24px -8px rgba(61, 42, 18, 0.2);
  --shadow-lg: 0 4px 10px rgba(61, 42, 18, 0.1),
    0 22px 44px -14px rgba(61, 42, 18, 0.3);
}

/* -------------------------------------------------------------------- base */

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
  /* THE PAGE HAS A TOP. A single non-repeating wash from `--canvas-top` into
     `--canvas` over the first 40rem of the document, painted by `body` so no
     template gains an element for it. Below the wash the canvas is the flat
     colour it has always been. */
  background-color: var(--canvas);
  background-image: linear-gradient(180deg, var(--canvas-top), var(--canvas) 40rem);
  background-repeat: no-repeat;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  /* Raised from 1.55 with the width pass: a full-measure line needs more
     air between rows so a reader's eye does not drop one. */
  line-height: 1.65;
  /* A case id, a placeholder token or a URL must not be able to push the page
     wider than the viewport. Part of 1.4.10, and the cheapest part. */
  overflow-wrap: break-word;
  text-underline-offset: 0.15em;
  -webkit-font-smoothing: antialiased;
}

/* TIGHTER AS THEY GET BIGGER (part 18). A heading set at the same tracking as
   body text looks loose at 2.75rem and cramped at 1rem; the ramp below is the
   standard optical correction, applied per level rather than once. */
h1,
h2,
h3,
h4 {
  color: var(--ink);
  font-weight: 650;
  line-height: 1.2;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

h1 {
  margin: 0 0 var(--space-3);
  font-size: var(--text-2xl);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.022em;
}

h2 {
  margin: 0 0 var(--space-4);
  font-size: var(--text-xl);
  letter-spacing: -0.016em;
}

h3 {
  margin: var(--space-5) 0 var(--space-2);
  font-size: var(--text-md);
}

h4 {
  margin: var(--space-4) 0 var(--space-1);
  font-size: var(--text-base);
}

/* THE MEASURE IS THE CONTAINER, NOT A CHARACTER COUNT (part 17).

   Every flowing text element used to carry a `ch` cap - 68ch on a paragraph
   and a list item, 78ch on a notice, 72ch on a hero caption. Typographically
   that is the orthodox answer and on this layout it was the wrong one: the
   container is 80rem, a 68ch cap resolves to roughly 34rem, and the result was
   a column of text down the left half of a wide screen with white space to the
   right of everything. The user walked the site and read that as a broken
   layout rather than as a reading measure, which is the only verdict that
   matters for a page meant to be judged in a browser.

   So the measure is `--measure` on the container and nothing narrower inside
   it. The one typographic compensation is on `body`: 1.55 to 1.65, because a
   longer line needs more room between lines to keep a reader's eye from
   dropping a row.

   THE CAPS WERE MAX-WIDTHS, WHICH IS WHY REMOVING THEM CANNOT BREAK THE
   REFLOW. A block element is already as wide as its container; a `max-width`
   only ever made it narrower. Removing one can therefore widen an element up
   to the container and never beyond it, and at 320 px the container is the one
   that binds. Checked in a browser at 320 and 390 px all the same.

   What KEEPS its width: tables and everything in them, form fields, selects,
   textareas and `.help` (which belongs to a field and reads with it). */
p {
  margin: 0 0 var(--space-3);
}

ul,
ol {
  margin: 0 0 var(--space-3);
  padding-left: var(--space-5);
}

li {
  margin-bottom: var(--space-2);
}

a {
  color: var(--brand-ink);
  text-decoration-thickness: 1px;
  transition: color var(--tempo) var(--ease);
}

a:hover {
  color: var(--brand-ink-strong);
}

strong {
  font-weight: 650;
}

/* An inline `<code>` is an IDENTIFIER: a rule id, a part id, a case id, a
   configuration version. On the light grounds `--code-ink` is the page's own
   ink and this renders exactly as it always has; on the machine ground it is
   One Dark's cyan, which is what tells a reader at a glance that the string is
   the machine's name for something rather than a word somebody wrote. */
code {
  background: var(--surface-sunken);
  border-radius: var(--radius-sm);
  padding: 0.1em 0.35em;
  color: var(--code-ink);
  font-family: var(--font-mono);
  font-size: 0.9em;
}

pre {
  margin: 0 0 var(--space-3);
  padding: var(--space-4) var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-alt);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1.6;
  white-space: pre-wrap;
  word-wrap: break-word;
  overflow-x: auto;
}

pre code {
  background: none;
  padding: 0;
}

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: var(--space-5) 0;
}

/* One icon treatment for the whole set: it inherits colour and size from the
   text beside it, and it never sits alone. */
.icon {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  vertical-align: -0.18em;
}

/* --------------------------------------------------- skip link and focus */

/* The first focusable element on every page. Offscreen until focused, then a
   solid block at the top-left that no card and no sticky header can hide. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 40;
  padding: var(--space-3) var(--space-5);
  background: var(--brand-ink-strong);
  color: var(--cta-ink);
  font-weight: 650;
  border-radius: 0 0 var(--radius) 0;
  box-shadow: var(--shadow-lg);
  text-decoration: none;
}

.skip-link:focus {
  left: 0;
}

/* Restyled, never removed. 3px is the visible weight; the offset is what keeps
   it readable on top of a tinted card or a brand fill. */
:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* Offscreen but READ. Not display:none, which would take it out of the
   accessibility tree along with the layout. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ------------------------------------------------------- the site header */

/* A BAR, NOT A BORDER (part 18). Three changes and each one is doing a job.
   The 3px brand rule moved from the bottom edge to the top, where it reads as
   the page's own masthead line instead of as an underline under a white strip.
   The background is translucent with a blur where the engine has one, so the
   page visibly passes UNDER the header when it scrolls - which is what tells a
   reader the bar is fixed furniture rather than the first row of content. And
   the elevation is the smallest of the three, because a header that floats
   too high competes with the menu panel it opens. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background-color: var(--surface);
  background-image: var(--grad-rule);
  background-repeat: no-repeat;
  background-size: 100% 3px;
  background-position: top left;
  border-bottom: 1px solid var(--line);
  box-shadow: var(--shadow-sm);
}

/* The enhancement, guarded: engines without `backdrop-filter` keep the solid
   white above and lose nothing but the effect. */
@supports (backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px)) {
  .site-header {
    background-color: var(--header-veil);
    -webkit-backdrop-filter: saturate(1.7) blur(14px);
    backdrop-filter: saturate(1.7) blur(14px);
  }
}

.site-header-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3) var(--space-5);
  max-width: var(--measure);
  margin: 0 auto;
  padding: var(--space-3) var(--space-4);
}

/* The wordmark is a MARK and a NAME since part 18. The mark is a rounded tile
   carrying the brand gradient and one white glyph; the glyph is decorative and
   `aria-hidden`, because the name is right beside it in text. A product with
   no mark reads as a template with a title. */
.wordmark {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--ink);
  text-decoration: none;
}

.wordmark-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.35rem;
  height: 2.35rem;
  flex: none;
  border-radius: var(--radius);
  background: var(--grad-mark);
  color: var(--cta-ink);
  box-shadow: var(--shadow-sm);
}

.wordmark-mark .icon {
  width: 1.35rem;
  height: 1.35rem;
  vertical-align: 0;
}

.wordmark-text {
  display: block;
  min-width: 0;
}

.wordmark-name {
  display: block;
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.15;
}

.wordmark-sub {
  display: block;
  color: var(--muted);
  font-size: var(--text-xs);
  line-height: 1.35;
}

a.wordmark:hover .wordmark-name {
  color: var(--brand-ink-strong);
}

.site-tools {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

/* The language toggle: an icon that repeats nothing, and two names. */
.lang-switch {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--muted);
}

.lang-switch ul {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: var(--text-sm);
}

.lang-switch li {
  margin: 0;
}

.lang-switch a,
.lang-current {
  display: inline-block;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
}

.lang-switch a {
  color: var(--brand-ink);
  font-weight: 600;
  text-decoration: none;
  transition: background-color var(--tempo) var(--ease),
    color var(--tempo) var(--ease);
}

.lang-switch a:hover {
  background: var(--tint-brand);
  color: var(--brand-ink-strong);
}

.lang-current {
  background: var(--surface-sunken);
  color: var(--ink);
  font-weight: 700;
}

/* The menu. A native disclosure: no script, no focus trap, no ARIA to keep in
   step with a state we would otherwise have to own. */
.menu {
  position: relative;
}

.menu-summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--grad-cta);
  color: var(--cta-ink);
  font-size: var(--text-sm);
  font-weight: 650;
  cursor: pointer;
  list-style: none;
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--tempo) var(--ease),
    transform var(--tempo) var(--ease);
}

.menu-summary::-webkit-details-marker {
  display: none;
}

.menu-summary:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}

/* The disclosure's own glyph turns when it opens: the cheapest possible way to
   say "this is the thing that is open". */
.menu-summary .icon {
  transition: transform var(--tempo) var(--ease);
}

.menu[open] > .menu-summary .icon {
  transform: rotate(90deg);
}

.menu-panel {
  position: absolute;
  right: 0;
  top: calc(100% + var(--space-2));
  z-index: 30;
  min-width: 18rem;
  padding: var(--space-2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}

.menu-panel ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.menu-panel li {
  margin: 0;
  max-width: none;
}

.menu-panel a {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
  transition: background-color var(--tempo) var(--ease),
    color var(--tempo) var(--ease);
}

.menu-panel a:hover {
  background: var(--tint-brand);
  color: var(--brand-ink-strong);
}

.menu-panel .icon {
  color: var(--brand-ink);
}

/* ------------------------------------------------------- the demo ribbon */

/* One line, on the two pages that are ABOUT this being a demonstration.

   PART 18 NARROWED WHERE IT RENDERS AND CHANGED NOTHING IT SAYS. From part 11
   to part 17 the notice sat on every page of a demo instance; the user's
   direction was that it belongs on the landing page and on the page it links,
   and nowhere else. The partial, its `id`, its translation keys and its gate
   are all untouched - `demo_base.html` exposes a `ribbon` block and exactly
   two templates fill it. Every other page still reaches the full notice
   through the menu, and the honesty text in those pages' own bodies and
   footers is unchanged.

   CAUTION ORANGE, NOT ALARM RED, since part 17. The red family means warning,
   refusal and alarm everywhere else in this project, and readers correctly
   applied that meaning here too: a red bar across the top of every page reads
   as "something has gone wrong", when what it says is "this is a demonstration
   and the data is synthetic". That is a thing to be aware of, permanently and
   calmly. */
.demo-ribbon {
  background: var(--tint-caution);
  border-bottom: 2px solid var(--caution);
  color: var(--ink);
  font-size: var(--text-sm);
}

.demo-ribbon p {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  max-width: var(--measure);
  margin: 0 auto;
  padding: var(--space-3) var(--space-4);
}

.demo-ribbon .icon {
  color: var(--caution-text);
}

.demo-ribbon strong {
  font-weight: 700;
  letter-spacing: 0.01em;
}

.demo-ribbon a {
  color: var(--caution-text);
  font-weight: 650;
  text-underline-offset: 0.2em;
}

/* ------------------------------------------------------ the page furniture */

main {
  display: block;
  max-width: var(--measure);
  margin: 0 auto;
  padding: var(--space-6) var(--space-4) var(--space-7);
}

/* The page's own title block: heading, the step strip or the unit picker, and
   on the landing page the hero. A band rather than a card, so the first thing
   under the header reads as part of the page rather than as its first item. */
.page-head {
  margin-bottom: var(--space-6);
}

.page-head > h1 {
  font-size: var(--text-title);
  margin-bottom: var(--space-4);
}

.page-head > p {
  color: var(--ink-soft);
  font-size: var(--text-md);
}

/* The lead paragraph: one size up, so the first thing a visitor reads is
   visibly the first thing. In the design system since part 21 - it was in
   `demo.css`, and `/metrics` and `/inbox` ask for it without loading that file.
   Neither page LOOKED wrong, because both put their lead inside `.page-head`
   and the rule above already gives it this size and this ink; the class they
   asked for simply did not exist. `demo.css` carries the longer note. */
.lead {
  font-size: var(--text-md);
  color: var(--ink-soft);
}

.language-note {
  border-left-color: var(--brand);
  background: var(--tint-brand);
}

/* Cards. Every section of every page, without a single template class: the
   markup was already `main > section`, which is what a card wants to be.

   PART 18 MADE THEM OBJECTS. Bigger radius, real padding, an elevation that
   separates them from the canvas, and a heading that closes with a rule so a
   section has a head and a body instead of being a heading followed by text. */
main > section {
  margin-bottom: var(--space-5);
  padding: var(--space-6);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

main > section > *:last-child {
  margin-bottom: 0;
}

/* A ZONE RATHER THAN ANOTHER CARD (part 18). The single most generic thing
   about the old pages was that every section was the same white rectangle, so
   a reader scrolling had no way to tell a list of links from a statement of
   principle. A tinted section is recessed instead of raised - tint, no
   elevation - which reads as a band the page passes through rather than as
   another object sitting on it. Used sparingly and by hand: it is a class a
   template asks for, not a rule that alternates automatically, because "every
   second one" is a pattern and not a meaning. */
main > section.section-tinted {
  background: var(--tint-brand-soft);
  box-shadow: none;
}

/* Cards inside a tinted zone go back to white, so the object/ground relation
   survives the change of ground. */
main > section.section-tinted .card-grid > .card {
  background: var(--surface);
}

/* The section head. The `::before` is an accent tick, drawn in the brand
   gradient and carrying no text - it is `content: ""`, so it adds nothing to
   any accessible name. */
main > section > h2 {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  margin: 0 0 var(--space-5);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--line);
}

main > section > h2::before {
  content: "";
  flex: none;
  align-self: center;
  width: 0.3rem;
  height: 1.35rem;
  border-radius: var(--radius-pill);
  background: var(--grad-cta);
}

/* A bare `div` under `main` is the case view's `aria-live` region and nothing
   else. Deliberately NOT a card: it is present on every render so a screen
   reader has something to announce into, and an empty card would be a white
   box saying nothing. */
main > div:not(.page-head) {
  margin-bottom: var(--space-4);
}

main > div:not(.page-head) > *:last-child {
  margin-bottom: 0;
}

/* A grid of small cards. The desktop pass: one column below 48rem, two to
   48rem, three above 64rem - by breakpoint, never by user agent. */
.card-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
  list-style: none;
  margin: 0 0 var(--space-4);
  padding: 0;
}

/* A CARD IS AN OBJECT (part 18). It used to be a tinted panel with a 4px brand
   stripe down its left edge - the shape every administrative template on the
   internet has. Now it is a white surface with its own elevation, a hairline
   accent along the top, and a lift under the pointer that says it is a thing
   rather than a paragraph. The lift is a `transform`, so it composites and
   never reflows, and the blanket reduced-motion query removes it. */
.card-grid > .card {
  position: relative;
  overflow: hidden;
  margin: 0;
  max-width: none;
  padding: var(--space-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--tempo) var(--ease),
    transform var(--tempo) var(--ease), border-color var(--tempo) var(--ease);
}

.card-grid > .card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--grad-rule);
}

.card-grid > .card:hover {
  border-color: var(--brand);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}

/* A `card-grid-links` grid holds single-destination cards: the heading
   carries the one anchor and the whole box is its click target - the anchor's
   `::after` stretches over the card, which already has `position: relative`
   for its hairline. The hover lift above promised a clickable object; this is
   what makes the promise true anywhere on the box. The focus ring stays on
   the heading text, drawn by the same rule as every other link's. */
.card-grid-links > .card h3 a::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* A card that contains a link is reached by keyboard through that link, so the
   whole object answers the link's focus rather than only the word. */
.card-grid > .card:focus-within {
  border-color: var(--brand);
  box-shadow: var(--shadow-md);
}

.card > *:last-child {
  margin-bottom: 0;
}

.card h3,
.card h4 {
  margin-top: 0;
}

.card-grid > .card h3,
.card-grid > .card h4 {
  font-size: var(--text-md);
  letter-spacing: -0.012em;
}

.card-grid > .card h4 a,
.card-grid > .card h3 a {
  text-decoration: none;
}

.card-grid > .card h4 a:hover,
.card-grid > .card h3 a:hover {
  text-decoration: underline;
}

/* THE ONE CARD IN A GRID THAT IS AN ACTION (part 23). A grid of identical
   white cards says every destination on it is worth the same, and on the
   landing page one of them is not: four are places to look at and the first
   one is the thing the page is asking a visitor to DO. So it is painted with
   the fill the buttons are painted with and inverts everything a card carries
   - the title, the body, the hairline along its top edge, its own border.

   No new colour. `--grad-cta` and `--cta-ink` are the pair part 21 built for
   exactly this - a fill and the ink that goes on it, which is white on the two
   light grounds and dark on the machine ground - so the card follows a ground
   without a second rule, and the arithmetic is the arithmetic already shipped
   on every button: white on the gradient's pale stop is 6.51:1 and on its deep
   stop 8.92:1. The border goes to `--brand-ink-strong` (8.92:1 against the
   white section it sits in) and to the ink under the pointer.

   THE FOCUS RING HAD TO BE OVERRIDDEN AND THAT IS THE PART WORTH READING.
   `--focus` is #0c4e73 on the citizen ground and measures 1.37:1 against this
   card's own fill - the ring drawn at `outline-offset: 2px` lands ON the blue,
   so a keyboard reader tabbing into the one call to action would have seen
   nothing at all. Same failure the closing band had in part 18, same fix and
   the same reason it is a separate rule: the ink measures 6.51:1 there. */
.card-grid > .card-cta {
  background: var(--grad-cta);
  border-color: var(--brand-ink-strong);
  color: var(--cta-ink);
}

.card-grid > .card-cta::before {
  background: var(--cta-ink);
}

.card-grid > .card-cta h3,
.card-grid > .card-cta h4,
.card-grid > .card-cta h3 a,
.card-grid > .card-cta h4 a,
.card-grid > .card-cta h3 a:hover,
.card-grid > .card-cta h4 a:hover {
  color: var(--cta-ink);
}

.card-grid > .card-cta:hover,
.card-grid > .card-cta:focus-within {
  border-color: var(--cta-ink);
}

.card-cta :focus-visible {
  outline-color: var(--cta-ink);
}

/* A nested article is a sub-card: a message in the inbox, a prepared letter. */
article {
  margin: var(--space-4) 0;
  padding: var(--space-5);
  background: var(--surface-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

article > *:last-child {
  margin-bottom: 0;
}

article h3 {
  margin-top: 0;
}

/* A letter fills the card it is read in. Its own line breaks come from the
   template that drafted it, so a wider box wraps less rather than more. */
article > pre {
  background: var(--surface);
}

/* ------------------------------------------------------- the closing band */

/* THE FOOTER IS A PLACE, NOT A LEFTOVER (part 18).

   What was here before: `max-width` on the `<footer>` element, a hairline
   above it, and two to four grey paragraphs in a single column - the page
   simply stopped. What is here now is the one dark surface in the project,
   full width like the header, with the wordmark on the left and the page's own
   sentences laid out as columns on the right. Not one word of those sentences
   changed; they are the honesty text - what this UI does not do, what the
   journal guarantees, where the accessibility self-assessment lives - and part
   18 restyled their container exactly as the brief requires.

   The band is `--band` (#0c3a56). Its text is `--band-ink` at 10.46:1 and its
   links are `--band-link` at 7.86:1; `<code>` inside it gets a translucent
   white fill instead of the light-surface one, which would otherwise render
   near-black text on a dark band. */
.site-footer {
  background-color: var(--band);
  background-image: var(--grad-rule);
  background-repeat: no-repeat;
  background-size: 100% 3px;
  background-position: top left;
  color: var(--band-ink);
  font-size: var(--text-sm);
}

.site-footer-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-5) var(--space-7);
  max-width: var(--measure);
  margin: 0 auto;
  padding: var(--space-7) var(--space-4) var(--space-7);
}

.site-footer a {
  color: var(--band-link);
  text-underline-offset: 0.2em;
}

.site-footer a:hover {
  color: #ffffff;
}

/* THE RING HAS TO BE VISIBLE ON THE SURFACE IT IS DRAWN ON, and this is the
   one surface in the project where the design system's ring is not. `--focus`
   is #0c4e73 and measures 1.34:1 against the band - a focus indicator nobody
   can see, on the one band that carries links. White measures 11.96:1 there.
   Found by measuring the pair rather than by looking at it, which is the whole
   argument for computing every pair a new surface creates. */
.site-footer :focus-visible {
  outline-color: #ffffff;
}

.site-footer code {
  background: var(--band-fill);
  color: inherit;
}

.site-footer p:last-child {
  margin-bottom: 0;
}

.footer-brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--band-line);
}

/* The mark carries the brand gradient in the header, where it sits on white.
   On the band that gradient IS the band, so it becomes a translucent tile with
   a white glyph - the same object, legible on the surface it is on. */
.footer-brand .wordmark-mark {
  background-color: var(--band-fill);
  background-image: none;
  border: 1px solid var(--band-line);
  box-shadow: none;
  color: #ffffff;
}

.footer-brand .wordmark-name {
  color: #ffffff;
}

.footer-brand .wordmark-sub {
  color: var(--band-ink);
}

.footer-notes {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-2) var(--space-6);
  line-height: 1.6;
}

.footer-notes > p {
  margin: 0 0 var(--space-3);
}

/* ------------------------------------------------------------- navigation */

.navbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  list-style: none;
  padding: 0;
  margin: var(--space-4) 0 0;
}

.navbar li {
  margin: 0;
}

.navbar a,
.navbar strong {
  display: inline-block;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--brand-ink);
  font-size: var(--text-sm);
  font-weight: 600;
  text-decoration: none;
  transition: background-color var(--tempo) var(--ease),
    border-color var(--tempo) var(--ease), color var(--tempo) var(--ease);
}

.navbar a:hover {
  background: var(--tint-brand);
  border-color: var(--brand-ink);
  color: var(--brand-ink-strong);
}

/* ------------------------------------------------- the three-phase strip */

.phase-strip {
  margin: var(--space-6) 0 0;
  padding: var(--space-5) var(--space-4);
  background: var(--grad-panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

.phase-strip ol {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
}

.phase {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  margin: 0;
  padding: 0 var(--space-2);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  text-align: center;
  color: var(--muted);
  font-size: var(--text-sm);
}

/* The connector, drawn between the previous circle and this one - EDGE TO
   EDGE, not centre to centre.

   The arithmetic is the whole of it. Every `.phase` is `flex: 1 1 0`, so they
   are equally wide and the mark sits at the horizontal centre of each; a
   connector from `left: -50%` to `right: 50%` therefore runs from the middle
   of the previous circle to the middle of this one and crosses both discs.
   That was invisible where a disc is filled and plainly visible everywhere it
   is a ring on a same-coloured surface - and it is on TOP of the previous
   circle rather than behind it, because this pseudo-element belongs to a later
   sibling than the circle it overlaps and positioned siblings paint in tree
   order.

   So both ends pull back by the mark's radius (2.6rem / 2) plus one step of
   breathing room. The stacked variant below 40rem has no connectors at all and
   is untouched. */
.phase::before {
  content: "";
  position: absolute;
  top: 1.3rem;
  right: calc(50% + 1.3rem + var(--space-3));
  left: calc(-50% + 1.3rem + var(--space-3));
  height: 2px;
  border-radius: var(--radius-pill);
  background: var(--line-strong);
}

.phase:first-child::before {
  content: none;
}

.phase-mark {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.6rem;
  height: 2.6rem;
  border: 2px solid var(--line-strong);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--ink-soft);
  font-weight: 700;
  box-shadow: var(--shadow-sm);
}

.phase-label {
  font-weight: 650;
}

.phase-current {
  color: var(--ink);
}

.phase-current .phase-mark {
  background: var(--brand);
  border-color: var(--brand-ink);
  color: var(--ink);
  box-shadow: var(--shadow-md);
}

.phase-done {
  color: var(--ink-soft);
}

.phase-done .phase-mark {
  background: var(--grad-cta);
  border-color: var(--brand-ink-strong);
  color: var(--cta-ink);
}

.phase-done::before,
.phase-current::before {
  background: var(--brand-ink);
}

/* ------------------------------------------------------------ definitions */

dl {
  display: grid;
  /* `minmax(0, max-content)` rather than `max-content`: a term like
     "Versicherungsnummer" may claim the width it needs and must still be
     allowed to shrink, or the row overflows at 320 px. */
  grid-template-columns: minmax(0, max-content) minmax(0, 1fr);
  gap: var(--space-3) var(--space-6);
  margin: 0 0 var(--space-4);
  padding: var(--space-4) 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

/* A definition list draws its own top and bottom rule, which doubles up with
   the rule under a section heading and with the card's own edge. Whichever one
   is redundant is the one that goes. */
main > section > h2 + dl {
  border-top: 0;
  padding-top: 0;
}

main > section > dl:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

dt {
  font-weight: 650;
  color: var(--muted);
  font-size: var(--text-sm);
  letter-spacing: 0.01em;
}

dd {
  margin: 0;
  min-width: 0;
  font-variant-numeric: tabular-nums;
}

/* ----------------------------------------------------------------- tables */

/* The reflow rule, and the half a `max-width` alone does not give you: wide
   content scrolls in its OWN box. Every table on every page lives inside one
   of these, which is what closes 1.4.10 for the caseworker pages too. */
.scroll-x {
  overflow-x: auto;
  /* POSITIONED SO THE BOX ACTUALLY CONTAINS WHAT IT SCROLLS (part 17, found in
     a browser at 320 px). The tables inside carry `.sr-only` spans - the
     offscreen sentences that give a flag or a tier its meaning in words - and
     `.sr-only` is `position: absolute`. With no positioned ancestor its
     containing block was the initial one, so it sat at its static position
     deep inside a 662px-wide table, escaped this box entirely and pushed the
     DOCUMENT to 545 CSS px on a 320 px viewport: the two-axis scroll 1.4.10
     forbids, on the caseworker overview, under a green test suite. The static
     check counts tables against scroll containers and cannot see this.
     Positioning this box makes it the containing block, so the offscreen text
     scrolls with its table and the page does not. */
  position: relative;
  margin: 0 0 var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
}

.scroll-x > table {
  margin: 0;
  border: 0;
}

table {
  border-collapse: collapse;
  width: 100%;
  margin: 0 0 var(--space-4);
  font-size: var(--text-sm);
}

caption {
  text-align: left;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--line-strong);
  background: var(--surface-alt);
  color: var(--ink-soft);
  font-weight: 650;
  font-size: var(--text-sm);
  letter-spacing: 0.01em;
}

th,
td {
  border-bottom: 1px solid var(--line);
  padding: var(--space-3) var(--space-4);
  text-align: left;
  vertical-align: top;
}

thead th {
  background: var(--surface-alt);
  border-bottom: 2px solid var(--line-strong);
  color: var(--ink);
  font-weight: 700;
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

tbody th {
  font-weight: 650;
}

/* THE ROW ANSWERS THE POINTER (part 18). A caseworker reading a 41-row queue
   across five columns loses the row under the cursor; a tint that follows it
   is the oldest fix in the book and the site had none. It is a hover state and
   carries no meaning - every fact in the row is in the row. */
tbody tr {
  transition: background-color var(--tempo) var(--ease);
}

tbody tr:hover > th,
tbody tr:hover > td {
  background: var(--tint-brand-soft);
}

tbody tr:last-child th,
tbody tr:last-child td {
  border-bottom: 0;
}

td {
  font-variant-numeric: tabular-nums;
}

/* NUMBERS COLUMN UP (part 18). `tabular-nums` alone makes digits the same
   width; it does not put the units under the units, which needs the column to
   be right-aligned. Applied per cell rather than per table, because a table
   here mixes counts with sentences. */
.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

thead th.num {
  text-align: right;
}

/* A TALL TABLE KEEPS ITS HEAD (part 18). Only where a table is long enough for
   the head to leave the screen, and only from 64rem up - on a phone the page
   is one column and an inner scroller would take the reader's own scroll away.

   `.scroll-x` is already a scroll container in both axes (a box with
   `overflow-x: auto` computes `overflow-y` to `auto` as well), so giving it a
   maximum height is the whole mechanism: the sticky head then sticks to the
   top of the box it is inside. */
@media (min-width: 64rem) {
  .scroll-x.is-tall {
    max-height: 78vh;
  }

  .scroll-x.is-tall thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    box-shadow: 0 1px 0 var(--line-strong);
  }

  .scroll-x.is-tall caption {
    position: sticky;
    top: 0;
    z-index: 2;
  }
}

/* ------------------------------------------------------------------ forms */

fieldset {
  /* A fieldset does not shrink below its min-content width on its own, and a
     `<select>` sizes to its longest option. On the case view that option is
     "Geschaeftsbereich Versicherung und Rente", which held the correction
     fieldset at 583px and the document at 612px on a 320px viewport - the
     second two-axis scroll the browser pass found and the static test could
     not (part 17). With this the fieldset takes its container's width and the
     `max-width: 100%` already on `select` does the rest. */
  min-width: 0;
  margin: 0 0 var(--space-5);
  padding: var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}

/* A REAL GROUP HEADING (part 18). A legend that reads as a caption over a grey
   box does not group anything; this one is a label on the card's edge. */
legend {
  padding: var(--space-1) var(--space-3);
  margin-left: calc(-1 * var(--space-1));
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--tint-brand-soft);
  font-weight: 700;
  font-size: var(--text-sm);
  color: var(--brand-ink-strong);
  letter-spacing: 0.01em;
}

.action-form {
  margin: var(--space-5) 0;
}

.field {
  margin: 0 0 var(--space-4);
  min-width: 0;
}

.field label {
  display: inline-block;
  margin-bottom: var(--space-2);
  font-weight: 650;
}

.field input[type="checkbox"] + label {
  font-weight: 400;
}

.field input[type="checkbox"] {
  margin-right: var(--space-2);
  width: 1rem;
  height: 1rem;
  accent-color: var(--brand-ink);
}

/* A CHECKBOX AND ITS LABEL ARE ONE ROW (part 20), and this is a browser-walk
   finding rather than a preference. A label is `inline-block`, so one longer
   than the line wraps BELOW the box while a short one stays beside it - on the
   intake page's document list at 390px that put two of three boxes on their
   own line and the third inline, which reads as three different controls. The
   grid gives the box its own column and lets the label and its help sentence
   wrap in the second one, at any width. */
.field-check {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: var(--space-2);
  align-items: start;
}

.field-check > label,
.field-check > .help {
  grid-column: 2;
}

.field-check > input[type="checkbox"] {
  /* Optically on the first line of the label rather than at its top edge. */
  margin: 0.3rem 0 0;
}

/* Fields that belong together in the persona file sit together on the page:
   the two halves of a name, the four parts of an address. One column below
   48rem, like everything else. */
.field-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-3) var(--space-4);
}

label {
  color: var(--ink);
}

/* ONE CONTROL SHAPE (part 18). Text inputs, dates, selects and textareas were
   three different heights and, in the selects' case, three different WIDTHS on
   the same form - each `<select>` sized itself to its longest option, so the
   intake page showed a short box, a medium box and a wide box stacked down the
   page and read as unfinished. They are one component now: same height, same
   radius, same border, same focus treatment, and full width by default. */
input[type="text"],
input[type="number"],
input[type="date"],
select,
textarea {
  font: inherit;
  width: 100%;
  max-width: 100%;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  transition: border-color var(--tempo) var(--ease),
    box-shadow var(--tempo) var(--ease);
}

input[type="text"]:hover,
input[type="number"]:hover,
input[type="date"]:hover,
select:hover,
textarea:hover {
  border-color: var(--brand-ink);
}

/* The focus ring is the design system's; this is the field TELLING the reader
   it is the active one, underneath that ring. */
input[type="text"]:focus,
input[type="number"]:focus,
input[type="date"]:focus,
select:focus,
textarea:focus {
  border-color: var(--brand-ink);
  box-shadow: 0 0 0 3px var(--tint-brand);
}

/* A MISSING ANSWER IS RED, AND NOTHING HERE DOES THE BLOCKING (part 20).

   The intake form's fields carry the HTML `required` attribute, so the BROWSER
   refuses the submission and shows its own message. This block is the visual
   half and nothing else: no script runs, no state is tracked, and every rule
   below is a selector over a state the engine already maintains.

   `:user-invalid` rather than `:invalid`, and the difference is the whole
   point: `:invalid` matches from the first paint, so a form that was merely
   OPENED would be red before anybody typed anything. `:user-invalid` waits
   until the field has been interacted with or the form submitted, which is
   exactly the moment the user's own sentence describes - "after pressing
   Antrag absenden with empty fields".

   The red family is used as the palette reserves it: `--alarm` (5.19:1 on
   white) draws the EDGE, `--alarm-text` (9.34:1) carries the WORDS. No new
   pair enters the project here. And colour is never the only carrier: every
   required field has a pre-rendered sentence underneath it that the rule at
   the bottom reveals, so the state has words as well as a tone.

   THE SELECTOR LIST IS SPELLED OUT PER CONTROL and this block sits AFTER the
   focus rules, which is a finding from the browser walk rather than a style.
   A bare `input:user-invalid` is specificity 0-1-1 and the control's own focus
   rule is `input[type="text"]:focus` at 0-2-1, so the blue focus edge won -
   and the browser moves focus TO the field it stopped at, which means the one
   field a visitor is looking at was the one field that was not red. Matching
   the focus rules' shape and coming after them makes an invalid field red
   whether it is focused or not. The focus INDICATOR is untouched: it is an
   `outline` on `:focus-visible` and measures 7.62:1 against this tint. */
input[type="text"]:user-invalid,
input[type="number"]:user-invalid,
input[type="date"]:user-invalid,
select:user-invalid,
textarea:user-invalid {
  border-color: var(--alarm);
  box-shadow: 0 0 0 3px var(--tint-alarm);
}

/* The label repeats the state, because a reader whose eye is on the label
   should not have to look at the box to know which one it is about. `:has()`
   is the only way from a control back to the label that precedes it, and it is
   deliberately the ONLY thing that depends on it: the border and the sentence
   below carry the meaning on their own. */
.field:has(:user-invalid) label {
  color: var(--alarm-text);
}

/* PRE-RENDERED, revealed by CSS. The sibling combinator reaches it because the
   sentence is written after its control, which is also where a reader expects
   to find it. */
.field-error {
  display: none;
  margin-top: var(--space-2);
  color: var(--alarm-text);
  font-size: var(--text-sm);
  font-weight: 650;
}

:user-invalid ~ .field-error {
  display: block;
}

select {
  /* The native arrow is drawn by the engine at the end of the control, so the
     text needs room not to run into it. */
  padding-right: var(--space-6);
}

/* THE CALENDAR BUTTON IS LEFT ALONE APART FROM ITS CURSOR, and that is a
   finding rather than a preference. It is a control inside the engine's own
   shadow tree with its own tab stop and its own focus ring, which this project
   cannot restyle and must not damage. A first pass dimmed it to `opacity:
   0.65` to sit more quietly in the field; a keyboard walk of the form then
   showed the stop with no visible ring, because an opacity on the host part
   dims whatever the engine paints on it - the focus indicator included. The
   cursor is the whole of what is safe to change here. */
input[type="date"]::-webkit-calendar-picker-indicator {
  cursor: pointer;
}

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

/* THE THIRD TIME THIS CLASS OF BUG SHIPPED, and the last one it can ship
   silently (part 22).

   `.help` was the one flowing-text class part 17 let keep a `ch` cap, on the
   reasoning that a help sentence "belongs to a field and reads with it". That
   reasoning held while every `.help` sat under an input. Part 20 gave the class
   two jobs it was never measured for: the intro paragraph of a fieldset and the
   description under each document checkbox - neither of which is beside a
   control, both of which span the fieldset. At `--text-sm` the 68ch cap
   resolves to roughly 500px inside a fieldset about 1180px wide on a 1920px
   screen, so the Anlagen descriptions rendered as a narrow column down the left
   of a box that spans the page. The user reported it from a screenshot, which
   is the same way the part-17 caps were found.

   The fix is the part-17 rule applied without the exception: THE MEASURE IS THE
   CONTAINER. Removing a `max-width` can only widen an element up to its
   container and never past it, so nothing about reflow changes - at 320px the
   container binds, as it always did. Under an input the sentence now matches
   the width of the control it describes, which is what "reads with it" should
   have meant in the first place.

   `tests/test_review_accessibility.py::test_no_flowing_text_carries_a_character
   _measure_cap` parses every `ch` cap out of both stylesheets and requires it to
   be a DECLARED exception, so a fourth one has to be argued rather than
   noticed. */
.help {
  display: block;
  margin-bottom: var(--space-2);
  color: var(--muted);
  font-size: var(--text-sm);
}

button {
  font: inherit;
  font-weight: 650;
  padding: var(--space-3) var(--space-5);
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--grad-cta);
  color: var(--cta-ink);
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--tempo) var(--ease),
    transform var(--tempo) var(--ease);
}

button:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}

button:active {
  transform: translateY(0);
  box-shadow: var(--shadow-sm);
}

/* The one call to action per step. A link styled as a button, and still a
   link: it navigates, it works with scripting off, it is in the tab order
   exactly once.

   IN THE DESIGN SYSTEM SINCE PART 17, and it had to be. It was defined in
   `demo.css`, which only the three citizen pages load, while `metrics.html`
   already used it - so the metrics reload control carried `class="cta
   cta-secondary"` on a page where no such rule existed and rendered as a plain
   inline link. A component used by four templates across three stylesheet
   combinations belongs in the one file every page loads. */
.cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-2) 0 var(--space-3);
  padding: var(--space-3) var(--space-6);
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--grad-cta);
  color: var(--cta-ink);
  font-weight: 650;
  text-decoration: none;
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--tempo) var(--ease),
    transform var(--tempo) var(--ease), background-color var(--tempo) var(--ease);
}

.cta:hover {
  color: var(--cta-ink);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}

.cta:active {
  transform: translateY(0);
  box-shadow: var(--shadow-sm);
}

.cta-secondary {
  background: var(--surface);
  border-color: var(--line-strong);
  color: var(--brand-ink);
}

.cta-secondary:hover {
  background: var(--tint-brand);
  border-color: var(--brand-ink);
  color: var(--brand-ink-strong);
}

/* The unit picker. A demo affordance that every page says is not
   authentication, so it is deliberately styled as a toolbar and not as a
   sign-in. */
.picker {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3) var(--space-4);
  margin: var(--space-5) 0 var(--space-4);
  padding: var(--space-4) var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--grad-panel);
  box-shadow: var(--shadow-sm);
}

.picker label {
  font-weight: 650;
}

/* The one select on the site that is NOT full width: it is a toolbar control
   beside its button. It may grow to 26rem and must still be allowed to shrink
   to nothing, which is what keeps the 320 px reflow intact. */
.picker select {
  flex: 1 1 min(100%, 26rem);
  min-width: 0;
  width: auto;
}

/* ------------------------------------------------------ notes and notices */

/* A NOTE READS AS AN ANNOTATION, NOT AS THE PAGE (part 18). Two of these stack
   at the top of every caseworker screen - the acting unit and the C-5 note
   about the role picker - and at body size on a grey fill they were the first
   and heaviest thing under the heading, which is the opposite of what a note
   is for. Same words, same tones, one size down and a lighter fill. */
.notice {
  margin: 0 0 var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-left: 4px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface-alt);
  font-size: var(--text-sm);
  line-height: 1.6;
}

.notice > *:last-child {
  margin-bottom: 0;
}

/* A note in the page head belongs to the page rather than to a card, so it
   sits ON the canvas with its own hairline elevation instead of looking like
   a recessed panel that lost its card. */
.page-head .notice {
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}

.page-head .ok-note {
  background: var(--tint-ok);
}

.page-head .language-note {
  background: var(--tint-brand);
}

.ok-note {
  border-left-color: var(--ok);
  background: var(--tint-ok);
}

.alarm-note {
  border-left-color: var(--alarm);
  background: var(--tint-alarm);
}

/* A sampled case gets the calmest surface on the page, deliberately: the whole
   of ADR-025 is that a random draw must not look like a suspicion. */
.sample-note {
  border-left-color: var(--brand);
  background: var(--tint-sample);
}

/* The same rule as a table row, and it lives here rather than in one of the
   page sheets because ADR-025 is a promise BOTH surfaces make: the citizen
   reading the pipeline page and the caseworker reading the case view must see
   a random draw rendered the same calm way. */
tbody .sample-row > th,
tbody .sample-row > td {
  background: var(--tint-sample);
}

.flags {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Every flag says what it is in words; the tone only adds a border and a tint
   that both keep their contrast. Colour carries nothing on its own. */
.flag {
  margin: 0 0 var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-left: 4px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-alt);
  font-size: var(--text-sm);
}

.flag:last-child {
  margin-bottom: 0;
}

.flag-attention {
  border-left-color: var(--alarm);
  background: var(--tint-alarm);
}

.tag {
  display: inline-block;
  padding: 0.05rem var(--space-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  background: var(--surface-alt);
  color: var(--ink-soft);
  font-size: var(--text-xs);
  font-weight: 600;
  white-space: nowrap;
  vertical-align: middle;
}

.muted {
  color: var(--muted);
}

/* ---------------------------------------------------------------- badges */

/* A STATE THAT LOOKS LIKE A STATE (part 18). "über Zielwert", "Tier 1 - klar
   und vollständig", "offen, wartet auf menschliche Bestätigung" were sentences
   in table cells and definition lists, which is where a reader scanning for
   the exception finds nothing to scan.

   1.4.1 IS SATISFIED BY CONSTRUCTION, NOT BY CARE. A badge is a box drawn
   AROUND a word; there is no badge in this project that does not contain the
   sentence it is a badge for, because the tone has nothing to render if the
   word is removed. Every pair below is measured on its own tint: `--ok` 6.40,
   `--caution-text` 7.70, `--alarm-text` 8.03, `--ink-soft` on the alt surface
   8.46, `--brand-ink-strong` on the brand tint 7.99. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  padding: 0.15em 0.7em;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  background: var(--surface-alt);
  color: var(--ink-soft);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1.6;
  white-space: nowrap;
  vertical-align: baseline;
}

.badge-ok {
  border-color: var(--ok);
  background: var(--tint-ok);
  color: var(--ok);
}

.badge-warn {
  border-color: var(--caution);
  background: var(--tint-caution);
  color: var(--caution-text);
}

/* There is no alarm badge, deliberately. The tone set here is the set that
   ships: nothing in this UI is a small inline state that means "something went
   wrong". The red family's places are the refusal notice, the anomaly flag and
   the failed gate, and all three of them are blocks rather than pills. A
   fourth tone defined against that day would be a rule with no user and a row
   of measurements in the self-check for a pair that never renders. */
.badge-brand {
  border-color: var(--brand-ink);
  background: var(--tint-brand);
  color: var(--brand-ink-strong);
}

/* A tier is a number, so it gets the one badge shape that is a disc. */
.badge-tier {
  justify-content: center;
  min-width: 1.8rem;
  padding: 0.15em 0.5em;
  font-variant-numeric: tabular-nums;
}

/* ----------------------------------------------------------- statistics */

/* THE NUMBER IS THE POINT (part 18). The census on the queue overview - the
   single most quotable figure in the whole demonstration - was a clause in a
   sentence next to a machine timestamp. These tiles are the scan layer; the
   sentence under them stays exactly as it was and remains the statement of
   record, because it is the one that carries the clock. */
.stat-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
  list-style: none;
  margin: 0 0 var(--space-5);
  padding: 0;
}

.stat {
  margin: 0;
  padding: var(--space-4) var(--space-5);
  background: var(--grad-panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}

.stat-label {
  display: block;
  margin-bottom: var(--space-1);
  color: var(--muted);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.stat-value {
  display: block;
  color: var(--ink);
  font-size: var(--text-stat);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.025em;
  line-height: 1.1;
}

/* The provenance line under a tile row: small, quiet, and still the sentence
   that says when the numbers were taken. */
.stat-source {
  margin: calc(-1 * var(--space-2)) 0 var(--space-4);
  color: var(--muted);
  font-size: var(--text-sm);
}

/* ------------------------------------------------------------ empty state */

/* A DESIGNED NOTHING (part 18). An empty queue used to be a table row reading
   "Keine offenen Vorgänge." and an empty inbox a grey notice - both of which
   look like a page that failed rather than a state that is fine. */
.empty {
  display: grid;
  justify-items: center;
  gap: var(--space-2);
  padding: var(--space-7) var(--space-5);
  text-align: center;
  color: var(--muted);
}

.empty-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3.5rem;
  height: 3.5rem;
  margin-bottom: var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--tint-brand-soft);
  color: var(--brand-ink);
}

.empty-mark .icon {
  width: 1.75rem;
  height: 1.75rem;
  vertical-align: 0;
}

.empty-title {
  margin: 0;
  color: var(--ink);
  font-size: var(--text-md);
  font-weight: 650;
}

.empty p {
  margin: 0;
}

/* An empty state inside a table cell keeps the cell's own padding out of the
   way, so the block is centred in the table and not in the first column. */
td > .empty,
th > .empty {
  padding: var(--space-6) var(--space-4);
}

/* ------------------------------ the components that a ground breaks */

/* PART 21. Every rule above this line reads its colours from tokens, so the
   three grounds get all of them for free. This section is the exceptions - the
   places where a component hard-codes a RELATION between two tokens and the
   relation stops holding when a ground moves one of them. There is exactly one
   of them, and it was found by computing the pair rather than by looking at the
   page, which is the whole method.

   THE CURRENT STEP'S NUMBER. `.phase-current .phase-mark` fills the disc with
   `--brand` and writes the number on it in `--ink`. That is 6.74:1 on the light
   ground, where `--ink` is nearly black and the sky blue is a light fill - and
   2.05:1 on the machine ground, where `--brand` is the SAME sky blue and
   `--ink` has gone light. Two light values on top of each other: the one number
   on the strip that says which phase a visitor is in, unreadable, on the one
   page in the project where phase 2 is the current phase. `--cta-ink` is the
   token for exactly this - the ink that goes on a brand-family fill - and it
   measures 7.47:1 there.

   The casework ground never renders this component: the step indicator is a
   demo affordance and the caseworker UI never learns that a tour is running
   (part 13, ruling 5). For the record, `--ink` on `--amber` would be 4.32:1 on
   that ground, which is why this note exists rather than a rule with no user:
   a template that ever puts the strip on a caseworker page has to bring the
   same override with it. */
.ground-machine .phase-current .phase-mark {
  color: var(--cta-ink);
}

/* ------------------------------------------------ the desktop breakpoints */

@media (min-width: 48rem) {
  .card-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .field-row-2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .field-row-4 {
    grid-template-columns:
      minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 2fr);
  }

  .stat-grid {
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  }

  .footer-notes {
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  }
}

@media (min-width: 64rem) {
  main {
    padding: var(--space-7) var(--space-5) var(--space-7);
  }

  .site-header-inner {
    padding: var(--space-3) var(--space-5);
  }

  .card-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .card-grid-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  main > section {
    padding: var(--space-6) var(--space-7);
  }

  .site-footer-inner {
    grid-template-columns: minmax(0, 17rem) minmax(0, 1fr);
    padding: var(--space-7) var(--space-5) var(--space-7);
  }

  .footer-brand {
    display: block;
    padding-bottom: 0;
    border-bottom: 0;
    border-right: 1px solid var(--band-line);
    padding-right: var(--space-6);
  }

  .footer-brand .wordmark-mark {
    margin-bottom: var(--space-3);
  }
}

/* ------------------------------------------------------------- the reflow */

/* 320 CSS px. The tables are handled by `.scroll-x` above; what is left is the
   two-column definition list, which is precisely what overflows on a narrow
   viewport, and the horizontal rows that have to become vertical stacks. */
@media (max-width: 40rem) {
  main {
    padding: var(--space-5) var(--space-3) var(--space-7);
  }

  /* A full-width tap target beats two half-width ones side by side. Moved
     here with the component itself in part 17. */
  .cta {
    display: flex;
    justify-content: center;
    text-align: center;
  }

  .site-header-inner {
    padding: var(--space-3);
  }

  .site-tools {
    gap: var(--space-3);
  }

  .wordmark-sub {
    display: none;
  }

  .wordmark-mark {
    width: 2rem;
    height: 2rem;
  }

  /* The menu panel stops floating and pushes the page instead: an absolutely
     positioned panel wider than a 320 px viewport is exactly the two-axis
     scroll 1.4.10 forbids. */
  .menu-panel {
    position: static;
    min-width: 0;
    margin-top: var(--space-2);
    box-shadow: none;
  }

  .menu {
    flex: 1 1 100%;
  }

  main > section {
    padding: var(--space-5) var(--space-4);
  }

  main > section > h2 {
    margin-bottom: var(--space-4);
  }

  dl {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }

  dt {
    margin-top: var(--space-3);
  }

  dd {
    margin-bottom: var(--space-2);
  }

  .picker {
    align-items: stretch;
    flex-direction: column;
    padding: var(--space-4);
  }

  .picker button,
  .picker select {
    width: 100%;
  }

  .phase-strip {
    padding: var(--space-4);
  }

  .phase-strip ol {
    flex-direction: column;
    gap: var(--space-3);
  }

  .phase {
    flex-direction: row;
    justify-content: flex-start;
    text-align: left;
    padding: 0;
  }

  .phase::before {
    content: none;
  }

  /* A tag is `white-space: nowrap` so a two-word badge never breaks in half.
     The intake form's sealing badges are not two words - "wird versiegelt:
     Organisation / Auftraggeber" is 279px that cannot break - and they pushed
     the document 5px past a 320px viewport. Above this breakpoint they fit and
     keep their nowrap; here they wrap rather than take the page with them. */
  .tag {
    white-space: normal;
  }

  /* And the badges that part 18 added obey the same rule for the same reason:
     "offen, wartet auf menschliche Bestätigung" is one unbreakable run. */
  .badge {
    white-space: normal;
  }

  th,
  td,
  caption {
    padding: var(--space-2) var(--space-3);
  }

  .site-footer-inner {
    padding: var(--space-6) var(--space-3);
  }
}

/* A SCROLL BOX IS A SCREEN AFFORDANCE AND PAPER HAS NO SCROLLBAR. A caseworker
   who prints a queue for a file must get all 41 rows, not the 78vh of them
   that happened to be in view - so the one rule that clips a table is undone
   for print. Everything else on the page prints as it renders. */
@media print {
  .scroll-x.is-tall {
    max-height: none;
    overflow: visible;
  }
}

/* Somebody who asked their system for less motion gets less motion. The one
   animated thing in this project is the landing hero, and `demo.css` answers
   this query for it explicitly rather than relying on the blanket rule below -
   a hero frozen at the END of its keyframes would be a hero with no visible
   caption at all.

   The blanket rule also removes every micro-transition part 18 added, and the
   hover lifts with them: `transform` is a transitioned property, so with the
   duration collapsed the card simply arrives at its lifted position with no
   travel. Nothing is lost but the movement. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms;
    animation-iteration-count: 1;
    transition-duration: 0.01ms;
  }
}
