/* THE ONE WEB FONT, AND WHY IT IS ONLY ON HEADLINES.
 *
 * ADR-012 said no web font, as part of no build step and no CDN. Two of those
 * three still hold and this changes only the third: the file is in this
 * repository, served by this application, fetched by nothing at runtime. A
 * reviewer with no network gets the whole design.
 *
 * Display only. Interface text keeps the system stack, because the dense
 * screens were measured against those metrics and because swapping body text
 * is what makes a page lurch on load. One file, 24KB, the whole 400-800 range.
 */
@font-face {
  font-family: "Manrope";
  src: url("/static/fonts/manrope-latin.woff2") format("woff2-variations");
  font-weight: 400 800;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Verbatim — one stylesheet. Tokens first, then components.
 *
 * Three type registers, each standing for a different kind of truth:
 *   --face-ui    the product's own voice. Interface text, labels, actions.
 *   --face-doc   the document speaking. Quoted regulatory prose, and only that.
 *   --face-data  the coordinates that make a quote checkable: version, offsets.
 * A reader must never mistake a quotation for the product's own words, so the
 * two never share a face. All three ship with the operating system: no web
 * font, no icon font, no framework, no build step (ADR-012).
 *
 * Greyscale carries structure. Exactly two hues exist: --accent for anything
 * you can act on, --alarm for escalation. The greys are --accent desaturated,
 * so the page reads as one family rather than a neutral with a colour dropped
 * on it.
 *
 * Meaning never rests on colour alone. Draft, final, material and withheld are
 * carried by label, weight and shape first, so they survive colour blindness
 * and a black-and-white screenshot. There is a print block at the bottom that
 * proves it.
 */

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

:root {
  color-scheme: light dark;

  /* Type. Interface text is small and tight; document text is larger and
     looser, because the analyst reads it rather than scans it. The document
     outranking the chrome is the point, not an accident of scale. */
  --face-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --face-doc: "Iowan Old Style", Charter, "Palatino Linotype", Palatino,
    "Book Antiqua", Georgia, "Times New Roman", serif;
  --face-data: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;
  --face-display: "Manrope", var(--face-ui);

  --t-micro: 0.75rem;     /* 12px — uppercase labels, codes, coordinates.
                             Was 11px. It is the MOST-USED size in the app (38
                             sites), it is nearly always uppercase and tracked
                             0.09em, and 11px tracked uppercase is where a
                             reader stops reading and starts guessing. The
                             analyst this is built for is often 40-plus and
                             often on a laptop in somebody else's meeting
                             room. 12px is the floor, not a preference. */
  --t-small: 0.8125rem;   /* 13px — secondary interface text */
  --t-ui: 0.9375rem;      /* 15px — interface base */
  --t-ui-lg: 1.0625rem;   /* 17px — section headings */
  --t-doc: 1.125rem;      /* 18px — quoted source text */
  --t-claim: 1.1875rem;   /* 19px — a claim's statement */
  --t-h1: 1.5rem;         /* 24px — page title. The page does not shout. */
  --t-count: 1.375rem;    /* 22px — a count read across a grid of cards */

  /* Display sizes. Every heading set in --face-display takes one of these, and
     there is exactly one thing outside the scale that takes the face: the
     wordmark in the masthead. It is a mark, not a heading -- its size is set in
     the SVG and its box is fixed at 32px tall -- so it borrows the face and the
     tracking and never a size from here. Said out loud because this comment
     read "these are the only places --face-display is used" until the masthead
     took it, and a comment that lists the sites is a comment that goes stale
     the first time somebody adds one. The interface scale above is untouched:
     --t-micro through --t-h1 were measured against the system stack and the
     12px floor
     was set for a real reader on real hardware. */
  --t-display: 3rem;      /* 48px — the hero line, marketing only */
  --t-title: 2rem;        /* 32px — page and section titles */
  --t-lead: 1.25rem;      /* 20px — the sentence under a title */
  /* Tracking slackens as the type gets smaller. A geometric face wants real
     negative tracking at hero size and almost none at heading size, so one
     value across the three would either loosen the hero or close up the h1. */
  --track-display: -0.02em;   /* 48px hero */
  --track-title: -0.014em;    /* 32px section title */
  --track-h1: -0.009em;       /* 24px page title, near the interface heading's
                                 own -0.008em, which it sits beside */

  --lh-ui: 1.5;
  --lh-micro: 1.55;       /* Tracked uppercase needs MORE leading, not less.
                             Letter-spacing pushes glyphs apart across the
                             line; tight leading then crowds the lines into
                             each other and the label reads as a grey block
                             rather than as words. 13 sites paired --t-micro
                             with 1.4, which is the tightest combination in
                             the stylesheet applied to its smallest text. */
  --lh-doc: 1.72;         /* generous leading: this text gets read closely */
  --track-label: 0.09em;
  --measure: 66ch;        /* the reading measure for document prose */

  /* Space, on a 4px grid. */
  --s1: 0.25rem;
  --s2: 0.5rem;
  --s3: 0.75rem;
  --s4: 1rem;
  --s5: 1.5rem;
  --s6: 2rem;
  --s7: 3rem;
  --s8: 4rem;

  --r: 3px;               /* a printed form field, not a pill */
  --r-sm: 2px;

  /* Shape. Larger radii than the record had, and deliberately not applied to
     the record: see the gradient rule. Chrome is soft; evidence is square.
     Nothing in this file consumes them yet. When something does, it has to be
     chrome -- a button, a card of controls, a pane -- and never the diff, the
     claims, the quoted source or the citation viewer. Those keep --r and
     --r-sm, because a filing with rounded corners is a card about a filing. */
  --r-card: 14px;
  --r-control: 10px;
  --r-pill: 999px;
  --shell: 76rem;

  /* Colour — light. */
  --bg: #eef1f9;
  --surface: #fbfcfe;
  --surface-sunk: #e2e7f2;
  --rule: #d5dbea;
  --rule-strong: #a5aec6;
  --ink: #0f1729;
  --ink-2: #414d68;
  --ink-3: #65728f;

  /* THE ACCENT FAMILY MOVES TOGETHER OR IT DOES NOT MOVE. The indigo pass set
     --accent and left -strong and -wash on the teal they were derived from,
     which is the same shape as the tint that moved without its literals: the
     head of a family changes, the members do not, and nothing fails because
     nothing pairs them. Parity with the teal set, which is the bar these were
     chosen against: --accent on --paper was 7.61:1 and is 6.73:1; -strong on
     -wash was 8.65:1 and is 8.14:1. White on --accent is 6.73:1, which is what
     the skip link needs since it paints --accent behind white text. */
  --accent: #2f4bd8;   /* institutional indigo: anything you can act on */
  --accent-strong: #1f34a8;
  --accent-wash: #e3e8fc;

  --alarm:  #a02c1d;   /* refusal keeps its oxblood weight */
  --alarm-strong: #6d2610;
  --alarm-wash: #f7e4dd;

  /* The hatch that fills a withheld claim's statement slot. */
  --hatch: rgba(15, 23, 41, 0.16);
  --focus: var(--accent);

  /* ---- glass, paper, depth -------------------------------------------
   * GLASS ON THE CHROME. PAPER FOR THE RECORD.
   *
   * A pane is translucent because you look past it. A filing is opaque
   * because you read it. So the masthead, the tab rail and the assistant
   * dock are panes, and the diff, the claims, the source extract and every
   * table of record are paper. A translucent tariff clause is a worse
   * tariff clause, and macOS has never applied vibrancy to a document
   * canvas either.
   *
   * Every value here is achromatic or in the grey family, so --accent and
   * --alarm are still the only two hues in this product.
   *
   * clerk.css may write no hex and no rgba() of its own -- a test enforces
   * it -- so it consumes these and nothing else.
   */
  --paper: #ffffff;          /* the record: diff, claims, source, tables */
  --glass-tint: #fdfeff;     /* the material the panes are made of       */
  /* THE HISTORY BELOW BELONGS TO THE TEAL PALETTE, NOT TO THIS ONE. Every hex,
     every ratio and every sampled pixel in the paragraphs that follow was written
     against the two-hue teal tokens this file carried until 2026-08-07, when the
     palette moved to indigo. They stay because the corrections are the value, not
     because they describe the colours declared above. Read none of them as a
     claim about the current palette. The closing paragraph, "WHAT IS TRUE", is
     the one that is current, and it says so.

     Raised from #f7fafb on 2026-08-05. At the old value a pane composited
     six values off the page behind it -- so the blur had signal but the
     PANE had none, and the white edge highlight had nothing to catch. Dark
     never had this problem: its tint sits about eighteen off its page.
     Measured against the real --ink token: body ink on the composited pane goes
     16.41:1 to 16.73:1, so this moves contrast the safe way.

     THE FIRST VERSION OF THIS COMMENT SAID 15.84 TO 16.15, and the correction is
     worth keeping rather than tidying away. Those figures were computed against
     #0f1e24 -- an ink colour that appears nowhere in this stylesheet. The
     DIRECTION was right and the digits were invented by measuring the wrong
     thing, which is a more comfortable error than it should be: it survives
     review precisely because a plausible number reads as a checked one. Nothing
     asserts either figure in a test, so nothing would have caught it.

     AND THE SAME COMMENT WAS WRONG A SECOND TIME, in a worse way, which is why
     both corrections stay. That edit raised --glass-tint and stopped there. It
     did not touch --glass-solid or --glass-1/2/3 -- the literals four lines
     below, which are the only values any rule in this product actually reads.
     Nothing anywhere consumes var(--glass-tint). So the token moved, the
     paragraph above was written in the past tense, and the rendered pane did
     not change by one value: Chromium at dpr 2 still reported rgb(243,247,248)
     on 2026-08-05, which is the OLD tint composited, and --ink on it is 16.35:1
     rather than the 16.41 claimed. 16.41 is --ink against the flat
     --glass-solid token, which is what you measure if you measure the token
     instead of the pane. A note that describes an intention as an outcome is
     the same failure as an invented figure wearing better clothes.

     The literals below are now recomputed from the tint above, and the pane
     did move: over open ground it composites to rgb(247,249,250), where --ink
     is 16.73:1, --ink-2 8.26:1 and --ink-3 4.57:1.

     AND THEN THE CORRECTION WAS WRONG A THIRD TIME, in exactly the way the two
     paragraphs above it condemn, which is why this one stays too. Those three
     figures were written here as "computed from the pixel Chromium drew, not
     from the token", and the 4.57 was used to announce that --ink-3 had cleared
     AA on the masthead for the first time. Every one of them is ratio() against
     the flat --glass-solid TOKEN. #f7f9fa returns 16.73 / 8.26 / 4.57 to the
     hundredth. The pane only reads rgb(247,249,250) on a page with nothing dark
     behind it -- which is measuring an empty page, the same error as measuring
     the token, wearing a fourth disguise.

     THE PANE IS GLASS. It has no colour of its own; it has the colour of
     whatever is behind it, and on this product something is always behind it,
     because content scrolls under a sticky masthead. That is the entire point
     of the surface. Measured properly -- /proceedings/MPUC-2026-0142 at
     1440x900 dpr 2, light, masthead text set transparent so the backdrop can be
     read, every pixel inside the .nav__link--off box sampled:

         best pixel in the box    rgb(245,247,249)   --ink-3 4.49:1
         worst pixel in the box   rgb(227,230,232)   --ink-3 3.85:1

     Identical at 1440, 1280, 1216 and 1200. So --ink-3 on the masthead does NOT
     clear AA. It does not clear it anywhere in that box, at its best pixel, on
     a freshly loaded page. The claim was false and it was written into three
     comments as checked fact.

     THOSE TWO PIXELS EXPIRED WITH THE TEAL PALETTE. Nobody has put a browser in
     front of the indigo pane, so this file has no measurement of it: not a best
     pixel, not a worst one, not a range. No arithmetic over the tokens gives one
     back, because the pane takes the colour of whatever scrolls under it. The
     honest state is that the rendered figure is unknown, and an unknown figure
     is written down as unknown here rather than replaced with a plausible one.
     What survives the palette change is the SHAPE of the finding -- the pane is
     a range, its worst pixel is well below its best, and --ink-3 on it should be
     assumed to fail AA until somebody measures it.

     WHAT IS TRUE, and what the figures below are: --glass-solid is the flat
     substitute the @supports fallback and prefers-reduced-transparency use, and
     for the current palette 16.96 / 8.01 / 4.57 are correct FOR THAT TOKEN --
     --ink, --ink-2 and --ink-3 in turn. They are labelled as token arithmetic
     here and asserted as token arithmetic in tests/test_glass_contrast.py.
     Nothing in this file now claims a rendered figure, because a rendered figure
     on a translucent surface is not a number -- it is a range that depends on
     what is underneath. */
  --glass-solid: #f7f9fd;    /* that tint already composited over --bg   */
  --glass-edge: #ffffff;     /* the 1px highlight that says "in front"   */
  --glass-cast: #060b1a;     /* shadows are blue-black, never neutral    */

  /* THE THREE ABOVE ARE THE SOURCES, NOT FILLS. --glass-1/2/3 are
     --glass-tint at three alphas, --edge is --glass-edge at 0.55, and the
     three shadows are --glass-cast at four. They are spelled out again as
     literals below because CSS cannot say "this colour at that alpha"
     without a colour function, and a token that becomes invalid on an
     older engine takes the whole declaration with it -- which is the one
     failure this design may not have. Change a source, recompute the
     literals under it. */

  /* The ladder inverts the reflex: the longer a surface lives and the more
     words it carries, the MORE opaque it is. The masthead holds two words
     per item and you look past it a thousand times. A tip holds a
     paragraph you read once. See --blur-1/2/3, which pair with these. */
  --glass-1: rgba(253, 254, 255, 0.62);   /* attached  */
  --glass-2: rgba(253, 254, 255, 0.86);   /* floating  */
  --glass-3: rgba(253, 254, 255, 0.92);   /* transient */

  --blur-1: blur(20px) saturate(180%);
  --blur-2: blur(30px) saturate(170%);
  --blur-3: blur(14px) saturate(160%);

  /* ---- the ground the panes sit on -----------------------------------
   * A frosted pane over one flat colour is a flat pane. Blur only reads as
   * glass when there is something varied behind it to soften, and there was
   * nothing to soften: on /proceedings, Chromium at dpr 2 returned exactly
   * rgb(238,241,242) at every point probed across the frame -- both gutters,
   * mid-column, and 1200px down. (Probed, not exhaustively sampled. The claim
   * is that no probe found any variation, which is weaker than "there was
   * none" and is what was actually done.) The filter ran correctly on a page
   * that gave it no work, which is why the chrome read flat while every
   * backdrop-filter rule was doing exactly what it said.
   *
   * WHAT IT BUYS, measured the same way afterwards, scanning x=0..320 on one
   * frame at 1440x900 dpr 2 with all text hidden. The desk/sheet edge, read on
   * open ground and then read through the masthead on the same frame:
   *
   *              open ground              through the masthead
   *   light      225 -> 238 in 18px       242 -> 247 in 57px
   *   dark         5 ->  12 in 16px        17 ->  19 in 30px
   *
   * Amplitude down by two thirds, spread out over three times the distance,
   * and no step left in it. That difference -- a crisp edge in the world, soft
   * where you see it through the pane -- is the whole of what "glass" means
   * here, and it is the one thing on the page you can point at to show the
   * filter working.
   *
   * (Written first as "twelve values over twelve pixels" and "five values over
   * ninety". Thirteen over eighteen, and five over fifty-seven. The comparison
   * was sound and every digit in it was approximate. Rounding a measurement you
   * did not take is the same habit as inventing one, and this file has enough
   * of that in it already.)
   *
   * EVERY PIXEL VALUE IN THIS BLOCK WAS READ OFF THE TEAL PALETTE, before
   * 2026-08-07. The desk and sheet are alpha layers, so the indigo page moves
   * all eight of those numbers and nothing has re-read them. The argument holds
   * -- a crisp edge on open ground, softened under the pane -- and the digits
   * are the old page's. They are left as they were rather than rewritten from
   * arithmetic, because arithmetic is not what took them.
   *
   * So the ground gets structure, taken from the subject rather than from a
   * hero gradient: a sheet of ruled filing paper lying on a desk. Three
   * layers, topmost first.
   *
   *   the desk    everything outside the 76rem shell steps down. It buys the
   *               best cue in the set: two crisp vertical edges that the
   *               masthead's blur visibly softens as they pass under it and
   *               that snap back the moment they clear it.
   *
   *               THE SAFETY ARGUMENT HERE WAS WRONG AS FIRST WRITTEN. It said
   *               "content never renders there -- .masthead, .main and
   *               .colophon each wrap their children in .shell". Those three
   *               do. .skip does not: it is a sibling of all three, and
   *               `.skip:focus { left: var(--s4) }` puts it 16px from the
   *               window edge, deep inside the desk, as the FIRST thing a
   *               keyboard user reaches. The invariant was stated about the
   *               elements somebody happened to look at.
   *
   *               It is safe for a different reason, and the real one is worth
   *               having written down: .skip carries `background: var(--accent)`
   *               -- an opaque fill -- so the ground behind it never shows
   *               through and the desk cannot touch its contrast. That is the
   *               rule for anything that lands out here in future: opaque fill,
   *               or stay inside .shell. tests/test_glass_contrast.py asserts
   *               .skip keeps an opaque background, which is the only part of
   *               this a test can hold.
   *
   *               The clearance is also tighter than "the gutter". The ramp
   *               runs calc(50% - 38rem) to calc(50% - 37rem); .shell's left
   *               edge is exactly calc(50% - 38rem) and its padding is 24px, so
   *               the ramp reaches 16px into the shell box and clears the first
   *               glyph by 8px. Measured at 1440: shell x=112, ramp ends x=128,
   *               first nav glyph x=136.
   *   the rule    a writing rule every 32px and a docket-column rule every
   *               96px. High-frequency detail is what a blur destroys, so
   *               this is the part that disappears under a pane and stays
   *               sharp beside it. One pixel each, hard-stopped: no ramp, so
   *               nothing to band and no fringe from interpolating to
   *               transparent.
   *
   * DIRECTION IS A CONTRAST DECISION, NOT A TASTE ONE. In light the rules
   * LIGHTEN and only lighten, so the darkest point of the light SHEET is still
   * exactly --bg and no ink-on-ground pair can move. In dark they DARKEN and
   * only darken, for the same reason read the other way. The desk darkens in
   * both, and is the one place either scheme is allowed to, because the only
   * thing that ever lands on it is opaque (see above).
   *
   * WHERE THE DESK ACTUALLY STOPS, since the first write-up of this got it
   * wrong in the other direction and conceded too much. It said that below
   * 76rem the ground is "a flat ruled sheet with no gutters". The two stops are
   * calc(50% - 38rem) and calc(50% - 37rem): below 76rem the first clamps to
   * zero but the SECOND stays positive until about 1184px, so a narrowing
   * sliver of desk survives the breakpoint. Measured on the ground row at
   * y=840, light: at 1216px the left edge runs 230, 226, 227, 229, 230, 232 out
   * to x=8 and reaches --bg at x=16; at 1200px it is an 8px sliver; at 1190px
   * 2px; at 1184px it is gone. Cosmetic -- no text sits there -- but a laptop
   * at 1200px shows a thin dark stripe at each edge rather than a clean sheet,
   * and that is a rendered fact rather than the tidy story.
   *
   * NOTHING HERE MOVES. Not on scroll, not on hover, not on load. A ground
   * that animates would need a reduced-motion escape and would be the exact
   * machine-made effect this is trying to get away from. Stated so nobody
   * adds one. --ground is switched off whole in print, in prefers-contrast:
   * more and in prefers-reduced-transparency: reduce -- see those blocks.
   */
  --desk: rgba(16, 26, 30, 0.055);
  --sheet: rgba(16, 26, 30, 0);      /* the same ink at zero: premultiplied
                                        or not, the ramp cannot fringe */
  /* THE LIGHT SCHEME HAS ALMOST NO ROOM HERE and the numbers should say so
     rather than the alphas being nudged until something shows. --bg is
     rgb(238,241,249); white is 17, 14 and 6 values away per channel -- the
     indigo page is bluer than the teal one it replaced on 2026-08-07, so the
     blue channel has almost no room at all. A lightening rule can move the
     ground by 5, 4 and 2 at 0.30, and by 9, 8 and 3 at 0.55 -- and the pane's
     own step above the page is 9, 8 and 4, so a 0.55 rule would put a hairline
     in the ground at the value of the chrome. The invariant survived the
     palette change; the digits behind it did not, and these are the new ones.

     RENDERED, AND THAT MEASUREMENT EXPIRED WITH THE TEAL PALETTE. The counts
     below were taken on 2026-08-05 against a sheet of rgb(238,241,242), which
     is the OLD --bg. Nothing has put a browser in front of the indigo ground,
     the arithmetic above does not reproduce a rendered figure, and these are
     kept for their method, not their values: counted across a full 1440x900
     frame at dpr 2 rather than read off one pixel, because reading one pixel is
     how three other figures in this file went wrong. Against a sheet of
     rgb(238,241,242):

       column rule    rgb(243,245,246) at 7 of the 12 columns in frame,
                      rgb(243,245,245) at the other 5 -- the calc() lands
                      between device pixels and the blue channel rounds down
       writing rule   rgb(241,244,244), every occurrence

     So five and three, not the five and four written here first. Dark has the
     opposite problem in the opposite direction and gets rgb(9,14,16) at all 12
     columns and rgb(10,16,19) on every writing rule, against rgb(12,19,22) --
     those two were right. This is why the desk -- the one surface allowed to
     darken -- carries most of the structure in light. It renders rgb(225,229,230)
     and rgb(226,229,230) in equal measure, dithering between the two across the
     gutter; rgb(5,9,10) in dark.

     The dark sheet moved too: rgb(12,19,22) was the teal --bg and the indigo one
     is rgb(11,16,32), so every dark figure in the paragraph above is as expired
     as the light ones. What survives both is the shape -- light has no room and
     the desk carries the structure, dark has room and the rules carry it -- and
     that is why the alphas below are split by scheme. */
  --ruled-col: rgba(255, 255, 255, 0.30);
  --ruled-line: rgba(255, 255, 255, 0.22);

  /* Written once. Only the four colours above flip between schemes: var()
     inside a custom property resolves against the element it lands on, so
     :root's dark block re-tints this without restating the geometry. The
     38rem/37rem pair is half of --shell (76rem) and a 1rem ramp inside it. */
  --ground:
    linear-gradient(
      90deg,
      var(--desk) 0,
      var(--desk) calc(50% - 38rem),
      var(--sheet) calc(50% - 37rem),
      var(--sheet) calc(50% + 37rem),
      var(--desk) calc(50% + 38rem),
      var(--desk) 100%
    ),
    repeating-linear-gradient(
      180deg,
      var(--ruled-line) 0 1px,
      transparent 1px 32px
    ),
    repeating-linear-gradient(
      90deg,
      var(--ruled-col) 0 1px,
      transparent 1px 96px
    );

  /* Depth comes from light, not from a heavy shadow: a one-pixel highlight
     along the top edge, and a cast that grows with how far the surface
     floats. --glass-cast is the colour all three are mixed from. */
  /* 0.55 over the old pane put the highlight four values above it, which is
     not a highlight. At 0.9 over --glass-solid it is rgb(254,254,255) against
     rgb(247,249,253) -- seven, five and two, and that is the whole budget a
     near-white pane has: white itself is only eight, six and two away. In the
     indigo palette the blue channel has less room still than the teal one had.
     (Recomputed 2026-08-07 from the tokens. The figure this replaced,
     rgb(254,254,254) on rgb(247,249,250), was the teal pane's.)
     In light the top edge can only ever whisper; the
     cast below is what carries "in front" here, and the bright edge is a
     dark-scheme device. Both are stated rather than assumed because the
     reflex is to keep raising the highlight until it works, and in light it
     cannot. */
  --edge: inset 0 1px 0 rgba(255, 255, 255, 0.9);
  --shadow-1: 0 1px 2px rgba(6, 11, 26, 0.07);
  /* THE STICKY PANES NEED THEIR OWN, and shadow-1 could not be widened to
     do it: four surfaces use it and most of them are flat cards that would
     start floating for no reason. A masthead that content passes underneath
     is the one place an elevation shadow is telling the truth -- something
     really is in front of something else -- and at 2px and 7 per cent the
     old one said nothing at all. This is the whole reason the chrome read
     flat while the blur was working correctly behind it.

     WHAT IT WAS ACTUALLY DOING, measured in Chromium at dpr 2 rather than
     reasoned about: deepest pixel rgb(211,216,216) hard against the pane's
     bottom edge, back to within five values of the ground eight pixels later,
     gone at twenty. So the cast was never shallow -- it was SHORT. A gradient
     that steep does not read as one surface floating above another; it reads
     as a slightly soft border, which is what a reader was seeing and calling
     flat.

     (THE FIRST DRAFT OF THIS PARAGRAPH SAID "two values deep, gone within
     eight", and it is corrected rather than quietly replaced because this is
     the third wrong measurement in this one token block. It came from
     sampling the column starting eight pixels BELOW the pane edge -- past the
     peak -- and reporting what was found there as the maximum. The number was
     real, the probe was in the wrong place, and it read as checked because it
     had a digit in it. That is the same failure as the two above it wearing a
     third disguise.)

     THE FIX WAS FIRST WRITTEN AS "REACH, NOT DEPTH" -- blur 16px to 40px,
     offset 6px to 18px, alpha down to pay for it -- and the paragraph here
     claimed that made "every ink-on-ground pair under the masthead improve
     slightly". THAT CLAIM WAS FALSE, and it was false for the fourth time in
     this one token block for the fourth version of the same reason: it was
     read off ONE pixel row, the peak at y=112, and asserted about all of them.

     The column at x=760 on /proceedings at 1440x900 dpr 2, light, before and
     after that version, --ink-3 against the ground:

         y=112   3.35 -> 3.43   the one row that improved
         y=120   3.94 -> 3.76
         y=128   4.17 -> 3.92   the worst, a quarter of a point down
         y=140   4.25 -> 4.10
         y=148   4.25 -> 4.21

     One row up, seventeen down. And the reasoning error underneath it is worth
     naming, because it is not arithmetic: SPREADING THE SAME INK FURTHER MUST
     DARKEN ROWS THAT WERE CLEAN. The old cast was over by 16px; the new one
     ran to 29px; every row in between paid for the extra reach. "Same ink,
     more distance, so nothing gets worse" is not a trade that exists.

     IT IS WORSE THAN A COSMETIC COST HERE, because of a number in another
     block: --stuck is the masthead's own height, so the .tabs rail sticks at
     exactly its bottom edge and sits inside the deepest part of that band
     PERMANENTLY, not
     while something scrolls past. Measured at scrollY 900, the rail behind a
     tab went rgb(236,239,241) -> rgb(232,234,236), --ink-2 7.55 -> 7.23, and
     the current-tab chip -- opaque --paper white -- rendered rgb(249,249,249)
     because the masthead is z-index 30 and its cast paints over the whole rail.
     A shadow that greys out white is not a shadow, it is a film.

     SO THE CAST IS SHORT AND SHALLOW, and the acceptance test is arithmetic
     rather than taste: no row may be darker than it was BEFORE this lane
     touched anything. Measured against that baseline, this value:

         peak at the pane edge   rgb(211,216,216) -> rgb(223,226,228)
         visible cast (>3 off)          16px      ->       13px
         worst row, any y            no row darker than the old cast
         rail behind a tab       --ink-2 7.55 -> 7.77, --ink-3 4.17 -> 4.30
         current-tab chip        rgb(255,255,255), white again

     Shallower and slightly shorter than what was there at the start, and every
     figure better. That is the honest shape of this fix: the lift a page can
     afford here is small, because a permanently stuck rail lives in the band,
     and the thing that actually made the chrome read flat was never the cast's
     depth. It was the ground behind the blur having no structure to soften.

     DARK KEEPS THE LONG CAST and is measured separately below: darkening a
     dark ground RAISES contrast for light ink, so the same reach that costs
     light 0.25 of a point buys dark 0.06. The schemes get different shadows
     because they are answering to different arithmetic.

     THE NEGATIVE SPREAD IS THE PART THAT SURVIVED: shrink the box, push it
     down, then blur it. What comes out pools UNDER the pane instead of haloing
     around it, so .tabs -- a rounded box with two visible sides -- does not
     grow a grey ring.

     EVERY RENDERED PIXEL AND EVERY RATIO IN THIS PARAGRAPH IS THE TEAL
     PALETTE'S, read in Chromium before 2026-08-07. That covers the whole ladder
     above: rgb(211,216,216) and the eight-pixel recovery, the five --ink-3 rows
     from y=112 to y=148, rgb(236,239,241) -> rgb(232,234,236) with --ink-2 at
     7.55 and 7.23, the rgb(249,249,249) chip, and the acceptance table's
     rgb(223,226,228), 7.77 and 4.30. The page under all of them was
     rgb(238,241,242) and it is now rgb(238,241,249); the cast's own colour moved
     from a teal-black to rgba(6, 11, 26, ...) with --glass-cast. Nobody has put
     a browser in front of the indigo cast, so none of these has been re-read,
     and none is recomputed here because no token arithmetic produces a screenshot.
     The dark block below carries the same note for the same reason, and this one
     exists so that an undated pixel figure anywhere in this file can be taken as
     current.

     What carries over is the reasoning, which is not measurement: spreading the
     same ink further darkens rows that were clean; --stuck, being the bar's own
     height, puts the tabs rail inside the deepest part of the band permanently
     at every width; and a cast that greys
     out an opaque white chip is a film, not a shadow. The acceptance test carries
     over too -- no row darker than the old cast -- but it now has no baseline to
     be tested against until somebody measures one. */
  --glass-lift: 0 10px 22px -10px rgba(6, 11, 26, 0.1),
    0 2px 4px -2px rgba(6, 11, 26, 0.05);
  --shadow-2: 0 12px 32px rgba(6, 11, 26, 0.14), 0 2px 6px rgba(6, 11, 26, 0.08);
  --shadow-3: 0 4px 14px rgba(6, 11, 26, 0.1);

  /* Two lifts for the new chrome, declared here rather than beside the radii
     because this is where the depth family lives and a shadow token away from
     its family is a shadow token that misses the next scheme sweep.
     --lift-1 is a resting card, --lift-2 something a reader has picked up.
     They are mixed from --ink so the light scheme's cast carries the page's
     own blue-black rather than a second neutral. Same rule as every shadow
     above them: they belong to the chrome, never to the record. */
  --lift-1: 0 1px 2px color-mix(in srgb, var(--ink) 6%, transparent),
            0 2px 8px -4px color-mix(in srgb, var(--ink) 18%, transparent);
  --lift-2: 0 2px 4px color-mix(in srgb, var(--ink) 7%, transparent),
            0 16px 32px -18px color-mix(in srgb, var(--ink) 32%, transparent);

  /* The outer companion to the focus ring. It separates the ring from a
     busy or translucent backdrop. It is an enhancement and may be lost;
     the ring itself is an outline and never is. */
  --focus-halo: rgba(255, 255, 255, 0.9);

  --track-label-tight: 0.04em; /* the same label, in a narrow track */
  --r-glass: 10px;             /* a window has a radius; a form field does not */

  /* MEASURED, not guessed, and it deviates from the design plan on
     purpose. The plan says 3.25rem, which would slide the sticky source panel
     under the masthead and land every anchor behind it. What the browser
     reports is three heights, not one -- 233px, 164px and 110px, depending on
     how many rows the nav has wrapped to -- and the sticky block below sets
     --stuck to each of them at the width where it becomes true. Read the table
     there before changing any of them. --rail-h is the tab rail, measured at
     46px. Both are overridden with the layout that makes them true; see
     "sticky chrome" below. */
  --stuck: 0rem;
  --rail-h: 2.875rem;
  --anchor: calc(var(--stuck) + var(--rail-h) + var(--s4));

  /* The same clearance, for focus rather than for anchors. WCAG 2.2 added
     2.4.11 Focus Not Obscured, and this layout was already failing it: three
     rules in this file carried --anchor, all of them on things a fragment link
     points at, none of them on the controls a person tabs through. Tab down a
     long page and the browser scrolls the next control to the top of the
     viewport, which is where the masthead and the tab rail are.

     A SEPARATE TOKEN, NOT --anchor ITSELF, because a scroll container lowers
     it for its own descendants. Inside a chat transcript or the tour's tip
     there is no sticky chrome, and 10.875rem of clearance in a box 20rem tall
     would push the focused control to the bottom edge -- solving 2.4.11 in the
     page and breaking it in the panel. Each scroller sets this down to a
     hairline in its own stylesheet. */
  --focus-anchor: var(--anchor);

  --ease-settle: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-sheet: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-offer: cubic-bezier(0.34, 1.42, 0.64, 1);

  /* The refusal gets a duration and a curve of its own, and the curve is the
     point. --ease-offer overshoots on purpose, which is right for a thing
     inviting you to take it and wrong for a claim that has just declined to
     assert itself. A refusal that bounces tells the reader the wrong thing
     about what happened. This one arrives and it stops. */
  --dur-refuse: 200ms;
  --ease-refuse: cubic-bezier(0.2, 0, 0.38, 1);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0b1020;
    --surface: #141b2e;
    --surface-sunk: #070b16;
    --rule: #252e45;
    --rule-strong: #3d4863;
    --ink: #e9edf7;
    --ink-2: #adb7cd;
    --ink-3: #818da8;

    /* Dark's accent was a cyan derived from the teal light accent, so it
       survived the indigo pass untouched and would have rendered a cyan action
       colour on an indigo page. Same parity test as light: on --bg the teal set
       read 10.18:1 and this reads 9.96:1, -strong on -wash 9.75:1 against
       10.62:1. Lighter than light's accent rather than darker, because on a
       dark ground the action colour has to come forward. */
    --accent: #a8b9ff;
    --accent-strong: #c2ccff;
    --accent-wash: #151c3a;

    --alarm: #ef9d80;
    --alarm-strong: #ffc2ac;
    --alarm-wash: #3a1a11;

    --hatch: rgba(233, 237, 247, 0.18);

    /* Dark glass is not light glass with another alpha. --surface sits four
       points off --bg here, so an alpha of it would be invisible: the tint
       is its own value, above surface, and the filter adds a little
       brightness because a blur of a dark page returns a dark page. White
       would read as a scratch along the top edge, so the edge takes a
       lifted grey instead. */
    /* ONE BELOW THE GLASS RAIL, AND THAT STEP IS THE POINT. The indigo pass
       first set this to #161d31, which is --glass-solid exactly. Teal kept the
       two a value apart -- paper #151e22 under glass #151f23 -- and the rule
       for the current tab reads "--paper sits below the glass rail", which a
       byte-identical pair makes untrue while nothing fails. Nothing asserts
       the two differ, so the guard here is this comment and the one value.
       --ink-3 on it is 5.09:1, up from 5.03:1 when they were equal. */
    --paper: #151c30;
    --glass-tint: #1d253b;
    --glass-solid: #161d31;
    /* THE FIFTH AND LAST TEAL REMNANT, FOUND BY COUNTING HUES RATHER THAN BY
       LOOKING. The indigo pass moved --glass-tint, --glass-solid and --paper
       and left this one at hue 196, in a scheme where everything else sits
       between 220 and 226. Nobody saw it because it is a 1px top edge at 16%
       saturation -- which is the whole argument for auditing a palette with
       arithmetic instead of eyes. Same lightness and saturation, moved onto
       the family's hue, so the edge reads exactly as bright as before. */
    --glass-edge: #5c667e;
    --glass-cast: #000000;

    --glass-1: rgba(29, 37, 59, 0.62);
    --glass-2: rgba(29, 37, 59, 0.86);
    --glass-3: rgba(29, 37, 59, 0.92);

    --blur-1: blur(20px) saturate(160%) brightness(1.08);
    --blur-2: blur(30px) saturate(150%) brightness(1.06);
    --blur-3: blur(14px) saturate(150%) brightness(1.05);

    /* The ground, read the other way round. Light ink on a dark page gains
       contrast when the page goes darker, so here the ruling is a groove
       rather than a sheen and every layer subtracts. --bg is the LIGHTEST
       point of the dark ground, which is the mirror of the light scheme's
       rule and means no ink-on-ground pair can move there either.

       The desk goes most of the way to black. It has twelve values of room
       and it uses them, because a dark page cannot afford to be shy about a
       step it can barely afford to make. */
    --desk: rgba(0, 0, 0, 0.55);
    --sheet: rgba(0, 0, 0, 0);
    --ruled-col: rgba(0, 0, 0, 0.26);
    --ruled-line: rgba(0, 0, 0, 0.14);

    /* Dark is where a top edge can actually do the work. --glass-edge at 0.35
       over the pane gave rgb(46,61,66) against rgb(21,31,35); at 0.5 it is
       rgb(56,74,80), a hairline you can see without looking for it. The
       highlight is the dark scheme's "in front", the cast is light's, and
       neither scheme gets to pretend it has both. */
    --edge: inset 0 1px 0 rgba(92, 117, 126, 0.5);
    --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.5);
    /* Measured before: ONE value deep and four pixels tall. Black on a page
       already at rgb(12,19,22) has almost nothing left to give, so the alpha
       goes most of the way up and the negative spread keeps it under the pane
       rather than around it.

       THIS ONE KEEPS THE LONG REACH the light scheme had to give back, and the
       reason is arithmetic, not preference. Dark ink is LIGHT; darkening the
       ground under it raises contrast rather than spending it. Measured at
       scrollY 900 on the stuck .tabs rail, old cast against this one: the
       lightest ground behind a tab goes rgb(20,32,37) -> rgb(19,31,36), so
       --ink-2 8.05 -> 8.14 and --ink-3 4.78 -> 4.84. The current-tab chip
       keeps 1.21:1 against the rail, against light's 1.06:1, so dark can also
       afford the film that light could not. Both schemes were measured; only
       one of them could pay.

       THOSE PIXELS AND THE FOUR RATIOS WITH THEM ARE THE TEAL PALETTE'S, read
       before 2026-08-07, and so is rgb(46,61,66)/rgb(56,74,80) in the --edge
       note above. Nobody has re-run the sample on indigo. The direction is the
       part that carries over -- darkening the ground under light ink buys
       contrast rather than spending it, which is true of any dark scheme -- and
       the digits are stale. They are not recomputed here because no token
       arithmetic produces them; they came out of a screenshot. */
    --glass-lift: 0 18px 40px -12px rgba(0, 0, 0, 0.72),
      0 3px 8px -3px rgba(0, 0, 0, 0.5);
    --shadow-2: 0 12px 32px rgba(0, 0, 0, 0.6), 0 2px 6px rgba(0, 0, 0, 0.4);
    --shadow-3: 0 4px 14px rgba(0, 0, 0, 0.45);

    /* THE LIFTS HAVE TO BE RESTATED HERE OR DARK GROWS A HALO. In light they
       are mixed from --ink, which is a blue-black; --ink in dark is #e9edf7,
       so the same declaration would paint a near-white glow under every card
       and call it a shadow. A shadow is the absence of light in both schemes,
       so dark mixes from black instead, and it takes the deeper alphas the
       rest of this block takes for the same reason: black on a page already
       near black has little left to give. */
    --lift-1: 0 1px 2px rgba(0, 0, 0, 0.4),
              0 2px 8px -4px rgba(0, 0, 0, 0.55);
    --lift-2: 0 2px 4px rgba(0, 0, 0, 0.45),
              0 16px 32px -18px rgba(0, 0, 0, 0.72);

    --focus-halo: rgba(4, 9, 11, 0.9);
  }
}

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

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

html {
  -webkit-text-size-adjust: 100%;
}

/* background-color and background-image, never the `background` shorthand.
   The shorthand would reset background-image every time somebody touched the
   colour, which is how a backdrop quietly disappears. Every layer in --ground
   sizes itself -- repeating gradients tile, the desk gradient varies only on
   90deg -- so there is no background-size or background-repeat to keep in step
   with it, and switching the ground off anywhere is one declaration:
   --ground: none. */
body {
  margin: 0;
  background-color: var(--bg);
  background-image: var(--ground);
  color: var(--ink);
  font: var(--t-ui) / var(--lh-ui) var(--face-ui);
  font-synthesis-weight: none;
  text-rendering: optimizeLegibility;
}

/* A hidden thing stays hidden, whatever display a component sets. The citation
   viewer toggles [hidden] on the source panel and must not be second-guessed. */
[hidden] {
  display: none !important;
}

h1,
h2,
h3 {
  margin: 0;
  font-weight: 600;
  letter-spacing: -0.008em;
  line-height: 1.25;
}

h1 {
  font-size: var(--t-h1);
}

h2 {
  font-size: var(--t-ui-lg);
}

/* The display face, and the first rule in this file to use it. It sits below
   the h1/h2/h3 block on purpose: for h1 the two rules have the same weight, so
   source order decides, and this one deliberately takes the weight and the
   tracking off the block above -- 600 becomes 750 and -0.008em becomes
   -0.02em. Manrope is a variable face with a 400-800 axis, so 750 renders as
   750 rather than snapping to bold, and a geometric face at heading size needs
   the tighter track or the letters drift apart.

   IT DOES NOT TOUCH THE SIZE. h1 stays at --t-h1, 24px, which the interface
   scale set and argued for. .t-display and .t-title are the opt-in sizes, and
   nothing in the templates asks for them yet. The page still does not shout;
   it now says what it says in our own face. */
h3 {
  font-size: var(--t-ui);
}

/* TRACKING IS A FUNCTION OF SIZE, AND ONE VALUE FOR THREE SIZES IS THE BUG.
   --track-display was chosen for a 48px hero. Applied whole, it also reached
   h1 at 24px, where -0.02em is too tight: negative tracking has to slacken as
   type gets smaller or the small end closes up. This is the type equivalent of
   the token-family errors above -- one value taken from the largest case and
   handed to every case.

   Three steps, tightest at the top. h1 keeps a value near the -0.008em the
   interface headings already used, because it belongs to that scale and only
   borrows the face. */
h1, .t-display, .t-title {
  font-family: var(--face-display);
  font-weight: 750;
}

.t-display {
  letter-spacing: var(--track-display);
}

.t-title {
  letter-spacing: var(--track-title);
}

h1 {
  letter-spacing: var(--track-h1);
}

/* The display sizes themselves, so a class that names a scale step carries
   one. Without these .t-title would change the face of a line and leave it at
   body size, which is the sort of half-applied token that gets "fixed" later
   by hard-coding a pixel value next to it. --t-lead is the sentence under a
   title and stays in the interface face: it is prose, not display. */
.t-display {
  font-size: var(--t-display);
  line-height: 1.1;
}

.t-title {
  font-size: var(--t-title);
  line-height: 1.2;
}

.t-lead {
  font-size: var(--t-lead);
  line-height: var(--lh-ui);
  color: var(--ink-2);
}

/* Every figure a reader compares down a column. A number that changes width
   between renders reads as a bug in the data, not in the type. */
.count, .metric, td.num, .coord { font-variant-numeric: tabular-nums; }

p {
  margin: 0;
}

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}

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

/* The ring is an OUTLINE, in every scheme, on every surface, glass
   included. Never take it away from :focus-visible, in this file or in any
   other under this directory -- a ring lost to a redesign is an accessibility
   regression that no screenshot catches, because whoever takes the screenshot
   is holding a mouse. One narrow form is allowed, `:focus:not(:focus-visible)`,
   and it appears exactly once below on .tabpanel with its reasons. Anything
   broader now fails tests/test_a11y_guards.py.

   The halo is the second layer, and it is where a focus ring usually dies:
   a 2px accent ring against a translucent pane full of blurred page
   underneath is hard to find. The halo is a box-shadow, so the withheld
   surface drops it -- which is fine, because that surface is opaque
   --surface-sunk and the ring reads 6.14:1 against it in light and
   10.67:1 in dark. Token arithmetic, not assumed. */
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--r-sm);
  box-shadow: 0 0 0 4px var(--focus-halo);
}

/* A filled control would swallow an accent ring. The ring sits outside the
   control, so ink against the page is the reading that always works. */
.btn--primary:focus-visible,
.citation-chip:focus-visible,
.skip:focus-visible {
  outline-color: var(--ink);
}

/* 2.4.11 Focus Not Obscured, and a ring drawn under the masthead is a ring
   nobody sees. Every tab stop keeps the sticky chrome's height clear of
   itself, so the browser scrolls far enough when it brings one into view.

   :where() so this carries NO specificity at all. It is a floor, not a
   decision: any component that knows better -- a taller chrome, a shorter one,
   none -- overrides it with a plain class selector and wins. A blanket rule
   that fights the components it applies to is a rule people delete.

   Attribute selectors, not the elements' own defaults, because tabindex is
   what makes a <span> a tab stop and a <span> is exactly the case that broke
   here. [tabindex] catches -1 as well, which is right: those are the targets
   script moves focus to, and they land under the chrome the same way. */
:where(a, area, button, input, select, textarea, summary, [tabindex]) {
  scroll-margin-top: var(--focus-anchor);
}

/* The scrollers inside the page. There is no sticky chrome in any of them, so
   the page's clearance would be a hole. tour.css and clerk.css set the same
   property on their own boxes; this one is the panel that holds the source
   text beside a claim. */
.source-panel__body {
  --focus-anchor: var(--s3);
}

code {
  font: var(--t-small) / 1.5 var(--face-data);
}

hr {
  height: 0;
  margin: var(--s5) 0;
  border: 0;
  border-top: 1px solid var(--rule);
}

/* ---------------------------------------------------------------- layout -- */

/* THE FIRST TAB STOP ON EVERY SCREEN, AND IT WAS PAINTED OVER.
 *
 * This read z-index 10 while .masthead reads 30, and both are children of
 * <body>, so above 48rem -- where the masthead sticks -- the bar covered the
 * skip link completely. Checked in Chromium by tabbing once and reading
 * elementFromPoint at the link's own centre: document.activeElement was the
 * link, and the topmost box at that point was the wordmark's svg. Focus was
 * there and nothing showed it, on all six demo-path screens at 800, 1000 and
 * 1280. That is WCAG 2.4.11 Focus Not Obscured on the one control whose entire
 * job is to be the first thing a keyboard reader finds.
 *
 * 40 rather than 31, so the number does not have to be re-argued the next time
 * something claims a layer between them. Nothing else in THIS file goes above
 * 30. Two component sheets do, and both are right to: clerk.css puts the
 * assistant dock at 50 and its backdrop at 60, tour.css puts the walkthrough at
 * 80, and a panel a reader has opened on purpose should cover a link that jumps
 * past it. clerk.css also has a floating launcher at 40, which ties this rule
 * and never meets it -- that button is pinned bottom-right and this link sits
 * top-left. Anything new above 30 has to say why it outranks the skip link. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 40;
  padding: var(--s2) var(--s4);
  background: var(--accent);
  color: var(--surface);
  font-weight: 600;
}

.skip:focus {
  left: var(--s4);
  top: var(--s2);
}

.shell {
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--s5);
}

/* The signed-out page, and the only screen in this product that is a form
   rather than a record. It took the same 76rem shell every other screen takes,
   which put a forty-character field alone in a twelve-hundred-pixel box: the
   first thing a reviewer ever sees read as an empty room with a lock in the
   middle of it. Narrowed here rather than by moving --shell, because every
   other screen wants the wide one and a token changed for one page is how a
   token family goes wrong. Declared after .shell so it wins on file order at
   the same specificity, which is what lets one element carry both. */
.signin {
  max-width: 44rem;
}

/* G1 -- attached glass. It spans the viewport, so it has no corners and no
   radius; depth is one hairline of light along the top and the smallest
   cast in the ladder.

   SOLID FIRST, UNCONDITIONALLY. Written the other way round -- declare the
   alpha and hope -- a browser without backdrop-filter renders the raw
   translucent value over whatever is behind it, which is how glass work
   ends up as grey text on a grey rectangle. --glass-solid is the tint
   already composited over --bg, computed rather than picked, so a browser
   that drops the filter lands on the colour the filter would have made.
   It loses translucency and keeps the whole hierarchy, because depth here
   is carried by the shadow ladder and the edge, and neither needs the
   filter to exist. */
/* THE CAST STAYS --glass-lift AND DOES NOT BECOME --lift-1, which is a
   deliberate refusal of the one instruction this restyle was given about depth.
   --lift-1 is the new chrome depth and the masthead is chrome, so the swap
   looks free. It is not. The token block above spends four corrected paragraphs
   on this exact shadow: it was measured in a browser at dpr 2, it was got wrong
   four times, and it ends with an acceptance test that is arithmetic rather
   than taste -- NO ROW UNDER THIS BAR MAY BE DARKER THAN IT WAS. In light,
   --lift-1 puts 18 per cent of --ink at 8px of blur hard against the pane's
   bottom edge where --glass-lift puts 10 per cent at 22px, so it is darker in
   the first few rows under the bar and lighter after them -- short and steep,
   which is the shape this cast was widened to get away from. (Dark is the other
   way round, which is worse rather than better: one swap would move the two
   schemes in opposite directions and only one of them was ever measured.)
   Those rows are not empty: --stuck is the bar's own height, the tab rail sticks
   at exactly the bar's bottom edge, and the masthead is z-index 30, so its cast
   paints over the rail permanently rather than while something scrolls past.
   The last time somebody spread this ink differently, the current tab's opaque
   white chip rendered grey.

   Nothing here has been in front of a browser, so the arithmetic above is a
   reason to leave it alone and not a measurement of what the swap would do.
   Weight came from type and shape instead -- the display face in the mark, the
   chip on the nav -- neither of which paints a single pixel below the bar. To
   change this value, measure the rows under it first. */
.masthead {
  border-bottom: 1px solid var(--rule);
  background: var(--glass-solid);
  box-shadow: var(--edge), var(--glass-lift);
}

@supports (
  (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))
) {
  .masthead {
    background: var(--glass-1);
    -webkit-backdrop-filter: var(--blur-1);
    backdrop-filter: var(--blur-1);
  }
}

.masthead__bar {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s5);
  flex-wrap: wrap;
  padding: var(--s5) 0 var(--s4);
}

/* ------------------------------------------------------- sticky chrome --
 *
 * The glass only means anything once something scrolls under it, so the
 * masthead has to stick. Above 48rem it does, and it tightens its own padding
 * at that width to earn the room, type untouched. Nothing resizes on scroll --
 * text that shrinks while a person is reading is the commonest tell of a
 * templated site and it buys nothing a padding step does not. The heights the
 * padding step leaves are in the table further down; read them there, because
 * there are three of them and there used to be one.
 *
 * BELOW 48rem IT DOES NOT STICK, and that is measured rather than
 * squeamish: the nav wraps to two rows at 640px and to three below 480px,
 * so the masthead is 193px and then 253px tall. A quarter of a phone
 * screen permanently occupied by chrome is worse than scrolling to the
 * top. The tab rail still sticks there, at top 0, because it is 46px and
 * it is the only thing telling a reader where they are on an 8,800px page.
 *
 * ONE NUMBER WAS WRONG FOR HALF THE WIDTHS IT COVERED, and this is the
 * correction. --stuck read 7rem -- 112px -- from 48rem all the way up, and the
 * comment above it claimed the bar was 124px at rest and 112px stuck at every
 * one of those widths. It is not. The bar does not collapse on scroll at all,
 * because nothing here runs on scroll; the padding step is the media query, and
 * it applies whether the page has moved or not. What changes with width is how
 * many rows the nav wraps to, and that is a content result, not a token.
 *
 * MEASURED IN CHROMIUM, stepping 4px from 760 to 1460, worst case across eight
 * screens -- /projects, a project, /review, a change, /workflow, /actions,
 * /proceedings, /escalations -- and across all three demonstration accounts,
 * reading .masthead's own height at rest and after scrolling 3000px (the two
 * agree everywhere, because nothing here runs on scroll):
 *
 *     768 to 855    233px   nav on three rows
 *     856 to 1171   164px   nav on two rows
 *     1172 and up   110px   nav on one row
 *
 * ALL THREE ACCOUNTS, not one, and that is where a first attempt at this went
 * wrong. The administrator gets an eighth nav item and the analyst does not, so
 * the nav wraps at a different width for each of them and a table measured on
 * one account is 69px short for another. The bands above are the worst case, so
 * the clearance is right for the reader who has the most chrome and merely
 * generous for the reader who has less.
 *
 * So 7rem was 121px short at 768 and 52px short across the whole 856-1171 band.
 * --stuck feeds --focus-anchor, so in that band a keyboard reader tabbing down
 * a page landed the next control under the bar and read it as unfocused -- the
 * exact failure --focus-anchor exists to prevent -- and .tabs, which sticks at
 * top: var(--stuck), parked its rail behind the masthead instead of under it.
 *
 * The token now steps where the bar steps. The breakpoints sit on the measured
 * step, not near it: 53.5rem is 856px and 73.25rem is 1172px, the first width
 * in each band. The values are the measured heights, not rounded up -- 233px is
 * 14.5625rem, 164px is 10.25rem, and 110px keeps 7rem because 2px of slack
 * costs nothing and every earlier comment in this file quotes that number.
 *
 * Anything that makes this bar taller, or that moves where the nav wraps, has
 * to remeasure all three bands in the same edit.
 */
@media (min-width: 48rem) {
  :root {
    --stuck: 14.5625rem;
  }

  .masthead {
    position: sticky;
    top: 0;
    z-index: 30;
  }

  .masthead__bar {
    padding: var(--s4) 0 var(--s3);
  }
}

@media (min-width: 53.5rem) {
  :root {
    --stuck: 10.25rem;
  }
}

@media (min-width: 73.25rem) {
  :root {
    --stuck: 7rem;
  }
}

/* The weight floor for text on glass. A blurred backdrop lowers apparent
   contrast even where the measured figure holds, because the eye reads
   edges and a blur softens them. Nothing on a pane goes below 500, which
   is why the nav below is 600 and its current item 700 rather than 400 and
   600. Do not reach for -webkit-font-smoothing: antialiased to compensate:
   on a Mac it thins the strokes, which is the opposite of what is wanted.

   MEASURED, and this is the one place the ladder bit. The G1 pane over the
   flat page floor is --ink-3 at 4.57:1 by token arithmetic and worse than that
   in use -- see below. Over a claim card it is 4.82:1, which is why it was
   missed. So the two labels a person actually reads up here, the "viewing"
   qualifier and the tenant id, take --ink-2 instead: 8.01:1.

   WHAT IS NOT FIXED, AND WHY. .nav__link--off keeps --ink-3 and keeps its
   shortfall. That colour is the only signal saying the item is not a link --
   it is a <span aria-disabled>, not an <a> -- and raising it to --ink-2 would
   make an unreachable screen look exactly like a reachable one. A nav item
   that leads nowhere while looking live is a small lie, and this product's
   argument is that it does not tell those. The cost is written down here
   rather than discovered later.

   AND IT WAS BRIEFLY WRITTEN UP AS FIXED, WHICH IT IS NOT. This paragraph
   spent a day claiming that recomputing --glass-1 had moved the pane to
   rgb(247,249,250), put --ink-3 at 4.57:1, and cleared AA "with the greyed nav
   item still plainly greyed" -- so that nobody had to choose between the honest
   signal and the contrast floor. The recompute was real. The conclusion was
   not. 4.57 is ratio(--ink-3, --glass-solid): the flat token, not the pane.
   (That episode belongs to the teal palette, before 2026-08-07. 4.57 was the
   teal token's figure, and the indigo token returns 4.57 to the hundredth as
   well -- a coincidence of two palettes, not a figure carried over. The error
   is what is kept here, not the digits, and the error is unchanged by the
   coincidence: it is still the flat token, not the pane.)

   The pane is glass and this masthead is sticky, so something is always
   scrolling underneath it. Sampled across the whole .nav__link--off box on a
   loaded /proceedings at 1440x900 dpr 2 with the masthead's own text set
   transparent, the backdrop ran rgb(227,230,232) to rgb(245,247,249) --
   --ink-3 3.85:1 to 4.49:1. Its BEST pixel was still under AA. Identical at
   1280, 1216 and 1200.

   THAT SAMPLE EXPIRED WITH THE TEAL PALETTE and nothing has replaced it. The
   indigo pane has not been put in front of a browser, so this file does not
   know its best pixel, its worst, or the range between them, and it will not
   guess: the numbers above are the old page's and no arithmetic over the new
   tokens reproduces them. What did not expire is the reasoning. The flat token
   is a CEILING and the rendered pane sits below it.

   AND THE CEILING NOW CLEARS AA, WHICH IS THE MOST DANGEROUS STATE THIS COMMENT
   HAS BEEN IN. The indigo token is 4.57:1 -- seven hundredths over the floor.
   That is the exact figure, to the hundredth, that this same comment records
   being written up as a fix and being wrong, because a ceiling is not a pane.
   The only time anybody put a browser in front of a pane here, the rendered
   backdrop came in between 0.08 and 0.72 below its token. Nothing has measured
   the indigo pane, so the honest reading is: the token passes, the pane is
   unknown, and a seven-hundredth margin on the ceiling is not a pass for the
   rendered case. Do not write this one up as fixed a second time.

   The trade this block described as dissolved is still live, and it is still
   resolved the same way: the honest signal is worth more than a shortfall that
   nothing has measured. What actually helps the reader who needs the contrast is
   the prefers-contrast: more block at the foot of this file, which raises
   --ink-3 to #49536b -- a real step, and still a step away from --ink-2.

   Two numbers admitting a shortfall have now been optimistic in this one
   comment, and both were optimistic because somebody measured a token and
   called it a pane. tests/test_glass_contrast.py pins the token figures so the
   third one has to be argued for rather than typed. */
.masthead .label,
.masthead .coord {
  color: var(--ink-2);
}

.masthead .coord {
  font-weight: 500;
}

/* The wordmark sits on three bars of falling width: layers, newest on top.
   Drawn in CSS, because an icon font would be a dependency (ADR-012). */
.wordmark {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 5px;
  color: var(--ink);
  font-size: var(--t-ui-lg);
  font-weight: 650;
  letter-spacing: 0.01em;
  text-decoration: none;
}

.wordmark:hover {
  color: var(--ink);
}

.wordmark .logo {
  /* The lockup carries the name, so the old text node and the three-rule
     span underneath it are gone. The tile keeps its accent fill in both
     schemes -- it is the one place the brand colour is not doing UI work --
     and the name takes currentColor so it follows the masthead's ink. */
  display: block;
  height: 30px;
  width: auto;
  color: var(--ink);
}

/* THE NAME IN THE MARK IS SET HERE, NOT IN THE MARKUP. The <text> inside the
   lockup carries font-family and letter-spacing as presentation attributes, and
   a presentation attribute is beaten by any rule in a stylesheet -- which is
   what lets the mark take the display face without editing the SVG. The SVG is
   left alone on purpose: the same lockup is pasted into deploy/site/index.html
   and the two are meant to stay byte-identical, so the fallback stack in the
   attribute stays as the answer for a page that never loads this sheet.

   THE TRACKING IS THE TOKEN, NOT THE 0.01em IN THE ATTRIBUTE. Manrope is a
   geometric face and the display family's tracking is a function of size; the
   name is set at 20px, so it takes --track-h1, the value chosen for the nearest
   size in that family. Two thousandths of an em from what the attribute said,
   and now it moves when the family moves.

   The name is a <text> in a fixed 132x32 viewBox and an outer <svg> clips to
   its viewport, so a wider face cuts the last letter and nothing says so.
   MEASURED, in Chromium with the font loaded: the name's box runs from x=42 to
   x=100.9, against 132. The system stack it replaced ran to 97.0, so Manrope
   costs four units and leaves thirty-one. There is room, and there is not so
   much room that a longer word could be dropped in without measuring again. */
.wordmark .logo text {
  font-family: var(--face-display);
  letter-spacing: var(--track-h1);
}

.wordmark__mark {
  display: block;
  width: 4.6rem;
  height: 8px;
  background-image: linear-gradient(var(--ink) 0 0),
    linear-gradient(var(--ink-3) 0 0), linear-gradient(var(--rule-strong) 0 0);
  background-size: 100% 2px, 66% 2px, 38% 2px;
  background-position: 0 0, 0 3px, 0 6px;
  background-repeat: no-repeat;
}

.masthead__company,
/* Same shape as the company block beside it. Who is holding the page is the
   same KIND of fact as which tenant is open -- both are context for everything
   below, neither is an action -- so they read as one row rather than as a
   label and an afterthought. */
.masthead__who {
  display: flex;
  align-items: baseline;
  gap: var(--s2);
  flex-wrap: wrap;
}

.masthead__name {
  font-weight: 600;
}

/* Nav — four screens. Two of them need a proceeding or a change in hand, and
   when there is none the item is plainly not a link. A nav item that leads to
   a 404 is a small lie, and this product's argument is that it does not tell
   those. */
.nav__list {
  display: flex;
  gap: var(--s5);
  flex-wrap: wrap;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* A TAB, NOT A WORD: soft at the top, square where it meets the page. The item
   is a chip with --r-control on its top corners and no radius at all on the
   bottom two, so the 2px rule underneath stays a straight bar. That rule is
   load-bearing three times over -- accent for the screen you are on, a plain
   grey on hover, dashed for an item that leads nowhere -- and a chip with four
   rounded corners has nowhere to draw it.

   THE PADDING COSTS THE LAYOUT NOTHING, AND IT HAS TO NOT COST IT ANYTHING. The
   horizontal padding is cancelled by an equal negative margin, so each item
   occupies exactly the width it occupied before this chip existed. That is not
   a trick for its own sake: --stuck is this bar's measured height, and every
   sticky offset and every focus anchor in this file is derived from it. Seven
   items each sixteen pixels wider would spend the slack the nav row has left,
   wrap it a row earlier than it wraps now, and move the width at which the bar
   steps -- with no test failing and no way to see it except by opening a
   browser at that one width. Measured both ways in Chromium: masthead 110px at
   1440 and at 900, 164px at 800, 233px at 768, identical before and after this
   chip to the pixel.

   THOSE THREE FIGURES WERE ALREADY WRITTEN HERE WHILE --stuck STAYED AT 7rem,
   which is 112px, and nobody reconciled the two. Reading a measurement and not
   spending it is the same defect as not measuring. The sticky block now carries
   all three. Grow the chip and you must remeasure every one of them.

   THE GAP IS WHY THE PADDING IS 8px AND NOT 12px. At 12px the chip is exactly
   as wide as the gap between items, so two neighbouring chips touch -- and the
   two items that are switched off sit next to each other, which drew their two
   dashed rules as one continuous dashed line under both. Eight leaves eight,
   which is enough to read them as two items. */
.nav__link {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s2) var(--s3);
  margin: 0 calc(var(--s2) * -1);
  border-bottom: 2px solid transparent;
  border-radius: var(--r-control) var(--r-control) 0 0;
  color: var(--ink-2);
  font-size: var(--t-ui);
  font-weight: 600;
  text-decoration: none;
}

/* Mixed from --ink rather than named as a colour, so it is a lift in the dark
   scheme and a shade in the light one without a second token. */
a.nav__link:hover {
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  color: var(--ink);
  border-bottom-color: var(--rule-strong);
}

/* BOTH SELECTORS, AND THE SECOND ONE IS THE POINT. `a.nav__link:hover` carries
   more specificity than a single class, so with only the class here the wash
   came off the current item the moment a pointer crossed it -- the one item on
   the bar that must never stop saying where you are. The mark is still carried
   by three things and only one of them is colour: the weight, the accent rule
   underneath, and aria-current in the markup for anything not reading pixels.
   --ink on --accent-wash is 14.65:1 in light and 14.23:1 in dark. */
.nav__link--current,
a.nav__link--current:hover {
  background: var(--accent-wash);
  color: var(--ink);
  font-weight: 700;
  border-bottom-color: var(--accent);
}

/* The two items that need a proceeding or a change in hand. They are drawn
   rather than dropped, because nav that appears and vanishes as you move
   cannot be learned -- and this product's rule everywhere else is that a thing
   which cannot be shown NAMES ITS REASON.

   THE REASON USED TO LIVE IN A title ATTRIBUTE, which is to say it did not
   live anywhere a keyboard could reach. Hence tabindex="0" in base.html and
   the panel below, shown on hover and on focus alike.

   MADE TO LOOK DELIBERATE. --ink-3 alone reads as a rendering fault -- the
   same word as its neighbours, a shade weaker, no other difference. The
   dashed rule underneath says the same thing this stylesheet says everywhere
   else it draws a dashed edge: provisional, not in force. cursor: help says
   there is something to read here, which there now is. */
.nav__link--off {
  position: relative;
  color: var(--ink-3);
  border-bottom-style: dashed;
  border-bottom-color: var(--rule-strong);
  cursor: help;
}

/* The reason, in text, on hover and on focus.

   IT IS NEVER TAKEN OUT OF THE ACCESSIBILITY TREE. clip-path and opacity hide
   it from the eye and leave it in the tree, so it is part of the item's
   accessible name at all times and a screen reader speaks it on arrival --
   before, and regardless of, anything to do with hover. display: none or
   visibility: hidden would have hidden it from both, which is how a "visually
   hidden" helper usually turns into no helper at all.

   THE POINTER CAN REST ON IT. WCAG 1.4.13 wants content shown on hover to be
   hoverable, and this panel is a CHILD of the item, so :hover still matches
   while the pointer is over the panel itself. pointer-events go back to none
   while it is hidden, so a clipped box never eats a click.

   WHAT THIS STILL DOES NOT DO, said plainly rather than discovered later:
   1.4.13 also asks that such content be dismissible without moving the pointer
   or the focus, which normally means Escape, which means script. There is none
   here. The panel is small, sits under its own item, and goes the moment the
   pointer or the focus leaves. The attribute it replaced met none of 2.1.1,
   4.1.2 or 1.4.13, so this is a step and not the last one. */
.nav__why {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 1;
  width: max-content;
  max-width: min(20rem, 70vw);
  padding: var(--s2) var(--s3);
  border: 1px solid var(--rule-strong);
  /* Chrome, and the softest kind of it: a panel that arrives over the page and
     leaves again. --r-control matches the chip it hangs from and --lift-2 is
     the depth of a thing a reader has picked up, which is what this is. Neither
     goes near the record; the diff, the claims and the quoted source keep --r
     and stay flat. */
  border-radius: var(--r-control);
  background: var(--surface);
  color: var(--ink-2);
  font: var(--t-small) / var(--lh-ui) var(--face-ui);
  font-weight: 500;
  box-shadow: var(--lift-2);
  clip-path: inset(50%);
  opacity: 0;
  pointer-events: none;
}

/* AND ON A NARROW SCREEN IT HANGS OFF THE END OF THE PAGE, so below 40rem it
   stops hanging from the item and spans the nav instead.
 *
 * An absolutely positioned box adds nothing to its parent's layout width, so
 * nothing in the nav grew and no static check saw anything. It does extend the
 * document's SCROLLABLE area. The two switched-off items sit third and fourth
 * in the row, which on a phone puts one of them at the right-hand end of the
 * first wrapped row, and a 269px panel anchored to its left edge ran clean off
 * the screen: measured in Chromium, document 484px against a 390px client on
 * /projects, a project, /review and /workflow, 484 at 414, 599 at 500, and
 * clear from 600 up. Every one of those pages scrolled sideways because of this
 * one box.
 *
 * THE FIX IS NOT TO HIDE IT. The hint is the whole reason the item is still in
 * the tab order -- tests/test_a11y_guards.py requires it as rendered text in
 * the accessible name, not a title attribute -- so shrinking it away, clipping
 * it or dropping it below a breakpoint would trade a scrollbar for the defect
 * this panel was written to fix.
 *
 * So it changes what it hangs from. `.nav__link--off` goes back to static, the
 * list becomes the containing block, and the panel spans the list from edge to
 * edge: it cannot be wider than the nav, and the nav cannot be wider than the
 * page. The vertical tie loosens -- on a phone the nav has wrapped to three
 * rows and the panel lands under the last of them rather than under its own --
 * but it is still inside the nav block, still shown by the same hover and the
 * same focus, and every word of it is on screen. Above 40rem nothing changes at
 * all: the per-item panel fits from 600px up, and 640 leaves 40px of slack over
 * the widest width that ever failed. */
@media (max-width: 40rem) {
  .nav__list {
    position: relative;
  }

  .nav__link--off {
    position: static;
  }

  .nav__why {
    right: 0;
    width: auto;
    max-width: none;
  }
}

.nav__link--off:hover .nav__why,
.nav__link--off:focus .nav__why,
.nav__link--off:focus-visible .nav__why {
  clip-path: none;
  opacity: 1;
  pointer-events: auto;
}

.nav__count {
  padding: 0.1em 0.4em;
  border: 1px solid var(--alarm);
  border-radius: var(--r-sm);
  background: var(--alarm-wash);
  color: var(--alarm-strong);
  font: 700 var(--t-micro) / var(--lh-micro) var(--face-data);
}

.main {
  display: block;
  padding: var(--s7) 0 var(--s8);
}

.page-head {
  margin-bottom: var(--s6);
  padding-bottom: var(--s4);
  border-bottom: 1px solid var(--rule);
}

.page-head__title {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  flex-wrap: wrap;
}

.page-head__note {
  margin-top: var(--s2);
  color: var(--ink-2);
  max-width: 60ch;
}

/* The labelled facts about the thing on screen: its jurisdiction, its owner,
   its docket. They were three label-and-value pairs run together inside one
   paragraph, so the page read "JURISDICTION State of Meridian OWNER Sarah
   Lindqvist DOCKET MPUC-2026-0142" as a single line of text and a reader had
   to use the uppercase labels as punctuation. Each pair is a unit now, and the
   row wraps rather than holding all three on one line. */
.page-head__facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2) var(--s5);
  margin-top: var(--s3);
  color: var(--ink-2);
}

.page-head__fact {
  display: inline-flex;
  align-items: baseline;
  gap: var(--s2);
}

/* The model's judgement, on the change screen and nowhere else yet.
 *
 * It sits in the page head, where "not assessed" used to be printed, and it
 * gets more room than a note because it carries a verdict, a reason, a quote
 * and an address. The four states are not four shades of one treatment:
 *
 *   --judged      a verdict that passed the gate. Badge, sentence, the source's
 *                 own words underneath, then who said it and when.
 *   --withheld    the citation did not verify. Borrows .withheld and .mismatch
 *                 from the claim further down the page, because it IS the same
 *                 mechanism refusing, and a reader who has met one recognises
 *                 the other with nothing to read.
 *   --dropped     the answer never reached the verifier. Same hatch, and the
 *                 detail says which field was wrong.
 *   --unjudged    nothing ran. A sentence, no badge, no hatch -- an absence of
 *                 work is not a refusal and must not borrow its clothes.
 *
 * No rule paints a verdict by colour alone: the badge classes carry a border
 * and a weight, so the page survives a monochrome print. */
.materiality {
  margin-top: var(--s4);
}

.materiality > * + * {
  margin-top: var(--s3);
}

.materiality .quote {
  margin-top: var(--s3);
}

.colophon {
  border-top: 1px solid var(--rule);
  padding: var(--s5) 0 var(--s7);
  color: var(--ink-3);
  font-size: var(--t-small);
}

.colophon p + p {
  margin-top: var(--s1);
}

/* Change detail: the claim column, and the source beside it. The source panel
   holds its place while the claim column scrolls, so opening a citation never
   costs the reader their position. */
.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(22rem, 26rem);
  gap: var(--s6);
  align-items: start;
}

/* Clear of the sticky masthead. Without this the panel slides under it and
   the reader loses the top of the cited section, which is the one line of
   a source extract they came for. */
.split__aside {
  position: sticky;
  top: calc(var(--stuck) + var(--s4));
}

/* The dead column. Measured on the screen this whole product rests on: the
   aside is 416px wide and 0px tall with every citation panel closed, so
   41% of the change page is empty. Collapse the track when nothing is in
   it.

   The cost, stated rather than hidden: pressing a citation chip now
   reflows the grid in one frame. Do NOT transition grid-template-columns
   to smooth that -- it janks, and it drags the claim text sideways while
   somebody is reading it. The panel's own 200ms fade covers the change.
   Floating the panel over the claim instead was rejected outright: the
   reader is comparing a quote against its source and both have to be on
   screen at once, which is the whole reason the panel is beside the claim
   rather than in a modal. A browser with no :has() keeps today's dead
   column and loses nothing else. */
.split:not(:has(.source-panel:not([hidden]))) {
  grid-template-columns: minmax(0, 1fr);
}

@media (max-width: 62rem) {
  .split {
    grid-template-columns: minmax(0, 1fr);
  }

  .split__aside {
    position: static;
  }
}

/* A list whose items are cards. The marker goes: a bullet beside a bordered
   box is a bullet beside a box, and it lands to the LEFT of the box, outside
   the column everything else on the page lines up to.

   review_centre.html has carried a note saying this since the three lists were
   given the class -- and the class had no rule, in this file or in any other,
   so every finding and every open question arrived with a bullet anyway. A
   template that names a class the stylesheet never heard of fails the same way
   a stylesheet that styles a class no template renders does, and it fails
   quietly in the same way: the page looks deliberate, it is just wrong. */
.plain-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.stack > * + * {
  margin-top: var(--s5);
}

.stack--tight > * + * {
  margin-top: var(--s3);
}

/* ------------------------------------------------------------ primitives -- */

/* The micro-label. Names the field beside it and does nothing else. */
.label {
  color: var(--ink-3);
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

/* A coordinate: version, section, character range. Monospace because it is
   machine-checkable, and looking checkable is the honest signal here. */
.coord {
  font: var(--t-micro) / 1.5 var(--face-data);
  /* Restated because the line above is the `font` shorthand, which resets
     font-variant to normal and so throws away the tabular-nums the figures
     rule sets earlier in this file. Monospace is already tabular, so nothing
     moves on screen -- but a rule that silently does nothing is the kind that
     gets copied to a proportional face and quietly fails there. */
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
  letter-spacing: 0;
  /* THIS SAID `white-space: nowrap` AND IT SET THE WIDTH OF A WHOLE SCREEN.
     Held on one line, the change id in the change screen's page head --
     CHG-ga-55378-...-revised-pd-002, 70 characters -- made the document 833px
     wide inside a 390px client, measured in Chromium at 390x844. Three more
     .coord spans on the same page ran to 494 and 414. The class is rendered by
     152 places in the templates, so tagging the long ones one at a time is the
     line and not the class.

     `overflow-wrap: anywhere` says the same thing the nowrap was protecting,
     with a floor under it: a word breaks only when it has no break opportunity
     and would otherwise overflow its line. A range like 316-332 is seven
     characters and always fits, so it never breaks and never has to. A document
     id does not fit, and now wraps rather than widening the page. */
  overflow-wrap: anywhere;
}

/* Help under a field, and any short sentence a page adds beside a control.
 *
 * IT IS PROSE AND IT WRAPS, which is the whole reason it is not .coord. A
 * coordinate is `white-space: nowrap` on purpose, and a whole sentence held on
 * one line sets the page's minimum width -- the defect tests/test_responsive.py
 * was written for, and one project_list.html has carried a note about since it
 * was found there.
 *
 * THE CLASS WAS RENDERED BY TWO TEMPLATES AND STYLED BY NOTHING. Whoever moved
 * those hints out of .coord named a class and never wrote its rule, so the hint
 * came out at body size in body ink and read louder than the label above it.
 * That is the same defect as a rule keyed to a class nothing renders, in the
 * other direction, and it is just as quiet: the page looks fine, it simply says
 * the wrong thing loudest. */
.field-hint {
  display: block;
  max-width: var(--measure);
  margin-top: var(--s1);
  color: var(--ink-3);
  font-size: var(--t-small);
  line-height: var(--lh-ui);
}

/* PAPER. Opaque, flat, still: no blur, no motion, no shadow past a
   hairline. Everything that is a record of something wears it -- this
   card, a verified claim, the source extract, a deliverable, a table.
   If a change ever puts a tariff clause on something translucent, the
   change is wrong. */
.card {
  padding: var(--s5);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--paper);
}

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-ui);
}

.table th {
  padding: var(--s2) var(--s3);
  border-bottom: 1px solid var(--rule-strong);
  color: var(--ink-3);
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  text-align: left;
  white-space: nowrap;
}

.table td {
  padding: var(--s3);
  border-bottom: 1px solid var(--rule);
  vertical-align: baseline;
}

.table tr:last-child td {
  border-bottom: 0;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s4);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--ink);
  font: 600 var(--t-small) / 1.4 var(--face-ui);
  /* Ten links across nine templates wear this class, and every one of them
     arrived with the underline a link gets by default sitting inside a filled
     button -- "Sign in to Verbatim" on the shared view, "Open a new project" on
     the project list, "Go to Projects" on the review centre. A control that
     carries a border, a fill and a hover state has already said it is a
     control, so the underline only says it twice and says it badly. Taking it
     off a bare link would be wrong for exactly the reason it is right here.
     Nothing about the focus ring changes: it comes from the shared :focus-visible
     rule and is untouched. */
  text-decoration: none;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease;
}

.btn:hover {
  border-color: var(--ink-3);
  background: var(--bg);
}

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

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

@media (prefers-color-scheme: dark) {
  .btn--primary,
  .btn--primary:hover {
    color: var(--surface-sunk);
  }
}

/* Text controls.
 *
 * THERE WAS NO RULE FOR ONE ANYWHERE IN THIS STYLESHEET. Every field on the
 * sign-in page, the new-project form and the steer form rendered as the
 * browser's own control -- a different shape, a different face and a different
 * ink from the `.btn` standing beside it in the same row. Both component sheets
 * had already written this rule for their own fields, and both carry the same
 * note saying why: the head of this family is `.btn`, which is in this file.
 * Two sheets agreeing about a rule the file they defer to never wrote is how a
 * product ends up with three kinds of field.
 *
 * So these take `.btn`'s border, radius, face and padding and nothing else,
 * and `--r` moves here with `.btn` when it moves. Grep `--r` across
 * verbatim.css, clerk.css and workflow.css and the whole family is in front of
 * you.
 *
 * max-width CAPS AN INTRINSIC WIDTH. `size="52"` on an input is a width in
 * characters, and an intrinsic width wider than the screen is one of the two
 * ways a page grows past its viewport. The attribute still does useful work on
 * a wide screen, so it stays and the cap goes here.
 *
 * Scoped by type, so a checkbox, a radio, a range and a hidden field keep the
 * control the platform draws. */
input[type="text"],
input[type="email"],
input[type="password"],
input[type="search"],
input[type="url"],
input[type="tel"],
input[type="number"],
input[type="date"],
select,
textarea {
  max-width: 100%;
  padding: var(--s2) var(--s3);
  border: 1px solid var(--rule-strong);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--ink);
  font: var(--t-ui) / var(--lh-ui) var(--face-ui);
}

/* ------------------------------------------------------ a control that is off

   Nothing in this file said what disabled looks like, so it looked like on.
   Measured on the frozen approval route: the name field and three buttons all
   carried the disabled attribute, all reported opacity 1 and cursor pointer,
   and Activate kept the full accent fill. A person taps the blue button, the
   page does nothing, and the product looks broken rather than careful -- the
   same failure a dead grid cell with no reason makes, which the permissions
   screen already goes out of its way to avoid.

   The reason is still text, always: the route says "This route is frozen. Copy
   it into a new draft to change it." right under those buttons. This only stops
   the control contradicting the sentence beside it. Colour is never the only
   signal -- the cursor changes too, and the disabled attribute is what a screen
   reader was already reading.

   :disabled and not a class, so a control switched off by script gets it too.
   clerk.js turns Send off while a request is in flight and workflow.js turns
   the step controls off when nothing is selected; both were invisible. The
   hover rules are cancelled here as well, or a dead button still lights up
   under the pointer, which is the same defect wearing a different hat.

   IT SITS BELOW THE FIELD RULE ABOVE ON PURPOSE. input:disabled and
   input[type="text"] carry the same specificity, so the one written later wins,
   and written earlier this block lost every text field back to the live look.
   Measured, not assumed. Anything added between the two has to stay above. */
.btn:disabled,
.btn[aria-disabled="true"],
button:disabled,
input:disabled,
select:disabled,
textarea:disabled {
  border-color: var(--rule);
  background: var(--surface-sunk);
  color: var(--ink-3);
  cursor: not-allowed;
}

.btn:disabled:hover,
.btn--primary:disabled,
.btn--primary:disabled:hover,
button:disabled:hover {
  border-color: var(--rule);
  background: var(--surface-sunk);
  color: var(--ink-3);
}

/* Badges. Shape and weight carry the meaning; colour only agrees with it.
   Dashed outline = provisional. Solid fill = binding. Barred = material. */
.badge {
  display: inline-flex;
  align-items: stretch;
  gap: 0.4em;
  padding: 0.28em 0.5em;
  border-radius: var(--r-sm);
  font: var(--t-micro) / 1.35 var(--face-ui);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  white-space: nowrap;
}

.badge--draft {
  border: 1px dashed var(--rule-strong);
  background: transparent;
  color: var(--ink-2);
  font-weight: 500;
}

.badge--final {
  border: 1px solid var(--ink);
  background: var(--ink);
  color: var(--surface);
  font-weight: 700;
}

.badge--material {
  border: 1px solid var(--alarm);
  background: var(--alarm-wash);
  color: var(--alarm-strong);
  font-weight: 700;
}

.badge--material::before {
  content: "";
  width: 3px;
  background: currentColor;
}

/* The counterpart to material. It exists so that materiality is read by
   contrast rather than by remembering what one colour means. */
.badge--routine {
  border: 1px solid var(--rule);
  background: transparent;
  color: var(--ink-3);
  font-weight: 500;
}

/* Project status. The three names are PROJECT_STATUSES in app/state/models.py
   and this file follows them rather than inventing prettier ones -- a class the
   template has to translate is a class that will be translated wrongly.
   They sit on the axis the other badges already use: dashed is provisional,
   solid outline is in force, filled is settled. Monitoring is watching without
   acting, which is the same kind of fact as a draft not binding anything, so it
   borrows the dashed edge rather than a new shape. */
.badge--active {
  border: 1px solid var(--ink);
  background: transparent;
  color: var(--ink);
  font-weight: 700;
}

.badge--monitoring {
  border: 1px dashed var(--rule-strong);
  background: transparent;
  color: var(--ink-3);
  font-weight: 500;
}

/* Filled, like final, because a closed project is settled -- but filled in
   ink-3 rather than ink, so it reads as retired rather than binding. Not
   rule-strong: white on rule-strong falls under 3:1 in both schemes, and a
   label nobody can read is not a quiet label, it is a broken one. */
.badge--closed {
  border: 1px solid var(--ink-3);
  background: var(--ink-3);
  color: var(--surface);
  font-weight: 600;
}

/* --------------------------------------------------- the document register -- */

/* Anything set in .doc is the document talking, not Verbatim. */
.doc {
  max-width: var(--measure);
  font: var(--t-doc) / var(--lh-doc) var(--face-doc);
  color: var(--ink);
}

.quote {
  max-width: var(--measure);
  margin: 0;
  padding: var(--s3) 0 var(--s3) var(--s4);
  border-left: 2px solid var(--rule-strong);
  font: var(--t-doc) / var(--lh-doc) var(--face-doc);
  color: var(--ink-2);
}

/* ------------------------------------------------------------- the diff -- */

/* THE TWO SIDES ARE A PAIR, SO THEY ARE LAID OUT AS A PAIR.
 *
 * change.html has carried the specification for this rule -- and a note saying
 * the rule did not exist -- since the markup landed. Stacked, the before and
 * the after sat about a hundred pixels apart, and on the change this product
 * demonstrates they differ by four words. A reader had to hold the first
 * sentence in their head to read the second, which is the one thing a diff
 * exists to stop.
 *
 * PAPER, AND IT STAYS PAPER. This is the filing itself. No gradient, no
 * --r-card, no lift, no motion: only geometry changes here, and
 * tests/test_design_guards.py holds the gradient half of that.
 *
 * Grid rather than flex, so the two halves are the same width whatever the two
 * passages weigh. A wider "before" would read as the more important side, and
 * neither side is.
 *
 * THE INHERITED MARGIN HAS TO GO, and the template said so before this rule
 * was written. .diff also carries .stack, so the sides keep a gap on a browser
 * that never got this rule; with the rule, `.stack > * + *` puts a top margin
 * on the second column and drops it half a line below the first. Two classes,
 * so it beats `.stack > * + *` on specificity rather than on file order. */
.diff {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s5);
  align-items: start;
}

.diff.stack > * + * {
  margin-top: 0;
}

/* Which side, which version, and where in it. One line above the passage, in
   interface type, so nothing here competes with the document face below it. */
.diff__head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--s2);
  color: var(--ink-2);
  font-size: var(--t-small);
}

/* Below this the two columns are narrower than the passage wants and the pair
   stops being readable, so it stacks again -- and the inherited gap comes back
   with it, because once the grid is gone that margin is the only thing between
   the two sides. Putting it back inside the breakpoint is the other half of
   the instruction the template left. */
@media (max-width: 52rem) {
  .diff {
    grid-template-columns: minmax(0, 1fr);
  }

  .diff.stack > * + * {
    margin-top: var(--s5);
  }
}

.source-panel {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--paper);
  overflow: hidden;
}

.source-panel__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  flex-wrap: wrap;
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--rule);
  background: var(--surface-sunk);
}

.source-panel__title {
  font-size: var(--t-small);
  font-weight: 600;
}

.source-panel__body {
  max-height: 62vh;
  overflow: auto;
  padding: var(--s4) var(--s5) var(--s6);
  /* The source is preformatted text with real line breaks. Keep them: the
     shape of a filing is part of what the analyst reads. */
  white-space: pre-wrap;
  overflow-wrap: break-word;
  font: var(--t-doc) / var(--lh-doc) var(--face-doc);
  color: var(--ink-2);
  scroll-behavior: smooth;
}

.source-panel__close {
  padding: var(--s1) var(--s2);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--ink-2);
  font: 600 var(--t-micro) / var(--lh-micro) var(--face-ui);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  cursor: pointer;
}

.source-panel__close:hover {
  border-color: var(--rule-strong);
  color: var(--ink);
}

/* The marked span: exactly the characters the citation covers. Weight and a
   rule under every line do the work, so the extent survives a monochrome
   screenshot. No character is inserted into the source, because annotating a
   quotation by altering it is the habit this product exists to remove.

   No side edges: a wrapped span would then draw an edge at each line break and
   read as several marks instead of one. The tint starts and stops at the true
   offsets, which is the boundary that matters. */
.source-mark {
  padding: 0.06em 0.1em;
  background: var(--accent-wash);
  box-shadow: inset 0 -2px 0 var(--accent);
  color: var(--ink);
  font-weight: 600;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

/* ------------------------------------------------------------- the claim -- */

/* Two states that must not read as two shades of one treatment.
 *
 * .claim--verified   an assertion. Solid left rule, flush to the column, the
 *                    statement set in the document register, a citation chip
 *                    at the foot.
 *
 * .claim--withheld   not an assertion, and structurally not one. The statement
 *                    position is occupied by a hatched slot naming the reason.
 *                    Broken left rule instead of solid, inset from the column
 *                    so it does not sit on the assertion axis, sunk instead of
 *                    raised, no chip, and a quoted-against-source pair that a
 *                    verified claim never carries. Six differences, none of
 *                    them colour, so it survives a monochrome screenshot and
 *                    needs no legend.
 */
.claim {
  padding: var(--s4) 0 var(--s4) var(--s5);
  /* change.html gives every claim an id and links straight to it. --anchor
     clears the sticky chrome; see .tabpanel, in the tabs section below,
     for how the number is built. */
  scroll-margin-top: var(--anchor);
}

.claim__head {
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex-wrap: wrap;
  margin-bottom: var(--s3);
}

.claim__state {
  font-size: var(--t-micro);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

/* The statement is Verbatim talking, so it stays in the interface face. Setting
   a finding in the document face would be the register mistake this stylesheet
   exists to prevent: the reader could no longer tell, at a glance, which words
   the commission wrote and which words the product did. It earns its weight by
   size, not by borrowing the document's clothes. */
.claim__statement {
  max-width: 58ch;
  font: 500 var(--t-claim) / 1.55 var(--face-ui);
  letter-spacing: -0.006em;
  color: var(--ink);
}

.claim__foot {
  display: flex;
  align-items: center;
  gap: var(--s3);
  flex-wrap: wrap;
  margin-top: var(--s4);
}

/* The way out to the filing itself, under a claim's citation. Moved here from
   a <style> block inside change.html, which said in its own comment that these
   two rules belonged beside .claim__foot and were only in the template because
   verbatim.css was not that change's to edit. Both values were already tokens, so
   nothing moves on screen; what changes is that both colour schemes, the print
   block and every guard in tests/ now see them. */
.claim__filing {
  max-width: 62ch;
  margin-top: var(--s4);
}

.claim__filing .claim__route {
  display: block;
  margin-top: var(--s1);
}

.claim--verified {
  border-left: 3px solid var(--ink-3);
  background: var(--paper);
  border-top: 1px solid var(--rule);
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  border-radius: 0 var(--r) var(--r) 0;
  padding-right: var(--s5);
}

.claim--verified .claim__state {
  color: var(--ink-2);
}

.claim--withheld {
  margin-left: var(--s5);
  padding-right: var(--s5);
  border: 1px solid var(--rule);
  border-left: 0;
  border-radius: 0 var(--r) var(--r) 0;
  background: var(--surface-sunk);
  /* A broken rule where a verified claim has a solid one. */
  background-image: repeating-linear-gradient(
    to bottom,
    var(--alarm) 0 6px,
    transparent 6px 12px
  );
  background-size: 3px 100%;
  background-repeat: no-repeat;
  background-position: left top;
}

.claim--withheld .claim__state {
  color: var(--alarm-strong);
}

/* The slot. Whatever a reader's eye reaches for in the statement position, it
   finds this: a filled block that plainly is not a sentence, and the reason. */
.withheld {
  max-width: var(--measure);
  padding: var(--s4);
  border: 1px dashed var(--rule-strong);
  border-radius: var(--r-sm);
  background-color: transparent;
  background-image: repeating-linear-gradient(
    135deg,
    var(--hatch) 0 1px,
    transparent 1px 7px
  );
}

/* Both sit on a solid plate, so the hatch never crosses a word. The plate is
   also the reading: a note laid on struck ground, not text written over it. */
.withheld__label,
.withheld__reason {
  width: fit-content;
  max-width: 100%;
  padding: 0.15em 0.45em;
  background: var(--surface);
  /* max-width: 100% caps the BOX and does nothing to the line inside it, so a
     reason code with no break opportunity simply ran out of its own label:
     CITATION_AMBIGUOUS_OCCURRENCE laid out to 272px in a 165px box, measured in
     Chromium at 320px, and pushed /review to 361px. The cap and the break are
     two different permissions and this pair needed both. */
  overflow-wrap: anywhere;
}

.withheld__label {
  font-size: var(--t-micro);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink);
}

/* The reason is the product speaking, so it stays in the interface face. Set
   it in the document face and it would start to look like the claim it
   replaces. */
.withheld__reason {
  margin-top: var(--s2);
  font-size: var(--t-ui);
  color: var(--ink-2);
}

/* What was quoted, against what the source actually says at those offsets.
   Only a withheld claim carries this. */
.mismatch {
  max-width: var(--measure);
  margin: var(--s4) 0 0;
  border-top: 1px solid var(--rule);
}

.mismatch__term {
  margin-top: var(--s3);
  color: var(--ink-3);
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.mismatch__value {
  margin: var(--s1) 0 0;
  font: var(--t-small) / 1.6 var(--face-data);
  color: var(--ink-2);
  overflow-wrap: break-word;
}

/* THE SAME ARGUMENT AS THE DIFF, ON THE SURFACE THE PRODUCT TURNS ON.
 *
 * What the source says and what the citation quoted were stacked, and on the
 * refusal this product demonstrates they differ by one character: twenty
 * megawatts against ten. A reader had to memorise a sentence to find the word
 * that was wrong in the next one. Side by side, the difference is the thing
 * you see first, which is the entire claim this screen makes.
 *
 * Column-major, so a term stays directly above its own value: with two rows
 * declared and the flow set to column, the four children land dt, dd, dt, dd
 * down then across. Any number of pairs works -- the escalation queue renders
 * a third when a quote appears more than once in a version -- because the
 * columns are implicit.
 *
 * ONLY GEOMETRY. This sits inside .claim--withheld, the stillest surface in
 * the product, and that block already forbids every shadow, transform and
 * transition here. Nothing is added to win the comparison.
 *
 * Paired only where there is room. Below the breakpoint two columns are
 * narrower than the sentences they hold, and a pair nobody can read is worse
 * than a stack. */
@media (min-width: 62rem) {
  .mismatch {
    display: grid;
    grid-template-rows: auto auto;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    column-gap: var(--s5);
    max-width: calc(var(--measure) * 2);
  }
}

/* The judgement block wears .page-head__note, which caps at 60ch. That is right
   for the sentence under a title, and it is right for three of the four
   materiality states. It is wrong for the fourth: a refused judgement carries
   the two-column pair above, and 60ch gives each column thirty characters.

   Scoped with :has so only that state widens. The other three are prose and
   keep the reading measure -- widening all four to fix one would trade a
   squeezed comparison for three sentences running the width of the page. The
   cap the widened one keeps is .mismatch's own. */
.materiality:has(.mismatch) {
  max-width: none;
}

.claim__route {
  color: var(--ink-2);
  font-size: var(--t-small);
}

/* Tripwire, not a safety net. A withheld claim must not carry a statement in
   the HTML at all (ADR-003), and a render test asserts that. Hiding one here
   would make a template mistake invisible, which is the exact failure the rule
   exists to prevent -- so if one ever appears, make it look broken. */
.claim--withheld .claim__statement {
  outline: 2px solid var(--alarm);
  background: var(--alarm-wash);
  color: var(--alarm-strong);
  text-decoration: line-through;
}

.claim--withheld .claim__statement::before {
  content: "Template error: a withheld claim must not carry a statement. ";
  display: block;
  font: 700 var(--t-micro) / var(--lh-micro) var(--face-ui);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  text-decoration: none;
}

/* The citation chip. The one interactive element on a claim, and the place the
   hard bet becomes visible (ADR-013). Two cells: the reference an analyst
   cites, and the coordinates a machine re-reads. The hairline between them is
   the whole argument in one detail. */
.citation-chip {
  display: inline-flex;
  align-items: stretch;
  /* The chip may never be wider than the column it sits in. Its coordinate cell
     carries a document id, and on the change screen one of those pushed the
     chip to 594px inside a 390px client, measured in Chromium -- the same
     defect .coord had two hundred lines up, in an inline-flex box that will
     not shrink below its content unless it is told it may. */
  max-width: 100%;
  padding: 0;
  border: 1px solid var(--accent);
  border-radius: var(--r);
  background: var(--accent-wash);
  color: var(--accent-strong);
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease;
}

.citation-chip:hover {
  background: var(--accent);
  color: var(--surface);
}

.citation-chip[aria-expanded="true"] {
  background: var(--accent);
  color: var(--surface);
}

@media (prefers-color-scheme: dark) {
  .citation-chip:hover,
  .citation-chip[aria-expanded="true"] {
    color: var(--surface-sunk);
  }
}

.citation-chip__ref {
  padding: 0.32em 0.6em;
  font: 600 var(--t-micro) / var(--lh-micro) var(--face-ui);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.citation-chip__coord {
  /* A flex item's automatic minimum is its min-content, so this cell has to be
     allowed to be narrow AND its id has to be allowed to break. Both halves, or
     neither works -- the same pair the .stage rows on the marketing site need.
     A range like 316-332 still never breaks, because it always fits. */
  min-width: 0;
  padding: 0.32em 0.6em;
  border-left: 1px solid currentColor;
  font: var(--t-micro) / var(--lh-micro) var(--face-data);
  overflow-wrap: anywhere;
}

/* ----------------------------------------------------- escalation queue -- */

/* A queue row is a withheld claim in list form, and it looks like one on
   purpose: the same broken rule, the same hatch, the reason in the position
   where a claim would be. One refusal, one visual language. */
.queue {
  margin: 0;
  padding: 0;
  list-style: none;
}

.queue__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s4);
  align-items: start;
  margin-bottom: var(--s3);
  padding: var(--s4) var(--s5);
  scroll-margin-top: var(--anchor);
  border: 1px solid var(--rule);
  border-left: 0;
  border-radius: 0 var(--r) var(--r) 0;
  background-color: var(--surface);
  background-image: repeating-linear-gradient(
    to bottom,
    var(--alarm) 0 6px,
    transparent 6px 12px
  );
  background-size: 3px 100%;
  background-repeat: no-repeat;
  background-position: left top;
}

/* THERE IS NO :hover RULE HERE, AND THERE MUST NOT BE ONE. A queue row is
   a refusal, and the refusal surface answers nothing. One used to live on
   this line -- background-color: var(--bg) -- and it was deleted rather
   than cancelled further down the file, because a cancelled rule left in
   place is an invitation: the next person finds a hover rule on the
   refusal surface and assumes it belongs there. See the block at the foot
   of this file. */

.queue__code {
  display: inline-block;
  /* A reason code is one unbreakable token and some of them are long:
     CITATION_AMBIGUOUS_OCCURRENCE ran /review and /escalations to 361px and
     327px inside a 320px client, measured in Chromium. Both halves are needed
     -- the box must be allowed to be narrow and the token to break -- and
     neither costs anything at any width where the code already fits. */
  max-width: 100%;
  margin-bottom: var(--s2);
  padding: 0.2em 0.45em;
  border: 1px solid var(--alarm);
  border-radius: var(--r-sm);
  color: var(--alarm-strong);
  font: 700 var(--t-micro) / var(--lh-micro) var(--face-ui);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  overflow-wrap: anywhere;
}

.queue__reason {
  max-width: 62ch;
  font-size: var(--t-ui);
  color: var(--ink);
}

.queue__meta {
  margin-top: var(--s2);
  font: var(--t-micro) / 1.6 var(--face-data);
  color: var(--ink-3);
}

.queue__actions {
  display: flex;
  gap: var(--s2);
  flex-wrap: wrap;
}

@media (max-width: 40rem) {
  .queue__row {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ------------------------------------------------------------- coverage -- */

/* The strip every surface that synthesises or counts must render: how many
 * findings it rests on, and how many it withheld. It is the workspace-scale
 * restatement of the claim rule. One claim refuses to assert itself; one
 * synthesis refuses to hide what it could not substantiate.
 *
 * The design problem is that "N included, M withheld" reads as a headline and
 * a footnote unless the two are built as equals, so they are:
 *
 *   same cell, same padding, same size, same weight, same numeric face, and
 *   the same ink. The withheld number is NOT set in the alarm colour. An alarm
 *   number becomes an error badge, and people learn to dismiss error badges.
 *   Equal standing is stated by equal treatment.
 *
 * What differs is the mark and the word. A filled square counts; a hatched
 * square is the same hatch a withheld claim's slot carries. The reader who has
 * seen one screen of this product already knows what it means.
 *
 * The strip renders at every count, including zero withheld and zero findings.
 * A coverage strip that disappears when it is convenient is worth nothing.
 */
.coverage {
  /* The share the template supplies. It defaults to the whole bar, not to
     none: a template that forgets to pass it has told this strip nothing about
     what verified, and a strip that answers "nothing was withheld" on no
     information is the lie this component exists to stop. Absence is denial
     here as well. */
  --withheld-share: 100%;
  border: 1px solid var(--rule-strong);
  border-radius: var(--r);
  background: var(--surface);
  overflow: hidden;
}

.coverage__scope {
  padding: var(--s2) var(--s4);
  border-bottom: 1px solid var(--rule);
  background: var(--surface-sunk);
  color: var(--ink-2);
  font-size: var(--t-small);
}

/* The 1px gap over a ruled background draws the divider, and keeps drawing it
   when the pair stacks on a narrow screen. The same hairline the citation chip
   puts between the human reference and the machine coordinates. */
.coverage__pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  background: var(--rule-strong);
}

.coverage__cell {
  padding: var(--s4);
  background: var(--paper);
}

.coverage__n {
  display: block;
  font: 700 var(--t-count) / 1.1 var(--face-data);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.coverage__label {
  display: flex;
  align-items: center;
  gap: 0.45em;
  margin-top: var(--s2);
  color: var(--ink);
  font-size: var(--t-micro);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.coverage__note {
  max-width: 36ch;
  margin-top: var(--s1);
  color: var(--ink-2);
  font-size: var(--t-small);
}

/* Two squares of one size. Filled counts, hatched does not. */
.coverage__mark {
  flex: 0 0 auto;
  width: 0.72em;
  height: 0.72em;
  border: 1px solid var(--ink-3);
  background: var(--ink-3);
}

.coverage__mark--withheld {
  border-color: var(--alarm);
  background-color: transparent;
  background-image: repeating-linear-gradient(
    135deg,
    var(--hatch) 0 1px,
    transparent 1px 4px
  );
}

/* The ratio, drawn once. Counts tell you the numbers; this tells you the share
   at a glance, which is the thing a reader otherwise has to work out and
   usually does not. Width comes from --withheld-share on the element. */
.coverage__bar {
  display: flex;
  height: 4px;
  border-top: 1px solid var(--rule-strong);
  background: var(--ink-3);
}

.coverage__bar-withheld {
  width: var(--withheld-share);
  margin-left: auto;
  background-color: var(--alarm-wash);
  background-image: repeating-linear-gradient(
    135deg,
    var(--alarm) 0 1px,
    transparent 1px 4px
  );
}

/* What the synthesis read, split the way app/state/review.py splits it. A take
   resting only on internal sources has read nothing the regulator actually
   wrote, and a single total hides that completely. The marks are the ones the
   .source component uses, so this line needs no legend for anyone who has seen
   the sources section. */
.coverage__basis {
  display: flex;
  gap: var(--s5);
  flex-wrap: wrap;
  padding: var(--s2) var(--s4);
  border-top: 1px solid var(--rule);
  font: var(--t-micro) / 1.6 var(--face-data);
  color: var(--ink-2);
}

.coverage__basis-item {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
}

.coverage__rule {
  flex: 0 0 auto;
  width: 3px;
  height: 0.9em;
}

.coverage__rule--external {
  background: var(--ink);
}

.coverage__rule--internal {
  background-image: linear-gradient(
    to right,
    var(--ink-3) 0 1px,
    transparent 1px 2px,
    var(--ink-3) 2px 3px
  );
}

/* Nothing has been synthesised yet. That is not the same as a clean sheet, so
   the bar carries no included portion at all. */
.coverage--empty .coverage__bar {
  background-color: transparent;
  background-image: repeating-linear-gradient(
    135deg,
    var(--hatch) 0 1px,
    transparent 1px 4px
  );
}

@media (max-width: 34rem) {
  .coverage__pair {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* -------------------------------------------------------------- projects -- */

/* Three per row at the shell width, two on a tablet, one on a phone. auto-fill
   rather than auto-fit on purpose: with two projects, auto-fit would stretch
   them across the whole shell and a card would change size as the account
   grows. A card is a fixed thing; it should not resize because a colleague
   added a project. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(18.5rem, 1fr));
  gap: var(--s4);
  align-items: stretch;
}

/* A column, not a row template. The count row is pushed to the foot with an
   auto margin rather than a named grid row, so a card renders correctly whether
   or not the project carries a summary line. A template with one fewer child
   should not move the thing the analyst scans for. */
.project-card {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s4) var(--s4) 0;
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--paper);
  color: inherit;
  text-decoration: none;
  transition: border-color 120ms ease, box-shadow 160ms var(--ease-settle),
    transform 160ms var(--ease-settle);
}

/* One pixel, not four. The card is paper and stays paper: no blur here,
   because what sits behind it is flat --bg, and a blur of a flat colour is
   indistinguishable from a tint at roughly ten times the cost. The lift
   and the cast are the part of the effect that was doing the work. */
a.project-card:hover {
  border-color: var(--ink-3);
  color: inherit;
  transform: translateY(-1px);
  box-shadow: var(--shadow-3);
}

a.project-card:hover .project-card__name {
  text-decoration: underline;
  text-underline-offset: 0.18em;
}

.project-card__head {
  display: flex;
  align-items: start;
  justify-content: space-between;
  gap: var(--s3);
}

.project-card__name {
  font-size: var(--t-ui-lg);
  font-weight: 600;
  letter-spacing: -0.008em;
  line-height: 1.3;
}

/* Where the work sits: jurisdiction in the product's voice, docket reference in
   the coordinate face, because a docket number is a reference someone else can
   look up rather than a phrase this product composed. */
.project-card__where {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  flex-wrap: wrap;
  color: var(--ink-2);
  font-size: var(--t-small);
}

.project-card__note {
  max-width: 40ch;
  color: var(--ink-3);
  font-size: var(--t-small);
}

/* The row the analyst scans. Numbers first and large, labels small underneath,
   all on one baseline grid so four cards in a row can be read down a column
   instead of one at a time. No rules between the cells: alignment does that
   work, and hairlines here would be decoration. */
.project-card__counts {
  display: grid;
  /* TWO COLUMNS, DECLARED, NOT FOUR NEGOTIATED.
     4.25rem gave a 74px track, and an 11px uppercase label tracked at 0.09em
     does not fit one. See .tally__label. 5.5rem fixed that and left a second
     problem behind: four cells at a 5.5rem minimum need about 400px and a card
     in this grid has roughly 250px to give, so auto-fit laid three across and
     dropped the fourth onto a row of its own. The date then read as a stranded
     fifth fact rather than as one of four, and the row a reader is meant to
     scan down a column across cards had a ragged bottom edge.
     Two by two fits at every card width this grid produces, so the four cells
     hold the same four positions on every card on the board -- which is the
     only reason the row is a grid rather than a sentence. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s3) var(--s4);
  margin-top: auto;
  padding: var(--s3) 0 var(--s4);
  border-top: 1px solid var(--rule);
}

/* min-width:0 so a long value wraps inside its track instead of widening the
   whole row and breaking the alignment the scan depends on. */
.tally {
  min-width: 0;
  overflow-wrap: break-word;
}

.tally__n {
  display: block;
  font: 700 var(--t-count) / 1.1 var(--face-data);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  color: var(--ink);
}

/* Tracking is what makes an 11px uppercase label readable across a row and
   unreadable down a column. At 0.09em in a 74px track this rendered
   "WAITING / ON A / PERSON" over three lines and hyphen-broke "NEXT
   RE-RUN", which is not a word break. Same size, same weight, tighter
   track, and hyphenation off. */
.tally__label {
  display: block;
  margin-top: 3px;
  color: var(--ink-3);
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: var(--track-label-tight);
  line-height: 1.35;
  hyphens: none;
  text-transform: uppercase;
}

/* A count that means a person has to do something. The solid bar is the same
   mark a material change carries, and it means the same thing in both places:
   this one needs a human. Colour agrees with the bar, it does not carry it. */
.tally--waiting {
  padding-left: var(--s2);
  border-left: 3px solid var(--alarm);
}

.tally--waiting .tally__n {
  color: var(--alarm-strong);
}

/* A date, not a count. Smaller and lighter than a number because it is a
   different kind of fact, and pretending otherwise would break the scan. */
.tally--date .tally__n {
  font-size: var(--t-ui);
  font-weight: 600;
}

/* Zero, or nothing scheduled. Rendered in words and never as a dash: a dash in
   a count row is the same lie as a blank table. */
.tally--none .tally__n {
  color: var(--ink-3);
  font-size: var(--t-small);
  font-weight: 600;
  line-height: 1.5;
}

/* The add-a-project tile. It reads as an action through the verb, the accent,
   and one behaviour: the dashed edge goes solid on hover and focus, which is
   what the tile itself does when you use it. No icon, because the icon would be
   a dependency and the verb is clearer than a plus sign anyway. */
.card--new {
  display: flex;
  flex-direction: column;
  justify-content: center;
  border-style: dashed;
  border-color: var(--rule-strong);
  background: transparent;
  color: inherit;
  text-decoration: none;
  transition: background-color 120ms ease, border-color 120ms ease;
}

.card--new .card__verb {
  color: var(--accent);
  font-size: var(--t-ui-lg);
  font-weight: 650;
}

.card--new .card__note {
  max-width: 32ch;
  margin-top: var(--s2);
  color: var(--ink-2);
  font-size: var(--t-small);
}

a.card--new:hover,
a.card--new:focus-visible {
  border-style: solid;
  border-color: var(--accent);
  background: var(--accent-wash);
  color: inherit;
}

/* ------------------------------------------------------------------ tabs -- */

/* The project's sections and the review centre's sections. These are links to
 * server-rendered URLs, so they are keyboard reachable because they are links,
 * not because JavaScript was persuaded to make them so. The CSS also matches
 * aria-selected, so a real tablist is styled the same if one is ever used.
 *
 * A rail with the current section lifted out of it, deliberately unlike the
 * masthead's underlined nav: those are screens, these are sections of one
 * screen, and two different jobs should not share one look. The rail was
 * sunk when it was opaque and never moved; it is a pane now, and the lift
 * comes from paper and a cast instead of from a step down.
 */
/* G1 -- attached glass, and the one place in this product where
 * translucency pays for itself twice over. The review centre is 8,823px
 * tall with nine sections and this rail left the screen 304px in; after
 * that a reader had no orientation at all. Now it stays, and it lets the
 * section it is describing show through it.
 *
 * z-index 20, under the masthead's 30: two attached panes, and the outer
 * one wins.
 */
.tabs {
  position: sticky;
  top: var(--stuck);
  z-index: 20;
  display: flex;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--glass-solid);
  box-shadow: var(--edge), var(--glass-lift);
  overflow-x: auto;
  scrollbar-width: thin;
}

@supports (
  (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))
) {
  .tabs {
    background: var(--glass-1);
    -webkit-backdrop-filter: var(--blur-1);
    backdrop-filter: var(--blur-1);
  }
}

.tab {
  display: inline-flex;
  align-items: baseline;
  gap: var(--s2);
  flex: 0 0 auto;
  padding: var(--s2) var(--s4);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  color: var(--ink-2);
  font: 600 var(--t-small) / 1.4 var(--face-ui);
  text-decoration: none;
  white-space: nowrap;
  /* Colour and edge only. No transform: a tab is a place, not a control
     that answers back. */
  transition: background-color 120ms ease, border-color 120ms ease,
    color 120ms ease;
}

/* Hover is the first half of what the current section already has: the
   edge appears, the fill does not. Against an opaque sunk rail this used
   to be --bg, a step lighter; on a pane --bg is a step DARKER, so hover
   would read as pressed while current reads as raised. One tint level up
   plus the border keeps both moving the same way, and the tint needs no
   @supports -- a translucent fill over the pane behind it composites
   correctly whether or not that pane is blurred.

   NOT ON THE CURRENT SECTION. a.tab:hover outweighs .tab--current, so
   without these two :not()s, moving the cursor onto the section you are
   already in takes its paper away and it stops looking current. That was
   true before this change too, and less visible only because the two
   fills were closer. */
a.tab:hover:not(.tab--current):not([aria-selected="true"]) {
  color: var(--ink);
  background: var(--glass-3);
  border-color: var(--rule);
}

/* The ring must sit inside: the rail scrolls, and an outset ring would be
   clipped at the first and last section.

   And it takes no halo. The halo is a box-shadow drawn OUTSIDE the box, so
   on a ring that sits inside it would be a stray glow separating nothing.
   The ring has contrast without it -- accent on the current tab's paper is
   7.61:1, measured. The current tab keeps its own cast while focused,
   which the blanket rule above would otherwise take away. */
.tab:focus-visible {
  outline-offset: -2px;
  box-shadow: none;
}

.tab--current:focus-visible,
.tab[aria-selected="true"]:focus-visible {
  box-shadow: var(--shadow-1);
}

/* The rail used to be sunk and the current section lifted out of it. The
   rail is a pane now, so the lift is carried by paper plus the smallest
   cast rather than by a step down into the rail. */
.tab--current,
.tab[aria-selected="true"] {
  border-color: var(--rule);
  background: var(--paper);
  box-shadow: var(--shadow-1);
  color: var(--ink);
  font-weight: 700;
}

/* In dark, --paper does not sit above the glass rail, so a paper chip cannot
   read as lifted out of it. In the teal palette it sat BELOW and read as pressed
   in; in the indigo palette it is #161d31, byte-identical to --glass-solid, so a
   paper chip on a solid rail would be invisible and on a translucent one would
   be a chip you can only find by its border. Either way the chip has to come
   from somewhere else. --rule is the one step above the rail that still carries
   --ink at full strength -- 11.5:1, by token arithmetic. Same relationship,
   opposite direction, because that is what the two schemes ask for.

   THE IDENTITY IS WORTH FLAGGING RATHER THAN LEANING ON. Nothing asserts
   --paper != --glass-solid in dark, and this rule is the only place the
   difference was ever load-bearing. If a later change wants a lifted dark chip
   without an explicit background, that is the token to move first. */
@media (prefers-color-scheme: dark) {
  .tab--current,
  .tab[aria-selected="true"] {
    border-color: var(--rule-strong);
    background: var(--rule);
  }
}

/* Where the work is, before anyone clicks. ink-2 rather than ink-3: this is a
   number someone reads, not a shade. On --surface-sunk ink-3 is 3.89:1, under
   AA; against --glass-solid it is 4.57:1, and ink-2 on the same 8.01:1. Those
   two are token arithmetic -- the rail is a translucent pane and its rendered
   backdrop moves with whatever scrolls beneath it, so the ink-3 figure is a
   ceiling and not a reading. ink-2 is the right weight for a count anyway, and
   it is the choice that does not depend on the ceiling holding.

   (This comment previously read "on the pane it is 4.57:1, which clears AA",
   restated "from the rendered pixel". It was not from a rendered pixel; it was
   ratio(--ink-3, #f7f9fa). Same error as the token block near the top of the
   file, third site. tests/test_glass_contrast.py now asserts these as token
   figures so they cannot drift again without a test going red. #f7f9fa in this
   paragraph is the teal --glass-solid, before 2026-08-07, and it is the record
   of the mistake rather than a colour anything here still names.

   AND 4.57 IS NOW BOTH NUMBERS AT ONCE, which is worth saying out loud. The
   indigo --glass-solid returns 4.57:1 for ink-3 to the hundredth, the same as
   the teal one did. So the figure this paragraph condemns as a false rendered
   reading is also the current true TOKEN reading -- and it clears AA by seven
   hundredths. That does not rehabilitate the old claim by one digit. A ceiling
   is not a pane, the rail's rendered backdrop is still unmeasured on indigo, and
   ink-2 is still the choice that does not depend on the ceiling holding.) */
.tab__count {
  font: var(--t-micro) / var(--lh-micro) var(--face-data);
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}

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

.tab__count--waiting {
  padding: 0.1em 0.4em;
  border: 1px solid var(--alarm);
  border-radius: var(--r-sm);
  background: var(--alarm-wash);
  color: var(--alarm-strong);
  font-weight: 700;
}

/* Anchor targets, and every one of them broke silently the moment the
   chrome above them started sticking. --anchor is the masthead plus the
   tab rail plus a space; without it an anchor lands behind the chrome and
   the reader is shown the middle of the thing they asked for. On a screen
   with no rail the clearance is 46px generous, which costs a gap and
   nothing else. */
.tabpanel {
  padding-top: var(--s5);
  scroll-margin-top: var(--anchor);
}

/* THE ONE PLACE THIS FILE TAKES A RING AWAY, and it is scoped so that it
   cannot take one from a keyboard. :focus:not(:focus-visible) is the browser's
   own judgement that this focus arrived by a click or by script rather than by
   a key -- and a 2px ring with a halo around a panel eight hundred pixels tall
   is noise nobody asked for. As written before, it was `.tabpanel:focus`,
   which outranks the `:focus-visible` rule three hundred lines above it and
   would have swallowed the ring for a keyboard reader too the moment anybody
   gave a panel a tabindex. Nothing had, so nothing was broken yet; the rule
   was a trap laid for the next person, and the comment on :focus-visible
   forbidding outline:none was already contradicted by this line.
   tests/test_a11y_guards.py holds every stylesheet in this directory to the
   scoped form. */
.tabpanel:focus:not(:focus-visible) {
  outline: none;
}

/* --------------------------------------------------------------- threads -- */

/* A research thread is a sequence, so it is set on a timeline and the order
 * carries the meaning. The marker says who spoke: hollow is the system, filled
 * is the analyst. Filled is the heavier mark on purpose — human input is the
 * thing this product wants more of, not the thing it tolerates. The head names
 * the speaker in words as well, so the shape never carries it alone.
 *
 * The two names are TURN_AUTHOR_KINDS in app/state/models.py.
 */
.thread {
  --rail: 1.75rem;
  margin: 0;
  padding: 0 0 0 var(--rail);
  list-style: none;
  position: relative;
}

.thread::before {
  content: "";
  position: absolute;
  left: 5px;
  top: 6px;
  bottom: 6px;
  width: 1px;
  background: var(--rule);
}

.turn {
  position: relative;
  padding-bottom: var(--s5);
}

.turn::before {
  content: "";
  position: absolute;
  left: calc(-1 * var(--rail));
  top: 4px;
  width: 11px;
  height: 11px;
  border: 1px solid var(--rule-strong);
  background: var(--bg);
}

.turn--system::before {
  border-color: var(--ink-3);
  background: var(--surface);
}

.turn--analyst::before {
  border-color: var(--ink);
  background: var(--ink);
}

.turn__head {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  flex-wrap: wrap;
  margin-bottom: var(--s2);
}

.turn__who {
  font-size: var(--t-small);
  font-weight: 600;
}

.turn__body {
  max-width: 62ch;
  color: var(--ink);
}

/* ----------------------------------------------------------- work plans -- */

/* Numbered, because a plan genuinely is a sequence and the analyst refers to
 * step three out loud. The number gives position; the marker on the rail gives
 * state, and the two are different facts.
 *
 * Four states, exactly STEP_STATES in app/state/models.py. The marker alphabet
 * is monochrome-safe and none of it is an icon:
 *   empty      todo
 *   half full  doing
 *   barred     blocked — a gate drawn across the step
 *   full       done
 *
 * Blocked is where the product's rule reaches the plan. A step blocked because
 * a finding it rests on did not verify carries a .withheld slot in its body
 * saying so, which is the treatment a withheld claim already uses. The state
 * says the step cannot proceed; the slot says why. Two facts, two components,
 * and no fifth state invented to hold the pair.
 */
.plan {
  --rail: 2.25rem;
  counter-reset: step;
  margin: 0;
  padding: 0 0 0 var(--rail);
  list-style: none;
}

.plan-step {
  position: relative;
  padding: var(--s4) 0;
  border-top: 1px solid var(--rule);
}

.plan-step:first-child {
  border-top: 0;
}

.plan-step::before {
  content: "";
  position: absolute;
  left: calc(-1 * var(--rail));
  top: calc(var(--s4) + 2px);
  width: 14px;
  height: 14px;
  border: 1px solid var(--ink-3);
  background-color: transparent;
}

.plan-step__head {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  flex-wrap: wrap;
  margin-bottom: var(--s2);
}

.plan-step__n {
  counter-increment: step;
  font: var(--t-micro) / var(--lh-micro) var(--face-data);
  color: var(--ink-3);
}

.plan-step__n::before {
  content: counter(step, decimal-leading-zero);
}

.plan-step__state {
  font-size: var(--t-micro);
  font-weight: 700;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-2);
}

.plan-step__what {
  max-width: 62ch;
  color: var(--ink);
}

.plan-step__owner {
  margin-top: var(--s2);
  color: var(--ink-2);
  font-size: var(--t-small);
}

/* Stated rather than left to the base rule. Every state in the alphabet has a
   line here, so reading the sheet tells you the whole set. */
.plan-step--todo::before {
  background-color: transparent;
}

.plan-step--doing::before {
  border-color: var(--ink);
  background-image: linear-gradient(to top, var(--ink) 50%, transparent 50%);
}

.plan-step--blocked::before {
  border-color: var(--alarm);
  background-image: linear-gradient(
    to bottom,
    transparent 0 40%,
    var(--alarm) 40% 60%,
    transparent 60% 100%
  );
}

.plan-step--blocked .plan-step__state {
  color: var(--alarm-strong);
}

.plan-step--done::before {
  border-color: var(--ink);
  background-color: var(--ink);
}

.plan-step--done .plan-step__what {
  color: var(--ink-2);
}

/* ---------------------------------------------------------------- runs -- */

/* A schedule that shows only the next run hides the question that matters,
 * which is whether the last one was clean. Stale derived state looks exactly
 * like fresh derived state, so this strip states three things at once: when it
 * last ran, what came back, and when it runs next.
 *
 * Overdue takes the broken alarm rule, because an overdue run means the state
 * on screen may no longer be current and the product cannot assert that it is.
 * Same rule as a withheld claim, one stage earlier.
 */
.run-schedule {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 1px;
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--rule);
  overflow: hidden;
}

.run-schedule__cell {
  padding: var(--s3) var(--s4);
  background: var(--paper);
}

.run-schedule__value {
  margin-top: var(--s1);
  font: var(--t-ui) / 1.45 var(--face-data);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.run-schedule__value--degraded {
  color: var(--alarm-strong);
  font-weight: 700;
}

.run-schedule--overdue {
  border-left: 0;
  border-radius: 0 var(--r) var(--r) 0;
  background-image: repeating-linear-gradient(
    to bottom,
    var(--alarm) 0 6px,
    transparent 6px 12px
  );
  background-size: 3px 100%;
  background-repeat: no-repeat;
  background-position: left top;
  padding-left: 3px;
}

/* Switched off. It keeps its last run and its result, and it is sunk rather
   than raised — the same axis a withheld claim uses. A disabled schedule that
   looks exactly like a running one is how a project goes quiet for a month and
   nobody notices. */
.run-schedule--off .run-schedule__cell {
  background: var(--surface-sunk);
}

.run-schedule--off .run-schedule__value {
  color: var(--ink-2);
}

/* No schedule at all. Said in words inside the same frame, so the absence is a
   statement rather than an empty strip. */
.run-schedule--none {
  display: block;
  padding: var(--s3) var(--s4);
  border-style: dashed;
  border-color: var(--rule-strong);
  background: transparent;
  color: var(--ink-2);
}

/* ------------------------------------------------------------ knowledge -- */

/* What the company has taught the product, and what the product has kept. The
 * mark is the wordmark's three bars: layers, newest on top. It appears in this
 * one component and nowhere else, and it means one thing — this is accumulated
 * company knowledge rather than something read off a filing today.
 *
 * Provenance is not optional here. Knowledge with no stated origin is folklore,
 * and folklore in a compliance tool is how a wrong assumption outlives the
 * person who made it.
 */
.knowledge-item {
  position: relative;
  padding: var(--s4) 0 var(--s4) var(--s6);
  border-top: 1px solid var(--rule);
}

.knowledge-item::before {
  content: "";
  position: absolute;
  left: 0;
  top: calc(var(--s4) + 5px);
  width: 1.5rem;
  height: 8px;
  background-image: linear-gradient(var(--ink-3) 0 0),
    linear-gradient(var(--rule-strong) 0 0), linear-gradient(var(--rule) 0 0);
  background-size: 100% 2px, 66% 2px, 38% 2px;
  background-position: 0 0, 0 3px, 0 6px;
  background-repeat: no-repeat;
}

/* A person vouched for this — confirmed_by is set. The top layer goes to full
   ink, because knowledge a colleague stood behind is the kind this product is
   trying to collect, and it outranks what the pipeline inferred on its own. */
.knowledge-item--confirmed::before {
  background-image: linear-gradient(var(--ink) 0 0),
    linear-gradient(var(--ink-3) 0 0), linear-gradient(var(--rule-strong) 0 0);
}

.knowledge-item__title {
  font-weight: 600;
}

.knowledge-item__body {
  max-width: 62ch;
  margin-top: var(--s2);
  color: var(--ink-2);
}

.knowledge-item__from {
  margin-top: var(--s3);
  font: var(--t-micro) / 1.6 var(--face-data);
  color: var(--ink-3);
}

/* ------------------------------------------------------------ questions -- */

/* An open question the research raised. A blocking one is the same species as
 * an escalation — work stopped, waiting for a person — so it wears the same
 * broken rule and the same code chip rather than inventing a second language
 * for the same event.
 */
.question {
  padding: var(--s4) var(--s5);
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--paper);
}

.question__text {
  max-width: 58ch;
  font: 500 var(--t-ui-lg) / 1.5 var(--face-ui);
  color: var(--ink);
}

.question__why {
  max-width: 62ch;
  margin-top: var(--s2);
  color: var(--ink-2);
  font-size: var(--t-small);
}

.question__actions {
  display: flex;
  gap: var(--s2);
  flex-wrap: wrap;
  margin-top: var(--s4);
}

.question--blocking {
  border-left: 0;
  border-radius: 0 var(--r) var(--r) 0;
  background-image: repeating-linear-gradient(
    to bottom,
    var(--alarm) 0 6px,
    transparent 6px 12px
  );
  background-size: 3px 100%;
  background-repeat: no-repeat;
  background-position: left top;
}

/* Same treatment as .queue__code, and the repetition is deliberate rather than
   shared: renaming the queue's class to cover both would rewrite a component
   this file already ships. */
.question__flag {
  display: inline-block;
  /* A reason code is one unbreakable token and some of them are long:
     CITATION_AMBIGUOUS_OCCURRENCE ran /review and /escalations to 361px and
     327px inside a 320px client, measured in Chromium. Both halves are needed
     -- the box must be allowed to be narrow and the token to break -- and
     neither costs anything at any width where the code already fits. */
  max-width: 100%;
  margin-bottom: var(--s2);
  padding: 0.2em 0.45em;
  border: 1px solid var(--alarm);
  border-radius: var(--r-sm);
  color: var(--alarm-strong);
  font: 700 var(--t-micro) / var(--lh-micro) var(--face-ui);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------- steer -- */

/* A directive a person gave to shift the research. The accent rail is the rule
 * this stylesheet already keeps: accent means something acted on. A steer is a
 * person acting on the system.
 *
 * The rail is also the state. In force, the rail is there; revoked, it is gone
 * and the words say when and by whom. A steer whose force is assumed rather than
 * shown is a one-turn illusion, which is why the provenance line — who set it,
 * when, and how many runs have obeyed it — is part of the component and not an
 * optional extra a template may drop.
 */
.steer {
  padding: var(--s4) var(--s5);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--r) var(--r) 0;
  background: var(--paper);
}

.steer__directive {
  max-width: 58ch;
  font: 500 var(--t-ui-lg) / 1.55 var(--face-ui);
  color: var(--ink);
}

.steer__provenance {
  margin-top: var(--s3);
  padding-top: var(--s3);
  border-top: 1px solid var(--rule);
  font: var(--t-micro) / 1.6 var(--face-data);
  color: var(--ink-3);
}

.steer--revoked {
  border-left: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--surface-sunk);
}

.steer--revoked .steer__directive {
  color: var(--ink-3);
}

/* --------------------------------------------------------- deliverables -- */

/* What leaves the building: a memo, a filing, a briefing, a register. Three
 * states, exactly DELIVERABLE_STATES in app/state/models.py, on the axis the
 * badges already use — dashed is provisional, solid is with a person, filled is
 * settled.
 *
 * There is no "blocked" state here on purpose. Whether a document is safe to
 * send is not a stage in its life, it is a fact about what it rests on, and
 * that fact belongs in the coverage strip where the numbers are. Folding the
 * two together would let a deliverable read as fine because nobody marked it
 * otherwise.
 *
 * A deliverable must state its coverage. That is the governing rule of this
 * whole surface: a document that summarises eleven findings and quietly omits
 * three is the exact failure this product exists to prevent, and it is worse on
 * paper than on screen because paper leaves the building.
 */
.deliverable {
  border: 1px solid var(--rule);
  border-radius: var(--r);
  background: var(--paper);
  overflow: hidden;
}

.deliverable__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  flex-wrap: wrap;
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--rule);
  background: var(--surface-sunk);
}

.deliverable__title {
  font-size: var(--t-ui);
  font-weight: 600;
}

.deliverable__body {
  padding: var(--s4);
  color: var(--ink-2);
}

.deliverable__foot {
  padding: var(--s4);
  border-top: 1px solid var(--rule);
}

.deliverable__actions {
  display: flex;
  gap: var(--s2);
  flex-wrap: wrap;
  margin-top: var(--s4);
}

.deliverable--draft {
  border-style: dashed;
  border-color: var(--rule-strong);
}

.deliverable--in-review {
  border-color: var(--ink-3);
}

.deliverable--final .deliverable__head {
  border-bottom-color: var(--ink);
  background: var(--ink);
  color: var(--surface);
}

@media (prefers-color-scheme: dark) {
  .deliverable--final .deliverable__head {
    color: var(--surface-sunk);
  }
}

/* Tripwire, the same kind this file already uses on a withheld claim carrying a
   statement. A deliverable with no coverage strip is a template mistake that
   would otherwise be invisible — the page would look finished and be dishonest.
   Make it look broken instead. Browsers without :has() drop the rule and lose
   the warning; none of them gain the wrong page. */
.deliverable:not(:has(.coverage)) {
  outline: 2px solid var(--alarm);
}

.deliverable:not(:has(.coverage))::before {
  content: "Template error: this deliverable does not state its coverage.";
  display: block;
  padding: var(--s2) var(--s4);
  background: var(--alarm-wash);
  color: var(--alarm-strong);
  font: 700 var(--t-micro) / var(--lh-micro) var(--face-ui);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

/* -------------------------------------------------------------- sources -- */

/* Where a finding came from. Two kinds, and confusing them is expensive: an
 * external filing is the public record everyone will be held to, an internal
 * document is the company's own account of itself, which can be out of date and
 * carries no authority with a regulator.
 *
 * One solid rule is the outside authority. Two hairlines are ours — a doubled
 * line, not a dashed or broken one, so it never collides with provisional or
 * withheld. An external source also shows the frozen hash of what was read,
 * because that is the claim the citation rests on and it is checkable.
 */
.source {
  position: relative;
  padding: var(--s4) 0 var(--s4) var(--s5);
  border-top: 1px solid var(--rule);
}

.source::before {
  content: "";
  position: absolute;
  left: 0;
  top: var(--s4);
  bottom: var(--s4);
  width: 3px;
}

.source--external::before {
  background: var(--ink);
}

.source--internal::before {
  background-image: linear-gradient(
    to right,
    var(--ink-3) 0 1px,
    transparent 1px 2px,
    var(--ink-3) 2px 3px
  );
}

.source__kind {
  color: var(--ink-3);
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.source__name {
  margin-top: var(--s1);
  font-weight: 600;
}

.source__meta {
  display: flex;
  gap: var(--s1) var(--s4);
  flex-wrap: wrap;
  margin-top: var(--s2);
  font: var(--t-micro) / 1.6 var(--face-data);
  color: var(--ink-3);
}

/* A flex item's default min-width is auto, which is its content, so a
   sixty-character address refuses to shrink and pushes the row -- and the
   page -- wider than the screen. Nothing else in this row is long enough for
   it to matter, and one rule for the row is one fewer class in the template. */
.source__meta > * {
  min-width: 0;
}

/* An empty screen states what it means. "Nothing here" and "nothing loaded"
   are different facts, and the product does not blur them. */
.empty {
  padding: var(--s6);
  border: 1px dashed var(--rule-strong);
  border-radius: var(--r);
  color: var(--ink-2);
  text-align: left;
}

.empty__title {
  margin-bottom: var(--s2);
  color: var(--ink);
  font-weight: 600;
}

/* The two halves an empty state owes the reader: what this means, and what to
   do next. A list that renders nothing and says nothing reads as "nothing has
   changed", which is a claim, and one the product has not checked. */
.empty__body {
  max-width: 56ch;
}

.empty__actions {
  display: flex;
  gap: var(--s2);
  flex-wrap: wrap;
  margin-top: var(--s4);
}

/* "There is nothing" and "we could not find out" are different facts, and only
   one of them is reassuring. The second takes the broken alarm rule that every
   other refusal in this product wears, so a reader never mistakes a failed read
   for a clean sheet. */
.empty--none {
  border-style: dashed;
}

.empty--unloaded {
  border: 1px solid var(--rule);
  border-left: 0;
  border-radius: 0 var(--r) var(--r) 0;
  padding-left: calc(var(--s6) + 3px);
  background-color: var(--surface);
  background-image: repeating-linear-gradient(
    to bottom,
    var(--alarm) 0 6px,
    transparent 6px 12px
  );
  background-size: 3px 100%;
  background-repeat: no-repeat;
  background-position: left top;
}

.empty--unloaded .empty__title {
  color: var(--alarm-strong);
}

/* --------------------------------------------------------------- motion --
 *
 * ONE ORCHESTRATED MOMENT, TWO MICRO-INTERACTIONS AND ONE REFUSAL. That is
 * the whole budget. Nothing else in this product is choreographed, nothing
 * is triggered by scroll, and no number ever animates.
 *
 * THE SETTLE, 420ms end to end, three bands, and it never repeats.
 *
 *   band 1   the masthead            0ms   260ms   up 6px, fading
 *   band 2   the page head          60ms   260ms   up 8px, fading
 *   band 3   the FRAME              120ms  300ms   opacity only
 *
 * THE RECORD IS IN BAND NOTHING. Claims, quotes, source extracts, tables
 * and the diff are painted at full opacity in the first frame. The frame
 * assembles around a document that was already there, which is what this
 * product says about itself: the filing is not something this software
 * produced and it should not appear to arrive.
 *
 * The withheld claim is the single exception, and R below argues for it.
 *
 * Two traps, both live.
 *
 * Band 3 selects the frame -- headings, rules, the tab rail -- and NOT the
 * containers. Selecting .main .shell > * would fade a parent whose opacity
 * composites onto every claim inside it, including a withheld one, and the
 * signature at the foot of this file would be gone with no line in the
 * diff mentioning it.
 *
 * animation-fill-mode: both ends at transform: none, so the masthead is
 * left with no transform and its position: sticky survives. Do not move
 * this animation up onto body: a transform on a sticky element's ancestor
 * kills the sticky.
 */

@keyframes settle-chrome {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes settle-head {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes settle-frame {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.masthead {
  animation: settle-chrome 260ms var(--ease-settle) 0ms both;
}

.page-head {
  animation: settle-head 260ms var(--ease-settle) 60ms both;
}

.main h2,
.main hr,
.main .tabs {
  animation: settle-frame 300ms var(--ease-settle) 120ms both;
}

/* M1 -- the citation chip opens the source. The central interaction of the
   product, so it is the most restrained one. The chip's own fill is
   unchanged at 120ms on colour. The panel arrives with a 6px slide and no
   overshoot: a spring on a panel showing the text of an order would be a
   small lie about how serious the thing is.

   ARRIVALS ANIMATE, DEPARTURES DO NOT. The panel is toggled with the
   hidden attribute, which this file forces to display:none. A transition
   cannot run out of display:none but an animation restarts every time an
   element is displayed again, so this needs no line of JavaScript changed.
   Closing is instant, and that is a decision: a person closing a panel has
   already decided, and making them watch it leave is the commonest small
   rudeness in modern interface work.

   Scoped to the change page's aside on purpose. The review centre renders
   a source extract that is never hidden, and that one is record: it is
   painted, not revealed. */
@keyframes panel-in {
  from {
    opacity: 0;
    transform: translateX(6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.split__aside .source-panel {
  animation: panel-in 200ms var(--ease-sheet) both;
}

/* The marked span inside it gets no motion at all. It is already correct
   when it arrives, and a highlight that animates in asks to be watched
   rather than read. Stated here so nobody adds one. */

/* R -- the refusal. A claim declining to assert itself because its citation
   did not verify is the one thing this product most wants a reader to
   notice, so it is the one piece of the record that moves. It is a narrow
   exception to "the record is in band nothing" above, and it is narrow on
   purpose: .claim--verified still paints in the first frame, and if both
   states animated the motion would say nothing, because a difference every
   claim has is not a difference.

   THE CLASS IS .claim--withheld, NOT .claim--refused. The plan for this
   change named a class no template renders; the state that reaches the
   screen on change.html, shared_claim.html and review_centre.html is
   .claim--withheld, and a rule keyed to the other name would never have
   fired.

   Down 2px and up to full, and no more than that. The withheld claim
   already carries its argument in six non-colour differences -- the broken
   left rule, the inset, the sunk ground, the hatched slot, the missing chip
   and the quoted-against-source pair. Motion is not being asked to make the
   point, only to mark the moment the point lands. No overshoot: see
   --ease-refuse for why. No lift, no gradient, no radius change; the record
   surface stays flat whatever it is doing.

   Reduced motion is answered by the global sweep at the foot of this file,
   and answered correctly rather than by accident. The fill mode is `both`,
   so with the animation off the element renders at its natural state, which
   IS the end state: full opacity, no transform. The refusal still shows.
   A refusal that rendered as nothing would be the worst failure in the
   file. */
@keyframes refuse {
  from {
    opacity: 0;
    transform: translateY(-2px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.claim--withheld {
  animation: refuse var(--dur-refuse) var(--ease-refuse) both;
}

/* ------------------------------------------------------------- printing -- */

/* The withheld state has to survive being screenshotted in black and white.
   Print is the honest test of that, so it is styled rather than left to the
   browser: tokens forced light, hatch and broken rule kept, colour removed. */
@media print {
  :root {
    --bg: #fff;
    --surface: #fff;
    --surface-sunk: #f2f2f2;
    --rule: #bbb;
    --rule-strong: #666;
    --ink: #000;
    --ink-2: #222;
    --ink-3: #444;
    --accent: #000;
    --accent-strong: #000;
    --accent-wash: #fff;
    --alarm: #000;
    --alarm-strong: #000;
    --alarm-wash: #fff;
    --hatch: rgba(0, 0, 0, 0.34);

    /* No glass on the page that leaves the building. Every pane prints as
       white paper, every cast and every filter is gone -- and so is the
       ground. A ruled sheet is a thing you draw on a screen to give a blur
       something to soften; printed, it is toner spent on a texture, and the
       desk would come out of a laser printer as two grey bars down the
       margins of a document somebody has to read in a hearing. */
    --ground: none;
    --paper: #fff;
    --glass-tint: #fff;
    --glass-solid: #fff;
    --glass-edge: #fff;
    --glass-cast: #fff;
    --glass-1: #fff;
    --glass-2: #fff;
    --glass-3: #fff;
    --edge: none;
    --shadow-1: none;
    --glass-lift: none;
    --shadow-2: none;
    --shadow-3: none;
    /* The lifts go with them. A cast printed by a laser printer is a grey
       smear under a box, and nothing on paper is in front of anything.

       A TRANSPARENT SHADOW RATHER THAN `none`, AND THE DIFFERENCE IS A FOCUS
       RING. These two are written to COMPOSE: clerk.css draws a focused pill
       as `box-shadow: var(--lift-1), 0 0 0 4px var(--focus-halo)`. Substitute
       `none` into that list and the value reads `none, 0 0 0 4px ...`, which
       is not valid box-shadow syntax, so the browser drops the WHOLE
       declaration and takes the focus halo with it. `0 0 transparent` paints
       exactly as much as `none` -- nothing -- and stays legal in a list.

       The other tokens above may keep `none`; they are used alone. These two
       may not, because the moment one is used alone somebody will write the
       composing version next to it and nothing will say a ring went missing.
       tests/test_design_guards.py holds them to it. */
    --lift-1: 0 0 transparent;
    --lift-2: 0 0 transparent;
    --blur-1: none;
    --blur-2: none;
    --blur-3: none;
  }

  /* Chrome that sticks on screen must not stick on paper: a sticky box in
     a paged medium either repeats or lands on top of the text. */
  .masthead,
  .tabs {
    position: static;
  }

  .masthead,
  .claim,
  .queue__row,
  .withheld,
  .source-panel,
  .coverage,
  .coverage__mark,
  .coverage__bar,
  .coverage__bar-withheld,
  .plan-step::before,
  .question,
  .deliverable,
  .run-schedule,
  .source::before,
  .empty--unloaded {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  /* Navigation does not print. Coverage always does: the count of what was
     withheld has to survive onto the page that leaves the building, which is
     the copy someone will read without the product beside them. */
  .nav__list,
  .skip,
  .tabs,
  .card--new,
  .queue__actions,
  .question__actions,
  .deliverable__actions,
  .empty__actions,
  .source-panel__close {
    display: none;
  }

  .coverage,
  .deliverable,
  .plan-step,
  .project-card {
    break-inside: avoid;
  }

  .source-panel__body {
    max-height: none;
    overflow: visible;
  }
}

/* REMOVED, NOT SHORTENED. The block that used to sit here set
   animation-duration: 0.01ms, which shortens motion to the point of
   invisibility and leaves every animation and transition running: the
   events still fire, the compositor still works, and an element caught
   mid-animation at a repaint can still flash. A reader who asked for
   stillness gets the end state directly.

   With animation: none the fill mode goes too, so the three settle bands
   render at their natural state -- full opacity, no transform -- which is
   exactly the intended end state. Nothing needs a second declaration to
   undo them.

   The honest cost, because it is more than the preference strictly asks
   for: this also removes the 120ms colour fade on a button hover, which
   would not have troubled anyone who set the preference for vestibular
   reasons. It is the reading the brief requires and it is defensible, but
   it is not free. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }
}

/* NOTHING IN THE GROUND OR THE GLASS MOVES, so the block above reaches none of
   it and that is deliberate rather than an oversight. A backdrop that drifts,
   breathes or parallaxes would need an escape here, would cost a repaint on
   every scrolled frame, and would be the machine-made effect this design is
   trying to get out of the way of. If one is ever added, it belongs in the
   rule above before it belongs on the page. */

/* ------------------------------------------- more contrast, less transparency --
 *
 * Two different requests, one remedy, so they are answered together.
 *
 * Somebody who asks for more contrast is telling you that a 1px rule five
 * values off its ground is not a rule to them. So the ground flattens to a
 * single colour, the filter comes off -- which also removes the softening that
 * lowers APPARENT contrast on a pane even where the measured figure holds --
 * and the panes go opaque.
 *
 * THE TWO QUERIES TAKE DIFFERENT FILLS, and the difference is the whole reason
 * they are not one block. prefers-contrast takes --surface, which is a full
 * step ABOVE the composited pane, because the request was for a bigger gap
 * between the chrome and the page and honouring it means widening one.
 * prefers-reduced-transparency takes --glass-solid, the value the filter would
 * have composited to anyway, because that reader asked to stop seeing through
 * things and did not ask for the page to be redrawn -- nothing should shift by
 * one value when the pane goes solid.
 *
 * It is also the one place --ink-3 gets raised. On --bg it is 4.27:1 and on
 * --surface-sunk 3.89:1 -- both under AA for small text, both true before this
 * change and both left alone by it, because moving a token used at 36 sites is
 * not a backdrop fix and it is not this file's turn to make that call. Under
 * this query the caller has asked, so here it goes up.
 *
 * RAISED TO A NEW VALUE, NOT TO --ink-2, AND THAT IS THE WHOLE POINT. The first
 * version of this block set --ink-3 to #414d52 in light and #a9b7ba in dark.
 * Those are not "near --ink-2", they were byte-identical to it -- to the TEAL
 * --ink-2, which is what this file carried until 2026-08-07. Both hexes are the
 * old palette's and are kept as the record of the collapse, not as colours
 * anything here still names. --ink-3 is used
 * at 36 sites in this file, 6 in clerk.css and 7 in workflow.css, and every one
 * of them is a deliberate second level of ink: a label under a value, a
 * coordinate under a claim, a count that is a shade rather than a figure. Set
 * the two tokens equal and every one of those hierarchies flattens to one
 * level -- for the single reader who asked to be able to SEE more, which is the
 * exact opposite of what they asked for.
 *
 * The worst case is .nav__link--off. It is a <span>, not an <a>, and the only
 * thing on screen saying so is that it is drawn in --ink-3 while its neighbours
 * are --ink-2. With the tokens equal a high-contrast reader gets a dead nav
 * item rendered pixel-identical to a live one -- and the block at .masthead
 * below spends a paragraph arguing that this specific lie is the one thing that
 * must never be traded away. The rule was written and the argument three
 * hundred lines above it was not read.
 *
 * So each scheme gets a value that is genuinely darker (lighter, in dark) than
 * base --ink-3 and still a plain step from --ink-2:
 *
 *   light  #49536b   on --bg 4.27 -> 6.80, on --surface-sunk 3.89 -> 6.20
 *                    (both now clear AA), --surface 7.49; gap to --ink-2 1.60:1
 *   dark   #9fabc4   on --bg 5.69 -> 8.20, on --surface 5.15 -> 7.42,
 *                    on --surface-sunk 5.90 -> 8.51; gap to --ink-2 1.55:1
 *
 * The base gaps are 1.75:1 and 1.65:1, so nine tenths of the separation
 * survives while the ground contrast rises by two and a half points. That is
 * the trade this query is asking for, and the indigo palette gets a better
 * bargain on it than the teal one did. Ratios are computed from the declared
 * tokens, and tests/test_glass_contrast.py asserts every one of them --
 * including that the two inks are never equal in any scheme.
 *
 * --ink AND --ink-2 GO UP HERE TOO, which the first version of this block did
 * not do. Raising only the third level answers the request at one site and
 * leaves the body text and the labels exactly where the reader said they could
 * not read them. All three levels step, and all three stay distinct.
 *
 * THEY DO NOT STAY AS FAR APART, and the first version of this paragraph said
 * they did. It claimed the hierarchy widens. It does not: --ink is already
 * against the ceiling in both schemes -- #05090f is 5, 9 and 15 off black,
 * #ffffff is white -- so the top has nowhere to go while --ink-3 climbs two and
 * a half points, and the gaps close. Light goes from 2.12:1 and 1.75:1 to
 * 1.62:1 and 1.60:1; dark from 1.72:1 and 1.65:1 to 1.49:1 and 1.55:1. Three
 * levels survive, none collapses, and the ladder is more even than it was --
 * but it is compressed, not widened. That is the price of answering the request
 * at the top as well as the bottom, and it is the right price: a reader who
 * cannot make out the body text is not helped by a wide gap underneath it.
 *
 * --rule HAD THE SAME BUG and it is fixed the same way. #a4afb3 and #3c4e55
 * were byte-identical to --rule-strong -- the teal --rule-strong, before
 * 2026-08-07 -- collapsing the border hierarchy for the same reader. In the
 * indigo palette --rule becomes #b5bed2 (on --bg 1.23 -> 1.65) and #39435c
 * (1.40 -> 1.92), and --rule-strong steps with it, so the two levels stay two.
 *
 * Reduced transparency asks for less see-through and nothing else, so it gets
 * the pane half and not the ink half.
 *
 * THE INK STEP IS SPLIT BY SCHEME AND HAS TO BE. prefers-contrast is answered
 * in both schemes at once, and an earlier version of this block raised --ink-3
 * in one flat :root -- which in dark is a near-black grey on a near-black page,
 * so every label, coordinate and muted count on the product would have gone
 * from readable to invisible. A high-contrast rule that only knows one scheme
 * is worse than no high-contrast rule.
 *
 * No --edge: none here either. box-shadow: none is only valid on its own, so
 * `box-shadow: var(--edge), var(--glass-lift)` with --edge nulled is invalid
 * at computed-value time and takes the cast down with it. The print block
 * above survives that only because it nulls both. A bright top edge costs a
 * high-contrast reader nothing, so it stays.
 */
@media (prefers-contrast: more) {
  :root {
    --ground: none;
  }

  .masthead,
  .tabs {
    background: var(--surface);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

@media (prefers-contrast: more) and (prefers-color-scheme: light) {
  :root {
    --ink: #05090f;
    --ink-2: #2b3547;
    /* A step darker than --ink-3, a step lighter than --ink-2. Never either. */
    --ink-3: #49536b;
    --rule: #b5bed2;
    --rule-strong: #7c87a1;
  }
}

@media (prefers-contrast: more) and (prefers-color-scheme: dark) {
  :root {
    --ink: #ffffff;
    --ink-2: #ccd4e6;
    --ink-3: #9fabc4;
    --rule: #39435c;
    --rule-strong: #5a6683;
  }
}

@media (prefers-reduced-transparency: reduce) {
  :root {
    --ground: none;
  }

  .masthead,
  .tabs {
    background: var(--glass-solid);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* ------------------------------------------------------- tables on a phone --

   Ten tables live across six templates and not one of them sat in a scroll
   container, so a table wider than the viewport pushed the whole page sideways.
   That is the symptom every reader recognises as "not built for a phone": the
   masthead scrolls away from the content and nothing lines up again.

   Fixed here rather than by wrapping ten tables in six templates, so a table
   added tomorrow is covered without anybody remembering. The table itself
   becomes the scrolling box: display:block gives it a block formatting context
   that can overflow, while its rows and cells keep their table behaviour, so
   column alignment survives.

   width:max-content with min-width:100% is the pair that matters. max-content
   lets the table keep its natural width and scroll; min-width stops a narrow
   table shrinking to its content and floating in a half-empty row.

   THE 62rem BOUND IS A KNOWN HOLE, NOT A DECISION. A table is too wide when it
   is wider than the space it has, and that is a fact about the table rather
   than about the screen: two registers push a 1280px laptop sideways while
   sitting perfectly inside a 390px handset. Lifting the bound was tried and
   rejected -- see .table-scroll below for the measurements and the reason.
   Until that is settled, a table known to be wide wears .table-scroll.

   This is a real fix for a real defect and it is not a substitute for opening
   the thing on a handset. Nothing here has been checked on a device.
   ---------------------------------------------------------------------------- */

@media (max-width: 62rem) {
  table {
    display: block;
    width: max-content;
    min-width: 100%;
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
  }

  /* A cell may wrap; the coordinate chip inside it still may not, because a
     character range broken across two lines is unreadable as a coordinate. */
  th, td { overflow-wrap: break-word; }
}

/* ------------------------------------------ a register too wide for a laptop --

   The rule above stops at 62rem, so a table with more columns than a laptop can
   hold pushes the whole document sideways above that width. Two do: the source
   registry on /admin/sources ran documentElement.scrollWidth to 1368 and the
   invite register on /admin/invites to 1377, both against a clientWidth of
   1280. Measured in a browser at that width, not guessed from the markup.

   WHY THE BOUND WAS NOT SIMPLY LIFTED. Making the table itself the scrolling
   box at every width also makes it a block, and a block table shrinks to the
   width of its columns instead of filling its row. Measured on a 1280px laptop,
   that pulled seven tables in off the right edge -- the two on /proceedings/...
   worst, from 1168px to 565px and 499px -- which is a visible change to screens
   somebody has already tuned by hand. Fixing an overflow on two screens is not
   worth restyling seven, so the treatment is named and applied where it is
   needed rather than imposed everywhere.

   Put this on the WRAPPER, never on the table. A table box ignores overflow, so
   the scrolling has to happen on an ordinary block around it, and the table
   keeps display:table and goes on filling that block when it fits.

   position:relative is part of the fix rather than decoration. An absolutely
   positioned descendant -- a visually hidden label, say -- is placed against its
   nearest positioned ancestor, and with none of them positioned it is measured
   against the page and escapes the scroll box, widening the document even
   though the thing that holds it scrolls. Positioning the wrapper keeps those
   boxes inside it. */
.table-scroll {
  position: relative;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}

/* Long unbroken strings -- a 64-character chain digest, a version id, a quoted
   fragment with no spaces -- are the other way a page grows wider than its
   screen. Break them anywhere rather than let one token set the page width. */
.digest, .hash, code, pre, .coord--long {
  overflow-wrap: anywhere;
}

/* AND .coord--long HAS TO UNDO .coord's nowrap, or it does nothing at all.
   `overflow-wrap: anywhere` cannot break a line that `white-space: nowrap` has
   already said may not break, so the modifier above was inert on the one class
   it is named for. It was also rendered by no template, so nobody found out:
   a rule keyed to a class nothing uses, whose declaration would not have
   worked if something had. Both halves are fixed here, and the review centre's
   source addresses are the first thing to wear it -- thirteen URLs, nowrap,
   were setting that page to 778 pixels inside a 390-pixel viewport.

   THE OPT-IN IS NOW A NO-OP, AND IT STAYS ANYWAY. .coord dropped its nowrap
   when the same defect turned up again on the change screen -- one id, 833px
   inside a 390px client -- so the modifier no longer has anything to undo. It
   is left in place because nine templates render it and because it still says
   what the author meant: this coordinate is long enough to fold. Delete it in a
   change that also deletes those nine, or not at all. */
.coord--long {
  white-space: normal;
}

/* ---------------------------------------------- the withheld surface --
 *
 * THE ONLY SURFACE IN THIS PRODUCT WITH NO EFFECTS AT ALL, and the one
 * place this design spends its boldness.
 *
 * Every other rule in this file adds something: a pane, a lift, a fill on
 * hover, a 200ms reveal. This block subtracts. It is placed LAST so that
 * it wins against anything added above it later, including a future
 * `* { transition: ... }` written by somebody who never read this comment.
 *
 * The argument, written down because a future editor will want to fix it.
 * The usual hierarchy gives the most important thing the most treatment.
 * This inverts it, and the inversion is the argument. A claim either
 * survives its citation or it does not; decoration cannot change which, so
 * decoration has no business on the surface that says so. Everything else
 * on a Verbatim screen answers a person -- a card lifts, a chip fills, a
 * panel slides, a pill springs, a masthead lets the page show through it.
 * One surface does not. It is the only place the interface has nothing to
 * offer, which is exactly what the system is saying: we could not verify
 * this, so we will not say it, and there is nothing here to make that
 * easier to accept.
 *
 * Three selectors, because the refusal has three forms and they are one
 * thing: .claim--withheld on a change page and on a shared claim,
 * .queue__row in the escalation queue, and .withheld -- the hatched slot
 * -- wherever it appears, including inside an assistant reply and inside a
 * blocked plan step. .withheld__label and .withheld__reason are named
 * separately because shares_admin.html and integrations.html use the
 * reason with no .withheld parent, and a refusal has to look the same on
 * an admin screen as it does on a change page.
 *
 * WHAT THIS BLOCK MUST NOT TOUCH: outline. The focus ring is an outline
 * and it survives here in full -- 6.14:1 against --surface-sunk in light,
 * 10.67:1 in dark, by token arithmetic. Only the halo goes, because the halo is a
 * box-shadow. Losing the ring would be an accessibility regression no test
 * in this repository catches.
 *
 * Everything structural stays exactly as it is: the broken amber rule, the
 * 135-degree hatch, the inset from the assertion axis, the sunk ground,
 * the quoted-against-source pair. Six differences, none of them colour,
 * all of them already surviving a monochrome screenshot and the print
 * block above.
 *
 * THE TEST THAT KEEPS THIS THE SIGNATURE: the withheld surface must be the
 * stillest thing on any screen it appears on. If a second element ever
 * competes -- equally still, equally prominent -- the second element gets
 * motion, or it is cut. Nothing is ever added here to win the comparison.
 */
.claim--withheld,
.claim--withheld *,
.queue__row,
.queue__row *,
.withheld,
.withheld *,
.withheld__label,
.withheld__reason {
  /* Stated rather than left to inherit, so a glass rule written later with
     a wider selector cannot reach in here. */
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  /* It casts nothing and it is lit by nothing. */
  box-shadow: none;
  text-shadow: none;
  filter: none;
  /* Not promoted to its own layer, because it has nothing to composite. */
  transform: none;
  will-change: auto;
  /* On every property, including the Approve and Reject buttons in a queue
     row. Their hover still appears -- instantly. */
  transition: none;
  /* It takes no part in the page-load sequence and no band may select it. */
  animation: none !important;
  /* Never faded and never dimmed. A greyed-out assertion is still an
     assertion: the reader's eye completes it. */
  opacity: 1;
}

/* Nothing happens here, so the cursor says so. The links and buttons
   inside a queue row keep their own pointer, because those do something:
   a declared rule on the control beats an inherited value from this one. */
.claim--withheld,
.queue__row,
.withheld {
  cursor: default;
}

/* There is deliberately no :hover, :active or :focus-within rule for any of
   the three. Not a rule that changes nothing -- no rule at all, because an
   empty rule is an invitation and the next person will fill it. */

/* The "Sign in as" row on the login page.
 *
 * One form per button, because each posts a different address and a single
 * form with three submit buttons would depend on which one the browser
 * decided to send -- which differs between browsers and is exactly the kind
 * of thing that works on the machine it was written on.
 *
 * The row wraps rather than scrolls: three buttons at 390px stack, and a
 * horizontal scroller holding the only way into the product is worse than a
 * taller page. Each button keeps its caption beside it, so a person choosing
 * between them reads the role and the name together rather than a role alone.
 */
.demo-signin {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s4);
}

.demo-signin__one {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  min-width: 13rem;
  flex: 1 1 13rem;
}

.demo-signin__one .btn {
  width: 100%;
  justify-content: center;
}
