/* ==========================================================================
   Seam — single-page stylesheet (formerly Seam)
   Brand: Newsreader, Libre Franklin and IBM Plex Mono; warm paper, ink text,
   ochre accents. Token names are kept from before: --lime is now ochre.
   Sharp edges only. Node/link motif taken from the logo, used everywhere:
   in the page background, the section dividers and the hero animation.
   No build step. Edit variables in :root to retune the whole site.
   ========================================================================== */

:root {
  /* Palette — taken from the Seam decks (theme colours used in slides) */
  --paper:      #f6f3ee;   /* page background            */
  --paper-2:    #fffdf9;   /* alternating sections       */
  --surface:    #fffdf9;   /* cards                      */
  --ink:        #211d19;   /* primary text               */
  --ink-soft:   #5d564d;   /* secondary text             */
  --muted:      #6f675c;   /* captions, meta — 5.0:1 on --paper. Before Seam: 4.6:1. It was #74716a,
                              which is 4.2:1 and fails the readable minimum at the
                              11-13px this token is mostly used at. */
  --lime:       #b9791f;   /* signature accent — ochre (name kept) */
  /* The footer's ground is var(--ink), which inverts between the light pages
     and the dark ones. --on-ink is the accent that reads against it either
     way: lime on the dark footer of a light page, deep green on the light
     footer of a dark one. The lime was used unconditionally and came out at
     1.2:1 on every register page's footer — not faint, invisible. */
  --on-ink:     #b9791f;
  --accent:     #9a6314;   /* text-safe ochre — kickers, links, prices (4.5:1) */
  --divider:    #ddd7cd;   /* warm border (light)        */
  --divider-2:  #c9c2b6;   /* warm border (stronger)     */

  /* --- The mark ---------------------------------------------------------
     The logo is a field of dots at five depths, near to far. The colours are
     tokens rather than fixed fills so ONE piece of markup works on paper, on
     the dark footer, on the dark analysis pages and on the dark hero: each of
     those contexts redefines these five and the same SVG re-colours itself. */
  --mark-1:     #211d19;   /* stitch bars 1 & 3 */
  --mark-2:     #b9791f;   /* stitch bars 2 & 4 */
  --mark-3:     #b9791f;
  --mark-4:     #b9791f;
  --mark-5:     #b9791f;   /* 3-5 unused by the Seam mark; kept defined */

  /* Type */
  --font-heading: "Newsreader", Georgia, "Times New Roman", serif;
  --font-body:    "Libre Franklin", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono:    "IBM Plex Mono", ui-monospace, Menlo, monospace;

  /* Shape & shadow — the deck has no rounded corners; everything is sharp */
  --radius:     0;
  --radius-sm:  0;
  --shadow-sm:  0 1px 2px  color-mix(in srgb, #211d19 12%, transparent);
  --shadow-md:  0 6px 20px color-mix(in srgb, #211d19 12%, transparent);
  --shadow-lg:  0 18px 48px color-mix(in srgb, #211d19 16%, transparent);

  /* Layout */
  --maxw:       1120px;
  --gap:        clamp(1rem, 2vw, 1.5rem);
  --header-h:   61px;      /* sticky masthead                       */
  --rail-h:     50px;      /* sticky "jump to" rail                 */

  /* --- Background field -------------------------------------------------
     The hero draws a field of small square points at four depths, loosely
     clustered. The page background is that same field held still and turned
     right down — squares, not dots, because squares are what the canvas
     actually paints. Two tiles at different sizes (260 and 170) so the
     repeat period is long enough that no pattern is visible. */
  --field-a: none;   /* Seam: plain paper, no dot field */
  --field-b: none;
  /* --- State colours -----------------------------------------------------
     The analysis pages score things: a rating passes or fails, a document is
     on your subject or is not, a body filed a return or did not. Those states
     were written as literal hex values inside each page, which meant eleven
     pages had to agree by hand and none of them could change theme. They are
     tokens now, defined here in the light values the pages already used, and
     redefined once for dark below. */
  --ok-ink:     #3d5400;   /* pass, positive, on your subject   */
  --ok-bg:      #eef4dc;
  --ok-bg-2:    #e7f0cb;
  --ok-bg-3:    #d7e9a5;   /* the strongest of the green fills  */
  --ok-line:    #cfe08f;
  --warn-ink:   #8a4a12;   /* weak, missing, a gap in the record */
  --warn-bg:    #f7ddc8;
  --warn-bg-2:  #fbf1e6;
  --warn-line:  #e6cdae;
  --warn-line-2:#d3ab77;
  --bad-ink:    #93211c;   /* fail, red opinion, worse off       */
  --bad-bg:     #f3d0cc;
  --bad-bg-2:   #eec6c1;
  --bad-line:   #d8b3aa;
  --info-ink:   #2f4562;   /* older, re-dated, second category   */
  --info-bg:    #dde3ec;
  --flat-bg:    #eeece2;   /* satisfactory / no signal           */
  --hatch:      #e3e0d4;   /* the diagonal "unassessable" fill   */
  --hatch-ink:  #6f6b5d;
  --bar-fill:   #c9c6b4;   /* the unshaded half of a two-tone bar */

}

/* ==========================================================================
   Dark — the eleven analysis pages only
   --------------------------------------------------------------------------
   govaudit, govintel and the nine registers carry data-theme="dark" on <html>;
   index.html and the marketing pages do not, and stay light. Everything below
   redefines tokens, never components, so a page picks the whole theme up from
   the variables it was already using.

   Two notes on the choices:
   * --accent is the deep ochre (#9a6314) in light, which is too dark on a
     dark ground. It becomes a lighter ochre (#d9a04a).
     --lime itself (ochre, #b9791f) is the brand colour and does not move.
   * color-scheme: dark is not decoration. Without it the browser paints its
     own light widgets — the search fields, the selects, the scrollbars — and
     they sit on the page like holes.
   ========================================================================== */
:root[data-theme="dark"] {
  color-scheme: dark;

  --paper:      #1a1612;
  --paper-2:    #211c17;
  --surface:    #27211b;
  --ink:        #f1ebe1;
  --ink-soft:   #cbbfae;
  --muted:      #a3978a;   /* 5.6:1 at worst (Seam); before, 4.7:1 at worst against the dark grounds here; #7d876a
                              was 3.7:1 on the darker panels at 11-12px. */
  --lime:       #b9791f;
  --accent:     #d9a04a;
  --on-ink:     #9a6314;

  /* the mark, seen against a dark ground: ink bars turn paper */
  --mark-1:     #f6f3ee;
  --mark-2:     #b9791f;
  --mark-3:     #b9791f;
  --mark-4:     #b9791f;
  --mark-5:     #b9791f;
  --divider:    #3a322a;
  --divider-2:  #4a4036;

  /* a shadow has to be darker than the surface to read at all */
  --shadow-sm:  0 1px 2px  rgba(0, 0, 0, 0.45);
  --shadow-md:  0 6px 20px rgba(0, 0, 0, 0.5);
  --shadow-lg:  0 18px 48px rgba(0, 0, 0, 0.6);

  /* the same field, in the hero's own depth ramp rather than in ink */
  --field-a: none;
  --field-b: none;

  --ok-ink:     #b3d95f;
  --ok-bg:      #23301a;
  --ok-bg-2:    #2a3a1c;
  --ok-bg-3:    #35491f;
  --ok-line:    #4e6a26;
  --warn-ink:   #e2a75f;
  --warn-bg:    #3a2716;
  --warn-bg-2:  #33240f;
  --warn-line:  #5d3f1c;
  --warn-line-2:#6b4c22;
  --bad-ink:    #f0907c;
  --bad-bg:     #3d1c17;
  --bad-bg-2:   #4a201a;
  --bad-line:   #6a2f26;
  --info-ink:   #9fbbe0;
  --info-bg:    #202c3c;
  --flat-bg:    #2a281f;
  --hatch:      #3a372c;
  --hatch-ink:  #bdb8a2;
  --bar-fill:   #4a4739;
}


/* --- Reset-ish --------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
/* The `hidden` attribute is how every toggle on the site hides things, so it
   has to beat any component that sets its own `display`. Without this,
   `.eyebrow { display: inline-flex }` keeps a hidden kicker on screen. */
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--font-body);
  font-size: clamp(1rem, 0.96rem + 0.25vw, 1.125rem);
  line-height: 1.6;
  color: var(--ink);
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;

  /* the hero's particle field (Seam: the tokens are none, so plain paper), held still and turned right down */
  background-color: var(--paper);
  background-image: var(--field-a), var(--field-b);
  background-size: 260px 260px, 170px 170px;
  background-position: 0 0, 60px 40px;
}
img { max-width: 100%; display: block; }
a { color: inherit; }

/* --- Typography -------------------------------------------------------- */
h1, h2, h3, h4 {
  font-family: var(--font-heading);
  font-weight: 400;
  line-height: 1.08;
  letter-spacing: -0.02em;
  margin: 0 0 0.5em;
  color: var(--ink);
}
h1 { font-size: clamp(2.4rem, 1.6rem + 3.6vw, 4.25rem); }
h2 { font-size: clamp(1.7rem, 1.2rem + 2vw, 2.6rem); }
h3 { font-size: clamp(1.2rem, 1.05rem + 0.7vw, 1.5rem); }
p  { margin: 0 0 1rem; }
.lead {
  font-size: clamp(1.15rem, 1rem + 0.7vw, 1.5rem);
  line-height: 1.45;
  color: var(--ink-soft);
  max-width: 44ch;
}

.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.69rem;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 1rem;
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
}
/* Deck "key" motif — a short dark-lime rule that precedes kickers */
.eyebrow::before {
  content: none;   /* Seam: no rule before kickers */
  width: 2.2em; height: 3px;
  background: var(--accent); flex: none;
}

/* Lime highlight behind key words — a round-capped stroke, not a block.
   A round-capped stroke rather than a
   square block, carrying the same rounded line-cap as the logo's traced path.
   Built from three background layers (round cap, bar, round cap) so .mark
   stays a plain inline element: an inline-block would be an atomic inline and
   would let the line break between the phrase and the full stop after it.
   box-decoration-break gives each line its own capped bar when it wraps. */
.mark {
  padding-inline: 0.09em;
  background-image:
    radial-gradient(circle at 50% 50%, var(--lime) 49.5%, transparent 50%),
    radial-gradient(circle at 50% 50%, var(--lime) 49.5%, transparent 50%),
    linear-gradient(var(--lime), var(--lime));
  background-size: 0.09em 0.09em, 0.09em 0.09em, calc(100% - 0.09em) 0.09em;
  background-position: left 0 bottom 0.11em, right 0 bottom 0.11em, center bottom 0.11em;
  background-repeat: no-repeat;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

/* Round full stops. Archivo sets a square period, which fights the node
   vocabulary everywhere else. The character stays in the markup (so it is
   still read aloud and still copies) and is drawn as a circle instead.
   Used on display type only — headings and straplines, not body copy. */
.stop { color: inherit; white-space: nowrap; }   /* Seam: Newsreader's full stop is already round */
.stop::after {
  content: none;
  display: inline-block;
  /* an empty inline-block takes its baseline from its bottom margin edge,
     so the dot's underside lands exactly on the text baseline — where a
     period's ink sits. Absolute positioning would measure from the line
     box instead and drop it into the descender space. */
  vertical-align: baseline;
  width: 0.15em; height: 0.15em;
  border-radius: 50%;
  background: var(--stop, var(--ink));
  /* pull back over the hidden period and re-space to its advance width */
  margin-left: -0.225em; margin-right: 0.07em;
}

/* --- Layout helpers ---------------------------------------------------- */
.container { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: clamp(1.15rem, 4vw, 2.5rem); }
.section { padding-block: clamp(2.75rem, 4.5vw, 4.75rem); position: relative; }
.section--alt {
  background-color: var(--paper-2);
  border-block: 1px solid var(--divider);
}

/* --- The homepage bands -------------------------------------------------
   Below the hero the sections are shaded rather than alternated. Each band
   is a translucent wash in the accent hue, and consecutive bands share the
   colour they meet at, so the page grades gently from one section to the
   next with no seam and no rule. The wash is translucent, so the background
   field carries straight through it unbroken.
   These ids exist on the homepage only. */
#approach, #in-the-works, #contact {
  background-color: transparent;
  background-image: var(--band);
  border-block: 0;
}
/* Each band carries three stops, so the colour moves WITHIN the section as
   well as between them, and consecutive bands share the value they meet at,
   so there is no seam where one ends and the next begins. */
#approach     { --band: linear-gradient(180deg, rgba(185,121,31,.005) 0%, rgba(185,121,31,.060) 55%, rgba(185,121,31,.035) 100%); }
#in-the-works { --band: linear-gradient(180deg, rgba(185,121,31,.050) 0%, rgba(185,121,31,.115) 60%, rgba(185,121,31,.065) 100%); }
#contact      { --band: linear-gradient(180deg, rgba(185,121,31,.065) 0%, rgba(185,121,31,.120) 40%, rgba(185,121,31,.015) 100%); }
.section__head { max-width: 60ch; margin-bottom: clamp(2rem, 4vw, 3rem); }

/* --- The opening screen of a register page ----------------------------
   Every page here asks the same question — "what does your organisation
   do?" — and on every page that box used to sit below the fold, at a
   different depth: 606px on one register, 3,952px on another. A visitor who
   learned where to look on one page had to hunt on the next.

   The box now sits directly under the first paragraph on all of them, and
   these three rules buy back the height that kept it off the first screen:
   a display size that does not need four lines for six words, a lead
   paragraph set at reading size rather than display size, and less air
   above the heading. The rest of the prose follows the box, where somebody
   who wants the background can still find it. */
.page-intro h1 { font-size: clamp(2.1rem, 1.3rem + 2.4vw, 3.15rem); }
.page-intro + .ga-strip,
.page-intro { margin-bottom: clamp(1.1rem, 2vw, 1.6rem); }
.section--intro { padding-block-start: clamp(1.6rem, 2.6vw, 2.6rem); }
.lead--intro { font-size: clamp(1.02rem, 0.95rem + 0.35vw, 1.18rem); max-width: 66ch; }

.section__sub  { margin-top: clamp(2rem, 3.5vw, 3rem); }
/* a section led by its eyebrow alone still needs the space a headline gave it */
.section > .container > .eyebrow,
.section > .container > div > .eyebrow:first-child { margin-bottom: clamp(1.5rem, 3vw, 2.25rem); }

/* Anchors clear the sticky masthead + rail */
/* --chrome-h is written by site.js from the measured heights of the
   masthead and the rail, because the rail now wraps and its height is not
   a constant. The calc() is the fallback for the first paint. */
[id] { scroll-margin-top: calc(var(--chrome-h, calc(var(--header-h) + var(--rail-h))) + 12px); }

.grid { display: grid; gap: clamp(1.1rem, 2.4vw, 1.75rem); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 860px) { .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }

/* --- Buttons ----------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: 0.5em;
  font: 500 0.9rem var(--font-body);
  text-decoration: none; cursor: pointer;
  padding: 1.05em 1.5em; border-radius: 0;
  border: 1px solid var(--ink);
  transition: transform .12s ease, box-shadow .12s ease, background .12s ease, color .12s ease;
}
.btn:hover { transform: none; box-shadow: none; }
.btn--primary { background: var(--ink); color: var(--paper); }
.btn--primary:hover { background: #000; }
.btn--accent  { background: #8a5810; color: #f6f3ee; border-color: #8a5810; }
.btn--ghost   { background: transparent; color: var(--ink); border-color: var(--divider-2); }
.btn--ghost:hover { background: var(--ink); color: var(--paper); }
.btn .arrow { transition: transform .12s ease; }
.btn:hover .arrow { transform: translateX(3px); }

/* --- Header / nav ------------------------------------------------------ */
.site-header {
  position: sticky; top: 0; z-index: 60;
  min-height: var(--header-h);
  /* SOLID, not frosted.

     It was `color-mix(in srgb, var(--paper) 90%, transparent)` with a 10px
     backdrop blur, on the assumption that the blur would smear whatever
     scrolled beneath it. It does not always: backdrop-filter needs the
     compositor, and where that is unavailable — remote desktops, software
     rendering, some Windows setups — the filter is silently skipped and the
     90% is all that is left. Ten per cent of a table row is perfectly
     legible, and on the procurement register, whose filter panel is not
     sticky, notices scroll straight under the masthead and read as though
     they are on top of the column headings.

     The `.is-over-hero` state below has been solid since it was written, for
     exactly this reason, with the note "a translucent dark would show the
     light body through". This is the same decision applied at rest. */
  background: var(--paper);
  border-bottom: 1px solid var(--divider);
  transition: background .25s ease, border-color .25s ease, color .25s ease;
}
/* Over the dark hero the masthead goes dark with it, and hands itself back
   the moment the hero has scrolled past. site.js toggles the class. */
.site-header.is-over-hero {
  background: #211d19;   /* solid: at rest the masthead sits ABOVE the hero, not over it,
                          so a translucent dark would show the light body through */
  border-bottom-color: rgba(185, 121, 31, 0.16);

  /* The masthead borrows the dark theme's TOKENS while it is over the hero,
     rather than setting colours on elements. That distinction matters: the
     dropdown panels live inside .mainnav, so a rule like ".mainnav a" also
     recolours every link inside them and leaves pale text on a white panel.
     Redefining the tokens instead carries the panels, their hover states,
     the dividing rules, the expand buttons and the hover descriptions with
     it, and every one of them reverts on its own once the hero scrolls
     past. Same approach the dark analysis pages use. */
  --paper:      #1a1612;
  --paper-2:    #211c17;
  --surface:    #27211b;
  --ink:        #f1ebe1;
  --ink-soft:   #cbbfae;
  --muted:      #a3978a;   /* 4.7:1 at worst against the dark grounds here; #7d876a
                              was 3.7:1 on the darker panels at 11-12px. */
  --divider:    #3a322a;
  --divider-2:  #4a4036;
  --accent:     #d9a04a;
  --on-ink:     #9a6314;   /* the footer band is light here — see --on-ink above */
  --shadow-lg:  0 18px 48px rgba(0, 0, 0, 0.6);
  --mark-1: #f6f3ee; --mark-2: #b9791f; --mark-3: #b9791f; --mark-4: #b9791f; --mark-5: #b9791f;
}
/* The masthead carries the lockup and one call to action. Section navigation
   lives entirely in the jump rail below it. */
.nav { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem 0.9rem; padding-block: 0.9rem; }
.brand { display: inline-flex; align-items: center; gap: 0.6rem; text-decoration: none; color: var(--ink); flex: none; }
.brand__mark { width: 32px; height: 32px; flex: none; display: block; overflow: visible; }
/* Presentation attributes do not reliably accept var(), so each depth is a
   class and the fill is set here. */
.brand__mark .m1 { fill: var(--mark-1); }
.brand__mark .m2 { fill: var(--mark-2); }
.brand__mark .m3 { fill: var(--mark-3); }
.brand__mark .m4 { fill: var(--mark-4); }
.brand__mark .m5 { fill: var(--mark-5); }
/* the footer is ink-dark on every page, light theme included */
.footer-brand .brand__mark {
  --mark-1: #f6f3ee; --mark-2: #b9791f; --mark-3: #b9791f; --mark-4: #b9791f; --mark-5: #b9791f;
}
.brand__word { font: 400 1.6rem/1 var(--font-heading); letter-spacing: -0.02em; }

/* --- Primary nav (with dropdown menus) -------------------------------- */
.mainnav { margin-left: auto; }
.mainnav__list { list-style: none; display: flex; align-items: center; gap: 0.15rem; margin: 0; padding: 0; }
.mainnav__list > li { position: relative; }
.mainnav a {
  display: inline-flex; align-items: center; gap: 0.35em;
  font: 400 0.9rem var(--font-body);
  color: var(--ink-soft); text-decoration: none; padding: 0.5rem 0.7rem; white-space: nowrap;
}
.mainnav > .mainnav__list > li > a:hover,
.mainnav > .mainnav__list > li > a:focus-visible { color: var(--ink); }
.mainnav .caret { font-size: 0.68em; color: var(--ink-soft); transition: transform .15s ease; }
/* Top-level nav labels lowercase, to match the "futureworks" wordmark
   (submenu items are already lowercased above). */
.mainnav__list > li > a { text-transform: lowercase; }

.submenu {
  list-style: none; margin: 0; padding: 0.4rem;
  position: absolute; top: 100%; right: 0; min-width: 250px;
  /* no panel: it reads as the masthead carrying on downwards, held off the
     page by its shadow alone rather than by a box */
  background: var(--paper); border: 0; box-shadow: var(--shadow-lg);
  opacity: 0; visibility: hidden; transform: translateY(6px);
  transition: opacity .14s ease, transform .14s ease, visibility .14s; z-index: 70;
}
.has-menu:hover .submenu,
.has-menu:focus-within .submenu,
.has-menu.is-open .submenu { opacity: 1; visibility: visible; transform: none; }
/* A hover menu should not fly open as the pointer merely crosses it on the
   way to somewhere else. The delay sits on the HOVER state only: a keyboard
   tab (:focus-within) and a tap (.is-open) are deliberate and open at once.
   Leaving falls back to the block above, whose `transition` shorthand resets
   the delay to zero - so it opens lazily and closes promptly, which is the
   way round that feels right. */
.has-menu:hover .submenu { transition-delay: .2s; }
.has-menu:hover .caret,
.has-menu:focus-within .caret,
.has-menu.is-open .caret { transform: rotate(180deg); }
.submenu a { display: block; padding: 0.55rem 0.7rem; font-size: 0.9rem; font-weight: 600; }
.submenu a:hover, .submenu a:focus-visible { background: var(--paper-2); color: var(--accent); }

/* The "nothing here matches you closely" line. Deliberately plain rather than
   alarming: it is a caveat, not an error, and it appears beside a count the
   reader is already reading. */
.weak {
  display: inline-block;
  margin-left: .4rem;
  padding: .05rem .4rem;
  border-left: 2px solid var(--divider-2);
  color: var(--ink-soft);
  font-style: italic;
}

.noscript {
  margin: 1rem 0; padding: .9rem 1.1rem;
  border: 1px solid var(--divider-2); background: var(--surface);
  color: var(--ink-soft); font-size: .95rem; max-width: 62ch;
}

/* --- when a register did not arrive (see fw-data.js) -------------------
   Deliberately plain and not alarming: this is information, not an error the
   reader caused. The warning form sits above real answers that are merely
   incomplete; the replacement form stands where the answers would have been. */
.fw-datawarn, .fw-dataout {
  margin: 0 0 1.1rem; padding: .8rem 1rem;
  border: 1px solid var(--divider-2); border-left: 3px solid var(--accent);
  background: var(--surface); color: var(--ink-soft);
  font-size: .93rem; line-height: 1.55; max-width: 68ch;
}
.fw-dataout { margin: 1.2rem 0; }
.fw-datawarn b, .fw-dataout b { color: var(--ink); display: block; margin-bottom: .2rem; }

/* --- fingertips -------------------------------------------------------
   The accepted minimum for a touch target is 44x44 — roughly the pad of an
   adult finger. Measured at 390px on 4 September 2026, GovAudit had eighty
   controls below 40px in one dimension, including the menu button (91x31),
   every link inside the menu it opens (38px tall) and "See my briefing"
   (157x33). The menu button matters most: it is the only way to navigate on
   a phone.

   This is padding, not layout — none of these controls were short of room. */
@media (max-width: 900px) {
  .navtoggle {
    min-height: 44px;
    padding-inline: 0.85rem;
  }
  .mainnav__list > li > a,
  .mainnav .submenu a,
  .mainnav__here a {
    min-height: 44px;
    display: flex;
    align-items: center;
  }
  .rail a {
    min-height: 44px;
    padding-block: 0.55rem;
  }
  button.go,
  .go,
  .more,
  .more--all,
  .fw-url__go,
  .chat__launch,
  .chat__send,
  .chat__chip {
    min-height: 44px;
  }
  /* Small inline actions sit in running text, where 44px would break the
     line box. They get a wider hit area instead of a taller one. */
  .fw-saved__act,
  .fw-carry__clear,
  .link {
    padding-block: 0.55rem;
  }
  /* The wordmark is a link home and is tapped as one. */
  .brand { min-height: 44px; align-items: center; }

  /* View switches, sort buttons and the filter boxes are discrete controls
     too, and were 28–34px. Links inside a sentence are deliberately NOT in
     this list: the standard exempts them, and stretching them would break
     the line they sit in. */
  .ga-view, .gi-view, .ga-sort, .gi-sort,
  .tn-chip, .chip,
  input[type="search"], .controls select, .controls input,
  select {
    min-height: 44px;
  }
  .ga-view, .gi-view, .ga-sort, .gi-sort { padding-inline: 0.8rem; }

  /* A cell whose only content is a link is a control, not a sentence. */
  .ga-plink, .gi-plink { display: inline-flex; align-items: center; min-height: 44px; }
}

/* --- very narrow screens ----------------------------------------------
   280px is a folding phone's cover screen and a handful of old handsets.
   The page put 290px of content in it and scrolled sideways; the padding is
   what did it. 320px — the width 400% zoom produces, and the one that
   matters for accessibility — was already clean. */
@media (max-width: 320px) {
  .container { padding-inline: 0.55rem; }
  /* The table's own minimum width is what is left over at this size; it
     scrolls inside its wrapper, which is correct, but the wrapper must not
     push the page itself sideways. */
  .ga-tablewrap, .gi-tablewrap { max-width: 100%; }
  body { overflow-x: hidden; }
}

.fw-cap {
  margin: .4rem 0 0; font-size: .82rem; line-height: 1.5;
  color: var(--ink-soft); max-width: 62ch;
  border-left: 2px solid var(--divider-2); padding-left: .6rem;
}

/* The skip link's target. Browsers refuse to move focus to an element that
   cannot receive it, so without tabindex="-1" here "Skip to content" scrolled
   the page and left the keyboard exactly where it was — the next Tab started
   again at the top of the menu. The outline is suppressed because the target
   is a whole page region, not a control; the skip link itself keeps its ring. */
#main:focus { outline: none; }
#main:focus-visible { outline: none; }

/* --- The menu button ---------------------------------------------------
   Above 860px the primary menu sits in the masthead and the section rail
   sits under it, both open. Below that, a horizontal strip of ten links in
   a 350px-wide window is not navigation — it scrolled, but with its
   scrollbar hidden and no fade at the edge nothing said so. So on phones
   the rail is folded into this panel instead (site.js clones the links in)
   and one button reaches every link on the site. */
.navtoggle {
  display: none; margin-left: auto; flex: none;
  align-items: center; gap: 0.5rem;
  font-family: var(--font-heading); font-weight: 700; font-size: 0.86rem;
  text-transform: lowercase; letter-spacing: 0.01em;
  color: var(--ink); background: transparent;
  border: 1px solid var(--divider-2); border-radius: 3px;
  padding: 0.42rem 0.7rem; cursor: pointer;
}
.navtoggle:hover { border-color: var(--muted); }
.navtoggle__bars {
  width: 15px; height: 10px; position: relative; display: block;
  border-top: 2px solid currentColor; border-bottom: 2px solid currentColor;
}
.navtoggle__bars::after {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 2px; margin-top: -1px; background: currentColor;
}
.navtoggle[aria-expanded="true"] { background: var(--surface); border-color: var(--muted); }

/* The rail's links, cloned into the menu panel on phones. */
/* Only ever shown inside the phone menu panel; above 860px the rail
   itself is on screen and this clone would be a second copy of it. */
.mainnav__here { display: none; margin: 0.35rem 0 0; padding: 0.6rem 0 0; border-top: 1px solid var(--divider); }
.mainnav__herelabel {
  display: block; margin: 0 0 0.25rem;
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--muted);
}
.mainnav__here ul { list-style: none; margin: 0; padding: 0; }
.mainnav__here a { display: block; padding: 0.45rem 0; font-size: 0.92rem; text-transform: lowercase; }

@media (max-width: 860px) {
  .nav { align-items: center; }
  .navtoggle { display: inline-flex; }
  .mainnav { margin-left: 0; width: 100%; display: none; order: 99; }
  .mainnav.is-open { display: block; }
  .mainnav__list { flex-direction: column; align-items: stretch; gap: 0; }
  .mainnav__list > li > a { display: block; padding: 0.55rem 0; }
  .submenu {
    position: static; opacity: 1; visibility: visible; transform: none;
    box-shadow: none; border: none; border-left: 2px solid var(--divider-2);
    margin: 0 0 0.3rem 0.7rem; min-width: 0; display: none; padding: 0.1rem 0;
  }
  .has-menu.is-open .submenu { display: block; }

  /* Folded into the panel above; see .mainnav__here. */
  .rail { display: none; }
  .mainnav__here { display: block; }
}

/* Tagline, set beside the wordmark and divided from it by a warm rule */
.brand__tag {
  margin: 0; flex: none;
  font-family: var(--font-heading); font-weight: 600; font-size: 0.86rem;
  color: var(--ink-soft); letter-spacing: 0.005em; white-space: nowrap;
  padding-left: 0.9rem; border-left: 1px solid var(--divider-2);
}
/* The tagline is part of the lockup, so it stays visible right down to
   narrow phones. It only steps down in size; it is dropped only below the
   width where it genuinely cannot sit beside the wordmark. */
@media (max-width: 620px) {
  .brand__tag { font-size: 0.78rem; padding-left: 0.65rem; }
}
@media (max-width: 400px) {
  .brand__tag { display: none; }
}

.footer-tag {
  font-family: var(--font-heading); font-weight: 600; font-size: 0.92rem;
  color: var(--on-ink); margin: -0.35rem 0 0.6rem;
}

/* --- "Jump to" rail — the skip-to buttons ------------------------------ */
/* NOT sticky. It used to be, and the result was that the one row a reader
   needs while scrolling a long table — the column headings — scrolled away
   while a row of section links they had already used followed them down the
   page. Now the rail behaves as a page heading, the table heads are pinned
   instead (see .ga-tablewrap), and the sticky chrome is one masthead tall
   rather than three rows. */
.rail {
  position: static; z-index: 55;
  background: color-mix(in srgb, var(--paper) 94%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--divider);
}
.rail__inner {
  display: flex; align-items: center; gap: 0.75rem;
  min-height: var(--rail-h);
}
/* Section links are set lower-case throughout, to match the wordmark. */
.rail__label {
  font-size: 0.78rem; font-weight: 700; letter-spacing: 0.04em; text-transform: lowercase;
  color: var(--muted); flex: none; display: inline-flex; align-items: center; gap: 0.55em;
}
.rail__label::after { content: ""; width: 1.4em; height: 2px; background: var(--divider-2); }
@media (max-width: 640px) { .rail__label { display: none; } }

/* Wraps rather than scrolls. It used to be one line with overflow-x:auto
   and scrollbar-width:none, which meant that at 1440px a 1,715px list sat
   in a 938px window with nothing on screen to say the rest was there. */
.rail ul {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0.35rem 0.35rem;
  list-style: none; margin: 0; padding: 0.35rem 0;
}
.rail a {
  display: inline-flex; align-items: center; gap: 0.5em;
  white-space: nowrap; text-decoration: none; text-transform: lowercase;
  font-family: var(--font-heading);
  font-size: 0.92rem; font-weight: 600; letter-spacing: -0.01em; color: var(--ink-soft);
  padding: 0.42em 0.8em; border: 1px solid transparent; border-radius: 0;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.rail a::before {
  content: ""; width: 6px; height: 6px; border-radius: 0;
  background: var(--divider-2); flex: none; transition: background .12s ease, transform .12s ease;
}
.rail a:hover { color: var(--ink); border-color: var(--divider-2); background: var(--surface); }
.rail a[aria-current="true"] {
  color: var(--ink); background: var(--surface); border-color: var(--ink);
}
.rail a[aria-current="true"]::before { background: var(--lime); transform: scale(1.35); }

/* --- The opening screen -------------------------------------------------
   The same ground and the same lime as the hero, so it does not so much
   disappear as resolve into it: the overlay fades, the hero is already
   underneath it in the same colour, and the headline rises in.

   Shown once per browsing session, never without JS (the markup only
   displays when the head script has put .booting on <html>), and never
   under prefers-reduced-motion.
   ------------------------------------------------------------------------ */
.boot {
  position: fixed; inset: 0; z-index: 300; display: none;
  background: #1a1612;
  --mark-1: #f6f3ee; --mark-2: #b9791f; --mark-3: #b9791f; --mark-4: #b9791f; --mark-5: #b9791f;
}
/* .is-done has to keep the overlay displayed on its own. site.js removes
   .booting from <html> in the same frame as it adds .is-done, and if display
   depended on .booting alone the overlay would flip straight to display:none
   and the fade below would never run — the opening screen snapped off. */
html.booting .boot,
.boot.is-done { display: grid; place-items: center; }
/* The gutter is reserved while the page cannot scroll, so that restoring the
   scrollbar at the handover does not shift the whole layout sideways. */
html.booting body { overflow: hidden; scrollbar-gutter: stable; }
/* the same field the hero runs, on the same ground, so the overlay is
   already the hero before it clears */
.boot__field { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.boot::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(52% 44% at 50% 50%, rgba(26, 22, 18,.86), rgba(26, 22, 18,0) 70%);
}
.boot__inner { position: relative; z-index: 2; display: grid; justify-items: center; gap: 0.85rem; }
.boot__mark { width: 54px; height: 54px; }
.boot__word {
  font-family: var(--font-heading); font-weight: 800; font-size: 1.35rem;
  letter-spacing: -0.03em; color: #f3f6ea; margin: 0;
}
.boot.is-done { opacity: 0; pointer-events: none; transition: opacity .3s ease; }

/* the hero arriving as the overlay clears */
.hero.is-revealed .hero__inner { animation: heroRise .85s cubic-bezier(.16, .84, .3, 1) both; }
.hero.is-revealed .hero__cue   { animation: heroFade .8s .35s ease both; }
@keyframes heroRise { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
@keyframes heroFade { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .hero.is-revealed .hero__inner, .hero.is-revealed .hero__cue { animation: none; }
}

/* --- Hero ---------------------------------------------------------------
   A dark, full-bleed band at the top of the homepage: a particle field on a
   canvas behind, a veil that darkens the left so the headline holds, and the
   copy in the usual .container so it lines up with every section below.
   The masthead above it goes dark to match (see .is-over-hero).
   ------------------------------------------------------------------------ */
.hero {
  position: relative;
  overflow: hidden;
  display: grid; align-items: center;
  min-height: min(82vh, 760px);
  background: #1a1612;
  color: #f1ebe1;
  --stop: #f4f8e8;                 /* the round full stop, on dark */
}
.hero__field { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.hero__veil {
  position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(120% 90% at 78% 45%, rgba(185, 121, 31, 0.10), rgba(26, 22, 18, 0) 55%),
    linear-gradient(90deg, rgba(26, 22, 18, 0.94) 0%, rgba(26, 22, 18, 0.78) 34%, rgba(26, 22, 18, 0) 62%);
}
.hero__inner { position: relative; z-index: 2; padding-block: clamp(3rem, 8vw, 6rem); }
/* the one place on the site that runs larger than the shared h1 scale */
.hero h1 {
  font-size: clamp(2.35rem, 1.3rem + 4.1vw, 4.6rem);
  max-width: 19ch; color: #f4f8e8; margin-bottom: 0;
}
.hero .lead { max-width: 52ch; color: #cbbfae; margin-bottom: 0; }

.hero__cue {
  position: absolute; z-index: 2;
  right: clamp(1.15rem, 4vw, 2.5rem); bottom: 1.5rem;
  display: flex; align-items: center; gap: 0.85rem; pointer-events: none;
}
.hero__cue-track {
  position: relative; display: block; width: 1px; height: 34px;
  background: rgba(185, 121, 31, 0.22); overflow: hidden;
}
.hero__cue-track i {
  position: absolute; top: 0; left: 0; width: 1px; height: 10px;
  background: var(--lime); animation: heroCue 2.1s ease-in-out infinite;
}
.hero__cue span {
  font-family: var(--font-heading); font-size: 0.75rem; font-weight: 700;
  letter-spacing: 0.2em; text-transform: uppercase; color: #7d876a;
}
@keyframes heroCue {
  0%   { transform: translateY(0);    opacity: 0.15; }
  45%  { transform: translateY(14px); opacity: 1; }
  100% { transform: translateY(28px); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .hero__cue-track i { animation: none; }
}
@media (max-width: 700px) {
  .hero { min-height: 66vh; }
  /* the veil turns vertical on a narrow screen: there is no room for a
     left-hand column, so the field is dimmed under the copy instead */
  .hero__veil { background: linear-gradient(180deg, rgba(26, 22, 18,.88) 0%, rgba(26, 22, 18,.66) 60%, rgba(26, 22, 18,.42) 100%); }
  .hero__cue { display: none; }
}

/* --- Cards ------------------------------------------------------------- */
.card {
  background: var(--surface); border: 1px solid var(--divider); border-radius: var(--radius);
  padding: clamp(1.35rem, 2.2vw, 1.9rem); box-shadow: var(--shadow-sm);
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.card:hover { transform: none; box-shadow: var(--shadow-sm); border-color: var(--divider-2); }
.card h3 { margin-bottom: 0.4rem; }
.card p:last-child { margin-bottom: 0; color: var(--ink-soft); }
.card__num {
  font-family: var(--font-mono); font-weight: 400; font-size: 0.69rem;
  color: var(--accent); letter-spacing: 0.12em; margin-bottom: 0.6rem;
}
/* Node glyphs — built from the logo's own vocabulary (nodes, links, one lime).
   No pictorial icons anywhere on the site. */
.glyph { width: 48px; height: 48px; display: block; color: var(--ink); margin-bottom: 1.15rem; overflow: visible; }
.glyph--lg { width: 60px; height: 60px; }
.glyph--row { width: 72px; height: auto; display: block; color: var(--ink); margin-bottom: 1rem; overflow: visible; }

/* --- Method: the three stages -----------------------------------------
   Research, ideation, translation. Deliberately airy: one full-width band
   per stage, a wide gutter between the label column and the text, hairline
   rules instead of boxes, and a lot of vertical space between bands.
   ---------------------------------------------------------------------- */
.method-flow { margin-top: clamp(0.5rem, 2vw, 1.5rem); }

.method-step {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 7fr);
  gap: clamp(1.25rem, 6vw, 5rem);
  padding-block: clamp(2.5rem, 6vw, 5rem);
  border-top: 1px solid var(--divider);
}
.method-step:first-child { border-top: 0; padding-top: 0; }

.method-step__label { align-self: start; }
.method-step__num {
  font-family: var(--font-heading); font-weight: 800; font-size: 0.95rem;
  letter-spacing: 0.02em; color: var(--accent); margin: 0 0 0.7rem;
}
/* the stage name carries h2 weight even though it is an h3 in the outline */
.method-step__label h3 {
  font-size: clamp(1.7rem, 1.2rem + 2vw, 2.6rem);
  margin: 0;
}

.method-step__body { max-width: 60ch; }
.method-step__lead {
  font-size: clamp(1.05rem, 1rem + 0.4vw, 1.28rem);
  line-height: 1.55; color: var(--ink-soft);
  margin: 0 0 clamp(1.75rem, 3.5vw, 2.75rem);
}

/* Points are plain text with a lime node in the margin — no cards, so the
   white space between them does the separating. */
.method-points {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: clamp(1.15rem, 2.4vw, 1.75rem);
}
.method-points li { position: relative; padding-left: 1.6rem; }
.method-points li::before {
  content: ""; position: absolute; left: 0; top: 0.5em;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--lime); box-shadow: 0 0 0 1px var(--divider-2);
}
.method-points b {
  display: block; font-family: var(--font-heading); font-weight: 700;
  font-size: 1.02rem; letter-spacing: -0.01em; margin-bottom: 0.2rem;
}
.method-points span { color: var(--ink-soft); }

@media (max-width: 860px) {
  .method-step { grid-template-columns: 1fr; gap: 1.25rem; }
}

/* the console shot now sits inside the Research stage, under its points */
.method-step__body .shot { margin: clamp(1.75rem, 4vw, 2.5rem) 0 0; max-width: none; }

/* --- Step / process list ---------------------------------------------- */
.steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 1rem; counter-reset: step; }
.step { display: grid; grid-template-columns: auto 1fr; gap: 1.1rem; align-items: start;
  background: var(--surface); border: 1px solid var(--divider); border-radius: var(--radius); padding: 1.35rem 1.5rem; }
.step__no { counter-increment: step; font-family: var(--font-heading); font-weight: 800; font-size: 1.05rem;
  width: 2.4rem; height: 2.4rem; border-radius: 50%; display: grid; place-items: center;
  background: var(--ink); color: var(--paper); flex: none; }
.step__no::before { content: counter(step, decimal-leading-zero); }
.step h3 { margin-bottom: 0.25rem; }
.step p { margin: 0; color: var(--ink-soft); }

/* --- Stat band --------------------------------------------------------- */
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
@media (max-width: 640px) { .stats { grid-template-columns: 1fr; } }
.stat { text-align: left; }
.stat__num { font-family: var(--font-heading); font-weight: 800; font-size: clamp(2.2rem, 1.5rem + 2.4vw, 3.4rem); line-height: 1; letter-spacing: -0.03em; }
.stat__num .u { color: var(--accent); }
.stat__label { color: var(--ink-soft); margin-top: 0.5rem; max-width: 30ch; }

/* --- The message ladder ------------------------------------------------
   One line, one sentence, one paragraph, one page — the same statement at
   four lengths. Type size steps *down* as the detail steps up. */
.ladder { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--divider-2); }
.ladder__step {
  display: grid; grid-template-columns: 11rem minmax(0, 1fr);
  gap: clamp(1rem, 3vw, 2.5rem); align-items: baseline;
  padding-block: clamp(1.4rem, 3vw, 2.2rem);
  border-bottom: 1px solid var(--divider);
}
@media (max-width: 720px) {
  .ladder__step { grid-template-columns: 1fr; gap: 0.6rem; }
}
.ladder__k {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--accent); display: inline-flex; align-items: center; gap: 0.6em; white-space: nowrap;
}
.ladder__k::before { content: ""; width: 1.8em; height: 3px; background: var(--accent); flex: none; }
.ladder__v { margin: 0; color: var(--ink-soft); max-width: 68ch; }
.ladder__v--xl {
  font-family: var(--font-heading); font-weight: 800; color: var(--ink);
  font-size: clamp(1.7rem, 1.2rem + 2vw, 2.6rem); line-height: 1.05; letter-spacing: -0.02em;
}
.ladder__v--lg {
  font-size: clamp(1.1rem, 1rem + 0.55vw, 1.35rem); line-height: 1.4; color: var(--ink);
}
.ladder__step--link .ladder__v a {
  font-family: var(--font-heading); font-weight: 700; color: var(--accent); text-decoration: none;
}
.ladder__step--link .ladder__v a:hover { text-decoration: underline; }
.ladder__step--link .arrow { display: inline-block; transition: transform .12s ease; }
.ladder__step--link a:hover .arrow { transform: translateX(3px); }

/* an <h3> that needs to carry h2 weight in the layout */
.h2-ish { font-size: clamp(1.7rem, 1.2rem + 2vw, 2.6rem); }

/* --- The questions behind the question ---------------------------------
   The ones a team has hours for read as answered; the rest fade out. That
   contrast is the whole problem statement, so it is carried by the type
   weight and the node colour rather than by a caption alone. */
.questions { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.8rem; max-width: 46ch; }
.questions li {
  position: relative; padding-left: 1.9rem;
  font-family: var(--font-heading); font-weight: 700;
  font-size: clamp(1.02rem, 0.96rem + 0.4vw, 1.22rem); line-height: 1.35;
  letter-spacing: -0.01em; color: var(--ink);
}
.questions li::before {
  content: ""; position: absolute; left: 0; top: 0.46em;
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--lime); box-shadow: 0 0 0 1.5px var(--ink);
}
.questions li.is-dim { color: var(--muted); font-weight: 600; }
.questions li.is-dim::before { background: transparent; box-shadow: 0 0 0 1.5px var(--divider-2); }
.questions__note {
  margin: 1.1rem 0 0; max-width: 42ch;
  font-size: 0.92rem; color: var(--muted);
  padding-top: 0.9rem; border-top: 1px solid var(--divider);
}

/* --- Use cases: sector cards ------------------------------------------ */
.sector { position: relative; padding-top: clamp(1.5rem, 2.4vw, 2rem); }
.sector .card__num { position: absolute; top: clamp(1.35rem, 2.2vw, 1.9rem); right: clamp(1.35rem, 2.2vw, 1.9rem); margin: 0; opacity: 0.55; }
.sector ul { list-style: none; margin: 1.1rem 0 0; padding: 0; display: grid; gap: 0.55rem; }
.sector li {
  position: relative; padding-left: 1.4rem;
  font-size: 0.95rem; color: var(--ink-soft); line-height: 1.45;
}
.sector li::before {
  content: ""; position: absolute; left: 0; top: 0.55em;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--ink); opacity: 0.35;
}
.sector li:last-child::before { background: var(--lime); opacity: 1; box-shadow: 0 0 0 1.5px var(--ink); }

/* --- Use cases: the task list ------------------------------------------ */
.tasks { list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 0; border-top: 1px solid var(--divider); }
@media (max-width: 760px) { .tasks { grid-template-columns: 1fr; } }
.task {
  display: grid; grid-template-columns: auto 1fr; gap: 0.9rem; align-items: start;
  padding: 1rem 1.1rem 1rem 0.9rem; border-bottom: 1px solid var(--divider);
  transition: background .12s ease;
}
.tasks > .task:nth-child(odd) { padding-right: clamp(1.1rem, 3vw, 2.25rem); }
@media (min-width: 761px) { .tasks > .task:nth-child(odd) { border-right: 1px solid var(--divider); } }
.task:hover { background: color-mix(in srgb, var(--lime) 9%, transparent); }
/* one lime node every fifth row — the dot-band's single accent, repeated */
.task::before {
  content: ""; width: 9px; height: 9px; border-radius: 50%; margin-top: 0.5em;
  background: var(--ink); opacity: 0.3; flex: none;
}
.task:nth-child(5n+3)::before { background: var(--lime); opacity: 1; box-shadow: 0 0 0 1.5px var(--ink); }
.task b { display: block; font-family: var(--font-heading); font-weight: 700; font-size: 1rem; letter-spacing: -0.01em; }
.task span { display: block; font-size: 0.92rem; color: var(--ink-soft); line-height: 1.45; margin-top: 0.15rem; }

/* --- Our work ---------------------------------------------------------- */
.work { display: grid; grid-template-columns: repeat(2, 1fr); gap: clamp(1.1rem, 2.4vw, 1.75rem); }
@media (max-width: 760px) { .work { grid-template-columns: 1fr; } }
.work-card {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--divider);
  padding: clamp(1.35rem, 2.2vw, 1.9rem);
  box-shadow: var(--shadow-sm);
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
a.work-card { text-decoration: none; color: inherit; }
a.work-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); border-color: var(--ink); }
.work-card__kind {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 0.9rem; display: flex; align-items: center; gap: 0.6rem;
}
.work-card__kind::before { content: ""; width: 1.6em; height: 2px; background: var(--lime); flex: none; }
.work-card h3 { margin-bottom: 0.5rem; }
.work-card p { color: var(--ink-soft); margin-bottom: 1.25rem; }
.work-card__foot { margin-top: auto; display: flex; align-items: center; gap: 0.5em;
  font-family: var(--font-heading); font-weight: 700; font-size: 0.95rem; color: var(--accent); }
a.work-card:hover .work-card__foot .arrow { transform: translateX(3px); }
.work-card__foot .arrow { transition: transform .12s ease; }
.work-card--soon .work-card__foot { color: var(--muted); font-weight: 600; }
.chip {
  display: inline-block; font-size: 0.75rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 0.35em 0.65em; border: 1px solid var(--divider-2); color: var(--muted); background: var(--paper-2);
}
.chip--live {
  color: var(--accent); border-color: var(--accent);
  background: color-mix(in srgb, var(--lime) 26%, var(--paper));
}

/* --- Openworks: the featured post ----------------------------------- */
.post {
  background: var(--surface); border: 1px solid var(--divider-2);
  box-shadow: var(--shadow-md);
  padding: clamp(1.5rem, 4vw, 3.25rem);
  position: relative; overflow: hidden;
}
/* a lime spine down the left edge — the logo's signature stroke */
.post::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 5px; background: var(--lime);
}
.post__head { margin-bottom: clamp(1rem, 2vw, 1.5rem); }
.post__kind {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--accent); margin: 0 0 0.7rem; display: inline-flex; align-items: center; gap: 0.6em;
}
.post__kind::before { content: ""; width: 2.2em; height: 3px; background: var(--accent); flex: none; }
.post__title { margin-bottom: 0; }
.post__body { max-width: 70ch; }
.post__body p:last-child { margin-bottom: 0; }
.post__foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem 1rem;
  margin-top: clamp(1.25rem, 3vw, 2rem); padding-top: 1.1rem;
  border-top: 1px solid var(--divider);
}
.post__meta { font-size: 0.88rem; color: var(--muted); }

/* --- Console shot ------------------------------------------------------ */
/* Capped and centred — at full container width it is 650px tall and swamps
   the section it sits in. */
.shot { margin: 0 auto; max-width: 760px; }
.shot__frame {
  border: 1px solid var(--divider-2);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.shot__frame img { width: 100%; height: auto; }
.shot figcaption {
  margin-top: 0.9rem; font-size: 0.92rem; color: var(--muted); max-width: 68ch;
}
.shot figcaption b { color: var(--ink-soft); font-weight: 700; }

/* --- CTA band ---------------------------------------------------------- */
.cta {
  --stop: var(--paper);   /* round full stops read paper-on-ink here */
  background: var(--ink); color: var(--paper); border-radius: 0;
  padding: clamp(2rem, 5vw, 3.75rem);
  padding-bottom: clamp(3.25rem, 6vw, 5.5rem);
  position: relative; overflow: hidden;
}
/* the same node lattice, inverted, so the dark band stays on-theme */
.cta::before {
  content: none; position: absolute; inset: 0; pointer-events: none;
  background-image:
    radial-gradient(circle at 50% 50%, rgba(246, 243, 238,.10) 1.15px, transparent 1.25px),
    radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--lime) 40%, transparent) 2px, transparent 2.1px);
  background-size: 28px 28px, 140px 140px;
  background-position: 0 0, 14px 14px;
}
/* Dot motif, paper-on-dark, bleeding off the box's right edge */
.cta__dots { position: absolute; left: 0; right: 0; bottom: clamp(1rem, 2.4vw, 1.9rem);
  width: 100%; line-height: 0; pointer-events: none; }
.cta__dots svg { display: block; width: 100%; height: clamp(20px, 2.6vw, 32px); }
.cta h2, .cta p { color: var(--paper); position: relative; z-index: 1; }
.cta .eyebrow { position: relative; z-index: 1; }
.cta .lead { color: color-mix(in srgb, var(--paper) 80%, transparent); }
.cta .eyebrow::before { background: var(--lime); }
.cta__actions { position: relative; z-index: 1; display: flex; flex-wrap: wrap; gap: 0.8rem; margin-top: 1.5rem; }
.cta .btn--primary { background: var(--paper); color: var(--ink); border-color: var(--paper); }
.cta .btn--primary:hover { background: #ece7de; }
.cta .btn--ghost { color: var(--paper); border-color: color-mix(in srgb, var(--paper) 50%, transparent); }
.cta .btn--ghost:hover { background: var(--paper); color: var(--ink); }

/* --- Prose (long-form) ------------------------------------------------- */
.prose { max-width: 68ch; }
.prose h2 { margin-top: 2.4rem; }
.prose h3 { margin-top: 1.8rem; }
.prose ul, .prose ol { padding-left: 1.2rem; }
.prose li { margin-bottom: 0.5rem; }
.prose blockquote {
  margin: 1.6rem 0; padding: 0.4rem 0 0.4rem 1.3rem; border-left: 4px solid var(--lime);
  font-size: 1.15rem; color: var(--ink); font-style: italic;
}
.prose code { background: var(--paper-2); border: 1px solid var(--divider); border-radius: 0; padding: 0.1em 0.4em; font-size: 0.9em; }

/* --- Contact form ------------------------------------------------------ */
.form { display: grid; gap: 1.1rem; }
.field { display: grid; gap: 0.4rem; }
.field label { font-weight: 600; font-size: 0.95rem; }
.field input, .field textarea {
  font-family: inherit; font-size: 1rem; color: var(--ink);
  background: var(--surface); border: 1.5px solid var(--divider-2); border-radius: var(--radius-sm);
  padding: 0.75rem 0.9rem; width: 100%; transition: border-color .12s ease, box-shadow .12s ease;
}
.field input:focus, .field textarea:focus {
  outline: none; border-color: var(--ink);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--lime) 55%, transparent);
}
.field textarea { min-height: 140px; resize: vertical; }
.form__note { font-size: 0.9rem; color: var(--muted); }

/* --- Contact split ----------------------------------------------------- */
.contact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 4rem); align-items: start; }
@media (max-width: 800px) { .contact-grid { grid-template-columns: 1fr; } }
.contact-detail { margin-bottom: 1.5rem; }
.contact-detail .k { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted); font-weight: 700; }
.contact-detail .v { font-size: 1.2rem; font-weight: 700; }
.contact-detail a { color: var(--accent); text-decoration: none; }
.contact-detail a:hover { text-decoration: underline; }

/* The form is folded away until someone wants it. A <details> rather than a
   button so it still opens with JavaScript off. */
.contact-form { border: 1.5px solid var(--ink); background: var(--surface); box-shadow: var(--shadow-sm); }
.contact-form > summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 1.05rem 1.25rem;
  font-family: var(--font-heading); font-weight: 700; font-size: 1.05rem;
}
.contact-form > summary::-webkit-details-marker { display: none; }
.contact-form > summary:hover { background: var(--paper-2); }
.contact-form > summary:focus-visible { outline: 3px solid var(--accent); outline-offset: -3px; }
.contact-form__icon {
  font-family: var(--font-heading); font-weight: 700; font-size: 1.4rem; line-height: 1;
  color: var(--accent); transition: transform .15s ease;
}
.contact-form[open] .contact-form__icon { transform: rotate(45deg); }
.contact-form .form { padding: 1.25rem; border-top: 1px solid var(--divider); }
@media (prefers-reduced-motion: reduce) { .contact-form__icon { transition: none; } }

/* --- Footer ------------------------------------------------------------ */
.site-footer {
  --stop: var(--paper);
  background: var(--ink); color: color-mix(in srgb, var(--paper) 85%, transparent); margin-top: 0;
  border-top: 1px solid color-mix(in srgb, var(--paper) 16%, transparent);
}
.site-footer a { color: color-mix(in srgb, var(--paper) 88%, transparent); text-decoration: none; }
.site-footer a:hover { color: var(--on-ink); }
.footer-grid { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 2rem; padding-block: clamp(2.5rem, 5vw, 4rem); }
@media (max-width: 700px) { .footer-grid { grid-template-columns: 1fr; gap: 1.5rem; } }
.footer-brand .brand { color: var(--paper); margin-bottom: 0.75rem; }
.footer-brand p { color: color-mix(in srgb, var(--paper) 75%, transparent); max-width: 34ch; }
/* Present to a screen reader, absent to the eye. Used where a section
   needs a heading for the document outline but already announces itself
   visually — the results list under a page's own h1, for one. */
/* The Open Justice Licence attribution on caselaw.html. It is a condition
   of using the judgments at all, so it is quiet but never hidden: same size
   as the copyright line beside it, and it wraps onto its own row on a
   phone rather than being squeezed. */
/* ---- the year control (fw-years.js) ----
   A register that holds six years but loads one has to say so, or a reader
   quite reasonably concludes the earlier years do not exist. This sits above
   the count, states what is on screen against what is available, and offers
   each year with its size so pressing it is an informed choice. */
.fw-years { margin: 0 0 0.9rem; }
.fw-years__line { margin: 0 0 0.5rem; font-size: 0.9rem; color: var(--ink-soft); }
.fw-years__row { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }
.fw-years__btn {
  font-family: var(--font-heading); font-weight: 600; font-size: 0.82rem;
  padding: 0.34rem 0.7rem; border: 1px solid var(--divider-2);
  background: var(--surface); color: var(--ink); cursor: pointer;
  border-radius: 2px; line-height: 1.3;
}
.fw-years__btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.fw-years__btn:disabled { opacity: 0.55; cursor: default; }
.fw-years__btn--all { border-style: dashed; }
/* The years already on the page. Shown in the same row as the buttons so the
   list reads 2026, 2025, 2024 rather than starting at last year, but flat and
   unpressable, because there is nothing to press: they are here. */
.fw-years__on {
  font-family: var(--font-heading); font-weight: 700; font-size: 0.82rem;
  padding: 0.34rem 0.7rem; border: 1px solid var(--ink-soft);
  background: transparent; color: var(--ink);
  border-radius: 2px; line-height: 1.3;
}
.fw-years__done, .fw-years__fail { font-size: 0.85rem; color: var(--ink-soft); }
.fw-years__fail { margin: 0.5rem 0 0; color: var(--ink); }
@media (max-width: 900px) {
  /* Same 44px minimum as every other control on a phone. */
  .fw-years__btn { min-height: 44px; padding-inline: 0.9rem; }
}

/* The "these are suggestions" line. It used to be the tail of a forty-word
   sentence repeated on every row; it is now said once, above the results, and
   set quieter than the count it follows so it informs without shouting. */
/* The held-back wider set. Four close matches and ninety plausible ones is
   not a shortlist, it is the register again with a heading on it — so the
   plausible ones sit behind one press that says how many there are. */
/* ---- the coverage box (fw-coverage.js) ----
   Facts, laid out as facts: the number first and big, the name of the thing
   second, the period it covers third and quiet. Reads down the page on a
   phone and across it on a desktop without needing a second layout. */
.cov { margin: 0 0 2.2rem; }
.cov__head { font-size: 1.02rem; color: var(--ink-soft); margin: 0 0 1.4rem; max-width: 60ch; }
.cov__head b { color: var(--ink); font-family: var(--font-heading); font-size: 1.6rem; }
.cov__set { margin: 0 0 1.6rem; }
.cov__setname { margin: 0 0 .15rem; font-size: 1.05rem; display: flex; gap: .6rem; align-items: baseline; }
.cov__setname a { color: var(--ink); text-decoration: none; }
.cov__setname a:hover { color: var(--accent); }
.cov__setn { font-family: var(--font-heading); font-weight: 700; font-size: .82rem; color: var(--muted); }
.cov__what { margin: 0 0 .7rem; font-size: .88rem; color: var(--muted); }
.cov__list { list-style: none; margin: 0; padding: 0;
  display: grid; gap: .4rem; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); }
/* flex-wrap so the period drops to a second line rather than being written
   over. min-width:0 alone let a single unbreakable word ("Announcements")
   overflow its own box and paint across the nowrap period beside it. */
.cov__row { display: flex; flex-wrap: wrap; gap: .2rem .55rem; align-items: baseline;
  text-decoration: none;
  border: 1px solid var(--divider-2); background: var(--surface); padding: .5rem .7rem; color: var(--ink); }
.cov__row:hover { border-color: var(--accent); }
.cov__n { font-family: var(--font-heading); font-weight: 700; font-size: .98rem; color: var(--accent); }
/* The period sits on its own line, always. Beside the label it had to be
   nowrap, and a single unbreakable name ("Announcements") then overflowed
   its own box and painted straight across it. Wrapping only the long ones
   fixed the collision and left the grid ragged, so every row wraps and the
   rows come out level. */
.cov__label { flex: 1 1 auto; min-width: 0; font-size: .86rem; overflow-wrap: anywhere; }
.cov__when { flex: 0 0 100%; text-align: right; font-size: 0.75rem; color: var(--muted); white-space: nowrap; }
.cov__foot { margin: 0; font-size: .82rem; color: var(--muted); }
@media (max-width: 900px) {
  .cov__row { min-height: 44px; align-items: center; }
}

.tierhold { margin: 1.4rem 0 0; display: flex; flex-wrap: wrap; gap: 0.3rem 0.7rem; align-items: baseline; }
.tierhold__note { font-size: 0.84rem; color: var(--muted); }

.matchnote__how { color: var(--muted); font-size: 0.88em; }

/* The one-line hover explanation (fw-hedge.js). Short enough now to read at a
   glance, so it is set as one line that truncates rather than a paragraph. */
.why-hover { font-size: 0.84rem; color: var(--ink-soft); margin: 0.35rem 0 0; }
.why-hover::before { content: ""; }

/* ---- column headings that stay put -------------------------------------
   Every table on this site is long enough to scroll past its own headings,
   and a column of ticks and dashes means nothing without the word at the top
   of it. A heading you cannot see is a column you cannot read.

   `top` clears the sticky masthead. --chrome-h is measured by site.js from
   whatever furniture actually stays on screen, so this is right whether or
   not a page has a rail, and stays right when the rail wraps to two lines on
   a narrow window. The fallback matters: --chrome-h is only set once site.js
   has run, and a heading that starts life underneath the header until the
   first scroll looks broken.

   A background is not optional. A sticky cell with a transparent background
   lets the rows slide visibly underneath it, which reads as a rendering
   fault rather than a fixed heading. */
thead th {
  position: sticky;
  top: var(--chrome-h, 64px);
  z-index: 3;
  background: var(--surface);
  /* box-shadow rather than border-bottom: a border on a sticky cell scrolls
     away with the cell's own box in Safari, leaving the heading floating. */
  box-shadow: inset 0 -1px 0 var(--divider-2);
}
/* A second heading row (the tables that group columns under a span) has to
   sit below the first rather than on top of it. */
thead tr:nth-child(2) th { top: calc(var(--chrome-h, 64px) + 2.4rem); z-index: 2; }

/* The register pages keep their own filter bar stuck under the masthead, so
   their headings have to clear that too. */
.reg thead th { top: calc(var(--chrome-h, 64px) + 3.6rem); }

/* A sticky heading sticks to its nearest SCROLLING ancestor, not to the page.
   .sc-scroll, .pir-scroll and the rest carry overflow:auto so a wide table can
   be dragged sideways on a small screen — and that quietly made them the
   scrollport, so the headings scrolled away with the page and the pinning did
   nothing at all. Measured at 1440, 1100 and 960: not one of these tables is
   wider than its wrapper, so above the phone breakpoint the sideways scroll is
   machinery for a problem that does not exist. Below it, the tables become
   cards and the pinning is off anyway. */
@media (min-width: 901px) {
  /* .sc-scroll and .pir-scroll are fixed in their own pages' <style> blocks,
     which load after this file and would win here. */
  .rpc-panel, .ga-tablewrap, .gi-tablewrap { overflow: visible; }
}

@media (max-width: 900px) {
  /* On a phone the masthead is not sticky and the tables become cards, so
     pinning a heading to a measured offset only pushed it off screen. */
  thead th, thead tr:nth-child(2) th, .reg thead th { position: static; top: auto; }
}
@media print {
  thead th, thead tr:nth-child(2) th, .reg thead th { position: static; top: auto; }
}

.footer-licence { flex-basis: 100%; opacity: 0.85; }
.footer-licence a { color: inherit; }

.vh {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.footer-col h3 { color: var(--paper); font-size: 0.82rem; text-transform: uppercase; letter-spacing: 0.14em; margin-bottom: 0.9rem; }
.footer-col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.55rem; }
.footer-bottom { border-top: 1px solid color-mix(in srgb, var(--paper) 16%, transparent); padding-block: 1.4rem;
  display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem; justify-content: space-between; font-size: 0.88rem;
  color: color-mix(in srgb, var(--paper) 72%, transparent); }

/* --- Back to top ------------------------------------------------------- */
.to-top {
  position: fixed; right: clamp(1rem, 3vw, 2rem); bottom: clamp(1rem, 3vw, 2rem); z-index: 70;
  width: 44px; height: 44px; display: grid; place-items: center;
  background: var(--ink); color: var(--paper); border: 1.5px solid var(--ink);
  text-decoration: none; box-shadow: var(--shadow-md);
  opacity: 0; visibility: hidden; transform: translateY(8px);
  transition: opacity .2s ease, transform .2s ease, visibility .2s;
}
.to-top.is-shown { opacity: 1; visibility: visible; transform: none; }
.to-top:hover { background: var(--lime); color: var(--ink); border-color: var(--ink); }

/* --- Chat assistant ---------------------------------------------------- */
/* lift the back-to-top button so it stacks above the chat launcher */
.to-top { bottom: calc(clamp(1rem, 3vw, 2rem) + 64px); }

/* ---- the register's own explanation, folded ---------------------------
   Two to six paragraphs used to sit between the search box and the register,
   and every reader scrolled past them every visit to reach the first result.
   On consultations that put the first row 1,922px down the page — more than
   two screens. The words are unchanged and one click away; what has gone is
   the obligation to read them again. */
/* ---- a stage column the filter has emptied ----------------------------
   fw-cols.js hides the heading and every cell of a column that has nothing
   in it for the rows on screen, and writes the line underneath saying which.
   Both tables are table-layout:fixed with the stage columns at 13% and the
   name column on auto, so the width a hidden column gives up goes straight
   to the names — which were wrapping to three lines with dashes beside them.
   See the note at the top of fw-cols.js for the measurement. */
.fw-colgone { display: none !important; }
.fw-colnote {
  margin: .6rem 0 0; font-size: .78rem; line-height: 1.5; color: var(--muted);
}
.fw-colnote:empty { display: none; }

/* .figs is the register's own summary figures, folded the same way — see the
   note beside the .figs markup. One look for "a folded section on a register
   page", so the two read as the same kind of thing. */
.reg-about, .figs { margin: 0 0 1.4rem; }
.figs { margin-top: 1.6rem; }
.reg-about > summary, .figs > summary {
  display: inline-block; cursor: pointer; list-style: none;
  font-family: var(--font-heading); font-weight: 700; font-size: .8rem;
  letter-spacing: .02em; color: var(--ink-soft);
  border-bottom: 1px solid var(--divider-2); padding-bottom: 2px;
}
.reg-about > summary::-webkit-details-marker,
.figs > summary::-webkit-details-marker { display: none; }
.reg-about > summary::after,
.figs > summary::after { content: " \2193"; }
.reg-about[open] > summary::after,
.figs[open] > summary::after { content: " \2191"; }
.reg-about > summary:hover,
.figs > summary:hover { color: var(--accent); border-color: var(--accent); }
.reg-about > summary:focus-visible,
.figs > summary:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.reg-about[open] > summary,
.figs[open] > summary { margin-bottom: .8rem; }
.reg-about > p:last-of-type { margin-bottom: 0; }
.figs > .tiles { margin-top: 0; }

/* ---- the whole tile is the link, not the word "explore" ---------------
   A register tile on either dashboard is about 207x146px and the only thing
   in it a reader could click was the word "explore" at 45x15 — two per cent
   of the box, with the count and the register's name inert beside it. The
   markup is not all in one place (GovAudit's tiles are written into the
   page, GovIntel's are half written and half built by its script), so the
   anchor is stretched over its tile in CSS instead: one rule, every tile,
   no markup to keep in step. Tiles with no link are untouched. */
.tile { position: relative; }
.tile .sub a::after { content: ""; position: absolute; inset: 0; }
.tile:has(.sub a):hover { background: var(--paper-2); }
.tile .sub a:focus-visible::after { outline: 2px solid var(--focus, currentColor); outline-offset: -3px; }

.chat { position: fixed; right: clamp(1rem, 3vw, 2rem); bottom: clamp(1rem, 3vw, 2rem); z-index: 80; }

.chat__launch {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.7rem 1.05rem; border: 1.5px solid var(--ink);
  background: var(--ink); color: var(--paper);
  font-family: var(--font-heading); font-weight: 700; font-size: 0.95rem;
  cursor: pointer; box-shadow: var(--shadow-md);
  transition: background .15s ease, color .15s ease, transform .15s ease;
}
.chat__launch:hover { background: var(--lime); color: var(--ink); transform: translateY(-2px); }
.chat__launch-label { line-height: 1; }
.chat.is-open .chat__launch { display: none; }
/* Set by site.js while a search box of the page's own is on screen. The
   launcher says "Find" and so does the box beside it; on the home page the
   two sat 200px apart doing the same job. It comes back the moment the
   reader scrolls past the box. */
.chat.is-redundant .chat__launch { opacity: 0; visibility: hidden; transform: translateY(6px); transition: opacity .18s ease, transform .18s ease, visibility 0s .18s; }
.chat__launch { transition: background .15s ease, color .15s ease, transform .15s ease, opacity .18s ease; }

.chat__panel {
  position: fixed; right: clamp(1rem, 3vw, 2rem); bottom: clamp(1rem, 3vw, 2rem);
  width: min(360px, calc(100vw - 2rem)); height: min(520px, calc(100vh - 3rem));
  display: flex; flex-direction: column;
  background: var(--surface); border: 1.5px solid var(--ink);
  box-shadow: var(--shadow-lg); overflow: hidden;
}
.chat__panel[hidden] { display: none; }

.chat__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 0.5rem;
  padding: 0.9rem 1rem; background: var(--ink); color: var(--paper);
}
.chat__title { margin: 0; font-family: var(--font-heading); font-weight: 800; font-size: 1rem; }
.chat__sub { margin: 0.15rem 0 0; font-size: 0.78rem; color: color-mix(in srgb, var(--paper) 72%, transparent); }
.chat__close { background: none; border: none; color: var(--paper); cursor: pointer; padding: 0.2rem; line-height: 0; opacity: 0.85; }
.chat__close:hover { opacity: 1; color: var(--lime); }

.chat__log { flex: 1; overflow-y: auto; padding: 1rem; display: flex; flex-direction: column; gap: 0.7rem; }
.chat__msg { max-width: 86%; padding: 0.6rem 0.8rem; font-size: 0.9rem; line-height: 1.45; }
.chat__msg a { color: var(--accent); font-weight: 600; }
.chat__msg--bot { align-self: flex-start; background: var(--paper-2); color: var(--ink); border: 1px solid var(--divider); }
.chat__msg--user { align-self: flex-end; background: var(--lime); color: var(--ink); border: 1px solid var(--ink); }
.chat__msg--typing { color: var(--ink-soft); letter-spacing: 0.15em; }
.chat__msg--blank { color: var(--ink-soft); font-style: italic; }

.chat__chips { display: flex; flex-wrap: wrap; gap: 0.4rem; padding: 0 1rem 0.6rem; }
.chat__chip {
  font-family: var(--font-heading); font-weight: 600; font-size: 0.78rem;
  padding: 0.35rem 0.6rem; background: var(--surface); color: var(--ink);
  border: 1px solid var(--divider-2); cursor: pointer; text-align: left;
  transition: background .12s ease, border-color .12s ease;
}
.chat__chip:hover { background: var(--paper-2); border-color: var(--ink); }

.chat__form { display: flex; gap: 0.4rem; padding: 0.7rem; border-top: 1px solid var(--divider); }
.chat__input {
  flex: 1; min-width: 0; padding: 0.55rem 0.7rem; font: inherit; font-size: 0.9rem;
  border: 1.5px solid var(--divider-2); background: var(--paper); color: var(--ink);
}
.chat__input:focus { outline: none; border-color: var(--ink); }
.chat__send {
  flex: none; width: 42px; display: grid; place-items: center;
  background: var(--ink); color: var(--paper); border: 1.5px solid var(--ink); cursor: pointer;
}
.chat__send:hover { background: var(--lime); color: var(--ink); }

@media (max-width: 480px) {
  .chat__panel { width: calc(100vw - 1.5rem); height: calc(100vh - 5rem); }
}

/* --- Utilities --------------------------------------------------------- */
.center { text-align: center; margin-inline: auto; }
.mt-0 { margin-top: 0; } .mb-0 { margin-bottom: 0; }
.pill { display: inline-block; font-size: 0.78rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  padding: 0.4em 0.8em; border-radius: 0; background: color-mix(in srgb, var(--lime) 30%, var(--paper)); color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent); }
/* Scoped to .js so the page still reads with scripting off, rather than
   leaving every revealed block stuck at opacity 0. */
.js .reveal { opacity: 0; transform: translateY(14px); transition: opacity .6s ease, transform .6s ease; }
.js .reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js .reveal { opacity: 1; transform: none; transition: none; }
  html { scroll-behavior: auto; }
}

/* --- Dot band ---------------------------------------------------------- *
   The deck's signature: a row of equal dots whose spacing expands to the
   right, the last one bleeding off the page edge, with a single lime dot.
   Full-bleed so it runs past the content margin. */
.dotband { position: relative; width: 100vw; left: 50%; right: 50%; margin-left: -50vw; margin-right: -50vw;
  overflow: hidden; line-height: 0; pointer-events: none; }
.dotband svg { display: block; width: 100%; height: clamp(26px, 3.2vw, 44px); }
.dotband--tight svg { height: clamp(20px, 2.4vw, 32px); }

:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; border-radius: 0; }

/* Footer brand inherits paper colour so the mark's mesh reads on dark ink */
.footer-brand .brand { color: var(--paper); }
.skip-link { position: absolute; left: 0.5rem; top: -3rem; background: var(--ink); color: var(--paper); padding: 0.6rem 1rem; border-radius: 0; z-index: 100; transition: top .15s ease; }
.skip-link:focus { top: 0.5rem; }

/* --- Openworks: remembered organisation descriptions ---------------------
       Injected by fw-profiles.js directly above the "what does your
       organisation do?" box, on the six pages that carry one. Deliberately
       quiet: it is an offer, not a control, and nothing happens until a name
       is clicked. --- */
/* --- "Carried over" ---------------------------------------------------
   A description given on one page is applied on the next, and this says so.
   Without it the page would silently show a filtered view of itself, which
   is the same as lying about how much is in the register. */
.fw-eg {
  margin: .45rem 0 .1rem; font-size: .82rem; line-height: 1.5; color: var(--muted);
  max-width: 68ch;
}
.fw-eg b { color: var(--ink-soft); font-weight: 700; }

.fw-carry {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.35rem 0.7rem;
  margin: 0 0 0.75rem; padding: 0.5rem 0.7rem;
  background: var(--surface); border: 1px solid var(--divider-2); border-radius: 3px;
  font-size: 0.86rem;
}
.fw-carry__k {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--muted); flex: none;
}
.fw-carry__text {
  color: var(--ink-soft); flex: 1 1 14rem; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fw-carry__clear {
  flex: none; font: inherit; font-size: 0.82rem; font-weight: 600;
  color: var(--accent); background: none; border: none; padding: 0;
  text-decoration: underline; cursor: pointer;
}
.fw-carry__clear:hover { color: var(--ink); }

.fw-saved {
  display:flex; flex-wrap:wrap; align-items:baseline;
  gap:.35rem .7rem; margin:0 0 .7rem;
}
/* class + attribute outranks .fw-saved above, so hiding actually hides */
.fw-saved[hidden] { display:none; }
.fw-saved__k {
  font-family:var(--font-heading); font-size:0.75rem; font-weight:700;
  letter-spacing:.09em; text-transform:uppercase; color:var(--accent);
}
.fw-saved__list {
  display:flex; flex-wrap:wrap; gap:.3rem .45rem;
  margin:0; padding:0; list-style:none;
}
.fw-saved__item { display:inline-flex; align-items:baseline; gap:.3rem; }
.fw-saved__use {
  font-family:var(--font-heading); font-size:.78rem; font-weight:700;
  color:var(--ink); background:var(--paper-2);
  border:1px solid var(--divider-2); border-radius:0;
  padding:.22rem .55rem; cursor:pointer;
  max-width:16rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.fw-saved__use:hover { background:var(--lime); border-color:#9a6314; }
.fw-saved__use:focus-visible { outline:2px solid var(--ink); outline-offset:1px; }
.fw-saved__act {
  font-family:var(--font-body); font-size:0.75rem; color:var(--muted);
  background:none; border:0; padding:.1rem; cursor:pointer;
  text-decoration:underline; text-underline-offset:2px;
}
.fw-saved__act:hover { color:var(--ink); }
.fw-saved__rename {
  font-family:var(--font-body); font-size:.78rem; color:var(--ink);
  background:var(--paper); border:1px solid var(--divider-2);
  padding:.2rem .4rem; border-radius:0; width:12rem;
}
.fw-saved__hint { font-size:0.75rem; color:var(--muted); flex:1 1 14rem; line-height:1.4; }

@media (max-width: 560px) {
  .fw-saved__use  { max-width:10rem; }
  .fw-saved__hint { flex-basis:100%; }
}

/* --- Openworks: the search-and-filters panel ----------------------------
       Wraps the register's search box and dropdowns, closed on arrival —
       only the "what does your organisation do?" box sits above it. Ten
       dropdowns open by default filled the first screen and pushed the
       records themselves below the fold. fw-filters.js opens the panel when
       a filter is already set and counts the active ones into the summary. */
.fw-filters { flex:1 1 100%; min-width:0; }
.fw-filters__sum {
  display:flex; align-items:center; gap:.5rem;
  font-family:var(--font-heading); font-size:0.75rem; font-weight:700;
  letter-spacing:.09em; text-transform:uppercase; color:var(--accent);
  cursor:pointer; padding:.35rem 0; list-style:none;
  -webkit-user-select:none; user-select:none;
}
/* the default triangle differs between engines, so hide it and draw one */
.fw-filters__sum::-webkit-details-marker { display:none; }
.fw-filters__sum::before {
  content:"\25B8"; display:inline-block; font-size:.8rem; line-height:1;
  color:var(--muted); transition:transform .12s ease;
}
.fw-filters[open] > .fw-filters__sum::before { transform:rotate(90deg); }
.fw-filters__sum:focus-visible { outline:2px solid var(--ink); outline-offset:2px; }
.fw-filters__count {
  font-family:var(--font-body); font-size:0.75rem; font-weight:400;
  letter-spacing:0; text-transform:none; color:#211d19;
  background:var(--lime); border:1px solid #9a6314; padding:.05rem .4rem;
}
.fw-filters__count[hidden] { display:none; }
.fw-filters__grid {
  display:flex; flex-wrap:wrap; gap:.6rem .9rem; align-items:flex-end;
  padding:.25rem 0 .2rem;
}
@media (max-width: 720px) {
  .fw-filters__grid > * { flex:1 1 100%; min-width:0; }
}

@media (prefers-reduced-motion: reduce) {
  .fw-filters__sum::before { transition:none; }
}


/* ==========================================================================
   Product sets — GovAudit and GovIntel, the two sets inside Openworks
   --------------------------------------------------------------------------
   GovAudit groups scorecards.html (the forecast), rpc-opinions.html (the
   verdict on it), reviews.html (the department's own review five years on)
   and nao.html (the independent audit), with govaudit.html as their landing
   page. GovIntel groups consultations.html (the question), news.html (the
   announcement), policy-papers.html (the commitment), guidance.html (the
   instruction) and foi.html (the account), with govintel.html as theirs.
   What lives here is only what more than one page needs: the grouped
   dropdown, the wide homepage card and the stage strip — four columns for
   GovAudit, five with .ga-strip--5 for GovIntel. Each dashboard's own layout
   stays inside its own page.
   ========================================================================== */

/* --- the grouped block inside the Openworks dropdown -------------------
   Each set is a heading row (a link to the set's dashboard) with the set's
   pages listed under it. The list starts collapsed so the dropdown opens
   short; the button on the right of the heading row opens it, and it stays
   open for the rest of the visit. */
.submenu__group { position: relative; }
.submenu__headrow {
  display: flex; align-items: stretch;
  border-left: 3px solid var(--lime); background: var(--paper-2);
}
.submenu__head {
  position: relative; flex: 1 1 auto; min-width: 0;
  display: flex !important; align-items: center; justify-content: space-between;
  font-weight: 800; letter-spacing: -0.01em;
}
.submenu__headarrow { color: var(--accent); font-size: 0.85em; }

/* the expand control. A plus that turns into a cross by rotating, which
   keeps one glyph doing both jobs and needs no icon file. */
.submenu__toggle {
  flex: 0 0 auto; width: 2.2rem; display: grid; place-items: center;
  padding: 0; background: transparent; border: 0; border-left: 1px solid var(--divider);
  font-family: var(--font-heading); font-size: 1.05rem; font-weight: 700; line-height: 1;
  color: var(--ink-soft); cursor: pointer;
}
.submenu__toggle:hover { background: var(--surface); color: var(--accent); }
.submenu__toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.submenu__toggleicon { display: block; transition: transform .15s ease; }
.submenu__toggle[aria-expanded="true"] .submenu__toggleicon { transform: rotate(45deg); }
@media (prefers-reduced-motion: reduce) { .submenu__toggleicon { transition: none; } }

.submenu__sub { list-style: none; margin: 0; padding: 0.15rem 0 0.2rem; }
.submenu__sub[hidden] { display: none; }
.submenu__sub a { padding-left: 1.5rem; font-weight: 500; font-size: 0.86rem; }
.submenu__rule { height: 1px; background: var(--divider); margin: 0.35rem 0.3rem; }

/* Method lists three stages of one process, not four separate places, so a
   thin lime rule sits between them rather than nothing at all. */
.submenu--split > li + li { border-top: 1px solid color-mix(in srgb, var(--lime) 65%, transparent); }

/* The hover description. Sits to the left of the menu on a pointer device;
   on narrow screens the menu is a stacked list, so it becomes a plain line
   of text under the heading instead of a floating panel that has nowhere
   sensible to go. */
.submenu__head::after {
  content: attr(data-tip);
  display: none;            /* not merely hidden: a visibility:hidden box still
                               counts towards the page's scrollable width, which
                               gave the whole site a horizontal scrollbar */
  position: absolute; right: 100%; margin-right: 12px; top: -0.4rem; width: 20rem;
  background: var(--ink); color: var(--paper);
  font-family: var(--font-body); font-weight: 400; font-size: 0.8rem;
  line-height: 1.45; letter-spacing: 0; text-transform: none;
  white-space: normal;      /* .mainnav a sets nowrap and pseudo-elements inherit it */
  padding: 0.7rem 0.85rem; box-shadow: var(--shadow-lg);
  pointer-events: none; z-index: 90;
}
.submenu__head:hover::after,
.submenu__head:focus-visible::after { display: block; }
/* A narrow window, but still a pointer. The menu is a stacked list here, so
   there is no room BESIDE the heading to float the description into. It drops
   below the heading instead, and OVER the links beneath it rather than pushing
   them down the page - running the pointer along the menu should never move
   the link you were aiming at. It stays a hover box either way: hidden until
   the pointer is on the heading, exactly as at full width. */
@media (max-width: 860px) {
  .submenu__head::after {
    right: auto; left: 0; top: 100%; margin: 0.25rem 0 0;
    width: min(20rem, calc(100vw - 3rem));
  }
}
/* No pointer at all - a phone - where nothing can hover and a description
   that waits for a hover is a description nobody ever reads. There it is part
   of the list: always shown, in the flow, covering nothing. */
@media (max-width: 860px) and (hover: none) {
  .submenu__head { flex-wrap: wrap; }
  .submenu__head::after {
    position: static; display: block; width: 100%; max-width: none;
    box-shadow: none; margin: 0.1rem 0 0.4rem;
  }
}

/* --- the wide set card on the homepage -------------------------------- */
.work-card--wide { grid-column: 1 / -1; }

/* --- Sets of boxes step through the ramp --------------------------------
   Where a section holds more than one box, each takes a different step of
   the brand ramp — a rule across the top and a wash fading out beneath it —
   so a set reads as several distinct things rather than one tile repeated.
   Teal at the start, the brand lime at the end.
   ------------------------------------------------------------------------ */
.work .work-card,
#contact .grid .card {
  border-top: 3px solid var(--tone, var(--divider));
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--tone, transparent) 9%, transparent), transparent 62%),
    var(--surface);
}
.work .work-card:nth-child(1)     { --tone: #2d5b57; }
.work .work-card:nth-child(2)     { --tone: #93b12b; }
#contact .grid .card:nth-child(1) { --tone: #2d5b57; }
#contact .grid .card:nth-child(2) { --tone: #3f7145; }
#contact .grid .card:nth-child(3) { --tone: #63903a; }
#contact .grid .card:nth-child(4) { --tone: #8aab2c; }
#contact .grid .card:nth-child(5) { --tone: #aac927; }
#contact .grid .card:nth-child(6) { --tone: #b9791f; }
a.work-card:hover { border-top-color: var(--tone, var(--ink)); }
.work-card__inner-list {
  font-size: 0.82rem !important; color: var(--muted) !important;
  border-top: 1px solid var(--divider); padding-top: 0.7rem;
}

/* --- the stage strip, carried by each page inside a set --------------- */
/* Hairlines drawn per cell rather than by painting the container — see the
   note on .reg .tiles. A stage strip whose count does not divide evenly used
   to leave a painted empty cell at the end of the row. */
.ga-strip {
  list-style: none; display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 1px; background: none; border: 0;
  box-shadow: var(--shadow-sm); margin: 1.4rem 0 1.7rem; padding: 0;
}
.ga-strip > li { background: var(--surface); box-shadow: 0 0 0 1px var(--divider); }
.ga-strip > li > a,
.ga-strip > li > span.ga-strip__cell {
  display: block; padding: 0.75rem 0.9rem; text-decoration: none; color: inherit; height: 100%;
}
.ga-strip > li > a:hover { background: var(--paper-2); }
.ga-strip > li.is-here { background: color-mix(in srgb, var(--lime) 12%, var(--surface)); box-shadow: 0 0 0 1px var(--divider), inset 3px 0 0 var(--lime); }
.ga-strip__n { display: block; font-family: var(--font-heading); font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em; color: var(--muted); }
.ga-strip > li.is-here .ga-strip__n { color: var(--accent); }
.ga-strip__l { display: block; font-family: var(--font-heading); font-weight: 700; font-size: 0.9rem; letter-spacing: -0.01em; margin-top: 0.2rem; }
.ga-strip__s { display: block; font-size: 0.76rem; color: var(--muted); margin-top: 0.15rem; }
.ga-strip > li > a:hover .ga-strip__s { color: var(--accent); }
@media (max-width: 700px) { .ga-strip { grid-template-columns: 1fr 1fr; } }

/* GovIntel's cycle has five stages rather than four. Same strip, one more
   column; these rules follow the generic ones so they win on source order. */
.ga-strip--5 { grid-template-columns: repeat(5, 1fr); }
@media (max-width: 980px) { .ga-strip--5 { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 700px) { .ga-strip--5 { grid-template-columns: 1fr 1fr; } }

/* --- footers that carry both sets get a fourth column ----------------- */
.footer-grid--sets { grid-template-columns: 1.6fr 1fr 1fr 1fr; }
@media (max-width: 860px) { .footer-grid--sets { grid-template-columns: 1fr 1fr; } }
@media (max-width: 700px) { .footer-grid--sets { grid-template-columns: 1fr; } }

/* The cross-cutting duties band. A sub-heading inside the section, one per
   reason, so a reader can see at a glance which hat each item is addressed
   to: as an employer, as a business, as a landlord. */
.dutyname {
  margin: 1.4rem 0 .5rem;
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}
.dutyname:first-of-type { margin-top: .8rem; }

/* ---- the tender section on govintel.html ---- */
.tenders { margin-top: 1.2rem; }
.tn-views { display:flex; flex-wrap:wrap; gap:.5rem; margin-bottom:.7rem; }
.tn-view, .tn-chip {
  font: inherit; font-size:.82rem; padding:.34rem .8rem; cursor:pointer;
  border:1px solid var(--rule); background:transparent; color:var(--ink);
  border-radius:999px;
}
.tn-view.is-on, .tn-chip.is-on { background:var(--ink); color:var(--paper); border-color:var(--ink); }
.tn-view:hover, .tn-chip:hover { border-color:var(--ink); }
.tn-chips { display:flex; flex-wrap:wrap; gap:.4rem; margin-bottom:.9rem; }
.tn-count { font-size:.82rem; color:var(--muted); margin:0 0 .9rem; }
.tn-empty { font-size:.9rem; color:var(--muted); max-width:60ch; }
.tn-list { display:grid; gap:.9rem; }
.tn { border-top:1px solid var(--rule); padding-top:.75rem; }
.tn h4 { margin:0 0 .25rem; font-size:.98rem; font-weight:600; line-height:1.35; }
.tn h4 a { color:inherit; text-decoration:none; }
.tn h4 a:hover { text-decoration:underline; }
.tn-meta { margin:0 0 .2rem; font-size:.78rem; color:var(--muted); }
.tn-tool { margin:0 0 .2rem; font-size:.75rem; letter-spacing:.03em; text-transform:uppercase; color:var(--muted); }
.tn-desc { margin:.2rem 0 0; font-size:.85rem; line-height:1.5; max-width:72ch; }
.tn-more {
  margin-top:1rem; font:inherit; font-size:.82rem; padding:.45rem 1rem; cursor:pointer;
  border:1px solid var(--rule); background:transparent; color:var(--ink); border-radius:2px;
}
.tn-more:hover { border-color:var(--ink); }
@media (max-width:640px) { .tn-list { gap:1.1rem; } }

/* The 'why this one' box, revealed on hover or keyboard focus. Hedged by
   design: a search result is a suggestion, and a confident sentence about a
   document nobody has read is how a tool loses trust. */
.tn { position:relative; }
.tn:focus { outline:2px solid var(--ink); outline-offset:3px; }
.tn-why {
  display:none; margin:.45rem 0 0; padding:.5rem .7rem;
  font-size:.78rem; line-height:1.5; max-width:56ch;
  color:var(--muted); background:var(--paper-2, rgba(0,0,0,.04));
  border-left:2px solid var(--rule);
}
.tn:hover .tn-why, .tn:focus-within .tn-why, .tn:focus .tn-why { display:block; }
@media (hover:none) { .tn-why { display:block; } }   /* no hover on touch - just show it */

/* The hover explanation on a search result. Same rules as .tn-why: a
   suggestion, shown on hover or keyboard focus, always visible on touch
   where there is no hover to speak of. */
.item:focus { outline:2px solid var(--ink); outline-offset:3px; }
.why-hover {
  display:none; margin:.5rem 0 0; padding:.5rem .7rem;
  font-size:.78rem; line-height:1.5; max-width:62ch;
  color:var(--muted); background:var(--paper-2, rgba(0,0,0,.04));
  border-left:2px solid var(--rule);
}
.item:hover .why-hover, .item:focus-within .why-hover, .item:focus .why-hover { display:block; }
@media (hover:none) { .why-hover { display:block; } }

/* Procurement's own description box. */
.tn-profile { margin:0 0 1.2rem; }
.tn-profile label { display:block; font-size:.82rem; font-weight:600; margin-bottom:.35rem; }
.tn-profile textarea {
  width:100%; font:inherit; font-size:.9rem; line-height:1.5; padding:.55rem .7rem;
  border:1px solid var(--rule); background:transparent; color:var(--ink); border-radius:2px; resize:vertical;
}
.tn-profile textarea:focus-visible { outline:2px solid var(--ink); outline-offset:1px; }
.tn-profile__row { display:flex; flex-wrap:wrap; gap:.6rem .9rem; align-items:center; margin-top:.55rem; }
.tn-profile button.go {
  font:inherit; font-size:.82rem; font-weight:600; padding:.42rem 1rem; cursor:pointer;
  border:1px solid var(--ink); background:var(--accent, #d9a04a); color:#111; border-radius:2px;
}
.tn-profile button.link {
  font:inherit; font-size:.8rem; padding:0; cursor:pointer; border:0; background:none;
  color:var(--muted); text-decoration:underline;
}
.tn-profile button.link:hover { color:var(--ink); }
.tn-profile__hint { font-size:.76rem; color:var(--muted); flex:1 1 20rem; line-height:1.45; }

/* The 'why this one' box on a briefing item. Same rules as .why-hover. */
.bf-why {
  display:none; margin:.5rem 0 0; padding:.5rem .7rem;
  font-size:.78rem; line-height:1.5; max-width:62ch;
  color:var(--muted); background:var(--paper-2, rgba(0,0,0,.04));
  border-left:2px solid var(--rule);
}
#bf-out li:hover .bf-why, #bf-out li:focus-within .bf-why, #bf-out li:focus .bf-why { display:block; }
#bf-out li:focus { outline:2px solid var(--ink); outline-offset:3px; }
@media (hover:none) { .bf-why { display:block; } }

/* ======================================================================
   REGISTER PAGES — NAO, Audit Scotland, Audit Wales

   These three pages share one layout, and until now they shared it by
   each carrying a byte-identical 255-line copy of it in their own <style>
   block. Two consequences, both of which bit: a fix to one was a fix to
   one, and the copies were scoped to .na — the class on the NAO page —
   so on the two audit pages, whose main element is .cf, not one of the
   107 rules applied and the pages rendered as browser defaults.

   The rules live here once now, scoped to .reg, and all three pages carry
   that class. A fourth register page needs the class and nothing else.
   ====================================================================== */
    /* ---- NAO reports: the same visual system as the RPC opinions, scorecards,
           reviews, consultations, news, policy paper, guidance and FOI pages.
           Everything is scoped to .reg so the generic class names the renderer
           emits cannot leak into the site. ---- */
    .na-lede { max-width: 62ch; }
    .na-meta {
      display:flex; flex-wrap:wrap; gap:.5rem 1.4rem; align-items:center;
      font-size:.82rem; color:var(--muted); margin-top:1.1rem;
      border-top:1px solid var(--divider); padding-top:.9rem;
    }

    /* ---- headline tiles ---- */
    /* The hairlines used to be the CONTAINER painted in the divider colour
       with a 1px gap showing through. That works while every cell is filled;
       the moment the last row is short the leftover cells keep that paint
       and read as blank tiles a reader tries to click. Each tile draws its
       own line instead — a 1px outer shadow, which meets its neighbour's
       across the gap and looks identical — so an empty cell shows the page
       behind it. Same change on .ga-strip and in the three dashboard style
       blocks. */
    .reg .tiles {
      display:grid; gap:1px; background:none;
      grid-template-columns:repeat(auto-fit,minmax(190px,1fr));
      border:0; margin:2rem 0 0; box-shadow:var(--shadow-sm);
    }
    .reg .tile { background:var(--surface); padding:1rem 1.1rem; box-shadow:0 0 0 1px var(--divider); }
    .reg .tile .v { display:block; font-family:var(--font-heading); font-size:2rem; font-weight:800; line-height:1; letter-spacing:-.02em; }
    .reg .tile .k { display:block; font-size:.85rem; color:var(--ink-soft); margin-top:.4rem; line-height:1.35; }
    .reg .tile .sub { display:block; font-size:.75rem; color:var(--muted); margin-top:.2rem; line-height:1.4; }
    .reg .tile.t-all .v   { color:var(--ink); }
    .reg .tile.t-new .v   { color:var(--ok-ink); }
    .reg .tile.t-old .v   { color:var(--info-ink); }
    .reg .tile.t-never .v { color:var(--warn-ink); }

    /* ---- the department chart ----
           Two-tone bars: the whole bar is everything the NAO published naming
           that body, the lime part is how much of it was a value-for-money
           study rather than an opinion on the accounts. ---- */
    .reg .vintage {
      display:flex; flex-direction:column; gap:1px; background:var(--divider);
      border:1px solid var(--divider); box-shadow:var(--shadow-sm); margin-top:1.4rem;
    }
    .reg .vrow {
      display:grid; grid-template-columns:19rem minmax(0,1fr) 3.6rem; gap:.8rem; align-items:center;
      background:var(--surface); border:0; border-radius:0; width:100%; text-align:left;
      padding:.42rem .9rem; cursor:pointer; font-family:var(--font-body); color:var(--ink-soft);
    }
    .reg .vrow:hover { background:var(--paper-2); }
    .reg .vrow .vy { font-family:var(--font-heading); font-weight:700; font-size:.78rem; color:var(--ink-soft);
                    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
    .reg .vrow .vbar { display:block; height:14px; background:var(--paper-2); box-shadow:inset 0 0 0 1px var(--divider); }
    .reg .vrow .vbar > span { display:block; height:100%; background:var(--bar-fill); }
    .reg .vrow .vbar > span > i { display:block; height:100%; background:var(--lime); }
    .reg .vrow .vn { font-family:var(--font-heading); font-weight:700; font-size:.82rem; text-align:right; }
    .reg .vrow:focus-visible { outline:2px solid var(--ink); outline-offset:-2px; }
    .reg .vnote { margin:.7rem 0 0; font-size:.8rem; color:var(--muted); }
    .reg .vkey { display:flex; flex-wrap:wrap; gap:.4rem 1.2rem; margin:.8rem 0 0; font-size:.78rem; color:var(--muted); }
    .reg .vkey span { display:inline-flex; gap:.45rem; align-items:center; }
    .reg .vkey i { width:14px; height:12px; display:inline-block; }

    /* ---- controls ---- */
    .reg .controls {
      display:flex; flex-wrap:wrap; gap:.6rem .9rem; align-items:flex-end;
      background:var(--surface); border:1px solid var(--divider-2);
      padding:.9rem 1rem; margin:0 0 1rem; box-shadow:var(--shadow-sm);
      position:sticky; top:calc(var(--header-h) + var(--rail-h)); z-index:4;
    }
    .reg .controls .field { display:flex; flex-direction:column; gap:.3rem; margin:0; padding:0; border:0; background:none; }
    .reg .controls label {
      font-family:var(--font-heading); font-size:0.75rem; font-weight:700;
      letter-spacing:.09em; text-transform:uppercase; color:var(--muted);
    }
    .reg .controls select, .reg .controls input[type=search] {
      font-family:var(--font-body); font-size:.85rem; color:var(--ink);
      background:var(--paper); border:1px solid var(--divider-2);
      padding:.45rem .6rem; border-radius:0; min-width:11rem; max-width:100%;
    }
    .reg .controls input[type=search] { min-width:16rem; }
    .reg button.reset {
      font-family:var(--font-heading); font-weight:700; font-size:.8rem; letter-spacing:.04em;
      background:var(--surface); border:1px solid var(--divider-2); color:var(--ink-soft);
      padding:.5rem .8rem; cursor:pointer; border-radius:0;
    }
    .reg button.reset:hover { background:var(--paper-2); color:var(--ink); }
    .reg .count { margin-left:auto; font-size:.78rem; color:var(--muted); padding-bottom:.6rem; }
    .reg select:focus-visible, .reg input:focus-visible, .reg button:focus-visible { outline:2px solid var(--ink); outline-offset:1px; }

    /* ---- results ---- */
    .reg .groupname {
      display:flex; justify-content:space-between; align-items:baseline; gap:.8rem;
      font-family:var(--font-heading); font-size:1rem; font-weight:700;
      margin:1.8rem 0 .6rem; padding-bottom:.4rem; border-bottom:1px solid var(--divider-2);
    }
    .reg .groupname .gc { font-size:.8rem; font-weight:500; color:var(--muted); }
    .reg .list { display:flex; flex-direction:column; gap:1px; background:var(--divider); border:1px solid var(--divider); box-shadow:var(--shadow-sm); }
    .reg .item { background:var(--surface); display:grid; grid-template-columns:200px minmax(0,1fr); }
    .reg .item .side {
      padding:.9rem .8rem .9rem .9rem; background:var(--paper-2);
      border-right:1px solid var(--divider); display:flex; flex-direction:column; gap:.5rem; align-items:flex-start;
    }
    .reg .item .body { padding:.9rem 1.1rem; min-width:0; }

    /* cells — the same flat, sharp-cornered treatment as the heatmaps */
    .reg .cell {
      display:inline-block; padding:.22rem .5rem; font-family:var(--font-heading);
      font-size:0.75rem; font-weight:800; letter-spacing:.04em; text-transform:uppercase;
      white-space:normal; overflow-wrap:anywhere;
    }
    .reg .c-aqua    { background:var(--ok-bg-2); color:var(--ok-ink); }
    .reg .c-warn    { background:var(--warn-bg); color:var(--warn-ink); }
    .reg .c-neutral { background:var(--flat-bg); color:var(--ink-soft); }
    .reg .c-violet  { background:var(--info-bg); color:var(--info-ink); }
    .reg .c-accent  { background:var(--lime); color:#211d19; }
    .reg .c-blank   { background:var(--flat-bg); color:var(--ink-soft); box-shadow:inset 0 0 0 1px var(--divider-2); }

    .reg .ran { font-size:0.75rem; color:var(--muted); line-height:1.4; }
    .reg .ran b {
      display:block; font-family:var(--font-heading); font-weight:700; font-size:0.75rem;
      letter-spacing:.08em; text-transform:uppercase; color:var(--ink-soft); margin-bottom:.1rem;
    }
    .reg .item h3 { margin:0; font-size:1rem; line-height:1.35; font-weight:700; }
    .reg .item h3 a { color:var(--ink); text-decoration:none; }
    .reg .item h3 a:hover { color:var(--accent); text-decoration:underline; }
    .reg .summary { margin:.35rem 0 0; font-size:.88rem; color:var(--ink-soft); max-width:84ch; }
    .reg .tags { display:flex; flex-wrap:wrap; gap:.35rem; margin-top:.6rem; align-items:center; }
    .reg .tag { font-size:0.75rem; padding:.1rem .45rem; border:1px solid var(--divider-2); color:var(--muted); background:var(--paper); }
    .reg .tag.dept { color:var(--ink-soft); font-weight:600; }
    .reg .tag.dept b { font-family:var(--font-heading); font-weight:700; color:var(--muted); }
    .reg .tag.theme { border-style:dashed; }
    .reg .tag.theme.sub { font-size:0.75rem; opacity:.85; }
    .reg .empty { padding:2.2rem; text-align:center; color:var(--muted); font-size:.9rem; background:var(--surface); border:1px solid var(--divider); }
    .reg .empty code { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:.85em; background:var(--paper-2); padding:.1rem .3rem; }
    .reg .more {
      display:block; width:100%; margin-top:.9rem; padding:.7rem;
      font-family:var(--font-heading); font-weight:700; font-size:.85rem;
      background:var(--surface); border:1px solid var(--divider-2); color:var(--accent); cursor:pointer; border-radius:0;
    }
    .reg .more:hover { background:var(--paper-2); }

    /* ---- legend ---- */
    .reg .legend {
      display:grid; gap:1px; background:var(--divider); border:1px solid var(--divider);
      grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); margin-top:1.1rem;
    }
    .reg .legend section { background:var(--surface); padding:.9rem 1rem; }
    .reg .legend h3 {
      font-family:var(--font-heading); font-size:0.75rem; letter-spacing:.09em;
      text-transform:uppercase; color:var(--muted); font-weight:700; margin:0 0 .55rem;
    }
    .reg .legend ul { margin:0; padding:0; list-style:none; display:flex; flex-direction:column; gap:.4rem; }
    .reg .legend li { display:flex; gap:.55rem; align-items:flex-start; font-size:.82rem; color:var(--ink-soft); line-height:1.4; }
    .reg .legend .sw { width:14px; height:14px; flex:none; margin-top:.2rem; }
    .reg .legend b { color:var(--ink); }

    /* ---- source callout and caveats ---- */
    .na-source {
      display:flex; gap:1.1rem; align-items:flex-start;
      background:var(--surface); border:1px solid var(--divider-2); border-left:5px solid var(--lime);
      padding:clamp(1.1rem,2.2vw,1.6rem); box-shadow:var(--shadow-sm); margin:0;
    }
    .na-source h3 { margin:0 0 .35rem; font-size:1.05rem; }
    .na-source p { margin:0 0 .7rem; color:var(--ink-soft); font-size:.95rem; }
    .na-source a.na-out {
      font-family:var(--font-heading); font-weight:700; font-size:.95rem;
      color:var(--accent); text-decoration:none; display:inline-flex; gap:.4em; align-items:center;
    }
    .na-source a.na-out:hover { text-decoration:underline; }
    .reg .caveats { margin-top:2rem; padding:1.2rem 1.4rem; border:1px solid var(--divider-2); background:var(--surface); box-shadow:var(--shadow-sm); }
    .reg .caveats h3 { margin:0 0 .7rem; font-size:1.05rem; }
    .reg .caveats ol { margin:0; padding-left:1.1rem; display:flex; flex-direction:column; gap:.55rem; }
    .reg .caveats li { font-size:.9rem; color:var(--ink-soft); line-height:1.5; max-width:88ch; }
    .reg .caveats strong { color:var(--ink); font-weight:700; }
    /* ---- the link out to methodology.html ----
       Every register used to carry its own "what is in this, and what is
       not" section. They now sit together on one page, because the gaps
       only make sense read against each other. What is left on the register
       is this one line, in the place the section used to be, so the rail
       link and the #source anchor still land somewhere. */
    .method-note { margin:0; }
    .method-note__link {
      font-family:var(--font-heading); font-weight:700; font-size:1rem;
      color:var(--accent); text-decoration:none;
      display:inline-flex; gap:.5em; align-items:baseline;
      border-bottom:1px solid transparent; padding-bottom:.15rem;
    }
    .method-note__link:hover { border-bottom-color:currentColor; }
    .method-note__link .arrow { transition:transform .18s ease; }
    .method-note__link:hover .arrow { transform:translateX(3px); }


    /* ---- "what does your organisation do?" box ----
           Same field, same lexicon and same two tiers as the consultation
           finder, the news feed, the policy paper register, the guidance
           register and the FOI register. ---- */
    .reg .reg-profile {
      flex:1 1 100%; min-width:0; margin:0 0 .25rem;
      border-bottom:1px solid var(--divider); padding-bottom:.9rem;
    }
    .reg .reg-profile label {
      display:block; font-family:var(--font-heading); font-size:0.75rem; font-weight:700;
      letter-spacing:.09em; text-transform:uppercase; color:var(--accent); margin-bottom:.4rem;
    }
    .reg .reg-profile textarea {
      display:block; width:100%; font-family:var(--font-body); font-size:.92rem; line-height:1.45;
      color:var(--ink); background:var(--paper); border:1px solid var(--divider-2);
      padding:.6rem .7rem; border-radius:0; resize:vertical; min-height:3.6rem;
    }
    .reg .reg-profile textarea:focus-visible { outline:2px solid var(--ink); outline-offset:1px; }
    .reg .reg-profile__row { display:flex; flex-wrap:wrap; gap:.6rem .9rem; align-items:center; margin-top:.55rem; }
    .reg .reg-profile button.go {
      font-family:var(--font-heading); font-weight:700; font-size:.82rem; letter-spacing:.03em;
      background:var(--lime); border:1px solid #9a6314; color:#211d19;
      padding:.5rem 1rem; cursor:pointer; border-radius:0;
    }
    .reg .reg-profile button.go:hover { background:#c7ec3a; }
    .reg .reg-profile__hint { font-size:.76rem; color:var(--muted); flex:1 1 18rem; line-height:1.4; }
    .reg .reg-profile__done { display:flex; flex-wrap:wrap; gap:.4rem .7rem; align-items:baseline; }
    /* the display rules above outrank the browser's own [hidden] styling, so the
       collapsed and expanded halves need this to actually take turns */
    .reg .reg-profile [hidden] { display:none; }
    .reg .reg-profile__k {
      font-family:var(--font-heading); font-size:0.75rem; font-weight:700; letter-spacing:.09em;
      text-transform:uppercase; color:var(--accent);
    }
    .reg .reg-profile__read {
      font-family:var(--font-heading); font-weight:700; font-size:.8rem;
      background:var(--ok-bg); color:var(--ok-ink); padding:.1rem .45rem; box-shadow:inset 0 0 0 1px var(--ok-line);
    }
    .reg .reg-profile__read[hidden] { display:none; }
    .reg .reg-profile__txt {
      font-size:.88rem; color:var(--ink-soft); font-style:italic;
      flex:1 1 20rem; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
    }
    .reg .reg-profile button.link {
      font-family:var(--font-heading); font-weight:700; font-size:0.75rem; letter-spacing:.04em;
      text-transform:uppercase; background:none; border:0; padding:0; cursor:pointer;
      color:var(--accent); text-decoration:underline; text-underline-offset:3px;
    }
    .reg .reg-profile button.link:hover { color:var(--ink); }

    .reg .cell.c-match { background:var(--ok-bg); color:var(--ok-ink); box-shadow:inset 0 0 0 1px var(--ok-line); }
    .reg .why {
      margin-top:.55rem; font-size:.78rem; color:var(--muted); line-height:1.45;
      border-top:1px dotted var(--divider-2); padding-top:.45rem;
    }
    .reg .why b { font-family:var(--font-heading); font-weight:700; color:var(--ink-soft); }
    .reg .why i { font-style:normal; color:var(--accent); }
    .reg .tiernote { margin:.3rem 0 .7rem; font-size:.82rem; color:var(--muted); line-height:1.45; max-width:78ch; }
    .reg .matchnote {
      margin:0 0 .9rem; padding:.6rem .8rem; font-size:.84rem; color:var(--ink-soft);
      background:var(--surface); border:1px solid var(--divider-2); border-left:4px solid var(--lime);
    }

    @media (max-width: 760px) {
      .reg .controls { position:static; }
      .reg .controls .field { flex:1 1 100%; min-width:0; }
      .reg .controls select, .reg .controls input[type=search] { width:100%; min-width:0; }
      .reg .count { margin-left:0; }
      .reg .item { grid-template-columns:1fr; }
      .reg .item .side {
        flex-direction:row; align-items:center; flex-wrap:wrap;
        border-right:0; border-bottom:1px solid var(--divider); padding:.6rem .9rem;
      }
      .reg .ran b { display:inline; margin-right:.35rem; }
      .reg .tag { max-width:100%; overflow-wrap:anywhere; }
      .na-source { flex-direction:column; gap:.7rem; }
      .reg .reg-profile__txt { flex:1 1 100%; white-space:normal; }
      .reg .vrow { grid-template-columns:minmax(0,1fr); gap:.3rem; padding:.6rem .8rem; }
      .reg .vrow .vy { white-space:normal; }
      .reg .vrow .vn { text-align:left; }
    }
      /* the one product link in the footer's brand column */
    .footer-brief { margin-top:.9rem; font-size:.86rem; line-height:1.5; }
    .footer-brief a { color:var(--on-ink); text-decoration:none; font-weight:600;
      border-bottom:1px solid transparent; }
    .footer-brief a:hover { border-bottom-color:var(--on-ink); }
    .footer-brief span { display:block; color:color-mix(in srgb, var(--paper) 72%, transparent);
      font-size:.8rem; margin-top:.15rem; }

/* ======================================================================
   PRINT

   Somebody who has narrowed a register to the nine documents that touch
   their organisation is exactly the person who wants that list in a
   meeting. Printed as it stood, they got the dark ground, the navigation,
   the section rail, the chat button, the back-to-top control — and only
   the ten rows that happened to be open.
   ====================================================================== */
@media print {
  /* The dark pages set their palette on :root[data-theme="dark"], which is
     more specific than a bare :root — so the reset has to match the same
     selector or the tokens never move and the page prints white-on-white. */
  :root, :root[data-theme="dark"], .site-header.is-over-hero {
    --paper: #ffffff; --paper-2: #ffffff; --surface: #ffffff;
    --ink: #000000; --ink-soft: #222222; --muted: #444444;
    --divider: #bbbbbb; --divider-2: #999999;
    --accent: #000000; --lime: #000000; --on-ink: #000000;
    --shadow-sm: none; --shadow-lg: none;
  }
  html, body { background: #fff !important; color: #000 !important; }

  /* Furniture that means nothing on paper. */
  .site-header, .rail, .navtoggle, .chat, .to-top, .site-footer,
  .fw-url, .fw-saved, .fw-eg, .boot,
  .more, .more--all, .ga-morewrap, .gi-morewrap, .fw-morewrap,
  .fw-pick-hint, .fw-pick-menu, [data-showall] { display: none !important; }

  /* The hero's canvas is decoration; the words above it are not. */
  canvas, .hero__field, .dotband { display: none !important; }

  /* Anything the page draws on a dark ground has to come back to paper. */
  main, section, .section, .section--alt, .tile, .list > *, table, thead th,
  .reg-profile, .ga-profile, .cf-profile, .bf-try {
    background: #fff !important; color: #000 !important;
    box-shadow: none !important;
  }
  a { color: #000 !important; text-decoration: underline; }

  /* Anything that paints its own background rather than taking a token —
     the date pills, the tags, the buttons — has to come back to paper too. */
  /* Rather than chase every component that paints its own dark background,
     paper is the default and the two things that should keep a fill — the
     filled marks in a Chain column — say so below. */
  main *, main *::before, main *::after {
    background-color: transparent !important;
    box-shadow: none !important;
  }
  main * { color: #000 !important; }
  .tag, .tags > *, .side, .cell, .summary, .matchnote, .why-hover, .weak,
  button, .go, .chip {
    border-color: #999 !important;
  }
  .tag, .side { border: 1px solid #999 !important; }

  /* The stage strip prints as an outline; the filled squares in a Chain
     column stay filled, because there they are the data. */
  .ga-strip li, .ga-strip li.is-here, .ga-strip__cell {
    background: #fff !important; color: #000 !important;
    border: 1px solid #999 !important;
  }
  .ga-dots i, .gi-dots i { border: 1px solid #000 !important; }
  .ga-dots i.on, .gi-dots i.on { background-color: #000 !important; }

  /* The description box is the page's control, not its content: print what
     was typed in it, not an empty field. */
  textarea, input { border: 1px solid #999 !important; }
  .go, button.go { display: none !important; }

  /* An address is worth having on paper; a fragment or a javascript: link
     is not. */
  main a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: .84em; word-break: break-all; color: #444;
  }

  /* Rows must not be cut in half by a page break, and a heading must not be
     the last thing on a page. */
  .list > *, tr, .tile { break-inside: avoid; }
  h1, h2, h3 { break-after: avoid; }

  /* Every row that has been loaded prints, not just the first screenful. */
  .reg #results .list > * { display: block !important; }

  /* Reveal animations never run on paper, so anything waiting on one has to
     be visible by default here. */
  .reveal, .reveal.is-in { opacity: 1 !important; transform: none !important; }

  @page { margin: 16mm; }
}

/* ---- the injected Category and Last touched controls -------------------
   The registers wrap every control in a .field with a label above it; the
   two dashboards lay their toolbar out as a flex row. fw-rows.js injects the
   same two dropdowns into both, so this is what one looks like in the
   dashboard row: label beside the select rather than above it, sized to sit
   with the search box and the sort buttons already there. */
.fw-inline { display:flex; align-items:center; gap:.45rem; }
.fw-inline label { font-size:0.75rem; letter-spacing:.06em; text-transform:uppercase; color:var(--ink-soft); }
.fw-inline select { font:inherit; font-size:.86rem; padding:.35rem .5rem; }


/* ==========================================================================
   The sector index — fw-bubbles.js
   The same results a page lists or tabulates, counted by sector: a numbered
   index, largest first, each cell carrying its count, its name and a circle
   whose area is proportional to that count. Hover unfolds the detail;
   pressing a cell puts the documents in the panel below.

   Built to Matt's design (Sector Index v2), and on the site's own tokens
   only — no colour is defined here that is not --paper / --ink / --surface
   / --divider / --mark-1..5. That is what lets the same markup read as warm
   paper and black type on a light page and invert by itself on the sixteen
   dark analysis pages.

   The hairlines are the gap:1px trick rather than borders: the grid's own
   background shows through a one-pixel gap between --surface cells, so
   crossings never double up into a two-pixel rule.
   ========================================================================== */

/* --- the toggle -------------------------------------------------------- */
.fwb__toggle { display: inline-flex; border: 1.5px solid var(--divider-2); margin-right: 0.75rem; }
.fwb__btn {
  font: inherit; font-size: 0.82rem; letter-spacing: 0.02em;
  padding: 0.32rem 0.85rem; border: 0; background: transparent;
  color: var(--ink-soft); cursor: pointer;
}
.fwb__btn + .fwb__btn { border-left: 1.5px solid var(--divider-2); }
.fwb__btn:hover { color: var(--ink); }
.fwb__btn.is-on { background: var(--accent); color: var(--paper); }
[data-theme="dark"] .fwb__btn.is-on { color: #1a1612; }
.fwb__btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* --- the head ---------------------------------------------------------- */
.fwb__empty { padding: 2rem 0; color: var(--muted); }
.fwb__head { margin: 0 0 1.75rem; }
.fwb__eyebrow { display: flex; align-items: center; gap: 0.6rem; margin: 0 0 0.9rem; }
.fwb__eyebrow::before { content: ""; width: 26px; height: 2px; background: var(--accent); flex: none; }
.fwb__title {
  margin: 0 0 0.75rem; font-family: var(--font-heading);
  font-size: clamp(2rem, 4.4vw, 3.4rem); line-height: 1.02;
  letter-spacing: -0.02em; font-weight: 700; color: var(--ink);
}
.fwb__lead { margin: 0; max-width: 58ch; color: var(--ink-soft); }

/* --- the four figures -------------------------------------------------- */
.fwb__stats {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px; background: var(--divider); border: 1px solid var(--divider);
  margin: 0 0 2.25rem;
}
.fwb__stat { background: var(--surface); padding: 1.15rem 1.25rem; min-width: 0; }
.fwb__stat-v {
  display: block; font-family: var(--font-heading); font-weight: 700;
  font-size: 2rem; line-height: 1; letter-spacing: -0.02em; color: var(--ink);
}
.fwb__stat-k {
  display: block; margin-top: 0.5rem; font-size: 0.75rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
}

/* --- the band key ------------------------------------------------------ */
.fwb__gridhead { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.5rem 1.25rem; margin: 0 0 1rem; }
.fwb__gridhead h4 {
  margin: 0; font-family: var(--font-heading); font-weight: 700;
  font-size: clamp(1.4rem, 2.4vw, 1.95rem); letter-spacing: -0.015em; color: var(--ink);
}
.fwb__bands { list-style: none; display: flex; flex-wrap: wrap; gap: 0.4rem 1rem; margin: 0; padding: 0; }
.fwb__bands li {
  display: flex; align-items: center; gap: 0.35rem;
  font-size: 0.75rem; font-weight: 600; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--muted);
}
.fwb__dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.fwb__dot.b1, .fwb__cell.b1 .fwb__mark { background: var(--mark-1); }
.fwb__dot.b2, .fwb__cell.b2 .fwb__mark { background: var(--mark-2); }
.fwb__dot.b3, .fwb__cell.b3 .fwb__mark { background: var(--mark-3); }
.fwb__dot.b4, .fwb__cell.b4 .fwb__mark { background: var(--mark-4); }
.fwb__dot.b5, .fwb__cell.b5 .fwb__mark { background: var(--mark-5); }

/* --- the index --------------------------------------------------------- */
.fwb__grid {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 1px; background: var(--divider); border: 1px solid var(--divider);
}
.fwb__cell {
  /* A flex column, not a block: a <button> centres its content box
     vertically, so a cell whose neighbour had unfolded sat half a row lower
     than the one beside it. */
  position: relative; display: flex; flex-direction: column; align-items: stretch;
  width: 100%; text-align: left;
  font: inherit; color: inherit; border: 0; cursor: pointer;
  background: var(--surface); padding: 1.05rem 1.1rem 1.15rem;
  transition: box-shadow 0.16s ease, z-index 0s;
}
.fwb__cell:hover, .fwb__cell:focus-visible, .fwb__cell.is-open { z-index: 2; box-shadow: var(--shadow-md); }
.fwb__cell:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }
.fwb__cell.is-open { background: var(--paper-2); }
.fwb__idx {
  display: block; font-size: 0.75rem; font-weight: 600;
  letter-spacing: 0.12em; color: var(--muted); margin-bottom: 0.45rem;
}
.fwb__num {
  display: block; font-family: var(--font-heading); font-weight: 700;
  font-size: 1.85rem; line-height: 1; letter-spacing: -0.02em; color: var(--ink);
}
.fwb__name {
  display: block; margin-top: 0.5rem; font-size: 0.82rem; line-height: 1.35;
  color: var(--ink); min-height: 2.7em;
}
/* The circle sits on its own baseline so the cells line up whatever the
   name's length, and the largest never pushes the row taller than the rest. */
.fwb__markwrap { display: flex; align-items: flex-end; height: 70px; margin-top: 0.65rem; }
/* The last row is almost never full. Empty cells finish it so the block has
   a straight edge rather than a ragged one. */
.fwb__fill { background: var(--surface); }
.fwb__mark { display: block; border-radius: 50%; flex: none; }

/* --- what unfolds on hover --------------------------------------------- */
.fwb__detail {
  display: block; overflow: hidden; max-height: 0; opacity: 0;
  transition: max-height 0.22s ease, opacity 0.16s ease;
}
.fwb__cell:hover .fwb__detail,
.fwb__cell:focus-visible .fwb__detail,
.fwb__cell.is-open .fwb__detail { max-height: 200px; opacity: 1; }
.fwb__detail-in { display: block; margin-top: 0.9rem; border-top: 1px solid var(--divider); padding-top: 0.6rem; }
.fwb__row { display: flex; justify-content: space-between; gap: 0.6rem; font-size: 0.75rem; line-height: 1.7; }
.fwb__k { color: var(--muted); }
.fwb__v { color: var(--ink); text-align: right; }
.fwb__v code { font-size: 0.95em; color: var(--ink-soft); }
.fwb__of { color: var(--muted); }
.fwb__open {
  display: block; margin-top: 0.45rem; font-size: 0.75rem; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent);
}

/* --- the tail ---------------------------------------------------------- */
.fwb__tail { margin: 1rem 0 0; font-size: 0.82rem; line-height: 1.6; color: var(--muted); max-width: 92ch; }
.fwb__tail b { color: var(--ink-soft); }

/* --- the documents, when a sector is pressed --------------------------- */
.fwb__panel { margin-top: 1.75rem; border: 1px solid var(--divider); background: var(--surface); padding: 1.15rem 1.25rem; }
.fwb__rest { margin: 0; font-size: 0.88rem; color: var(--ink-soft); }
.fwb__rest-hint { color: var(--muted); }
.fwb__phead { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; border-bottom: 1px solid var(--divider); padding-bottom: 0.55rem; }
.fwb__phead h4 { margin: 0; font-size: 1.05rem; }
.fwb__pcount { font-size: 0.78rem; color: var(--muted); white-space: nowrap; }
.fwb__chips { display: flex; flex-wrap: wrap; gap: 0.3rem; margin: 0.7rem 0 0; }
.fwb__chip { font-size: 0.75rem; padding: 0.14rem 0.45rem; border: 1px solid var(--divider-2); color: var(--muted); }
.fwb__chip b { color: var(--ink-soft); font-weight: 600; }
.fwb__hits {
  margin: 0.9rem 0 0; padding: 0; list-style: none;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.6rem 1.75rem;
  font-size: 0.86rem; line-height: 1.4;
}
.fwb__hits li { counter-increment: fwbhit; }
.fwb__hits a { color: var(--ink); }
.fwb__hits a:hover { color: var(--accent); }
.fwb__meta { display: block; color: var(--muted); font-size: 0.76rem; margin-top: 0.15rem; }
.fwb__more { margin: 0.8rem 0 0; font-size: 0.8rem; color: var(--muted); }
.fwb__how { margin: 0.7rem 0 0; padding-top: 0.6rem; border-top: 1px solid var(--divider); font-size: 0.76rem; color: var(--muted); line-height: 1.45; }

@media (max-width: 1000px) {
  .fwb__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .fwb__stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  .fwb__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fwb__hits { grid-template-columns: 1fr; }
  .fwb__markwrap { height: 52px; }
}
@media (prefers-reduced-motion: reduce) {
  .fwb__cell, .fwb__detail { transition: none; }
}

/* ==========================================================================
   Loading — what a data page shows before it has anything to show.

   Every register and dashboard ships its data as one or more large script
   files. Until those have parsed, the page is a full set of headings with
   nothing underneath them: a page that is working perfectly well and looks
   broken. Two states say "not yet" instead.

   1. .fw-load covers the page while the data files are still arriving. It
      is only ever displayed when the head script has put .is-loading on
      <html>, so a browser with JavaScript switched off never sees it, and
      site.js clears it on DOMContentLoaded — which is after every deferred
      page script has run, and therefore after the table has been written.
   2. .fw-busy sits over one table while an asynchronous pass re-orders it
      (the semantic search, which fetches a vector and a 4MB file). The
      table stays where it is and dims rather than emptying.

   Both are the mark: the same nineteen dots at five depths, filled from
   --mark-1..--mark-5, so one piece of markup re-colours itself on paper and
   on the dark analysis pages without a second copy.
   ========================================================================== */
.fw-load {
  position: fixed; inset: 0; z-index: 280; display: none;
  background: var(--paper); place-items: center;
}
html.is-loading .fw-load { display: grid; }
/* .is-done has to hold the overlay on screen by itself: site.js takes
   .is-loading off <html> in the same frame as it adds .is-done, and if
   display depended on the class alone the fade would never run. */
.fw-load.is-done { display: grid; opacity: 0; pointer-events: none; transition: opacity .18s ease; }
.fw-load__inner { display: grid; justify-items: center; gap: .75rem; text-align: center; padding: 1rem; }
/* Each dot shrinks to under a third of its size and grows a little past
   it again. A narrower swing than this and the movement is not visible
   at a glance, which is the whole job. */
@keyframes fwSettle {
  0%, 100% { opacity: .10; transform: scale(.30); }
  40%      { opacity: 1;   transform: scale(1.18); }
}
/* The opacity-only version, for a reader who has asked for less motion.
   Not "animation: none": a loading mark that has stopped dead is the exact
   thing all of this was built to avoid. Nothing grows and nothing travels;
   it just breathes. */
@keyframes fwFade {
  0%, 100% { opacity: .35; }
  50%      { opacity: 1; }
}
/* The container turns, slowly, as well as the dots pulsing.
   Belt and braces: if a browser will not hand the nineteen dot
   animations to the graphics card, they stop dead the moment the page
   starts parsing its data — which is exactly when somebody is looking
   at this. One transform on one box is the cheapest thing there is to
   composite, so something is still moving even in that case. Nine
   seconds a turn: a drift, not a spinner.
   The mark is a scattered field with no upright, so turning it does not
   read as a tipped-over logo. */
@keyframes fwTurn { to { transform: rotate(360deg); } }
.fw-load__mark {
  position: relative; width: 66px; height: 66px; display: block;
  will-change: transform; animation: fwTurn 9s linear infinite;
}
/* PLAIN HTML DOTS, NOT SVG CIRCLES.
   The mark was an <svg> with nineteen <circle>s, and it froze exactly when
   it was needed. Chrome cannot hand an animation on an SVG child to the
   compositor — it runs on the main thread, which on these pages is busy
   parsing several megabytes of register data. The result was a mark that
   stuttered for a second and then stopped dead while the page loaded.
   The same nineteen dots as absolutely positioned <i> elements animate on
   transform and opacity, which Chrome DOES composite, so they keep moving
   while the main thread is blocked solid. Geometry below is the mark's own
   32-unit box turned into percentages, so it is the same picture.
   Do not put this back into SVG. */
.fw-load__mark i {
  position: absolute; display: block; border-radius: 50%;
  transform-origin: 50% 50%; will-change: transform, opacity;
  animation: fwSettle 1.71s ease-in-out infinite;
}
.fw-load__mark .m1 { background: var(--mark-1); }
.fw-load__mark .m2 { background: var(--mark-2); }
.fw-load__mark .m3 { background: var(--mark-3); }
.fw-load__mark .m4 { background: var(--mark-4); }
.fw-load__mark .m5 { background: var(--mark-5); }
/* One beat per DOT, spread across the whole loop, and the slots scrambled by
   (n*7) mod 19 — coprime, so every slot is used once, just out of order.
   Staggered by depth instead, all nineteen sat within a quarter-second of
   each other and the mark blinked as one thing rather than moving; in plain
   order the big dots share a phase and the field throbs. Negative, so
   nothing opens on a dark frame. */
.fw-load__mark i:nth-child(1) { left:4.063%; top:50.313%; width:5.625%; height:5.625%; animation-delay:-0.00s; }
.fw-load__mark i:nth-child(2) { left:56.25%; top:7.5%; width:6.25%; height:6.25%; animation-delay:-0.63s; }
.fw-load__mark i:nth-child(3) { left:22.5%; top:89.375%; width:5.0%; height:5.0%; animation-delay:-1.26s; }
.fw-load__mark i:nth-child(4) { left:79.062%; top:7.187%; width:4.375%; height:4.375%; animation-delay:-0.18s; }
.fw-load__mark i:nth-child(5) { left:92.5%; top:45.0%; width:5.0%; height:5.0%; animation-delay:-0.81s; }
.fw-load__mark i:nth-child(6) { left:42.812%; top:35.312%; width:4.375%; height:4.375%; animation-delay:-1.44s; }
.fw-load__mark i:nth-child(7) { left:18.438%; top:42.188%; width:4.375%; height:4.375%; animation-delay:-0.36s; }
.fw-load__mark i:nth-child(8) { left:90.312%; top:88.438%; width:5.625%; height:5.625%; animation-delay:-0.99s; }
.fw-load__mark i:nth-child(9) { left:35.0%; top:21.25%; width:8.75%; height:8.75%; animation-delay:-1.62s; }
.fw-load__mark i:nth-child(10) { left:65.312%; top:82.812%; width:9.375%; height:9.375%; animation-delay:-0.54s; }
.fw-load__mark i:nth-child(11) { left:5.312%; top:74.688%; width:8.125%; height:8.125%; animation-delay:-1.17s; }
.fw-load__mark i:nth-child(12) { left:71.875%; top:49.375%; width:6.25%; height:6.25%; animation-delay:-0.09s; }
.fw-load__mark i:nth-child(13) { left:10.312%; top:24.062%; width:11.875%; height:11.875%; animation-delay:-0.72s; }
.fw-load__mark i:nth-child(14) { left:45.0%; top:48.125%; width:10.0%; height:10.0%; animation-delay:-1.35s; }
.fw-load__mark i:nth-child(15) { left:84.062%; top:20.938%; width:10.625%; height:10.625%; animation-delay:-0.27s; }
.fw-load__mark i:nth-child(16) { left:40.625%; top:75.625%; width:13.75%; height:13.75%; animation-delay:-0.90s; }
.fw-load__mark i:nth-child(17) { left:71.25%; top:59.375%; width:16.25%; height:16.25%; animation-delay:-1.53s; }
.fw-load__mark i:nth-child(18) { left:55.625%; top:29.375%; width:18.75%; height:18.75%; animation-delay:-0.45s; }
.fw-load__mark i:nth-child(19) { left:18.125%; top:50.625%; width:22.5%; height:22.5%; animation-delay:-1.08s; }

.fw-load__word {
  font-family: var(--font-heading); font-weight: 700; font-size: .8rem;
  letter-spacing: .18em; text-transform: uppercase; color: var(--ink-soft); margin: 0;
}
@media (prefers-reduced-motion: reduce) {
  .fw-load__mark { animation: none; }
  .fw-load__mark i { animation-name: fwFade; animation-duration: 2.4s; transform: none; }
  .fw-load.is-done { transition: none; }
}

/* --- one table, working ------------------------------------------------- */
.is-busy { position: relative; }
.is-busy > *:not(.fw-busy) { opacity: .35; transition: opacity .15s ease; }
/* Fixed under the masthead rather than pinned to the top of the table.
   Absolute inside the table was the obvious thing and it was wrong: by the
   time somebody has typed a description and is watching the rows, the top of
   the table has scrolled up behind the masthead, and the one thing telling
   them the page is still working was up there with it. */
.fw-busy {
  position: fixed; z-index: 120; left: 0; right: 0; top: var(--header-h, 61px);
  display: flex; align-items: center; justify-content: center; gap: .7rem;
  padding: .55rem .8rem;
  background: color-mix(in srgb, var(--paper) 94%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--divider); box-shadow: var(--shadow-sm);
  font-family: var(--font-heading); font-weight: 700; font-size: 0.75rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-soft);
}
/* --- the mini mark ------------------------------------------------------
   The same six dots as the favicon — the mark reduced to the shapes that
   still read at 20px — breathing on the same loop as the full one. Small
   enough to sit inside a line of text, so anywhere a sentence says the page
   is working can show it instead of saying so in six-point grey.
   Its floor is higher than the full mark's: at this size a dot at 45% of
   its width and 14% opacity has simply gone. */
/* Each dot on its own delay rather than one per depth. Depth-staggered,
   the two largest shared a beat and the whole mark went dark together —
   at 20px that reads as a glitch, not as waiting. Six delays across the
   loop means something is always lit. The floor is higher than the full
   mark's too: a 20px dot at 45% width and 14% opacity has simply gone. */
/* Same lesson as the big mark, at six dots: the delays were 0.11s apart on
   a 1.6s loop, so all six lived in the first third of the cycle and the
   whole thing pulsed as one. They now span the full loop, and the slots are
   assigned so the two biggest dots sit half a cycle apart — with size and
   phase lined up, the mark still reads as one object breathing rather than
   as something working. */
@keyframes fwSettleMini {
  0%, 100% { opacity: .30; transform: scale(.66); }
  38%      { opacity: 1;   transform: scale(1); }
}
.fw-mini {
  position: relative; width: 20px; height: 20px; flex: none;
  display: inline-block; vertical-align: -.34em;
  will-change: transform; animation: fwTurn 5s linear infinite;
}
/* Dots, not circles, for the same reason as the big mark above. */
.fw-mini i {
  position: absolute; display: block; border-radius: 50%;
  transform-origin: 50% 50%; will-change: transform, opacity;
  animation: fwSettleMini 1.6s ease-in-out infinite;
}
.fw-mini .m1 { background: var(--mark-1); }
.fw-mini .m2 { background: var(--mark-2); }
.fw-mini .m3 { background: var(--mark-3); }
/* Three depths, not five: at 20px the two faintest tokens are dark green on
   a dark ground and simply do not arrive. Slots assigned so the two biggest
   dots sit half a cycle apart. */
.fw-mini i:nth-child(1) { left:71.875%; top:49.375%; width:10.0%; height:10.0%; animation-delay:-1.33s; }
.fw-mini i:nth-child(2) { left:12.5%; top:25.625%; width:13.75%; height:13.75%; animation-delay:-0.53s; }
.fw-mini i:nth-child(3) { left:39.375%; top:72.5%; width:17.5%; height:17.5%; animation-delay:-1.07s; }
.fw-mini i:nth-child(4) { left:68.75%; top:58.125%; width:20.0%; height:20.0%; animation-delay:-0.27s; }
.fw-mini i:nth-child(5) { left:53.75%; top:24.375%; width:25.0%; height:25.0%; animation-delay:-0.80s; }
.fw-mini i:nth-child(6) { left:14.375%; top:46.25%; width:30.0%; height:30.0%; animation-delay:-0.00s; }
.fw-busy .fw-mini { width: 17px; height: 17px; }
@media (prefers-reduced-motion: reduce) {
  .fw-mini { animation: none; }
  .fw-mini i { animation-name: fwFade; animation-duration: 2.4s; transform: none; }
}

/* --- Seam loading mark, 23 September 2026 ------------------------------
   The markup is unchanged: the loader still writes nineteen <i> and the mini
   mark six. Here the first four become the four stitches of the Seam mark
   and the rest are hidden. The stitches grow in and out rather than the
   mark turning, because an upright mark cannot spin. Still transform and
   opacity only, so it keeps moving while the page parses its data. */
@keyframes fwStitch {
  0%, 100% { transform: scaleX(.25); opacity: .25; }
  45%      { transform: scaleX(1);   opacity: 1; }
}
.fw-load__mark, .fw-mini { animation: none; }
.fw-load__mark i, .fw-mini i { border-radius: 0; animation: fwStitch 1.6s ease-in-out infinite; }
.fw-load__mark i:nth-child(n+5), .fw-mini i:nth-child(n+5) { display: none; }
.fw-load__mark i:nth-child(1), .fw-mini i:nth-child(1) { left:40%; top:10%;   width:55%; height:15%; transform-origin:100% 50%; animation-delay:-0.0s; }
.fw-load__mark i:nth-child(2), .fw-mini i:nth-child(2) { left:5%;  top:32.5%; width:55%; height:15%; transform-origin:0 50%;   animation-delay:-0.4s; }
.fw-load__mark i:nth-child(3), .fw-mini i:nth-child(3) { left:40%; top:55%;   width:55%; height:15%; transform-origin:100% 50%; animation-delay:-0.8s; }
.fw-load__mark i:nth-child(4), .fw-mini i:nth-child(4) { left:5%;  top:77.5%; width:55%; height:15%; transform-origin:0 50%;   animation-delay:-1.2s; }
.fw-load__mark i:nth-child(odd),  .fw-mini i:nth-child(odd)  { background: var(--mark-1); }
.fw-load__mark i:nth-child(even), .fw-mini i:nth-child(even) { background: var(--mark-2); }
@media (prefers-reduced-motion: reduce) {
  .fw-load__mark i, .fw-mini i { animation-name: fwFade; animation-duration: 2.4s; transform: none; }
}


/* ==========================================================================
   The rail as a dropdown.

   The rail carried up to thirteen links: two or three places on this page,
   and then every other register. Wrapped over two lines it read as one
   undifferentiated strip, and the sections a reader actually wanted on the
   page they were on were lost in it. The places on this page stay in view;
   the other registers fold into one menu that opens on hover.
   ========================================================================== */
/* The rail now has something that hangs out of it, so it has to sit above
   the sections below. It was declared position:static, which makes its
   z-index inert — the first panel down the page painted straight over the
   open menu. Relative, not sticky: the reason it is not sticky is still
   good and is written out where the rail is defined. */
.rail { position: relative; }
.rail-menu { position: relative; }
.rail-menu__btn {
  display: inline-flex; align-items: center; gap: .5em;
  white-space: nowrap; text-transform: lowercase; cursor: pointer;
  font-family: var(--font-heading);
  font-size: .92rem; font-weight: 600; letter-spacing: -.01em; color: var(--ink-soft);
  padding: .42em .8em; border: 1px solid transparent; border-radius: 0;
  background: none;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.rail-menu__btn::before {
  content: ""; width: 6px; height: 6px; border-radius: 0;
  background: var(--divider-2); flex: none; transition: background .12s ease, transform .12s ease;
}
.rail-menu__btn:hover,
.rail-menu.is-open .rail-menu__btn { color: var(--ink); border-color: var(--divider-2); background: var(--surface); }
.rail-menu__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.rail-menu__caret { font-size: .8em; color: var(--muted); transition: transform .14s ease; }
.rail-menu:hover .rail-menu__caret,
.rail-menu:focus-within .rail-menu__caret,
.rail-menu.is-open .rail-menu__caret { transform: rotate(180deg); }
/* Qualified with .rail because `.rail ul` above sets this one to flex-wrap
   with its own padding, and would win on specificity otherwise — which it
   did, and put "case law" and "briefings" side by side. */
.rail .rail-menu__list {
  position: absolute; top: calc(100% + 1px); left: 0; z-index: 70;
  display: block; min-width: 15rem; margin: 0; padding: .3rem; list-style: none;
  background: var(--surface); border: 1px solid var(--divider); box-shadow: var(--shadow-md);
  opacity: 0; visibility: hidden; transform: translateY(-4px);
  transition: opacity .14s ease, transform .14s ease, visibility .14s;
}
.rail-menu:hover .rail-menu__list,
.rail-menu:focus-within .rail-menu__list,
.rail-menu.is-open .rail-menu__list { opacity: 1; visibility: visible; transform: none; }
/* Same lazy-open as the masthead menu, and for the same reason. */
.rail-menu:hover .rail-menu__list { transition-delay: .2s; }
.rail .rail-menu__list li { display: block; }
.rail .rail-menu__list a { display: flex; width: 100%; padding: .42em .7em; }
.rail .rail-menu__list a:hover { background: var(--paper-2); color: var(--ink); }
/* A hover menu is no use to a finger, and the rail is hidden below 860px
   anyway — but the panel it is cloned into is not, so site.js unfolds it
   there. Nothing here needs a mobile case. */

/* ==========================================================================
   The page sets inside the Openworks dropdown open on hover too.
   The + button stays, because a keyboard and a finger both need it, but a
   mouse should not have to click twice to see the eight GovAudit pages.
   ========================================================================== */
/* Done in site.js rather than here: [hidden] carries display:none !important
   near the top of this file, and a hover rule that fought it would have had
   to shout back. The script sets the attribute instead, which also keeps
   aria-expanded telling the truth. The + icon already turns on
   [aria-expanded="true"], so it follows on its own. */

/* === nested set inside a set (Parliament) ============================
   The three legislatures — Westminster, Holyrood, the Senedd — are one
   subject rather than three sets, so inside the GovIntel list they sit
   under a heading of their own instead of as three sibling links.

   No JavaScript was needed. site.js binds the + button to
   [data-submenu-toggle] and the hover-to-open to .submenu__group, both in
   general, so a group one level deeper is picked up as it stands. What was
   missing was only the indenting, which is here.

   The heading is a <span>, not an <a>: there is no single page called
   "Parliament", the three registers are the thing. That also means none of
   the `.submenu a` rules reach it, so its padding and face are set here
   rather than inherited.                                                */
.submenu__sub .submenu__group { margin: 0.25rem 0 0.15rem; }

.submenu__sub .submenu__headrow {
  margin-left: 1.15rem;               /* lines the heading up with the links above */
  border-left-width: 2px;
  border-left-color: color-mix(in srgb, var(--lime) 55%, transparent);
  background: transparent;            /* the outer heading owns the filled bar */
}

.submenu__sub .submenu__head {
  padding: 0.5rem 0.35rem;
  font-family: var(--font-heading);
  font-weight: 700; font-size: 0.86rem; letter-spacing: 0;
  color: var(--ink);
}
/* A heading that is not a link gets no pointer and no arrow: the arrow on
   GovAudit and GovIntel means "go to that dashboard", and there is no page
   called Parliament for it to point at. */
.submenu__head--plain { cursor: default; }

.submenu__sub .submenu__toggle { width: 1.9rem; font-size: 0.95rem; }

/* One notch deeper again than .submenu__sub a, so the three parliaments
   read as sitting under Parliament rather than beside it. */
.submenu__sub--deep a { padding-left: 2.4rem; font-size: 0.84rem; }

@media (max-width: 860px) {
  /* stacked list: everything shifts in rather than out, and the description
     panel is re-anchored under its heading further up this file */
  .submenu__sub .submenu__headrow { margin-left: 0.6rem; }
  .submenu__sub--deep a { padding-left: 1.9rem; }
}


/* ============================================================================
   fw-more.js and fw-pick.js — ten rows first, and filtering by what you see.
   Added 6 September 2026.
   ==========================================================================*/

/* The control fw-more.js puts under a table. Deliberately the same buttons
   the registers already use, so a reader meets one thing, not two — but the
   register rule is written as `.reg .more`, and a table page is not a .reg,
   so the look is restated here rather than widening that selector. */
.fw-morewrap { margin: 0.9rem 0 0; display: flex; gap: 0.5rem; flex-wrap: wrap; }
.fw-morewrap .more {
  padding: 0.6rem 1rem;
  font-family: var(--font-heading); font-weight: 700; font-size: 0.85rem;
  background: var(--surface); border: 1px solid var(--divider-2);
  color: var(--accent); cursor: pointer; border-radius: 0;
}
.fw-morewrap .more:hover { background: var(--paper-2); }
.fw-morewrap .more--all { color: var(--ink-soft); }

/* The two dashboards keep their own button; it just has company now. */
.ga-morewrap, .gi-morewrap { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.ga-morebtn--all, .gi-morebtn--all { opacity: 0.85; }

/* ---- filtering by a value on the page (fw-pick.js) ----------------------
   The tint is the same faint white the table shading already uses, so the
   crosshair reads as part of the table rather than a new colour scheme. The
   value itself gets the accent, because it is the thing being offered. */
.fw-pick {
  cursor: pointer;
  border-radius: 3px;
  box-shadow: inset 0 0 0 1px var(--accent);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
}
tbody .fw-pick-row > td, tbody .fw-pick-row > th { background: rgba(255, 255, 255, 0.045); }
tbody td.fw-pick-col, tbody th.fw-pick-col { background: rgba(255, 255, 255, 0.045); }

/* The heading of the highlighted column. Never a background: these cells are
   sticky, and a translucent one lets the rows scroll through underneath. */
thead .fw-pick-colhead { color: var(--accent); box-shadow: inset 0 -2px 0 var(--accent); }

/* The floating "show only X". Never in the layout, so a fixed table's column
   widths do not move when the pointer crosses a cell. */
.fw-pick-hint {
  position: absolute; z-index: 60; pointer-events: none;
  font-family: var(--font-heading, inherit); font-size: 0.75rem; font-weight: 700;
  letter-spacing: 0.01em; white-space: nowrap;
  padding: 0.28rem 0.5rem; border-radius: 4px;
  background: var(--ink); color: var(--paper);
  box-shadow: var(--shadow-md, 0 2px 8px rgba(0,0,0,.35));
}

/* A heading whose column can be filtered. The mark is small on purpose: the
   heading is still a heading, and the menu is the thing that does the work. */
/* No position here. thead th is sticky, and a class that says "relative"
   quietly wins the cascade and drops the heading out of the sticky row. */
.fw-pick-th { cursor: pointer; }
.fw-pick-th::after {
  content: "\25BE"; margin-left: 0.35em; font-size: 0.85em;
  color: var(--muted); vertical-align: baseline;
}
.fw-pick-th:hover::after, .fw-pick-th[aria-expanded="true"]::after,
.fw-pick-th:focus-visible::after { color: var(--accent); }

.fw-pick-menu {
  position: absolute; z-index: 70; min-width: 12rem; max-width: 20rem;
  max-height: 20rem; overflow-y: auto;
  background: var(--surface, var(--paper)); color: var(--ink);
  border: 1px solid var(--divider-2); border-radius: 6px;
  box-shadow: var(--shadow-lg, 0 8px 24px rgba(0,0,0,.4));
  padding: 0.4rem 0;
}
.fw-pick-menu__h {
  margin: 0 0 0.25rem; padding: 0.1rem 0.7rem;
  font-family: var(--font-heading, inherit); font-size: 0.75rem; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
}
.fw-pick-menu ul { list-style: none; margin: 0; padding: 0; }
.fw-pick-menu button {
  display: flex; width: 100%; gap: 0.75rem; align-items: baseline;
  justify-content: space-between; text-align: left;
  background: none; border: 0; cursor: pointer;
  padding: 0.32rem 0.7rem; font: inherit; font-size: 0.84rem; color: inherit;
}
.fw-pick-menu button:hover, .fw-pick-menu button:focus-visible { background: var(--paper-2); color: var(--accent); }
.fw-pick-menu button.is-on { color: var(--accent); font-weight: 700; }
.fw-pick-menu button i { font-style: normal; color: var(--muted); font-size: 0.76rem; }
.fw-pick-menu__f { margin: 0.3rem 0 0; padding: 0.3rem 0 0; border-top: 1px solid var(--divider); }
.fw-pick-menu__f button { color: var(--muted); }

/* Said to a screen reader, shown to nobody. */
.fw-pick-live {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; margin: -1px;
}

@media (prefers-reduced-motion: no-preference) {
  .fw-pick, .fw-pick-row > td, .fw-pick-row > th, td.fw-pick-col, th.fw-pick-col {
    transition: background-color 90ms linear, box-shadow 90ms linear;
  }
}

/* ==== PROCUREMENT: who won ===============================================
   Added 6 September 2026 when procurement moved out of the GovIntel
   dashboard onto tenders.html. Everything here is scoped to the .tn- prefix,
   which is the procurement register's own, so nothing leaks.
   ======================================================================== */

/* The band above the search box. "Who won" is a question people arrive
   with, so it is offered before they are asked to describe themselves. */
.tn-jump {
  margin: 0 0 1.3rem; padding: 0.85rem 1rem 0.95rem;
  border: 1px solid var(--rule); border-left: 3px solid var(--accent, #d9a04a);
  background: var(--paper-2, rgba(0,0,0,.02));
}
.tn-jump__k {
  margin: 0 0 0.3rem; font-family: var(--font-heading); font-size: 0.75rem;
  font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
}
.tn-jump__t { margin: 0 0 0.7rem; font-size: 0.88rem; line-height: 1.55; max-width: 66ch; }
.tn-jump__row { display: flex; flex-wrap: wrap; gap: 0.6rem 0.9rem; align-items: center; margin: 0; }
.tn-jump button.go {
  font: inherit; font-size: 0.82rem; font-weight: 600; padding: 0.42rem 1rem; cursor: pointer;
  border: 1px solid var(--ink); background: var(--accent, #d9a04a); color: #111; border-radius: 2px;
}
.tn-jump__hint { font-size: 0.76rem; color: var(--muted); flex: 1 1 18rem; line-height: 1.45; }

/* The winner, on the card, above the description. */
.tn-won { margin: 0.3rem 0 0.1rem; font-size: 0.86rem; line-height: 1.5; }
.tn-won__k {
  font-family: var(--font-heading); font-size: 0.75rem; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
  margin-right: 0.45rem; vertical-align: 0.06em;
}
.tn-won__gap { color: var(--muted); font-size: 0.8rem; font-style: italic; }

/* A supplier's name is a button: it shows everything else that firm won
   inside the window. Styled as text with a rule under it, because it reads
   as part of the sentence and a row of grey pills would not. */
.tn-sup {
  font: inherit; font-size: 0.86rem; font-weight: 600; padding: 0; cursor: pointer;
  border: 0; background: none; color: var(--ink);
  border-bottom: 1px solid var(--divider-2, var(--rule)); text-align: left;
}
.tn-sup:hover { color: var(--accent-ink, #9a6314); border-bottom-color: currentColor; }
.tn-sup + .tn-sup { margin-left: 0.1rem; }
.tn-sup + .tn-sup::before { content: "\00b7"; color: var(--muted); margin-right: 0.35rem; font-weight: 400; }

/* What replaces the filter chips while a supplier filter is on. The chips
   would be lying at that point: the filter ignores them. */
.tn-supbar {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.3rem 0.8rem;
  margin: 0 0 0.9rem; padding: 0.6rem 0.8rem 0.7rem;
  border: 1px solid var(--rule); border-left: 3px solid var(--accent, #d9a04a);
  background: var(--paper-2, rgba(0,0,0,.02));
}
.tn-supbar__k {
  margin: 0; font-family: var(--font-heading); font-size: 0.75rem; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
}
.tn-supbar__n { margin: 0; font-size: 0.95rem; font-weight: 700; flex: 1 1 20rem; line-height: 1.35; }
.tn-supbar button.link {
  font: inherit; font-size: 0.8rem; padding: 0; cursor: pointer; border: 0; background: none;
  color: var(--muted); text-decoration: underline;
}
.tn-supbar button.link:hover { color: var(--ink); }

/* The coverage gap, stated rather than left to be inferred from a thousand
   cards that say "not recorded". */
.tn-gap {
  margin: -0.4rem 0 0.9rem; font-size: 0.78rem; line-height: 1.5;
  color: var(--muted); max-width: 68ch;
}
.tn-gap b { color: var(--ink); }

/* ==== PROCUREMENT: the filter panel and the table ========================
   Added 6 September 2026, when the procurement register stopped being a list
   of cards. Everything is scoped to the .tn- prefix, which is this register's
   own, so none of it can reach the pages that draw their own tables.

   The <details class="fw-filters"> wrapper and its summary are styled higher
   up in this file and shared with every other register; what follows is only
   the fields inside it and the table underneath.
   ======================================================================== */

.tn-controls { margin: 0 0 1.2rem; }
.tn-controls .fw-filters {
  background: var(--surface); border: 1px solid var(--divider-2);
  padding: 0.7rem 1rem 0.8rem; box-shadow: var(--shadow-sm);
}
.tn-controls .fw-filters__grid {
  display: flex; flex-wrap: wrap; gap: 0.7rem 0.9rem;
  align-items: flex-end; margin-top: 0.8rem;
}
.tn-controls .field { display: flex; flex-direction: column; gap: 0.3rem; margin: 0; padding: 0; border: 0; background: none; }
.tn-controls label {
  font-family: var(--font-heading); font-size: 0.75rem; font-weight: 700;
  letter-spacing: 0.09em; text-transform: uppercase; color: var(--muted);
}
/* One width for every control, set rather than left to the content. A select
   sizes itself to its widest option, so Sector came out half a row wide and
   Buyer — 2,500 organisations, some of them a full line of text — wider
   still, and the panel read as a jumble. */
.tn-controls select,
.tn-controls input[type=search] {
  font-family: var(--font-body); font-size: 0.85rem; color: var(--ink);
  background: var(--paper); border: 1px solid var(--divider-2);
  padding: 0.45rem 0.6rem; border-radius: 0;
  width: 13rem; min-width: 0; max-width: 100%;
}
.tn-controls input[type=search] { width: 15rem; }
.tn-controls button.reset {
  font-family: var(--font-heading); font-weight: 700; font-size: 0.8rem; letter-spacing: 0.04em;
  background: var(--surface); border: 1px solid var(--divider-2); color: var(--ink-soft);
  padding: 0.5rem 0.8rem; cursor: pointer; border-radius: 0;
}
.tn-controls button.reset:hover { background: var(--paper-2, rgba(0,0,0,.04)); color: var(--ink); }
.tn-controls select:focus-visible,
.tn-controls input:focus-visible,
.tn-controls button:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }
.tn-controls__note {
  flex: 1 1 100%; margin: 0.3rem 0 0; font-size: 0.76rem; line-height: 1.5;
  color: var(--muted); max-width: 70ch;
}
.tn-controls__note b { color: var(--ink-soft); }

/* ---- the table ---- */
.tn-tablewrap { margin: 0.2rem 0 0; }
.tn-table {
  width: 100%; border-collapse: collapse; table-layout: fixed;
  font-size: 0.86rem;
}
.tn-table th {
  text-align: left; font-family: var(--font-heading); font-size: 0.75rem;
  font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted); padding: 0.5rem 0.6rem; vertical-align: bottom;
}
.tn-table td {
  padding: 0.6rem; vertical-align: top; line-height: 1.45;
  border-top: 1px solid var(--divider);
}
/* Translucent, so a hovered row and a hovered column double into a grid
   rather than one hiding the other. Same values as the dashboards. */
.tn-table tr.is-alt td { background: rgba(255,255,255,.022); }
.tn-table tr:hover td { background: rgba(255,255,255,.04); }

/* Percentages rather than rem. Six fixed columns in rem came to more than a
   1,200px container had to give, and the notice column — the only one with a
   sentence in it — was left with 280px and titles running to three lines. */
/* Widened from 37% on 7 September 2026, when the description stopped being
   clamped to two lines and the per-row "why this matched" line came off. The
   five points came from the columns holding a single short value; Date gained
   one because it now says "published" or "closes" before the date itself. */
.tn-table .tn-c-notice { width: 42%; }
.tn-table .tn-c-buyer  { width: 13%; }
.tn-table .tn-c-sector { width: 12%; }
.tn-table .tn-c-value  { width: 9%; }
.tn-table .tn-c-cat    { width: 6%; }
.tn-table .tn-c-stage  { width: 6%; }
.tn-table .tn-c-date   { width: 12%; }

/* This page's filter panel is not sticky, so its headings should not clear
   the 3.6rem the register pages reserve for one — that left a strip of
   scrolling rows visible between the masthead and the heading row. */
.tn-table thead th { top: var(--chrome-h, 64px); }
.tn-table .tn-c-value  { font-variant-numeric: tabular-nums; }

/* A sortable heading. It has to look like the other headings and behave like
   a button, so it is a button reset back to the heading's own type. */
.tn-table th .tn-sortbtn {
  font: inherit; letter-spacing: inherit; text-transform: inherit;
  color: inherit; background: none; border: 0; padding: 0; margin: 0;
  cursor: pointer; display: inline-flex; align-items: center; gap: 0.3rem;
  text-align: left;
}
.tn-table th .tn-sortbtn:hover { color: var(--ink); }
.tn-table th .tn-sortbtn.is-on { color: var(--ink); }
.tn-table th .tn-sortbtn:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
/* The idle state is a faint up-down arrow: an invitation, not a claim about
   the order. It fills in and turns one way once the column is doing the
   sorting. */
.tn-sortarrow { font-size: 0.85em; line-height: 1; opacity: 0.35; }
.tn-sortbtn:hover .tn-sortarrow { opacity: 0.7; }
.tn-sortbtn.is-on .tn-sortarrow { opacity: 1; color: var(--accent, #d9a04a); }

.tn-t { color: inherit; text-decoration: none; font-weight: 600; border-bottom: 1px solid var(--divider-2); }
.tn-t:hover { color: var(--accent-ink, #9a6314); border-bottom-color: currentColor; }

/* The description. Two lines: enough to tell what "RM6263 Call-Off" is,
   not enough to turn a row into a paragraph. */
/* NOT CLAMPED ANY MORE. It was two lines with an ellipsis, which on a tender
   is the difference between "RM6263 Call-Off" and knowing what is being
   bought. The whole of what the register holds is now on the row — that is at
   most 200 characters, because update-tenders.mjs stores no more than that;
   going beyond it is a data change, not a styling one.
   (.tn-w, the per-row "why this matched" line, was removed on 7 September 2026
   along with its rule — the claim is made once above the table instead.) */
.tn-d {
  display: block; margin-top: 0.3rem; font-size: 0.82rem;
  color: var(--ink-soft); line-height: 1.45;
}
.tn-pub { color: var(--muted); }
.tn-none { color: var(--muted); }
.tn-closes { color: var(--ok-ink, var(--ink)); }
.tn-table .tn-tool {
  display: block; margin: 0.25rem 0 0; font-size: 0.75rem; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted);
}
/* .tn-won was a <p> on the card and is a <span> in the cell. */
.tn-table .tn-won { display: block; margin-top: 0.3rem; }
.tn-won__rest { color: var(--muted); font-size: 0.8rem; }

@media (max-width: 900px) {
  /* The table becomes a stack of records. Each cell carries the column name
     in data-l, so a value keeps its heading when the headings go. */
  .tn-table, .tn-table tbody, .tn-table tr, .tn-table td { display: block; width: auto; }
  .tn-table thead { display: none; }
  .tn-table tr { border-top: 1px solid var(--divider); padding: 0.7rem 0; }
  .tn-table td { border: 0; padding: 0.1rem 0; }
  .tn-table td[data-l]::before {
    content: attr(data-l) " ";
    font-family: var(--font-heading); font-size: 0.75rem; font-weight: 700;
    letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
    margin-right: 0.4rem;
  }
  .tn-table tr.is-alt td, .tn-table tr:hover td { background: none; }
  .tn-controls .field { flex: 1 1 100%; min-width: 0; }
  .tn-controls select, .tn-controls input[type=search], .tn-controls #f-buyer {
    width: 100%; min-width: 0; max-width: none;
  }
}


/* --- Openworks: filters as buttons -------------------------------------
       Every register used to filter through a row of dropdowns. A dropdown
       hides its own contents: you cannot see what is in it, or how much is
       behind each value, without opening it, and you cannot tell at a
       glance which one is doing the narrowing. fw-controls.js leaves the
       <select> in place — every script on the site drives the filters by
       setting its value — and draws a row of buttons over it. The buttons
       are the filter's face; the select is still its state.

       Long lists show the twelve commonest, in the count order the page
       already puts them in, and unfold the rest behind "More" with a
       type-to-narrow box. .control is the old wrapper on the thirteen
       registers that never had CSS of its own; it is styled here so those
       pages look right even before the script runs. */
.reg .controls .control { display:flex; flex-direction:column; gap:.3rem; margin:0; padding:0; border:0; background:none; }

.fw-chips__native { display:none !important; }

.fw-chips { display:flex; flex-wrap:wrap; gap:.3rem; align-items:center; }
/* The attribute is in the selector because three pages carry their own
   `button[aria-pressed="true"]` rule for their heatmap switch, and it would
   otherwise repaint every chip on the page. */
.fw-chips .fw-chip[aria-pressed] {
  font-family:var(--font-heading); font-weight:700; font-size:.78rem; letter-spacing:.01em;
  line-height:1.2; padding:.34rem .7rem; cursor:pointer; border-radius:0;
  background:var(--surface); border:1px solid var(--divider-2); color:var(--ink-soft);
  max-width:30rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.fw-chips .fw-chip[aria-pressed]:hover { border-color:var(--ink); color:var(--ink); }
.fw-chips .fw-chip[aria-pressed="true"] { background:var(--accent); border-color:var(--accent); color:var(--paper); }
[data-theme="dark"] .fw-chips .fw-chip[aria-pressed="true"]:not(.fw-chip--all) { color:#1a1612; }
.fw-chips .fw-chip[aria-pressed]:focus-visible { outline:2px solid var(--ink); outline-offset:1px; }
.fw-chip[hidden] { display:none; }
/* "All" is where the filter rests, not a choice among the others. Painting
   it in the accent made every panel look as though eight filters were set. */
.fw-chips .fw-chip--all[aria-pressed] { font-weight:400; color:var(--muted); }
.fw-chips .fw-chip--all[aria-pressed="true"] {
  background:transparent; border-color:var(--ink-soft); color:var(--ink); font-weight:700;
}

.fw-chips__more {
  font-family:var(--font-body); font-size:.78rem; color:var(--accent);
  background:none; border:0; border-bottom:1px solid currentColor;
  padding:.2rem 0; margin-left:.2rem; cursor:pointer;
}
.fw-chips__more:hover { color:var(--ink); }

/* Opened out, a hundred and forty departments need a lid on them. */
.fw-chips--long { max-height:15rem; overflow:auto; align-content:flex-start; padding:.35rem; border:1px solid var(--divider-2); }
.fw-chips__find {
  flex:1 1 100%; font-family:var(--font-body); font-size:.82rem; color:var(--ink);
  background:var(--paper); border:1px solid var(--divider-2); padding:.35rem .5rem; border-radius:0;
  position:sticky; top:0; z-index:1;
}

/* A row of buttons is as wide as the panel, so it takes a line of its own
   instead of trying to share one with a search box. */
.fw-filters__grid .fw-field--chips { flex:1 1 100%; min-width:0; }
.fw-filters__grid { align-items:flex-start; }
.fw-filters__grid .field > label { padding-top:.15rem; }
/* Open, ten of these fill more than a screen. The bar is sticky, so let the
   panel scroll inside itself rather than covering the records. */
.fw-filters[open] > .fw-filters__grid { max-height:62vh; overflow-y:auto; }

/* The three heatmap pages carry their own controls bar, written before the
   registers had a shared one. Give it the same box. */
.rpc-controls, .sc-controls, .pir-controls {
  display:flex; flex-wrap:wrap; gap:.6rem .9rem; align-items:flex-start;
  background:var(--surface); border:1px solid var(--divider-2);
  padding:.9rem 1rem; margin:2rem 0 1rem;
}
.rpc-controls .fw-filters, .sc-controls .fw-filters, .pir-controls .fw-filters { flex:1 1 100%; min-width:0; }
.rpc-controls .field, .sc-controls .field, .pir-controls .field { display:flex; flex-direction:column; gap:.3rem; margin:0; }
.rpc-controls .field > label, .sc-controls .field > label, .pir-controls .field > label {
  font-family:var(--font-heading); font-size:0.75rem; font-weight:700;
  letter-spacing:.09em; text-transform:uppercase; color:var(--muted);
}

/* --- Openworks: a date range of the reader's own choosing ---------------
       "Last touched" answers what has moved lately. It cannot answer any
       question with a date in it, so the presets gained a sixth chip and
       fw-range.js unfolds two date boxes underneath when it is pressed.
       Hidden until then: five of the six answers never need them, and a
       filter panel that grows a row on every register for the sixth is a
       cost paid by everybody. */
.fw-range { flex:1 1 100%; min-width:0; }
.fw-range .fw-range__row { display:flex; flex-wrap:wrap; gap:.4rem; align-items:center; }
.fw-range .fw-range__in {
  font-family:var(--font-body); font-size:.82rem; color:var(--ink);
  background:var(--paper); border:1px solid var(--divider-2);
  padding:.35rem .5rem; border-radius:0;
  /* .field input is width:100% for the contact form, which stacked the two
     boxes and pushed "and" onto a line of its own. */
  width:auto; flex:0 0 auto; min-width:8.5rem; max-width:11rem;
  /* Tells the browser to draw its own calendar button for the theme in
     use. Without it the icon is black on a dark panel and invisible. */
  color-scheme:light;
}
[data-theme="dark"] .fw-range .fw-range__in { color-scheme:dark; }
.fw-range .fw-range__in:focus { outline:2px solid var(--ink); outline-offset:1px; }
.fw-range .fw-range__and { font-size:.8rem; color:var(--muted); }
.fw-range .fw-range__clear {
  font-family:var(--font-body); font-size:.78rem; color:var(--accent);
  background:none; border:0; border-bottom:1px solid currentColor;
  padding:.2rem 0; margin-left:.2rem; cursor:pointer;
}
.fw-range .fw-range__clear:hover { color:var(--ink); }
/* What the two boxes currently mean, in a sentence. A range where the first
   date is after the second returns nothing, and a panel that says so is
   better than a table that has silently emptied. */
.fw-range .fw-range__note { font-size:.78rem; color:var(--muted); margin:.35rem 0 0; }

/* A way out of the panel from the bottom of it. The summary already closes
   the box, but it is at the top of something that can be most of a screen
   tall and scrolls inside itself, so it is not where the reader's hand is by
   the time they have finished choosing. This sits outside the scrolling grid
   and so stays put however far down the list they are. */
.fw-filters__foot {
  display:flex; justify-content:flex-end;
  padding:.6rem 0 .1rem; margin-top:.5rem;
  border-top:1px solid var(--divider-2);
}
.fw-filters__close {
  font-family:var(--font-heading); font-weight:700; font-size:.78rem; letter-spacing:.04em;
  background:var(--surface); border:1px solid var(--divider-2); color:var(--ink-soft);
  padding:.4rem .9rem; border-radius:0; cursor:pointer;
}
.fw-filters__close:hover { border-color:var(--ink); color:var(--ink); }
.fw-filters__close:focus-visible { outline:2px solid var(--ink); outline-offset:1px; }


@media (max-width: 720px) {
  .fw-chip { max-width:100%; }
  .fw-chips--long { max-height:14rem; }
}

/* ==========================================================================
   THE DEPARTMENT AND PUBLIC BODY VIEW — fw-orgview.js
   "Find an official" only. The index of bodies re-uses the sector index's
   own classes above (.fwb__head, .fwb__stats, .fwb__grid, .fwb__cell), so
   nothing is restated here that is already written there; what follows is
   the reporting line, which is new.

   Tokens only, as everywhere else, so the same markup reads as warm paper on
   a light page and inverts by itself on the dark analysis pages.
   ========================================================================== */

.fwo__panel { padding: 1.25rem 1.35rem 1.4rem; }
.fwo__note { margin: 0.85rem 0 0; font-size: 0.84rem; line-height: 1.55; color: var(--ink-soft); }
.fwo__note b { color: var(--ink); font-weight: 700; }

/* The two things a reader wants next to the line itself: the same posts in
   the list they came from, and the file this was all read out of. */
.fwo__acts {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem 1.5rem;
  margin: 0.9rem 0 0; padding-top: 0.75rem; border-top: 1px solid var(--divider);
}
.fwo__out { font-size: 0.8rem; font-weight: 600; color: var(--accent); }
.fwo__out:hover { text-decoration: underline; }
/* .link carries no look of its own — every page that uses it styles it in
   place — so this one is stated here rather than inheriting a grey browser
   button in the middle of a dark panel. */
.fwo__acts .link {
  font: inherit; font-size: 0.8rem; font-weight: 600; line-height: inherit;
  background: transparent; border: 0; padding: 0; cursor: pointer;
  color: var(--accent);
}
.fwo__acts .link:hover { text-decoration: underline; }
.fwo__acts .link:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* --- the tree ----------------------------------------------------------
   A post is a row of three: the grade, the twist that opens what reports to
   it, and the post itself. The indent is a hairline so a long line reads as
   a line rather than as a stack of unrelated rows. */
.fwo__tree { list-style: none; margin: 1.1rem 0 0; padding: 0; }
.fwo__kids {
  list-style: none; margin: 0 0 0 0.85rem; padding: 0 0 0 1.1rem;
  border-left: 1px solid var(--divider);
}
.fwo__post {
  display: grid; grid-template-columns: 2.5rem 3.4rem minmax(0, 1fr);
  align-items: start; gap: 0.55rem;
  padding: 0.45rem 0.5rem 0.5rem 0; border-bottom: 1px solid var(--divider);
}
/* What the filters kept. A tint alone is easy to miss at the bottom of a
   deep line, so the row is marked at its edge as well. */
.fwo__post.is-hit { background: var(--paper-2); box-shadow: inset 3px 0 0 var(--accent); padding-left: 0.5rem; }

.fwo__twist {
  display: flex; align-items: center; justify-content: center; gap: 0.15rem;
  width: 2.4rem; padding: 0.1rem 0; margin-top: 0.05rem;
  font: inherit; font-size: 0.75rem; font-weight: 700; line-height: 1.4;
  background: transparent; color: var(--ink-soft);
  border: 1px solid var(--divider-2); cursor: pointer;
}
.fwo__twist:hover { color: var(--ink); border-color: var(--ink-soft); }
.fwo__twist:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.fwo__twist--none { border-color: transparent; cursor: default; }
.fwo__twistmark { font-size: 0.85rem; line-height: 1; }
.fwo__twistn { font-variant-numeric: tabular-nums; }

.fwo__grade {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--muted); padding-top: 0.28rem;
  white-space: nowrap;
}
.fwo__body { min-width: 0; }
.fwo__title { display: block; font-size: 0.9rem; line-height: 1.35; color: var(--ink); }
.fwo__who { display: block; font-size: 0.82rem; color: var(--ink-soft); margin-top: 0.1rem; }
/* "Post vacant" is what the file says when no name was published against the
   post. It is said quietly, because it is the commonest thing on the page. */
.fwo__who.is-vacant { color: var(--muted); font-style: italic; }
.fwo__meta { display: block; font-size: 0.75rem; line-height: 1.45; color: var(--muted); margin-top: 0.2rem; }

/* A post whose named manager is not in the file sits at the top level with
   everything else, marked with a broken edge rather than a solid one: the
   post is real, the line above it is what is missing. */
.fwo__post.is-loose { border-left: 2px dashed var(--divider-2); padding-left: 0.4rem; }
.fwo__post.is-loose.is-hit { padding-left: 0.4rem; }

@media (max-width: 620px) {
  .fwo__post { grid-template-columns: 2.5rem minmax(0, 1fr); }
  .fwo__grade { grid-column: 2; padding-top: 0; order: -1; }
  .fwo__kids { margin-left: 0.4rem; padding-left: 0.7rem; }
}
@media (prefers-reduced-motion: reduce) {
  .fwo__twist { transition: none; }
}

/* --- Openworks: the two dashboards' controls ----------------------------
       govaudit.html and govintel.html each carried their own copy of these
       rules in a <style> block — identical but for the prefix, and version-
       stamped separately, so the two dashboards drifted apart from each
       other and from every register. Their pressed state was var(--ink), a
       near-white fill, where every other button on the site turns lime.
       One definition here, deleted from both pages in the same commit. */
.ga-controls, .gi-controls {
  display:flex; flex-wrap:wrap; gap:.8rem 1.4rem; align-items:flex-start; margin:1.6rem 0 1rem;
}
.ga-search, .gi-search {
  font-family:var(--font-body); font-size:.9rem; padding:.5rem .7rem; min-width:16rem;
  border:1.5px solid var(--divider-2); background:var(--surface); color:var(--ink); border-radius:0;
}
.ga-search:focus-visible, .gi-search:focus-visible { outline:2px solid var(--accent); outline-offset:1px; }
.ga-sorts, .gi-sorts { display:flex; flex-wrap:wrap; gap:.3rem; align-items:center; }
.ga-views, .gi-views { display:flex; flex-wrap:wrap; gap:.3rem; align-items:center; margin:1.6rem 0 0; }
.ga-sortlabel, .gi-sortlabel {
  font-family:var(--font-heading); font-size:0.75rem; font-weight:700; letter-spacing:.12em;
  text-transform:uppercase; color:var(--muted); margin-right:.3rem;
}
/* Same button as .fw-chip on every register, so "sort by A-Z" here and
   "sort by A-Z" there are visibly the same control. */
.ga-sort, .gi-sort, .ga-view, .gi-view {
  font-family:var(--font-heading); font-weight:700; font-size:.78rem; letter-spacing:.01em;
  line-height:1.2; padding:.34rem .7rem; cursor:pointer; border-radius:0;
  background:var(--surface); border:1px solid var(--divider-2); color:var(--ink-soft);
}
.ga-sort:hover, .gi-sort:hover, .ga-view:hover, .gi-view:hover { border-color:var(--ink); color:var(--ink); }
.ga-sort.is-on, .gi-sort.is-on, .ga-view.is-on, .gi-view.is-on {
  background:var(--accent); border-color:var(--accent); color:var(--paper);
}
[data-theme="dark"] .ga-sort.is-on, [data-theme="dark"] .gi-sort.is-on,
[data-theme="dark"] .ga-view.is-on, [data-theme="dark"] .gi-view.is-on { color:#1a1612; }
.ga-sort:focus-visible, .gi-sort:focus-visible,
.ga-view:focus-visible, .gi-view:focus-visible { outline:2px solid var(--ink); outline-offset:1px; }

/* --- a clear button in every search box (fw-clear.js) ------------------
   The wrapper is inline-grid rather than a block so it does not change the
   width the box had inside its .field, and the button sits over the box's
   right-hand end with the input's padding widened to keep text out from
   under it. */
.fw-clearwrap { position:relative; display:grid; }
.fw-clearwrap > input { padding-right:2.2rem; }
.fw-clear {
  position:absolute; right:.35rem; top:50%; transform:translateY(-50%);
  width:1.7rem; height:1.7rem; padding:0; display:grid; place-items:center;
  background:transparent; border:1px solid transparent; border-radius:0;
  color:var(--ink-soft); cursor:pointer; line-height:0;
}
.fw-clear svg { width:.85rem; height:.85rem; stroke:currentColor; stroke-width:1.8; fill:none; stroke-linecap:round; }
.fw-clear:hover { color:var(--ink); border-color:var(--divider-2); }
.fw-clear:focus-visible { outline:2px solid var(--ink); outline-offset:1px; }
/* Chrome and Safari draw their own cross in a type="search" box; two
   crosses in one box is worse than either. */
.fw-clearwrap > input::-webkit-search-cancel-button { -webkit-appearance:none; appearance:none; }

@media (max-width: 720px) {
  .fw-clear { width:2.1rem; height:2.1rem; }
  .fw-clearwrap > input { padding-right:2.6rem; }
}

/* ==========================================================================
   FIND — homepage front door.  Append to styles.css and bump the ?v= on
   every page that links it.  Every value here is an existing token: no new
   colours, no radius, no new font.
   ========================================================================== */

.find { position: relative; z-index: 1; padding: clamp(2.5rem, 6vw, 5.25rem) 0 0; }

.find__kicker {
  display: flex; align-items: center; gap: 0.75rem; margin: 0;
  font-family: var(--font-heading); font-weight: 800; font-size: 0.75rem;
  letter-spacing: 0.22em; text-transform: uppercase; color: var(--lime);
}
.find__dot { width: 9px; height: 9px; background: var(--lime); display: block; }

.find__title {
  margin: 1.125rem 0 0.875rem;
  font-family: var(--font-heading); font-weight: 800;
  font-size: clamp(2.125rem, 5vw, 3.375rem); line-height: 1.04;
  letter-spacing: -0.02em; color: var(--paper); max-width: 20ch;
}

/* What the site is, for somebody who has never heard of it. Narrower than the
   field it sits above (58ch), because a line of explanatory prose stretched to
   1120px is a line nobody finishes. */
.find__sub {
  margin: 0 0 clamp(1.5rem, 3vw, 2.125rem);
  max-width: 58ch; font-size: clamp(0.95rem, 1.25vw, 1.0625rem); line-height: 1.55;
  color: #e8ecd8; text-wrap: pretty;
}
@media (max-width: 640px) { .find__sub { font-size: 0.95rem; } }

/* The field: one 2px paper rule around field + button, no gap between them. */
.find__form {
  display: flex; flex-wrap: wrap; align-items: stretch;
  border: 2px solid var(--paper);
}
.find__fieldwrap {
  flex: 1 1 20rem; min-width: 0; display: flex; align-items: center; gap: 0.875rem;
  background: var(--paper); padding: 0 clamp(0.875rem, 1.5vw, 1.375rem);
  min-height: 72px;
}
.find__icon { flex: none; color: var(--ink-soft); }
.find__input {
  flex: 1; min-width: 0; border: 0; background: transparent; color: var(--ink);
  font: inherit; font-weight: 500; font-size: clamp(1rem, 1.5vw, 1.25rem);
  padding: 0.875rem 0;
}
.find__input::placeholder { color: var(--muted); }
.find__input:focus { outline: none; }
.find__form:focus-within { box-shadow: 0 0 0 2px var(--lime); }

.find__go {
  flex: none; display: flex; align-items: center; gap: 0.875rem; min-height: 72px;
  padding: 0 clamp(1.125rem, 2.5vw, 1.875rem); cursor: pointer; border: 0;
  background: var(--lime); color: var(--ink);
  font-family: var(--font-heading); font-weight: 800;
  font-size: clamp(1rem, 1.5vw, 1.125rem); line-height: 1;
}
.find__go:hover  { background: #d9a04a; }
.find__go:active { background: #9a6314; }

/* Examples — one per audience. */
.find__egs {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem;
  padding: 1rem 0 clamp(1.75rem, 4vw, 2.75rem);
}
.find__egs-label {
  font-family: var(--font-heading); font-weight: 500; font-size: 0.8rem;
  color: color-mix(in srgb, var(--lime) 45%, var(--paper));
  margin-right: 0.25rem;
}
.find__eg {
  display: block; text-align: left; cursor: pointer; padding: 0.625rem 0.8rem;
  background: transparent; color: #e8ecd8;
  border: 1.5px solid color-mix(in srgb, var(--lime) 30%, transparent);
  font-family: var(--font-heading);
}
.find__eg:hover { border-color: var(--lime); }
.find__eg[aria-pressed="true"] { background: var(--lime); border-color: var(--lime); color: var(--ink); }
.find__eg-tag {
  display: block; font-weight: 800; font-size: 0.75rem; letter-spacing: 0.16em;
  text-transform: uppercase; opacity: 0.7; margin-bottom: 0.3rem;
}
.find__eg-q { display: block; font-weight: 600; font-size: 0.8rem; line-height: 1; }

/* The answer, on paper, keyed to the band above by the lime edge. It runs to
   the foot of the hero: margin-bottom -2px so its edge meets the next
   section's rule rather than doubling it. */
.find__out {
  /* The answer sits on paper inside a dark band, so it must state its own
     colour: without this the prose and the figure inherit the hero's light
     text and come out nearly invisible on their own ground. */
  background: var(--paper); color: var(--ink); border-left: 5px solid var(--lime);
  padding: clamp(1.375rem, 2.5vw, 2rem) clamp(1.25rem, 2.5vw, 2.125rem);
  margin-bottom: -2px;
  animation: find-rise 0.28s ease-out both;
}
.find__out[hidden] { display: none; }
@keyframes find-rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* Working state */
.find__bar { height: 3px; background: var(--divider); overflow: hidden; }
.find__bar span { display: block; width: 34%; height: 100%; background: var(--lime); animation: find-sweep 1s linear infinite; }
@keyframes find-sweep { from { transform: translateX(-100%); } to { transform: translateX(300%); } }
.find__working {
  margin: 1rem 0 0; display: flex; align-items: center; gap: 0.5rem;
  font-family: var(--font-heading); font-weight: 600; font-size: 0.9rem; color: var(--ink-soft);
}

/* What it is, under every answer. Set apart from the prose by a rule rather
   than by shrinking it into invisibility: it is a caveat, not small print. */
.find__caveat {
  margin: 0.9rem 0 0; padding-left: 0.7rem; border-left: 2px solid var(--divider-2);
  font-size: 0.78rem; line-height: 1.5; color: var(--muted); max-width: 62ch;
}
.chat__msg--answer .find__caveat { margin-top: 0.55rem; font-size: 0.75rem; }

/* Answer body: figure block left, prose + sources right. */
.find__answer { display: flex; flex-wrap: wrap; gap: clamp(1.5rem, 3vw, 2.5rem); align-items: flex-start; }
.find__figure { flex: none; min-width: 14.5rem; border-top: 2px solid var(--ink); padding-top: 0.75rem; }
.find__figure b {
  display: block; font-family: var(--font-heading); font-weight: 800;
  font-size: clamp(1.875rem, 3.4vw, 2.625rem); line-height: 1; letter-spacing: -0.02em;
}
.find__figure span { display: block; margin-top: 0.625rem; font-size: 0.8rem; font-weight: 500; color: var(--muted); }
.find__body { flex: 1 1 20rem; min-width: 0; }
.find__prose { margin: 0; font-size: clamp(1rem, 1.4vw, 1.125rem); line-height: 1.55; max-width: 56ch; text-wrap: pretty; }
.find__prose .chat__cite { font-size: 0.7em; vertical-align: super; font-weight: 800; color: var(--accent); text-decoration: none; }

.find__sources { margin-top: 1.25rem; border-top: 1px solid var(--divider); }
.find__source {
  display: flex; gap: 0.75rem; align-items: baseline; padding: 0.7rem 0;
  border-bottom: 1px solid var(--divider); font-size: 0.875rem; line-height: 1.45;
  color: var(--ink); text-decoration: none;
}
.find__source:hover { color: var(--accent); }
.find__source b { flex: none; font-weight: 800; color: var(--accent); }
.find__source i { margin-left: auto; flex: none; font-style: normal; color: var(--muted); }

.find__foot {
  display: flex; justify-content: space-between; align-items: center; gap: 1.25rem;
  flex-wrap: wrap; margin-top: 1.375rem; padding-top: 0.875rem;
  border-top: 2px solid var(--ink);
}
.find__meta { font-size: 0.8rem; font-weight: 500; line-height: 1.4; color: var(--muted); }
.find__through { font-family: var(--font-heading); font-weight: 700; font-size: 0.875rem; color: var(--accent); text-decoration: none; }
.find__through:hover { color: var(--ink); }

/* Phone: field and button stack, examples become full-width 44px rows. */
@media (max-width: 640px) {
  .find__form { flex-direction: column; }
  .find__fieldwrap { min-height: 52px; }
  .find__go { min-height: 52px; border-top: 2px solid var(--paper); justify-content: flex-start; }
  .find__egs { flex-direction: column; align-items: stretch; }
  .find__egs-label { margin: 0 0 0.25rem; }
  .find__eg { width: 100%; min-height: 44px; }
  .find__out { margin-left: calc(-1 * var(--gap)); margin-right: calc(-1 * var(--gap)); }
  .find__figure { min-width: 0; }
}

/* The corner panel on the inside pages: same answer furniture. Overrides on
   the existing .chat block — the launcher itself is unchanged.

   THE PANEL READ UPSIDE DOWN. The form carried order:-1 to lift the search
   box to the top, but without the matching .chat__head { order:-2 } that the
   support panel has — so on a phone the input floated ABOVE the title bar it
   belongs to, unattached to anything, and the panel opened with its heading
   in the middle. The suggested questions had the opposite problem: they teach
   a reader what can be asked, and a log with flex:1 grew to fill the empty
   panel and pushed them off the bottom of the screen.

   So: title, greeting, the questions right under it, and the input at the
   foot where every other chat puts it. The log only takes the room it needs
   until there is something in it; auto margin under the chips pushes the
   free space — and the form — to the bottom. */
.chat__panel { width: min(420px, calc(100vw - 2rem)); }
.chat__panel .chat__form { order: 0; border-top: 2px solid var(--ink); border-bottom: 0; padding: 0; gap: 0; }
.chat__panel .chat__log { flex: 0 1 auto; min-height: 0; }
.chat__panel .chat__chips { margin-bottom: auto; }
.chat__panel .chat__input { border: 0; min-height: 50px; background: var(--surface); }
.chat__panel .chat__send { width: 50px; border: 0; }

/* The same figure in the corner panel, at panel size. */
.find__figure--panel {
  min-width: 0; margin: 0 0 0.6rem; border-top-width: 2px; padding-top: 0.5rem;
}
.find__figure--panel b { font-size: 1.625rem; }
.find__figure--panel span { margin-top: 0.35rem; font-size: 0.75rem; }

/* When nothing was found, the way through is the whole answer, so it is a
   button rather than a line of small type. */
.find__steer {
  margin: 0 0 1rem; font-size: clamp(0.95rem, 1.3vw, 1.0625rem); line-height: 1.55;
  max-width: 58ch; color: var(--ink); text-wrap: pretty;
}
.find__through--cta {
  background: var(--lime); color: var(--ink); border: 0;
  padding: 0.65rem 1.1rem; font-size: 0.9375rem;
}
.find__through--cta:hover, .find__through--cta:focus-visible { background: #d9a04a; }
.chat__msg--answer .find__steer { font-size: 0.86rem; margin-bottom: 0.6rem; }

@media print { .find { display: none; } }


/* ==========================================================================
   THE RELEASE CALENDAR  (fw-calendar.js)

   Only the statistics register uses this. It sits above the register table
   and answers the other half of the question: not "what has been published"
   but "what is coming, and has any of it moved".

   Two things worth knowing before editing it:

   1. It follows the page's palette rather than staying light. The eleven
      analysis pages are always dark, so a white panel dropped into one of
      them reads as a different site. Everything below is a token.

   2. A day is a row, not a card. An earlier version drew one card per
      release and the eye had no way of telling that six of them were the
      same Thursday, which is the only thing a release calendar is for.
   ========================================================================== */

.cal { margin: 0 0 2.5rem; }

.cal__head { border-top: 2px solid var(--ink); padding-top: 0.75rem; margin-bottom: 1.25rem; }
.cal__title { margin: 0; font-size: 1.5rem; line-height: 1.15; }
.cal__sub { margin: 0.4rem 0 0; color: var(--muted); font-size: 0.875rem; max-width: 60ch; }
.cal__sub strong { color: var(--ink); }

.cal__none { margin: 0; color: var(--muted); font-size: 0.875rem; }

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

/* The date sits in its own column on a wide screen and above the list on a
   narrow one, so a phone never gets a 6ch column squeezing every title into
   a ladder of single words. */
.cal__day {
  display: grid;
  grid-template-columns: 11rem 1fr;
  gap: 0 1.5rem;
  padding: 0.9rem 0;
  border-top: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
}
.cal__day:first-child { border-top: 0; }

.cal__when { display: flex; flex-direction: column; gap: 0.1rem; }
.cal__date { font-weight: 700; font-size: 0.9375rem; }
.cal__weekday { color: var(--muted); font-size: 0.8125rem; }
.cal__away { color: var(--lime); font-size: 0.75rem; text-transform: lowercase; }

.cal__items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.75rem; }

.cal__item { min-width: 0; }
.cal__name { margin: 0; font-size: 0.9375rem; line-height: 1.35; }
.cal__name a { color: var(--ink); text-decoration-color: color-mix(in srgb, var(--ink) 35%, transparent); }
.cal__name a:hover { text-decoration-color: var(--lime); }

.cal__meta { margin: 0.2rem 0 0; display: flex; flex-wrap: wrap; gap: 0.5rem; font-size: 0.75rem; color: var(--muted); }
.cal__state {
  color: var(--ink); border: 1px solid color-mix(in srgb, var(--ink) 30%, transparent);
  border-radius: 999px; padding: 0 0.5rem; line-height: 1.5;
}
.cal__moved { margin: 0.25rem 0 0; font-size: 0.75rem; color: var(--lime); }

/* A release that has been called off or pushed back is still worth seeing —
   that IS the story — so it is dimmed rather than removed, and its title
   struck through only when it is cancelled outright. */
.cal__item.is-off .cal__name a { color: var(--muted); }
.cal__item.is-off .cal__state { border-color: var(--lime); color: var(--lime); }

.cal__cap { margin: 0 0 1rem; font-size: 0.8125rem; color: var(--muted); }
.cal__more { margin: 1.25rem 0 0; }
.cal__caveat { margin: 0.75rem 0 0; font-size: 0.75rem; color: var(--muted); max-width: 72ch; }

@media (max-width: 700px) {
  .cal__day { grid-template-columns: 1fr; gap: 0.5rem; }
  .cal__when { flex-direction: row; align-items: baseline; gap: 0.6rem; }
}

@media print { .cal__more { display: none; } }

/* --- High contrast mode -------------------------------------------------
       Windows (and now every major browser) has a mode that throws away the
       page's colours and paints everything in a palette the reader chose
       themselves — usually white on black, or black on yellow. It is what
       people with low vision actually use, and it is not the same thing as
       our dark theme: it discards backgrounds, shadows and any colour we set,
       and keeps only text, borders and outlines.

       That hits this site harder than most, because so much of the design is
       carried by things it deletes. The alternating row tint is white at 2.2%
       opacity. A pressed filter chip is told apart from an unpressed one by
       its background alone. The two-tone bars in the year rails are pure
       background. All of that vanishes, and the reader is left with a wall of
       undifferentiated text.

       So everything below re-states, in borders and system colours, whatever
       was being said in colour. The system keywords (Canvas, CanvasText,
       Highlight, ButtonBorder) are the reader's own palette, so they are
       always legible whichever scheme they picked — and they are the only
       colours honoured here. forced-color-adjust:none appears exactly twice,
       on the two bars whose whole job is to be a length of filled colour. */
@media (forced-colors: active) {

  /* 1. Focus. The accent outline is discarded, so name a system colour and
        widen it — this is the one thing a keyboard user cannot do without. */
  :focus-visible,
  .fw-chips .fw-chip[aria-pressed]:focus-visible,
  .fwb__btn:focus-visible, .fwb__cell:focus-visible,
  .rail-menu__btn:focus-visible, .submenu__toggle:focus-visible,
  .fw-filters__sum:focus-visible, .fw-saved__use:focus-visible,
  .reg .vrow:focus-visible,
  .reg select:focus-visible, .reg input:focus-visible, .reg button:focus-visible {
    outline: 3px solid Highlight;
    outline-offset: 2px;
  }

  /* 2. Filter chips. Pressed and unpressed differ only by background, which
        is gone — so the pressed one takes the reader's selection colour and a
        heavier border, and both keep a border so the row still reads as a row
        of buttons rather than a run of words. */
  .fw-chips .fw-chip[aria-pressed] {
    border: 1px solid ButtonBorder;
  }
  .fw-chips .fw-chip[aria-pressed="true"],
  .fw-chips .fw-chip--all[aria-pressed="true"],
  .find__eg[aria-pressed="true"],
  .fwb__btn.is-on {
    background: Highlight;
    color: HighlightText;
    border-color: Highlight;
    forced-color-adjust: none;   /* [B-065] keep the text on the highlight */
  }
  .fw-chips .fw-chip[aria-pressed="true"] *,
  .fw-chips .fw-chip--all[aria-pressed="true"] * { color: HighlightText; }
  .fwb__toggle, .fwb__btn { border-color: ButtonBorder; }
  .fwb__btn + .fwb__btn { border-left-color: ButtonBorder; }

  /* 3. Tables. The row tint and the hover tint both go, so the grid comes
        back as real lines. A one-pixel rule under every cell is enough to
        follow a row of eight columns across; the hovered row gets the
        selection colour instead of a 4% wash. */
  .tn-table td, .tn-table th {
    border-bottom: 1px solid CanvasText;
  }
  .tn-table tr.is-alt td { background: Canvas; }
  .tn-table tr:hover td { background: Highlight; color: HighlightText; }
  .tn-table tr:hover td a { color: HighlightText; }
  .tn-table thead th { border-bottom-width: 2px; }

  /* 4. Sticky headings. A translucent heading over scrolling rows becomes
        transparent here, and the rows read straight through it. */
  thead th, thead tr:nth-child(2) th, .reg thead th {
    background: Canvas;
    border-bottom: 2px solid CanvasText;
  }

  /* 5. The two-tone bars in the year rails. These are not decoration — the
        filled length IS the number — so they keep their own colours, and the
        track gets a border so an empty one is still visibly a bar. */
  .reg .vrow .vbar {
    border: 1px solid CanvasText;
    background: Canvas;
  }
  .reg .vrow .vbar > span,
  .reg .vrow .vbar > span > i {
    forced-color-adjust: none;
  }

  /* 6. The loading cover. Its background is discarded, so the page it is
        meant to be hiding shows through it. */
  .fw-load { background: Canvas; }

  /* 7. Anything whose edge was a shadow rather than a border. Shadows are
        removed outright, so the panels they outlined lose their shape. */
  .fwb__cell:hover, .fwb__cell:focus-visible, .fwb__cell.is-open,
  .fw-pick-menu, .submenu, .rail-menu__list {
    border: 1px solid CanvasText;
  }

  /* 8. Links keep the underline they were carrying in colour. */
  a { text-decoration: underline; }
  .mainnav a, .rail a, .site-footer a { text-decoration: none; }
}

/* The expander inside a dashboard row heading. It replaced role="button" on
   the <tr> itself, which had been quietly throwing away the row and
   column-heading semantics a screen reader reads out. A button carries its
   own look, so it is reset back to the text it is wrapping. */
.ga-rowbtn, .gi-rowbtn {
  display: block; width: 100%; text-align: left;
  font: inherit; color: inherit; background: none; border: 0;
  padding: 0; margin: 0; cursor: pointer;
}
.ga-rowbtn:focus-visible, .gi-rowbtn:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
@media (forced-colors: active) {
  .ga-rowbtn:focus-visible, .gi-rowbtn:focus-visible { outline: 3px solid Highlight; }
}

/* "You are here." site.js writes aria-current="page" onto whichever menu,
   rail or footer link points at the page you are already reading. The
   section rail's scroll-spy writes aria-current="true" for the section you
   have scrolled to — a different statement, so these selectors name the
   value rather than the attribute. */
.mainnav a[aria-current="page"],
.submenu a[aria-current="page"] {
  color: var(--accent);
  font-weight: 700;
}
.site-footer a[aria-current="page"] { text-decoration: underline; }
@media (forced-colors: active) {
  .mainnav a[aria-current="page"],
  .submenu a[aria-current="page"],
  .site-footer a[aria-current="page"] { text-decoration: underline; font-weight: 700; }
}

/* "Skip to results", injected by fw-controls.js at the top of every filter
   bar. Out of the way until it is focused, then above the filter buttons.

   It used to copy .skip-link exactly: parked at top:-3rem and slid to 0 on
   focus. That works at the top of the page, where -3rem is off the screen.
   Half way down a page it is not off anything — it is 3rem higher, sitting
   permanently on top of whatever the bar is beneath, in full view of every
   reader, and on focus it landed on the first row of filter buttons. Both
   were reported on 8 Sep 2026. So it is hidden the honest way instead (the
   same clip as .vh), and the bar reserves the height it needs while it has
   focus, so it covers nothing. :has() is in every current browser; an older
   one just gets the link over the first row, which is where it used to be. */
.fw-skip {
  position: absolute; left: 0; top: 0; z-index: 60;
  width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap;
  background: var(--ink); color: var(--paper);
  font-family: var(--font-heading); font-weight: 700; font-size: 0.75rem;
  letter-spacing: 0.04em; text-decoration: none;
}
.fw-skip:focus {
  width: auto; height: auto; margin: 0; padding: 0.5rem 0.9rem;
  overflow: visible; clip: auto; clip-path: none;
}
.controls, .rpc-controls, .sc-controls, .pir-controls,
.tn-controls, .ga-controls, .gi-controls { position: relative; }
.controls:has(> .fw-skip:focus),
.rpc-controls:has(> .fw-skip:focus),
.sc-controls:has(> .fw-skip:focus),
.pir-controls:has(> .fw-skip:focus),
.tn-controls:has(> .fw-skip:focus),
.ga-controls:has(> .fw-skip:focus),
.gi-controls:has(> .fw-skip:focus) { padding-top: 2.5rem; }
@media (forced-colors: active) {
  .fw-skip { background: Canvas; color: CanvasText; border: 2px solid CanvasText; }
}

/* The column a sort is currently obeying. aria-sort is set by the two
   dashboard scripts; this is the only place it is shown rather than just
   announced. */
thead th[aria-sort] { color: var(--accent); }
thead th[aria-sort]::after { content: " \2193"; }

/* The glossary on methodology.html. A <dl> is the right shape for it —
   a screen reader announces how many terms there are and pairs each with
   its definition — but browsers indent the definitions and nothing else,
   which reads as an outline rather than a list of answers. */
.gloss { margin: 0 0 2rem; }
.gloss dt {
  font-family: var(--font-heading); font-weight: 700; font-size: 0.95rem;
  color: var(--ink); margin: 1.1rem 0 0.2rem;
}
.gloss dt:first-child { margin-top: 0; }
.gloss dd {
  margin: 0; padding: 0 0 0 0.9rem;
  border-left: 1px solid var(--divider);
  color: var(--ink-soft); line-height: 1.55; max-width: 62ch;
}
@media (forced-colors: active) {
  .gloss dd { border-left-color: CanvasText; }
}

/* ==========================================================================
   FIND — the three faults that made the homepage look broken on a phone.
   8 Sep 2026, after "I entered a search into Ask a question and nothing
   happened", reported from the installed app on an iPhone. Measured at 390px
   before the fix: the field 322px tall, the Find button 512px down, and the
   answer opening at 881px in a 664px viewport.
   ========================================================================== */

/* 1. THE FIELD WAS A 320px SLAB.
   .find__fieldwrap carries `flex: 1 1 20rem`, and 20rem is a WIDTH basis in
   the row layout it was written for. The phone rule turns the form into a
   column, at which point flex-basis applies to the block axis instead: the
   search box grew to 20rem TALL and pushed the button to the foot of the
   screen. flex-basis:auto hands min-height back the job. */
@media (max-width: 640px) {
  .find__fieldwrap { flex: 0 0 auto; }
}

/* 2. NO SIDE GUTTER.
   .find is a .container, and .container sets its gutter with padding-inline —
   but .find's own `padding: X 0 0` shorthand appears later in this file and
   resets left and right to zero, so the heading and the examples ran off the
   edge of a phone screen. The answer's `margin-left: calc(-1 * var(--gap))`
   below is the proof it was never meant to: that negative margin exists to
   escape a gutter, and with no gutter it pulls the answer off the screen. */
.find { padding-inline: clamp(1.15rem, 4vw, 2.5rem); }

/* 3. THE BUTTON SAYS IT IS WORKING.
   The loading bar, mark and word open inside .find__out, which is below the
   examples — off the bottom of a phone even with faults 1 and 2 fixed, and
   further still with the keyboard up. So the mark is shown on the button as
   well, which is the one thing certainly under the reader's eye when they
   press it. find-hero.js also scrolls the answer up to meet them.
   --mark-* invert inside .hero for a dark ground; this button is lime, so the
   light-ground ramp is put back on it. */
.find__go.is-busy {
  --mark-1: #211d19; --mark-2: #f6f3ee; --mark-3: #f6f3ee;   /* Seam: ink and paper stitches on the ochre button */
  background: var(--lime); color: var(--ink); cursor: progress;
}
/* a button that dims reads as broken, which is the opposite of the point:
   it is working, and it should look like it */
.find__go[disabled] { opacity: 1; }
.find__go .fw-mini { width: 22px; height: 22px; vertical-align: -.3em; }

/* Appended 8 September 2026 from the Claude Design handoff
   (design_handoff_support_panel/support-panel.css). The markup that uses it is
   put on every page by scripts/add-support-panel.mjs, which also bumps this
   file's ?v= — the two have to travel together, or a cached stylesheet leaves
   the panel unstyled in the middle of the page.

   ONE CHANGE FROM THE HANDOFF: the panel's <section> carries support__panel
   and NOT chat__panel. .chat__panel is itself position:fixed with a RIGHT
   offset, so a section carrying both opened in the opposite corner from its
   own button. Everything the shell needs is stated below. */
/* ==========================================================================
   THE SUPPORT PANEL  (fw-support.js)

   Append to styles.css, after the .chat block it sits alongside. Everything
   here is either a token or a value the .chat panel already uses; the answer
   furniture (.find__foot, .find__meta, .find__caveat, .find__bar,
   .find__working, .fw-mini) is reused as-is and not restated.

   Find is the front door and keeps its filled launcher bottom right. Support
   is quieter: outlined, smaller, bottom left.
   ========================================================================== */

.support { position: fixed; left: clamp(1rem, 3vw, 2rem); bottom: clamp(1rem, 3vw, 2rem); z-index: 80; }

.support__launch {
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.5rem 0.75rem;
  background: var(--paper); color: var(--ink);
  border: 1px solid var(--divider-2); cursor: pointer;
  font-family: var(--font-heading); font-weight: 600; font-size: 0.8rem; line-height: 1;
  transition: background .12s ease, border-color .12s ease;
}
.support__launch:hover { background: var(--paper-2); border-color: var(--ink); }
.support__launch:active { background: var(--divider); }
.support__launch:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.support.is-open .support__launch { display: none; }

/* The panel: the .chat panel's shell, moved to the left edge. z-index is
   stated because the hero's .find block is positioned (z-index 1) and the
   panel is not a child of it. */
.support__panel {
  position: fixed; left: clamp(1rem, 3vw, 2rem); bottom: clamp(1rem, 3vw, 2rem); z-index: 85;
  width: min(420px, calc(100vw - 2rem)); height: min(600px, calc(100vh - 3rem));
  display: flex; flex-direction: column;
  background: var(--surface); border: 2px solid var(--ink);
  box-shadow: var(--shadow-lg); overflow: hidden;
}
.support__panel[hidden] { display: none; }

/* The review gate, said once for the session rather than in every reply. */
.support__banner {
  order: -1;
  margin: 0; padding: 0.6rem 0.9rem;
  background: var(--warn-bg-2); border-bottom: 1px solid var(--warn-line-2); color: var(--warn-ink);
  font-size: 0.76rem; line-height: 1.45; font-weight: 500;
}
.support__banner[hidden] { display: none; }

.support__text { margin: 0; }

/* An answer is prose, a footer and sometimes a ticket: it needs the width. */
.support__panel .chat__msg--bot { max-width: 100%; }
.support__panel .chat__msg--user { max-width: 86%; }

.support__working { max-width: 100%; padding: 0; }
.support__working .find__working { margin: 0.55rem 0.8rem 0.6rem; font-size: 0.85rem; }

.support__foot { margin-top: 0.85rem; padding-top: 0.6rem; }
.support__action { display: inline-block; margin-top: 0.6rem; }

/* The hand-off. Ruled and labelled, because a reference number a reader
   cannot find again is not a reference. */
.support__ticket { margin-top: 0.7rem; border: 1px solid var(--divider); background: var(--surface); }
.support__ticket-kicker {
  margin: 0; padding: 0.45rem 0.6rem; background: var(--ink); color: var(--paper);
  font-family: var(--font-heading); font-weight: 800; font-size: 0.66rem;
  letter-spacing: 0.16em; text-transform: uppercase;
}
.support__ticket-body { padding: 0.6rem; display: flex; flex-direction: column; gap: 0.3rem; }
.support__ticket-ref {
  font-family: var(--font-heading); font-weight: 800; font-size: 1.05rem; letter-spacing: -0.01em;
}
.support__ticket-note { font-size: 0.76rem; line-height: 1.45; color: var(--muted); }

/* The field at the top, as the Find corner panel does it: the thing you came
   to do is the first thing in the box. Log below it, suggestions at the foot. */
.support__panel .chat__form { order: -1; border-top: 0; border-bottom: 2px solid var(--ink); padding: 0; gap: 0; }
.support__panel .chat__head { order: -2; }
.support__panel .chat__input { border: 0; min-height: 50px; background: var(--surface); }
.support__panel .chat__send { width: 50px; border: 0; }
.support__panel .chat__log { flex: 1; }

@media (max-width: 480px) {
  .support__panel { left: 0.5rem; right: 0.5rem; width: auto; height: min(70vh, 560px); }
}

/* ==========================================================================
   TEXT ON THE LIME, 8 September 2026.

   Found while rendering the Support panel, and it is NOT the Support panel's
   bug — it is in .chat__msg--user, which the Find panel has used all along.

   --lime is #b9791f in both themes; --ink flips from #22211c to #f1ebe1. So
   `background: var(--lime); color: var(--ink)` reads at 11.6:1 in the light
   theme and 1.2:1 in the dark one, which is not text, it is a rumour of text.
   Measured with the WCAG formula, not guessed.

   The fix is a token that does not flip, because the surface it sits on does
   not flip. Defined on :root only and deliberately NOT redefined in the dark
   block — that is the whole point of it, and check-files.mjs fails if anybody
   does. Anything painted on the lime should use it.
   ========================================================================== */
:root { --ink-on-lime: #211d19; }   /* Seam: dark ink on ochre, 4.6:1 */

.chat__msg--user,
.chat__launch:hover,
.chat__launch:focus-visible,
.chat__send:hover { color: var(--ink-on-lime); }

/* [B-024] Room for the fixed Support and Find buttons at the foot of the
   page on a phone, so they never sit over the last lines. */
@media (max-width: 600px) {
  body { padding-bottom: 72px; }
}
/* [B-067] A dropdown dismissed with Escape stays shut until focus leaves it. */
.has-menu.is-dismissed .submenu { opacity: 0; visibility: hidden; }
.rail-menu.is-dismissed .rail-menu__list { opacity: 0 !important; visibility: hidden !important; }

/* [U2-10] Only the closed bar is sticky. Opened, the panel scrolls away
   with the page instead of covering the records it is filtering. */
.reg .controls:has(> .fw-filters[open]),
.reg .controls:has(.fw-filters[open]) { position: static; }

/* ==========================================================================
   Seam type, 22 September 2026
   Small uppercase labels move to IBM Plex Mono. Only the typeface, weight and
   tracking change here, so the coloured states on chips and pills keep their
   colours.
   ========================================================================== */
.chip, .pill, .footer-col h3, .fw-load__word, .fw-busy {
  font-family: var(--font-mono); font-weight: 400; letter-spacing: 0.14em;
}
.footer-col h3 { font-size: 0.69rem; }
b, strong { font-weight: 600; }
