/* Woodlark tender portal: the house style every screen shares.

   Tokens are Woodlark's own (~/code/woodlark/colors_and_type.css and the live site's
   site/colors_and_type.css and site.css): DM Sans only, ink ground, bone text, citron and fern as
   punctuation, red only for a failed check. Every page opens on the website's home-hero ground (a
   field of small Woodlark marks under the purple-and-blue glow from the top), its accent line in
   the strapline gradient, and primary buttons are the website's purple with its glow (Trent,
   16 October 2026). Working areas (tables, forms, charts, lists) stay on plain ink. Groups are made
   with space and hairlines; a box is kept for the one thing on a screen that must stand apart (the
   current bid step, a tooltip, a menu, an empty state).

   Everything named here is served by the portal itself: the content policy allows no other
   source, no inline style attributes and no inline scripts. Paths are relative to this file.

   Sections: fonts, tokens, base, icons, mark, frame, page, opening, tracker, figures, actions,
   tables, status, buttons, forms, timeline, steps, meter, states, charts, motion, print. */

/* ---- Fonts ------------------------------------------------------------------------------------ */
@font-face {
  font-family: "DM Sans";
  font-style: normal;
  font-weight: 100 1000;
  font-display: swap;
  src: url("fonts/DMSans-VariableFont_opsz_wght.woff2") format("woff2");
}
@font-face {
  font-family: "DM Sans";
  font-style: italic;
  font-weight: 100 1000;
  font-display: swap;
  src: url("fonts/DMSans-Italic-VariableFont_opsz_wght.woff2") format("woff2");
}

/* ---- Tokens ----------------------------------------------------------------------------------- */
:root {
  color-scheme: dark;

  /* Palette (Woodlark primitives) */
  --wl-ink: #0E0B14;
  --wl-ink-2: #171412;
  --wl-ink-3: #1F1B26;
  --wl-sunken: #08060C;
  --wl-bone: #F2F2ED;
  --wl-purple: #4D0E88;
  --wl-purple-2: #6B1FB8;
  --wl-purple-3: #2A0850;
  --wl-violet: #983FB2;
  --wl-violet-2: #B870D0;
  --wl-purple-bright: #A56CFF;
  --wl-citron: #E6D943;
  --wl-fern: #11CC55;
  --wl-fern-2: #3FE07A;
  --wl-blue: #5170FF;
  --wl-mid: #B06BDF;
  --wl-pink: #FF66C4;
  --wl-grey-800: #26222F;
  --wl-grey-700: #3A3545;
  --wl-grey-500: #807A8B;
  --wl-grey-400: #A8A2B0;

  /* Meaning */
  --bg: var(--wl-ink);
  --bg-elev: var(--wl-ink-3);
  --bg-sunken: var(--wl-sunken);
  --bg-field: rgba(255, 255, 255, 0.045);
  --fg: var(--wl-bone);
  --fg-dim: rgba(242, 242, 237, 0.8);
  --fg-muted: var(--wl-grey-400);          /* 7.9:1 on ink */
  --fg-faint: #8E889A;                     /* 5.7:1 on ink, 4.9:1 on the elevated surface */
  --border: rgba(255, 255, 255, 0.08);
  --border-strong: rgba(255, 255, 255, 0.16);
  --border-field: rgba(255, 255, 255, 0.24);
  --wash: rgba(255, 255, 255, 0.05);
  --wash-strong: rgba(255, 255, 255, 0.09);
  --lit-edge: inset 0 1px 0 rgba(255, 255, 255, 0.06);

  /* Status: the icon carries the colour, the words carry the meaning. */
  --good: var(--wl-fern);
  --attention: var(--wl-citron);
  --failed: #E5484D;
  --failed-fg: #FF9EA1;
  --waiting: var(--wl-grey-400);

  /* Charts: a lightness ladder of the 3PL blue, then one neutral grey. Purple is kept for the
     brand (the opening, primary buttons, focus), and citron, fern and red only ever mean status,
     so none of them is a series. Each step is told apart by lightness (at least 1.4:1 from its neighbour) and every
     mark is over 3:1 on ink; the legend or a direct label always names it. */
  --series-1: #5170FF;                     /* 4.8:1 on ink */
  --series-2: #8C9FFF;                     /* 7.9:1 */
  --series-3: #C4CDFF;                     /* 12.6:1 */
  --series-4: #3D57E0;                     /* 3.4:1; on ink only */
  --series-other: #807A8B;                 /* 4.7:1 */
  --series-1-lift: #7189FF;
  --grid: rgba(255, 255, 255, 0.07);
  --axis: rgba(255, 255, 255, 0.22);
  --reference: var(--wl-bone);

  /* Type: one family, weight and tracking do the work. */
  --font: "DM Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --t-display: clamp(38px, 5.8vw, 68px);
  --t-hero: clamp(52px, 7vw, 84px);
  --t-h2: clamp(21px, 2vw, 25px);
  --t-h3: 18px;
  --t-figure: clamp(26px, 2.5vw, 32px);
  --t-body: 16px;
  --t-table: 15px;
  --t-small: 14px;
  --t-caption: 13px;
  --t-micro: 12px;
  --tr-tight: -0.035em;
  --tr-snug: -0.015em;
  --tr-mark: 0.18em;

  /* Shape and space (8px rhythm) */
  --r-xs: 4px;
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 18px;
  --r-xl: 28px;
  --r-pill: 999px;
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 20px;
  --s-6: 24px;
  --s-7: 32px;
  --s-8: 40px;
  --s-9: 56px;
  --s-10: 72px;
  --s-11: 96px;
  --shadow-lg: 0 18px 48px rgba(0, 0, 0, 0.45);
  --shadow-glow: 0 0 0 1px rgba(152, 63, 178, 0.3), 0 12px 40px rgba(77, 14, 136, 0.3);
  --gutter: 16px;
  --page: 1240px;
  --brand-row: 56px;
  --section-gap: var(--s-9);

  /* Motion: calm, never bouncy. Out-expo for entrances, standard for state changes. */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-state: cubic-bezier(0.4, 0, 0.2, 1);
  --t-state: 180ms;
  --t-layout: 320ms;
  --t-enter: 600ms;

  --mark-image: url("assets/logo-mark.svg");

  /* The website's hero, carried exactly (site.css .hero--a, colors_and_type.css). The strapline
     gradient fills the opening's accent line as clipped text. The glow is the home hero's light
     from the top. The field is a still frame of the home hero's tiles: Woodlark's mark at
     five-twelfths of each tile in the ghost colour #262033 (assets/mark-field.svg), 20px tiles on
     a phone, 26px on a tablet and 30px from a laptop up, as hero-field.js sizes them.
     The field runs at the website's own strengths (hero-field.js ghostCalm): behind the reading
     column 60% on a phone and tablet and 50% from a laptop up (--field-quiet), rising to full in
     the open space beside it. Bone and grey words stay well above 4.5:1 on that. The strapline's
     blue start does not, so shared/opening-calm.mjs measures where the gradient line sits and a
     soft patch of ink (.opening-inner::before) calms the marks under those words only. Until an
     opening is measured (.is-calmed) its field stays at --field-quiet-unmeasured, which needs no
     patch. web/dev/contrast.mjs reads these numbers; keep it in step.
     The website centres its glow over centred words; the portal's words sit on the left, so the
     same glow comes down over the open side (--glow-x). Its depth is a fixed 300px rather than
     the website's half-height, so a short opening still gets the whole light and a tall one is lit
     as deep as the website's hero. On a phone the words fill the width, so the glow sits further
     out and is lighter and shallower there, to keep the gradient words at 4.5:1. */
  --grad-strapline: linear-gradient(90deg, #5170FF 0%, #B06BDF 50%, #FF66C4 100%);
  --glow-x: 92%;
  --glow-depth: 240px;
  --glow-a1: 0.45;
  --glow-a2: 0.30;
  --hero-glow: radial-gradient(60% var(--glow-depth) at var(--glow-x) 0%, rgba(176, 107, 223, var(--glow-a1)) 0%, rgba(81, 112, 255, var(--glow-a2)) 40%, transparent 100%);
  --field-image: url("assets/mark-field.svg");
  --field-tile: 20px;
  --field-ghost: #262033;
  /* The website's ripple, held still as its reduced-motion frame holds it (woodlark-signature.js):
     one ring of brand colour part-way out through the marks, here from the opening's top right
     corner, so it stays in the open space and out of the reading column. Purple, blue, lilac and
     pink only: citron is the portal's 'needs attention' colour. Drawn from a laptop up, where the
     right of the opening is clear of words (--ring-on); --ring-r is the ring's outer radius. */
  --ring-on: 0;
  --ring-r: 0px;
  --field-ring: none;
  --field-quiet-unmeasured: 0.2;
  --field-quiet-measured: 0.6;
  --field-quiet-to: 45%;
  --field-loud: 1;
  --field-loud-at: 100%;
  --calm-strength: 0.7;
  --calm-feather: 18px;
  --calm-reach: 26px;
}
@media (min-width: 400px) { :root { --gutter: 20px; } }
@media (min-width: 641px) { :root { --field-tile: 26px; } }
@media (min-width: 720px) { :root { --gutter: 40px; --brand-row: 64px; --section-gap: var(--s-10); --glow-x: 80%; --glow-depth: 300px; } }
@media (min-width: 1020px) {
  :root {
    --glow-x: 66%; --field-quiet-measured: 0.5; --field-quiet-to: 42%; --field-loud-at: 70%; --calm-feather: 24px; --calm-reach: 32px;
    --ring-on: 1;
    --ring-r: 280px;
    --field-ring: radial-gradient(circle at 100% 0, transparent calc(var(--ring-r) - 132px), #4D0E88 calc(var(--ring-r) - 100px), #5170FF calc(var(--ring-r) - 66px), #B06BDF calc(var(--ring-r) - 32px), #FF66C4 var(--ring-r), transparent calc(var(--ring-r) + 24px));
  }
}
@media (min-width: 1025px) { :root { --field-tile: 30px; } }
@media (min-width: 1280px) { :root { --ring-r: 360px; } }

/* ---- Base ------------------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-padding-top: 72px; }
body {
  margin: 0;
  min-width: 320px;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--fg);
  font: 400 var(--t-body)/1.55 var(--font);
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-wrap: break-word;
  overflow-x: clip;
}
[hidden] { display: none !important; }
h1, h2, h3, h4, p, ul, ol, dl, dd, figure, blockquote { margin: 0; }
ul, ol { padding: 0; }
h1, h2, h3, h4 { font-weight: 700; text-wrap: balance; }
h1 { font-size: var(--t-display); line-height: 1.02; letter-spacing: var(--tr-tight); }
h2 { font-size: var(--t-h2); line-height: 1.18; letter-spacing: var(--tr-snug); }
h3 { font-size: var(--t-h3); line-height: 1.3; letter-spacing: -0.01em; }
h1:focus, h2:focus { outline: none; }
p { text-wrap: pretty; }
strong, b { font-weight: 600; }
small { font-size: var(--t-caption); }
img, svg { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
legend { padding: 0; }
a { color: inherit; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; text-decoration-color: rgba(242, 242, 237, 0.4); transition: color var(--t-state), text-decoration-color var(--t-state); }
a:hover { color: var(--wl-violet-2); text-decoration-color: currentColor; text-decoration-thickness: 2px; }
/* A target focused by script (tabindex -1: <main>, a section or row opened from a link) never
   gets the ring; only what a person can tab to does. */
/* :where keeps this rule at the weight of one class, so a component's own focus shape (a pill, a
   button, a row link) wins over it. */
:where(a, button, input, select, textarea, summary, [tabindex]:not([tabindex="-1"])):focus-visible {
  outline: 2px solid var(--wl-purple-bright);
  outline-offset: 3px;
  border-radius: var(--r-xs);
}
::selection { background: rgba(165, 108, 255, 0.4); color: var(--wl-bone); }

.visually-hidden { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.num { font-variant-numeric: tabular-nums; }
.muted { color: var(--fg-muted); }
.faint { color: var(--fg-faint); }
.dim { color: var(--fg-dim); }
.small { font-size: var(--t-small); }
.caption { font-size: var(--t-caption); color: var(--fg-muted); }
.nowrap { white-space: nowrap; }

/* ---- Icons (shared/icons.mjs) ----------------------------------------------------------------- */
.icon { flex: none; width: 20px; height: 20px; stroke-width: 1.75; }
.icon--16 { width: 16px; height: 16px; }
.icon--24 { width: 24px; height: 24px; }

/* ---- The mark and the wordmark ----------------------------------------------------------------
   The mark is the brand's own drawing, used as a mask so it takes the colour of the text around
   it. WOODLARK is DM Sans Bold, uppercase, tracked +0.18em, never extended. */
.mark {
  display: block;
  flex: none;
  width: 28px;
  height: 28px;
  background: currentColor;
  -webkit-mask: var(--mark-image) center / contain no-repeat;
  mask: var(--mark-image) center / contain no-repeat;
}
.wordmark { font-size: 15px; font-weight: 700; line-height: 1; letter-spacing: var(--tr-mark); text-transform: uppercase; white-space: nowrap; }

/* ---- Frame (shared/frame.mjs) ----------------------------------------------------------------
   The brand row scrolls away; the row of sections stays at the top, as the audit portal's does. */
.skip { position: absolute; left: 8px; top: -64px; z-index: 100; padding: 10px 16px; border-radius: var(--r-sm); background: var(--wl-bone); color: var(--wl-ink); font-size: var(--t-small); font-weight: 600; text-decoration: none; transition: top 120ms; }
.skip:focus { top: 8px; }

.frame { position: relative; z-index: 60; border-bottom: 1px solid var(--border); }
.frame-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  max-width: calc(var(--page) + 2 * var(--gutter));
  min-height: var(--brand-row);
  margin: 0 auto;
  padding: 0 var(--gutter);
}
.brand { display: inline-flex; align-items: center; gap: 10px; min-height: 44px; color: var(--fg); text-decoration: none; }
.brand:hover { color: var(--fg); }
.frame .brand .mark { width: 24px; height: 24px; }
.frame .wordmark { font-size: 13px; }
/* The tender line, and with it the dual branding: the client's own logo (as the client publishes
   it: never recoloured, cropped or stretched; its height set and its width following) and then the
   tender's title. From a tablet up it sits in the brand row after a hairline, so Woodlark's mark
   and the client's logo read as one lockup; on a phone it is the row's second line. */
.frame-tender {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-width: 0;
  color: var(--fg-muted);
  font-size: var(--t-small);
  font-weight: 500;
  white-space: nowrap;
}
.frame-tender-words { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.frame-tender strong { color: var(--fg); font-weight: 600; }
/* The client's SERIOUS lettering fills its whole image, so the logo is set a little below the
   brand row's type height: its capitals sit at about one and a half times WOODLARK's, clearly the
   client's mark without outshouting Woodlark's own. */
.client-logo { display: block; flex: none; width: auto; height: 14px; max-width: none; }
.frame-tender--row { display: none; }
.frame-tender--line { padding: 0 var(--gutter) 14px; max-width: calc(var(--page) + 2 * var(--gutter)); margin: -2px auto 0; }
.frame-right { display: flex; align-items: center; gap: var(--s-2); margin-left: auto; }
@media (min-width: 720px) {
  .frame .brand .mark { width: 28px; height: 28px; }
  .frame .wordmark { font-size: 15px; }
  .frame-row { gap: var(--s-5); }
  .frame-tender--row { display: flex; gap: 16px; min-height: 28px; padding-left: var(--s-5); border-left: 1px solid var(--border-strong); line-height: 24px; }
  .frame-tender--row .client-logo { height: 16px; }
  .frame-tender--line { display: none; }
}

/* The sample-data marker: quiet, but always there. A dashed edge is Woodlark's draft badge. */
.sample-mark {
  display: inline-flex;
  align-items: center;
  min-height: 28px;
  padding: 0 10px;
  border: 1px dashed rgba(242, 242, 237, 0.42);
  border-radius: var(--r-pill);
  color: var(--fg-dim);
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* The narrowest phones (under 360px): the marker draws in a little and the person menu shows just
   the initials (still the menu's button), so the brand row fits inside the page's gutters. */
@media (max-width: 359px) {
  .frame-row { gap: var(--s-2); }
  .frame .brand { gap: 8px; }
  .sample-mark { padding: 0 6px; letter-spacing: 0.04em; }
  .person > summary { gap: 4px; padding-right: 4px; }
  .person > summary .icon { display: none; }
}

/* The person menu: who is looking, and (in the demo) a way back to the role picker. */
.person { position: relative; }
.person > summary {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 4px 6px 4px 4px;
  border-radius: var(--r-pill);
  color: var(--fg);
  font-size: var(--t-small);
  font-weight: 500;
  list-style: none;
  cursor: pointer;
  transition: background var(--t-state);
}
.person > summary::-webkit-details-marker { display: none; }
.person > summary:hover { background: var(--wash); }
.person > summary:focus-visible { border-radius: var(--r-pill); }
.person[open] > summary { background: var(--wash-strong); }
.avatar {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 32px;
  height: 32px;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  background: var(--wl-ink-3);
  color: var(--fg);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.person-name { display: none; white-space: nowrap; }
.person > summary .icon { width: 16px; height: 16px; color: var(--fg-muted); transition: transform var(--t-state) var(--ease-state); }
.person[open] > summary .icon { transform: rotate(180deg); }
.person-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  z-index: 70;
  width: min(300px, calc(100vw - 2 * var(--gutter)));
  padding: var(--s-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  background: var(--wl-ink-3);
  box-shadow: var(--shadow-lg), var(--lit-edge);
}
.person-who { padding: var(--s-3) var(--s-3) var(--s-4); border-bottom: 1px solid var(--border); }
.person-who strong { display: block; font-size: 15px; }
.person-who span { display: block; margin-top: 2px; color: var(--fg-muted); font-size: var(--t-caption); }
.person-menu a, .person-menu-action {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  margin-top: var(--s-1);
  padding: 0 var(--s-3);
  border-radius: var(--r-sm);
  color: var(--fg);
  font-size: var(--t-small);
  font-weight: 500;
  text-decoration: none;
}
.person-menu-action { width: 100%; border: 0; background: transparent; font-family: inherit; text-align: left; cursor: pointer; }
.person-menu a:hover, .person-menu-action:hover { background: var(--wash); color: var(--fg); }
.person-menu a .icon, .person-menu-action .icon { width: 18px; height: 18px; color: var(--fg-muted); }
.person-menu-action:disabled { opacity: 0.6; cursor: progress; }
@media (min-width: 960px) { .person-name { display: inline; } }

/* The row of sections, styled as the website's .nav: translucent ink with a blur, so the page passes
   softly beneath it, and the pills in the site's capsule. On a phone it scrolls sideways under a
   fade. It stays at the top of the window while the brand row scrolls away. */
.sections {
  position: sticky;
  top: 0;
  z-index: 50;
  border-bottom: 1px solid var(--border);
  background: rgba(14, 11, 20, 0.62);
  -webkit-backdrop-filter: blur(20px) saturate(140%);
  backdrop-filter: blur(20px) saturate(140%);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { .sections { background: rgba(14, 11, 20, 0.96); } }
.sections-inner { width: 100%; max-width: calc(var(--page) + 2 * var(--gutter)); margin: 0 auto; padding: 0 var(--gutter); }
.pills {
  display: flex;
  gap: 2px;
  margin: 0 calc(-1 * var(--gutter));
  padding: 8px 40px 8px var(--gutter);
  list-style: none;
  position: relative;
  overflow-x: auto;
  scrollbar-width: none;
  scroll-padding-inline: 48px;
}
/* A fade on whichever edge has more sections beyond it (frame.mjs sets the classes as it scrolls),
   so the row plainly scrolls and nothing looks cut off by accident. */
.pills.more-after { -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 56px), transparent); mask-image: linear-gradient(90deg, #000 calc(100% - 56px), transparent); }
.pills.more-before { -webkit-mask-image: linear-gradient(90deg, transparent, #000 56px); mask-image: linear-gradient(90deg, transparent, #000 56px); }
.pills.more-before.more-after { -webkit-mask-image: linear-gradient(90deg, transparent, #000 56px, #000 calc(100% - 56px), transparent); mask-image: linear-gradient(90deg, transparent, #000 56px, #000 calc(100% - 56px), transparent); }
.pills::-webkit-scrollbar { display: none; }
.pills a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 36px;
  padding: 0 14px;
  border-radius: var(--r-pill);
  color: var(--fg-muted);
  font-size: var(--t-small);
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  transition: color var(--t-state), background var(--t-state);
}
.pills a:hover { color: var(--fg); background: rgba(255, 255, 255, 0.05); }
.pills a[aria-current="page"] { color: var(--fg); background: rgba(255, 255, 255, 0.08); }
.pills a:focus-visible { border-radius: var(--r-pill); outline-offset: -2px; }
.pill-count {
  display: inline-grid;
  place-items: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--r-pill);
  background: var(--wl-bone);
  color: var(--wl-ink);
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
/* From a tablet up the row sits in the capsule. Where a role's sections are wider than the page
   (eight owner sections at about 720 to 830px), the capsule keeps to the page and scrolls inside
   itself under the same fades, so it never widens the page. */
@media (min-width: 720px) {
  .pills {
    width: fit-content;
    max-width: 100%;
    margin: 10px 0;
    padding: 5px;
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
    background: rgba(255, 255, 255, 0.035);
    scroll-padding-inline: 40px;
  }
  .pills a { padding: 0 16px; }
}
/* The site's own pill width, where eight sections still fit on one line. */
@media (min-width: 1120px) { .pills a { padding: 0 22px; } }

/* The foot: the mark, who runs the tender, and the sample-data line again. */
.foot { margin-top: auto; border-top: 1px solid var(--border); color: var(--fg-faint); font-size: var(--t-caption); line-height: 1.5; }
.foot-inner { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 24px; max-width: calc(var(--page) + 2 * var(--gutter)); margin: 0 auto; padding: var(--s-7) var(--gutter) var(--s-8); }
.foot .mark { width: 22px; height: 22px; color: var(--fg-faint); }
.foot p { flex: 1 1 260px; max-width: 70ch; }

/* ---- Page ------------------------------------------------------------------------------------- */
main { display: block; width: 100%; }
main:focus, main:focus-visible { outline: none; }
.page { width: 100%; max-width: calc(var(--page) + 2 * var(--gutter)); margin: 0 auto; padding: 0 var(--gutter); }
.page-body { padding-top: var(--s-7); padding-bottom: var(--s-9); }
.page-body > * + * { margin-top: var(--section-gap); }
@media (min-width: 720px) { .page-body { padding-top: var(--s-8); } }

/* A section heading states the finding; the measure and period sit beneath in muted text. An
   optional link to the detail sits on the right. No eyebrow labels. */
.section-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 6px 24px; margin-bottom: var(--s-5); }
.section-head > div { min-width: 0; max-width: 62ch; }
.section-head p { margin-top: 4px; color: var(--fg-muted); font-size: var(--t-small); }
.section-head .more { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; color: var(--fg); font-size: var(--t-small); font-weight: 600; text-decoration: none; }
.section-head .more .icon { width: 16px; height: 16px; transition: transform var(--t-state) var(--ease-state); }
.section-head .more:hover { color: var(--fg); }
.section-head .more:hover .icon { transform: translateX(3px); }

/* Two regions side by side from a laptop up: the explanation (wider) beside its companion. */
.split { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--section-gap); }
/* A region never grows past its column to fit a long unbroken word, a file name or a link. */
.split > * { min-width: 0; }
@media (min-width: 1020px) {
  .split { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: var(--s-9); }
  .split--even { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .split > .split-rule { padding-left: var(--s-9); border-left: 1px solid var(--border); margin-left: calc(-1 * var(--s-9) / 2); }
}
.rule-top { padding-top: var(--s-6); border-top: 1px solid var(--border); }

/* ---- Opening: where the tender stands, and the person's next step ---------------------------
   Every opening sits on the website's home-hero ground: ink, a still field of small Woodlark marks
   (quiet behind the reading column on the left, full in the open space to the right), and the
   purple-and-blue glow from the top over it. Both layers are decoration: drawn by the panel's own
   ::before and ::after, so they are never read out and never take a click, and every page's
   opening gets them whichever screen built it. The headline is two-tone: the first line bone, the
   date or count in the website's strapline gradient. */
/* The words settle into place as the page opens. They never start invisible, so a page caught
   before its first frames have run still reads in full. */
@keyframes rise { from { transform: translateY(8px); } to { transform: none; } }

.opening { position: relative; isolation: isolate; padding: var(--s-8) 0 var(--s-7); border-bottom: 1px solid var(--border); background-color: var(--wl-ink); }
.opening::before, .opening::after { content: ""; position: absolute; inset: 0; pointer-events: none; }
.opening { --field-quiet: var(--field-quiet-unmeasured); }
.opening.is-calmed { --field-quiet: var(--field-quiet-measured); }
/* The field: the ghost colour (and, from a laptop up, the ripple ring's colours over it), let
   through only where the tile's marks are and only as strongly as --field-quiet and --field-loud
   allow across the opening. */
.opening::before {
  z-index: -2;
  background: var(--field-ring) var(--field-ghost);
  -webkit-mask-image: var(--field-image), linear-gradient(90deg, rgba(0, 0, 0, var(--field-quiet)) 0%, rgba(0, 0, 0, var(--field-quiet)) var(--field-quiet-to), rgba(0, 0, 0, var(--field-loud)) var(--field-loud-at));
  -webkit-mask-size: var(--field-tile) var(--field-tile), 100% 100%;
  -webkit-mask-position: center top, 0 0;
  -webkit-mask-repeat: repeat, no-repeat;
  -webkit-mask-composite: source-in;
  mask-image: var(--field-image), linear-gradient(90deg, rgba(0, 0, 0, var(--field-quiet)) 0%, rgba(0, 0, 0, var(--field-quiet)) var(--field-quiet-to), rgba(0, 0, 0, var(--field-loud)) var(--field-loud-at));
  mask-size: var(--field-tile) var(--field-tile), 100% 100%;
  mask-position: center top, 0 0;
  mask-repeat: repeat, no-repeat;
  mask-composite: intersect;
}
.opening::after { z-index: -1; background: var(--hero-glow); }
.opening-inner { position: relative; display: grid; grid-template-columns: minmax(0, 1fr); }
/* The calm patch: a soft-edged piece of ink over the field, under the glow, just behind the
   strapline line (opening-calm.mjs sets where). Its core covers the words with --calm-reach less
   --calm-feather to spare; the last --calm-feather fades out, so it never reads as a box. */
.opening-inner::before {
  content: "";
  position: absolute;
  z-index: -1;
  left: calc(var(--calm-x, 0px) - var(--calm-reach));
  top: calc(var(--calm-y, 0px) - var(--calm-reach));
  width: calc(var(--calm-w, 0px) + 2 * var(--calm-reach));
  height: calc(var(--calm-h, 0px) + 2 * var(--calm-reach));
  display: none;
  pointer-events: none;
  background: var(--wl-ink);
  opacity: var(--calm-strength);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 var(--calm-feather), #000 calc(100% - var(--calm-feather)), transparent), linear-gradient(180deg, transparent, #000 var(--calm-feather), #000 calc(100% - var(--calm-feather)), transparent);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(90deg, transparent, #000 var(--calm-feather), #000 calc(100% - var(--calm-feather)), transparent), linear-gradient(180deg, transparent, #000 var(--calm-feather), #000 calc(100% - var(--calm-feather)), transparent);
  mask-composite: intersect;
}
.opening.is-calmed > .opening-inner.has-calm::before { display: block; }
@media (min-width: 1020px) {
  .opening-inner { grid-template-columns: minmax(0, 1fr) minmax(0, 0.42fr); column-gap: var(--s-9); }
  .opening-inner > .opening-words { grid-column: 1; }
  .opening-inner > .tracker-wrap { grid-column: 1 / -1; }
}
.opening-words { min-width: 0; }
.opening-words > * { animation: rise var(--t-enter) var(--ease) both; }
.opening-words > :nth-child(2) { animation-delay: 80ms; }
.opening-words > :nth-child(3) { animation-delay: 160ms; }
.opening-words > :nth-child(4) { animation-delay: 240ms; }
.opening-words > :nth-child(5) { animation-delay: 320ms; }
/* The line above the headline is the website's eyebrow (colors_and_type.css .wl-eyebrow): small,
   spaced capitals. It is drawn in the dimmed bone rather than the eyebrow's grey, because a long
   one ('Kestrel 3PL: your bid, due tomorrow') runs under the glow on a phone, where the grey falls
   below 4.5:1. */
.opening-kicker { color: var(--fg-dim); font-size: 12px; font-weight: 600; line-height: 1.5; letter-spacing: var(--tr-mark); text-transform: uppercase; }
.opening-title { margin-top: var(--s-3); max-width: 17ch; font-size: var(--t-display); line-height: 1.04; letter-spacing: var(--tr-tight); text-wrap: balance; }
.opening-title .line { display: block; }
/* The website's strapline gradient as the type fill (colors_and_type.css .wl-strapline). The line
   is inline and each wrapped line is drawn as its own box (box-decoration-break: clone), so every
   line runs the whole blue-violet-pink gradient across its own words, as the website's centred
   lines do. The line before it is a block, so the gradient line still starts on a line of its own. */
.opening-title .line--when {
  display: inline;
  padding-bottom: 0.06em;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  background: var(--grad-strapline);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .opening-title .line--when { background: none; color: var(--wl-mid); -webkit-text-fill-color: currentColor; }
}
/* Forced colours drop background images, which would leave the deadline invisible: draw it in the
   system's text colour instead, and leave out the decoration. */
@media (forced-colors: active) {
  .opening-title .line--when { background: none; color: CanvasText; -webkit-text-fill-color: CanvasText; }
  .opening::before, .opening::after, .opening-inner::before { content: none; }
}
/* .opening--quiet once stepped the headline back for a page whose purple button was its one purple
   moment. Purple is no longer rationed (Trent, 16 October 2026), so a quiet opening's headline is
   drawn as every other: bone, then the strapline gradient. Screens may still pass the class. */
.opening-lead { margin-top: var(--s-4); max-width: 54ch; color: var(--fg-dim); font-size: clamp(17px, 1.5vw, 19px); line-height: 1.5; text-wrap: pretty; }
.opening-lead strong { color: var(--fg); }
/* The person's next step, with its button, inside the opening so it is seen first. */
.opening-next { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4) var(--s-6); max-width: 680px; margin-top: var(--s-6); padding-top: var(--s-5); border-top: 1px solid var(--border-strong); }
.opening-next p { flex: 1 1 280px; min-width: 0; overflow-wrap: anywhere; color: var(--fg); font-size: 17px; font-weight: 500; line-height: 1.4; text-wrap: pretty; }
.opening-next p strong { font-weight: 700; }
.opening-action { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3) var(--s-5); margin-top: var(--s-6); }
.opening-action .aside { color: var(--fg-muted); font-size: var(--t-small); }
.opening .tracker-wrap { margin-top: var(--s-8); }
@media (min-width: 720px) { .opening { padding: var(--s-9) 0 var(--s-8); } }
@media (min-width: 1020px) { .opening { padding: var(--s-10) 0 var(--s-8); } .opening .tracker-wrap { margin-top: var(--s-9); } }

/* ---- Tracker: the eight stages ----------------------------------------------------------------
   Above the line, in words: which stage of how many, and what comes next, with a key that says
   what each shape means. Done stops are bone with a tick, the current stop is purple, stops ahead
   are open rings, and a stop where Serious Tissues decides is a diamond. The line draws to the
   current stop as the page opens. frame.mjs builds it and sets --progress. */
.tracker-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--s-3) var(--s-7); margin-bottom: var(--s-5); }
.tracker-now { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px var(--s-4); color: var(--fg-muted); font-size: var(--t-small); }
.tracker-now strong { color: var(--fg); font-size: 15px; font-weight: 700; }
.tracker-key { display: flex; flex-wrap: wrap; gap: 6px var(--s-5); margin: 0; padding: 0; list-style: none; color: var(--fg-muted); font-size: var(--t-caption); }
.tracker-key li { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.tracker-key i { display: inline-block; flex: none; width: 12px; height: 12px; border: 1.5px solid rgba(242, 242, 237, 0.6); border-radius: 50%; }
.tracker-key .key-done { border-color: var(--wl-bone); background: var(--wl-bone); }
.tracker-key .key-now { border: 2px solid var(--wl-bone); background: var(--wl-purple-2); }
.tracker-key .key-gate { width: 10px; height: 10px; margin: 0 1px; border-radius: 2px; transform: rotate(45deg); }

.tracker { --node: 26px; --progress: 0; position: relative; display: grid; grid-template-columns: repeat(var(--stages, 8), minmax(0, 1fr)); margin: 0; padding: 0; list-style: none; }
.tracker::before, .tracker::after {
  content: "";
  position: absolute;
  top: calc(var(--node) / 2 - 1px);
  left: calc(100% / var(--stages, 8) / 2);
  right: calc(100% / var(--stages, 8) / 2);
  height: 2px;
  border-radius: 2px;
}
.tracker::before { background: rgba(242, 242, 237, 0.2); }
.tracker::after { background: var(--wl-bone); transform: scaleX(var(--progress)); transform-origin: 0 50%; transition: transform 1200ms var(--ease) 200ms; }
.tracker-stage { position: relative; display: grid; justify-items: center; align-content: start; gap: 10px; min-width: 0; text-align: center; }
.tracker-node {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: var(--node);
  height: var(--node);
  border: 1.5px solid rgba(242, 242, 237, 0.5);
  border-radius: 50%;
  background: var(--wl-ink);
  color: var(--wl-ink);
  transition: background 400ms var(--ease), border-color 400ms var(--ease), transform 500ms var(--ease);
}
.tracker-node .icon { width: 14px; height: 14px; stroke-width: 2.5; opacity: 0; transition: opacity 300ms var(--ease) 150ms; }
.tracker-stage[data-position="done"] .tracker-node { border-color: var(--wl-bone); background: var(--wl-bone); }
.tracker-stage[data-position="done"] .tracker-node .icon { opacity: 1; }
.tracker-stage[data-position="current"] .tracker-node { border: 2px solid var(--wl-bone); background: var(--wl-purple-2); box-shadow: 0 0 0 4px var(--wl-ink); }
.tracker-stage[data-gate] .tracker-node { border-radius: 5px; transform: rotate(45deg) scale(0.86); }
.tracker-stage[data-gate] .tracker-node .icon { transform: rotate(-45deg); }
.tracker-label { color: var(--fg-muted); font-size: var(--t-caption); font-weight: 500; line-height: 1.25; }
.tracker-date { display: block; margin-top: 2px; color: var(--fg-muted); font-size: var(--t-micro); font-weight: 400; font-variant-numeric: tabular-nums; }
.tracker-stage[data-position="current"] .tracker-label { color: var(--fg); font-weight: 700; }
/* A quiet tracker: the current stop is bone on ink, not purple. */
.tracker-wrap--quiet .tracker-key .key-now,
.tracker-wrap--quiet .tracker-stage[data-position="current"] .tracker-node { background: var(--wl-ink); }
.tracker-stage[data-position="current"] .tracker-date { color: var(--fg-dim); }
/* On a phone, and on a tablet narrower than 900px (where nine stops leave under 90px each and
   'Recommend' would run into 'Award'), the words above the line name the stage, so the stops stand
   alone; every name stays for screen readers. */
@media (max-width: 899px) {
  .tracker-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
}
@media (max-width: 719px) {
  .tracker { --node: 22px; }
  .tracker-head { display: grid; gap: var(--s-3); }
  .tracker-now { display: grid; gap: 2px; }
}
@media (min-width: 720px) { .tracker { --node: 28px; } }
@media (min-width: 1020px) { .tracker-label { font-size: var(--t-small); } }

/* ---- Figures: one dominant figure, then the supporting ones -----------------------------------
   No tiles. The lead figure is visibly largest, with its comparison drawn as two bars from zero.
   The supporting figures are a ruled list: label and detail on the left, the figure on the right.
   From a laptop up the list stands in a column beside the lead figure, so no cell is left empty. */
.figures { display: grid; grid-template-columns: minmax(0, 1fr); }
.figure { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 2px var(--s-5); min-width: 0; padding: var(--s-4) 0; border-top: 1px solid var(--border); }
.figure:not(.figure--lead) > .figure-label { grid-column: 1; }
.figure:not(.figure--lead) > .figure-note { grid-column: 1; }
.figure:not(.figure--lead) > .figure-value { grid-column: 2; grid-row: 1 / span 2; justify-self: end; margin: 0; }
.figure--lead { display: block; padding: 0 0 var(--s-6); border-top: 0; }
.figure-label { color: var(--fg-muted); font-size: var(--t-small); font-weight: 500; line-height: 1.35; }
.figure-value { margin-top: 6px; font-size: var(--t-figure); font-weight: 700; line-height: 1.05; letter-spacing: -0.02em; white-space: nowrap; }
.figure-value small { margin-left: 4px; color: var(--fg-muted); font-size: 0.5em; font-weight: 500; letter-spacing: 0; }
.figure-note { margin-top: 4px; color: var(--fg-muted); font-size: var(--t-caption); line-height: 1.45; }
.figure-note .status { font-size: var(--t-caption); }
.figure--lead .figure-label { color: var(--fg-dim); font-size: 15px; }
.figure--lead .figure-value { margin-top: var(--s-2); font-size: var(--t-hero); line-height: 0.95; letter-spacing: -0.04em; }
.figure--lead .figure-note { margin-top: var(--s-2); }
.figure-compare { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin-top: var(--s-3); color: var(--fg-dim); font-size: 15px; }
.figure-bars { max-width: 560px; margin-top: var(--s-5); }
@media (min-width: 1020px) {
  .figures { grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); column-gap: 0; align-items: stretch; }
  .figure--lead { grid-row: span 3; padding: 0 var(--s-9) 0 0; }
  .figures[data-supporting="2"] .figure--lead { grid-row: span 2; }
  .figures[data-supporting="4"] .figure--lead { grid-row: span 4; }
  .figure:not(.figure--lead) { grid-column: 2; padding: var(--s-5) 0 var(--s-5) var(--s-7); border-left: 1px solid var(--border-strong); }
  .figure--lead + .figure { padding-top: 0; border-top: 0; }
}

/* A change, in words, with its direction drawn beside it. Colour only on the icon. */
.delta { display: inline-flex; align-items: center; gap: 6px; color: var(--fg); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.delta .icon { width: 18px; height: 18px; }
.delta[data-tone="good"] .icon { color: var(--good); }
.delta[data-tone="attention"] .icon { color: var(--attention); }
.delta[data-tone="failed"] .icon { color: var(--failed); }

/* ---- Action list: what to do next, ranked ------------------------------------------------------ */
.actions-list { margin: 0; padding: 0; list-style: none; counter-reset: action; border-top: 1px solid var(--border-strong); }
.action {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  gap: 6px var(--s-4);
  align-items: start;
  padding: var(--s-5) 0;
  border-bottom: 1px solid var(--border);
  counter-increment: action;
}
.action::before {
  content: counter(action);
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 1.5px solid var(--border-strong);
  border-radius: 50%;
  color: var(--fg);
  font-size: var(--t-caption);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.action:first-child::before { border-color: var(--wl-bone); background: var(--wl-bone); color: var(--wl-ink); }
/* A second list that carries on the numbering of the list before it (a sibling, with a heading
   between them if need be): its first item is not the first thing to do, so it is not filled. */
.actions-list--continued { counter-reset: none; }
.actions-list--continued .action:first-child::before { border-color: var(--border-strong); background: none; color: var(--fg); }
.actions-list--continued .action[data-state="later"]:first-child::before { border-style: dashed; }
.action-words { min-width: 0; padding-top: 2px; }
.action-what { color: var(--fg); font-size: 17px; font-weight: 600; line-height: 1.35; overflow-wrap: anywhere; }
/* The details under an action, one item per span, a middle dot between them. Items wrap whole, and
   the dot sits before each item in a strip clipped off the left edge, so a line never starts with
   a dot. */
.action-meta { display: flex; flex-wrap: wrap; gap: 4px 0; margin: 4px 0 0 -17px; color: var(--fg-muted); font-size: var(--t-small); clip-path: inset(-4px -4px -4px 17px); }
.action-meta > * { position: relative; min-width: 0; padding-left: 17px; overflow-wrap: anywhere; }
.action-meta > *::before { content: "\00B7"; position: absolute; left: 4px; width: 9px; color: var(--fg-muted); text-align: center; }
.action-side { grid-column: 2; display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3) var(--s-5); }
.action-value { color: var(--fg); font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.action-when { color: var(--fg-muted); font-size: var(--t-small); font-variant-numeric: tabular-nums; white-space: nowrap; }
.action[data-state="later"] .action-what { color: var(--fg-dim); font-weight: 500; }
.action[data-state="later"]::before { border-style: dashed; }
@media (min-width: 720px) {
  .action { grid-template-columns: 28px minmax(0, 1fr) auto; align-items: center; }
  .action-side { grid-column: 3; justify-content: flex-end; }
  .action-value, .action-when { min-width: 7ch; text-align: right; }
}

/* ---- Tables ------------------------------------------------------------------------------------
   Hairline rows, the first column a readable name, numbers right-aligned in tabular figures, a
   header that stays in view, sortable columns. .data--stack turns rows into a list on a phone. */
.table-wrap { position: relative; max-width: 100%; overflow-x: auto; scrollbar-width: thin; scrollbar-color: var(--wl-grey-700) transparent; }
.data { width: 100%; border-collapse: collapse; font-size: var(--t-table); line-height: 1.4; }
.data caption { padding-bottom: var(--s-3); color: var(--fg-muted); font-size: var(--t-small); text-align: left; caption-side: top; }
.data th, .data td { padding: 14px var(--s-5) 14px 0; border-bottom: 1px solid var(--border); text-align: left; vertical-align: middle; }
.data th:last-child, .data td:last-child { padding-right: 0; }
/* A right-aligned figure followed by left-aligned words gets extra room, so the two never touch. */
.data th.num + th:not(.num), .data td.num + td:not(.num) { padding-left: var(--s-6); }
/* .fit columns take only the width their content needs; the rest goes to the words. */
.data .fit { width: 1%; white-space: nowrap; }
.data .col-name { width: 20%; }
.data thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  padding-top: 10px;
  padding-bottom: 10px;
  border-bottom-color: var(--border-strong);
  background: var(--bg);
  color: var(--fg-muted);
  font-size: var(--t-caption);
  font-weight: 600;
  line-height: 20px;
  vertical-align: bottom;
  white-space: nowrap;
}
.data tbody th { color: var(--fg); font-weight: 600; }
.data .num, .data td.num, .data th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.data td.sub { color: var(--fg-muted); font-size: var(--t-small); }
.data tbody tr { position: relative; transition: background var(--t-state); }
.data tbody tr:hover { background: rgba(255, 255, 255, 0.025); }
.data tbody tr[aria-selected="true"] { background: rgba(81, 112, 255, 0.1); }
.data tfoot td, .data tfoot th { border-top: 1px solid var(--border-strong); border-bottom: 0; color: var(--fg); font-weight: 600; }
.row-link { color: var(--fg); font-weight: 600; text-decoration: none; }
.row-link::after { content: ""; position: absolute; inset: 0; }
.row-link:hover { color: var(--fg); text-decoration: underline; }
.row-link:focus-visible { outline: none; }
.row-link:focus-visible::after { outline: 2px solid var(--wl-purple-bright); outline-offset: -2px; border-radius: var(--r-xs); }
.row-sub { display: block; margin-top: 2px; color: var(--fg-muted); font-size: var(--t-caption); font-weight: 400; }
.data .cell-note { display: block; min-width: 15ch; margin-top: 2px; color: var(--fg-muted); font-size: var(--t-caption); white-space: normal; overflow-wrap: anywhere; }
/* Lines under a cell's value (a scorer's reasons, a note): beneath it in the table, across the
   card on a phone. Pair it with a .cell holding the value. */
.data .cell-after { display: grid; gap: 2px; margin-top: 4px; color: var(--fg-muted); font-size: var(--t-caption); font-weight: 400; line-height: 1.45; white-space: normal; overflow-wrap: anywhere; }
.data .cell-after:empty { display: none; }
/* Every sortable header has its arrow in the same place: straight after the words. */
.sort {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 20px;
  margin: 0;
  padding: 0;
  line-height: 20px;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.sort .icon { width: 14px; height: 14px; color: var(--fg-faint); }
.sort:hover { color: var(--fg); }
th[aria-sort="ascending"] .sort, th[aria-sort="descending"] .sort { color: var(--fg); }
th[aria-sort="ascending"] .sort .icon, th[aria-sort="descending"] .sort .icon { color: var(--fg); }
.inline-bar { display: block; height: 4px; margin-top: 6px; border-radius: 2px; background: var(--series-1); transform-origin: 0 50%; }
@media (max-width: 719px) {
  .data--stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  /* On a phone each row becomes a card: a group that must stand apart from its neighbours. */
  .data--stack, .data--stack tbody, .data--stack tr, .data--stack th, .data--stack td { display: block; }
  .data--stack tbody { display: grid; gap: var(--s-3); }
  .data--stack tbody tr { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); gap: 8px 16px; padding: var(--s-4); border: 1px solid var(--border-strong); border-radius: var(--r-lg); background: rgba(255, 255, 255, 0.03); }
  .data--stack tbody th, .data--stack tbody td { padding: 0; border: 0; }
  .data--stack tbody th { grid-column: 1 / -1; padding-bottom: var(--s-3); margin-bottom: 4px; border-bottom: 1px solid var(--border); font-size: 16px; }
  .data--stack td.num + td:not(.num) { padding-left: 0; }
  .data--stack tbody td { display: contents; }
  .data--stack tbody td::before { content: attr(data-label); color: var(--fg-muted); font-size: var(--t-small); text-align: left; }
  .data--stack tbody td .status { white-space: normal; text-align: left; }
  .data--stack tbody td > * , .data--stack tbody td > .cell { justify-self: end; text-align: right; }
  .data--stack tbody td.num { white-space: normal; }
  /* Low specificity, so a screen's own rule for its cells wins. */
  :where(.data--stack tbody td) .cell { display: block; color: var(--fg); font-size: var(--t-small); }
  /* The caption stays a heading above the cards, not squeezed into the table's first column. */
  .data--stack caption { display: block; text-align: left; }
  /* A cell whose words need the whole width of the card (a reason, a note). */
  .data--stack td.cell-wide::before, .data--stack td.cell-wide > * { grid-column: 1 / -1; }
  .data--stack td.cell-wide > * { justify-self: start; text-align: left; }
  .data--stack tbody td > .cell-after { grid-column: 1 / -1; justify-self: stretch; margin-top: -4px; text-align: left; }
}

/* ---- Status: a drawn mark and plain words -------------------------------------------------------
   fern = passed or good, citron = needs attention, red = a failed check only, grey dashed ring =
   waiting or not started. The words are always there. */
.status { display: inline-flex; align-items: center; gap: 8px; color: var(--fg-dim); font-size: var(--t-small); font-weight: 500; line-height: 1.3; white-space: nowrap; }
.status .icon { width: 18px; height: 18px; color: var(--waiting); }
.status--wrap { white-space: normal; align-items: flex-start; }
.status--wrap .icon { margin-top: 0; }
.status[data-tone="good"] { color: var(--fg); }
.status[data-tone="good"] .icon { color: var(--good); }
.status[data-tone="attention"] { color: var(--fg); }
.status[data-tone="attention"] .icon { color: var(--attention); }
.status[data-tone="failed"] { color: var(--failed-fg); }
.status[data-tone="failed"] .icon { color: var(--failed); }
.status[data-tone="current"] { color: var(--fg); }
/* In progress is not a brand moment: purple stays for the brand and focus rings, not for status. */
.status[data-tone="current"] .icon { color: var(--fg); }
.status[data-tone="waiting"] { color: var(--fg-muted); }
.status[data-tone="neutral"] .icon { color: var(--fg-muted); }

/* ---- Buttons ------------------------------------------------------------------------------------
   The primary button is the website's .btn--primary: Woodlark purple with its soft purple glow,
   lifting to the brighter purple on hover. It carries each opening's next step. Bone is kept for a
   main action inside a working area (a form's save, an approval), so a list of them never glows.
   Everything else is the quiet, outlined kind. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 48px;
  padding: 14px 22px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: var(--wl-purple);
  box-shadow: var(--shadow-glow);
  color: var(--wl-bone);
  font: 600 15px/1.2 var(--font);
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--t-state) var(--ease-state), border-color var(--t-state) var(--ease-state), color var(--t-state) var(--ease-state), transform 80ms;
}
.btn:hover { background: var(--wl-purple-2); color: var(--wl-bone); }
.btn:active { background: var(--wl-purple-3); transform: scale(0.98); }
.btn:focus-visible { border-radius: var(--r-md); }
.btn .icon { width: 18px; height: 18px; }
.btn--bone { background: var(--wl-bone); box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35); color: var(--wl-ink); }
.btn--bone:hover { background: #FFFFFF; color: var(--wl-ink); }
.btn--bone:active { background: #E6E4DC; }
.btn--quiet { background: transparent; border-color: var(--border-strong); box-shadow: none; color: var(--fg); }
.btn--quiet:hover { background: var(--wash); border-color: rgba(255, 255, 255, 0.3); color: var(--fg); }
.btn--quiet:active { background: var(--wash); }
.btn--small { min-height: 36px; padding: 6px 14px; border-radius: var(--r-pill); font-size: var(--t-caption); }
.btn--small:focus-visible { border-radius: var(--r-pill); }
.btn--small .icon { width: 16px; height: 16px; }
.btn--text { min-height: 44px; padding: 0 2px; border: 0; background: none; box-shadow: none; color: var(--fg); text-decoration: underline; text-decoration-color: rgba(242, 242, 237, 0.4); text-underline-offset: 4px; }
.btn--text:hover { background: none; color: var(--wl-violet-2); text-decoration-color: currentColor; }
.btn--text:active { background: none; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; transform: none; }
.btn[aria-busy="true"] { cursor: progress; }
.btn[aria-busy="true"] .icon { animation: turn 1s linear infinite; }
@keyframes turn { to { transform: rotate(360deg); } }
.buttons { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); }
@media (max-width: 559px) { .buttons--stack-phone > .btn { width: 100%; } }

/* ---- Forms ---------------------------------------------------------------------------------- */
.form { display: grid; gap: var(--s-6); align-content: start; max-width: 640px; }
.field { display: grid; gap: 6px; align-content: start; justify-items: stretch; min-width: 0; }
.field > .segmented, .field > .input-money { justify-self: start; }
.field-label, .form legend { color: var(--fg); font-size: 15px; font-weight: 600; line-height: 1.35; }
.field-label .optional { margin-left: 6px; color: var(--fg-muted); font-weight: 400; }
.field-help { max-width: 60ch; color: var(--fg-muted); font-size: var(--t-small); line-height: 1.45; }
/* A field that needs fixing is citron, as anything needing attention is; red stays for failed checks. */
.field-error { display: flex; align-items: center; gap: 8px; min-width: 0; color: var(--fg); font-size: var(--t-small); font-weight: 500; overflow-wrap: anywhere; }
.field-error > :not(.icon) { min-width: 0; }
.field-error .icon { width: 16px; height: 16px; color: var(--attention); }
.input, .select, .textarea {
  width: 100%;
  min-height: 48px;
  padding: 11px 14px;
  border: 1px solid var(--border-field);
  border-radius: var(--r-md);
  background: var(--bg-field);
  color: var(--fg);
  font: 400 16px/1.4 var(--font);
  transition: border-color var(--t-state), background var(--t-state);
}
.input:hover, .select:hover, .textarea:hover { border-color: rgba(255, 255, 255, 0.36); }
.input:focus-visible, .select:focus-visible, .textarea:focus-visible { border-color: var(--wl-purple-bright); border-radius: var(--r-md); background-color: rgba(255, 255, 255, 0.07); outline-offset: 2px; }
/* A field that cannot be changed now (a closed stage, a submitted bid) looks it, not just behaves it. */
.input:disabled, .select:disabled, .textarea:disabled { border-style: dashed; border-color: var(--border-strong); background-color: transparent; color: var(--fg-muted); cursor: not-allowed; opacity: 1; -webkit-text-fill-color: var(--fg-muted); }
.input:disabled:hover, .select:disabled:hover, .textarea:disabled:hover { border-color: var(--border-strong); }
.input:disabled::placeholder, .textarea:disabled::placeholder { color: var(--fg-faint); -webkit-text-fill-color: var(--fg-faint); }
.select:disabled { cursor: not-allowed; }
.input[aria-invalid="true"], .select[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--attention); }
.input[readonly] { border-style: dashed; color: var(--fg-dim); }
.input::placeholder, .textarea::placeholder { color: var(--fg-faint); opacity: 1; }
.input--num { font-variant-numeric: tabular-nums; text-align: right; }
.input--short { max-width: 220px; }
.textarea { min-height: 120px; line-height: 1.55; resize: vertical; }
.select {
  -webkit-appearance: none;
  appearance: none;
  padding-right: 44px;
  background-image: linear-gradient(45deg, transparent 50%, var(--wl-bone) 50%), linear-gradient(135deg, var(--wl-bone) 50%, transparent 50%);
  background-position: calc(100% - 22px) 52%, calc(100% - 16px) 52%;
  background-size: 6px 6px;
  background-repeat: no-repeat;
  cursor: pointer;
}
.select option { background: var(--wl-ink-3); color: var(--wl-bone); }
/* A money field: the pound sign sits inside the field, never typed. */
.input-money { position: relative; display: flex; align-items: center; max-width: 240px; }
.input-money .input-prefix { position: absolute; left: 14px; color: var(--fg-muted); font-size: 16px; font-weight: 500; pointer-events: none; }
.input-money .input { padding-left: 32px; font-variant-numeric: tabular-nums; text-align: right; }
.input-money .input-suffix { margin-left: 10px; color: var(--fg-muted); font-size: var(--t-small); white-space: nowrap; }
.input-money--wide { max-width: 360px; }
/* Segmented control: radio buttons in the capsule. The chosen one lifts, it does not turn purple. */
.segmented { display: inline-flex; flex-wrap: wrap; gap: 2px; max-width: 100%; margin: 0; padding: 4px; border: 1px solid var(--border-strong); border-radius: var(--r-pill); background: rgba(255, 255, 255, 0.03); min-width: 0; }
.segmented legend { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.segmented label { position: relative; display: inline-flex; }
.segmented input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.segmented span {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 16px;
  border-radius: var(--r-pill);
  color: var(--fg-muted);
  font-size: var(--t-small);
  font-weight: 500;
  white-space: nowrap;
  transition: background var(--t-state), color var(--t-state);
}
.segmented label:hover span { color: var(--fg); }
.segmented input:checked + span { background: var(--wl-bone); color: var(--wl-ink); font-weight: 600; }
.segmented input:focus-visible + span { outline: 2px solid var(--wl-purple-bright); outline-offset: 2px; }
/* Checkbox and radio: a drawn box, bone when chosen. */
.check { display: flex; align-items: flex-start; gap: 12px; color: var(--fg-dim); font-size: 15px; line-height: 1.5; cursor: pointer; }
.check input {
  -webkit-appearance: none;
  appearance: none;
  flex: none;
  display: inline-grid;
  place-content: center;
  width: 22px;
  height: 22px;
  margin: 1px 0 0;
  border: 1.5px solid var(--border-field);
  border-radius: 6px;
  background: var(--bg-field);
  cursor: pointer;
  transition: background var(--t-state), border-color var(--t-state);
}
.check input[type="radio"] { border-radius: 50%; }
.check input::before { content: ""; width: 10px; height: 6px; margin-top: -3px; border: solid var(--wl-ink); border-width: 0 0 2px 2px; transform: rotate(-45deg) scale(0); transition: transform 120ms var(--ease); }
.check input[type="radio"]::before { width: 8px; height: 8px; margin: 0; border: 0; border-radius: 50%; background: var(--wl-ink); transform: scale(0); }
.check input:checked { border-color: var(--wl-bone); background: var(--wl-bone); }
.check input:checked::before { transform: rotate(-45deg) scale(1); }
.check input[type="radio"]:checked::before { transform: scale(1); }
.check input:hover { border-color: rgba(255, 255, 255, 0.5); }
.check input:focus-visible { border-radius: 6px; }
.check strong { color: var(--fg); }
.choices { display: grid; gap: var(--s-2); margin: 0; padding: 0; border: 0; min-width: 0; }
.choices legend { margin-bottom: var(--s-2); padding: 0; }
.choice { padding: 14px 16px; border: 1px solid var(--border-strong); border-radius: var(--r-md); transition: border-color var(--t-state), background var(--t-state); }
.choice:has(input:checked) { border-color: rgba(242, 242, 237, 0.55); background: var(--wash); }
.field-row { display: grid; gap: var(--s-5); }
@media (min-width: 720px) { .field-row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ---- Timeline: what moved, newest first --------------------------------------------------------- */
.timeline { position: relative; margin: 0; padding: 0; list-style: none; }
.timeline::before { content: ""; position: absolute; left: 4px; top: 10px; bottom: 10px; width: 1px; background: var(--border-strong); }
.timeline li { position: relative; display: grid; grid-template-columns: minmax(0, 1fr); gap: 0; padding: 0 0 var(--s-6) 28px; }
.timeline li:last-child { padding-bottom: 0; }
.timeline li::before { content: ""; position: absolute; left: 0; top: 7px; width: 9px; height: 9px; border: 1.5px solid rgba(242, 242, 237, 0.4); border-radius: 50%; background: var(--bg); }
.timeline li:first-child::before { border-color: var(--wl-bone); background: var(--wl-bone); }
.timeline time { color: var(--fg-muted); font-size: var(--t-caption); font-variant-numeric: tabular-nums; }
.timeline p { color: var(--fg); font-size: 15px; line-height: 1.45; }
.timeline .who { margin-top: 2px; color: var(--fg-muted); font-size: var(--t-caption); line-height: 1.4; }
/* From a tablet up the day sits in its own column; the item's words and its detail stay tight
   together, and the space goes between items. */
@media (min-width: 560px) {
  .timeline li { grid-template-columns: 56px minmax(0, 1fr); column-gap: var(--s-4); row-gap: 0; align-items: baseline; }
  .timeline time { grid-row: 1; }
  .timeline p { grid-column: 2; grid-row: 1; }
  .timeline .who { grid-column: 2; grid-row: 2; }
}

/* ---- Steps: the bidder's route to a submitted bid -----------------------------------------------
   Numbered stops down a line that fills, as the person scrolls, as far as the step they are on.
   The current step opens into the one panel on the page. */
.steps-wrap { --node: 32px; position: relative; }
.steps { position: relative; margin: 0; padding: 0; list-style: none; counter-reset: step; }
.steps-rail { position: absolute; left: calc(var(--node) / 2 - 1px); top: calc(var(--node) / 2); bottom: calc(var(--node) / 2); width: 2px; background: rgba(242, 242, 237, 0.14); pointer-events: none; }
.steps-fill { position: absolute; inset: 0; border-radius: 2px; background: var(--wl-bone); transform: scaleY(var(--fill, 0)); transform-origin: 50% 0; transition: transform 200ms linear; }
.step { position: relative; display: grid; grid-template-columns: var(--node) minmax(0, 1fr); gap: 0 var(--s-4); padding: 0 0 var(--s-6); counter-increment: step; }
.step:last-child { padding-bottom: 0; }
.step-node {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: var(--node);
  height: var(--node);
  border: 1.5px solid rgba(242, 242, 237, 0.32);
  border-radius: 50%;
  background: var(--bg);
  color: var(--fg-muted);
  font-size: var(--t-caption);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  transition: background 400ms var(--ease), border-color 400ms var(--ease), color 400ms var(--ease);
}
.step-node::before { content: counter(step); }
.step-node .icon { display: none; width: 16px; height: 16px; stroke-width: 2.5; }
.step[data-position="done"] .step-node { border-color: var(--wl-bone); background: var(--wl-bone); color: var(--wl-ink); }
.step[data-position="done"] .step-node::before { content: none; }
.step[data-position="done"] .step-node .icon { display: block; }
.step[data-position="current"] .step-node { border: 2px solid var(--wl-bone); background: var(--wl-ink); color: var(--wl-bone); box-shadow: 0 0 0 5px rgba(242, 242, 237, 0.12); }
.step--final .step-node::before { content: none; }
.step--final .step-node { border-style: dashed; }
.step--final .step-node .icon { display: block; color: var(--fg-muted); stroke-width: 1.75; }
.step-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: 4px 16px; align-items: baseline; min-width: 0; padding-top: 4px; }
.step-title { font-size: 17px; font-weight: 600; line-height: 1.35; letter-spacing: -0.005em; }
.step-title a { text-decoration: none; }
.step-title a:hover { color: var(--fg); text-decoration: underline; }
.step-meta { color: var(--fg-muted); font-size: var(--t-small); }
.step[data-position="ahead"] .step-title { color: var(--fg-dim); font-weight: 500; }
/* The step the bidder is on is the page's one needs-you callout, so it takes the website's dark
   callout card (site.css .cta): elevated ink with two soft blooms, purple from the top left and blue
   from the bottom right, each gone by 60% of its reach so the words sit on plain ink-3. */
.step[data-position="current"] .step-body {
  gap: var(--s-2) var(--s-4);
  margin-top: -14px;
  padding: var(--s-5) var(--s-5) var(--s-6);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  background:
    radial-gradient(60% 80% at 20% 0%, rgba(176, 107, 223, 0.30) 0%, transparent 60%),
    radial-gradient(50% 80% at 80% 100%, rgba(81, 112, 255, 0.30) 0%, transparent 60%),
    var(--wl-ink-3);
  box-shadow: var(--lit-edge), 0 24px 48px -28px rgba(0, 0, 0, 0.7);
}
.step[data-position="current"] .step-title { font-size: clamp(20px, 2vw, 24px); font-weight: 700; letter-spacing: var(--tr-snug); }
.step-detail { display: grid; gap: var(--s-4); margin-top: var(--s-3); }
.step-detail .buttons { margin-top: var(--s-2); }
@media (min-width: 720px) {
  .steps-wrap { --node: 36px; }
  .step-body { grid-template-columns: minmax(0, 1fr) auto; }
  .step-body > .status { grid-column: 2; grid-row: 1; justify-self: end; }
  .step-body > .step-meta { grid-column: 1; }
  .step-detail { grid-column: 1 / -1; }
  .step[data-position="current"] .step-body { padding: var(--s-6) var(--s-7) var(--s-7); }
}

/* ---- Meter: progress to a known total (bullet form; never a gauge) ------------------------------- */
.meter { display: grid; gap: 8px; max-width: 520px; }
.meter-track { position: relative; height: 6px; border-radius: var(--r-pill); background: rgba(242, 242, 237, 0.12); overflow: hidden; }
.meter-fill { position: absolute; inset: 0 auto 0 0; width: 100%; border-radius: var(--r-pill); background: var(--wl-bone); transform: scaleX(var(--value, 0)); transform-origin: 0 50%; transition: transform 900ms var(--ease) 150ms; }
.meter-words { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 16px; color: var(--fg-muted); font-size: var(--t-small); }
.meter-words strong { color: var(--fg); font-variant-numeric: tabular-nums; }

/* ---- States: empty, loading, error -------------------------------------------------------------- */
.empty {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  display: grid;
  gap: var(--s-3);
  justify-items: start;
  align-content: start;
  padding: var(--s-7) var(--s-6);
  border: 1px dashed rgba(242, 242, 237, 0.24);
  border-radius: var(--r-lg);
}
.empty::after { content: ""; position: absolute; right: -40px; bottom: -60px; z-index: -1; width: 220px; height: 220px; background: var(--wl-bone); opacity: 0.045; -webkit-mask: var(--mark-image) center / contain no-repeat; mask: var(--mark-image) center / contain no-repeat; }
.empty > .icon { width: 24px; height: 24px; color: var(--fg-muted); }
.empty h3 { font-size: 19px; }
.empty p { max-width: 52ch; color: var(--fg-dim); font-size: 15px; }
.empty .buttons { margin-top: var(--s-2); }

@keyframes sweep { from { background-position: 120% 0; } to { background-position: -20% 0; } }
.skeleton { display: block; height: 14px; border-radius: var(--r-xs); background: linear-gradient(90deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0.05) 100%); background-size: 220% 100%; animation: sweep 2.4s var(--ease-state) infinite; }
.skeleton--figure { width: min(320px, 70%); height: 56px; border-radius: var(--r-sm); }
.skeleton--line { width: 100%; }
.skeleton--short { width: 40%; }
.skeleton--bar { height: 18px; border-radius: 0 var(--r-xs) var(--r-xs) 0; }
.loading { display: grid; gap: var(--s-3); }
.loading .skeleton:nth-child(2) { width: 82%; }
.loading .skeleton:nth-child(3) { width: 64%; }
.loading .skeleton:nth-child(4) { width: 48%; }
/* Refreshing keeps the last picture, dimmed, so nothing jumps. */
.is-refreshing { opacity: 0.55; transition: opacity var(--t-layout) var(--ease-state); pointer-events: none; }

.notice {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: var(--s-3) 12px;
  padding: 14px 18px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--wl-ink-3);
  color: var(--fg);
  font-size: 15px;
  line-height: 1.45;
}
.notice > .icon { width: 20px; height: 20px; margin-top: 1px; color: var(--good); }
.notice > :not(.icon):not(p) { grid-column: 2; justify-self: start; }
.notice:focus { outline: none; }
.notice[data-kind="error"] { border-color: rgba(230, 217, 67, 0.4); background: rgba(230, 217, 67, 0.06); }
.notice[data-kind="error"] > .icon { color: var(--attention); }
.notice[data-kind="failed"] { border-color: rgba(229, 72, 77, 0.5); background: rgba(229, 72, 77, 0.08); }
.notice[data-kind="failed"] > .icon { color: var(--failed); }
.notice[data-kind="info"] > .icon { color: var(--fg-muted); }
.notice--toast { position: fixed; left: 50%; bottom: 24px; z-index: 80; width: min(560px, calc(100vw - 2 * var(--gutter))); box-shadow: var(--shadow-lg); transform: translateX(-50%); animation: toast-in var(--t-layout) var(--ease) both; }
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, 8px); } to { opacity: 1; transform: translate(-50%, 0); } }

/* ---- Charts (shared/charts.mjs) -----------------------------------------------------------------
   Thin marks with rounded data ends, square at the zero line; a 2px gap in the surface colour
   between touching marks; hairline gridlines; labels in text colours, never the series colour.
   Every chart has a tooltip on hover and focus, and a table view. */
.chart { position: relative; min-width: 0; }
.chart-head { margin-bottom: var(--s-4); }
.chart-title { font-size: var(--t-h2); line-height: 1.18; letter-spacing: var(--tr-snug); }
.chart-sub { margin-top: 4px; color: var(--fg-muted); font-size: var(--t-small); }
.chart-plot { position: relative; width: 100%; }
.chart-svg { display: block; width: 100%; height: auto; overflow: visible; font-family: var(--font); }
.chart-svg text { fill: var(--fg-muted); font-size: 13px; font-variant-numeric: tabular-nums; }
.chart-svg .label { fill: var(--fg); font-size: 14px; font-weight: 500; }
.chart-svg .label--strong { font-weight: 700; }
.chart-svg .value { fill: var(--fg); font-size: 14px; font-weight: 600; }
.chart-svg .value-note { fill: var(--fg-muted); font-size: 12px; font-weight: 500; }
.chart-svg .tick { fill: var(--fg-faint); font-size: 12px; }
.chart-svg .grid { stroke: var(--grid); stroke-width: 1; shape-rendering: crispEdges; }
.chart-svg .baseline { stroke: var(--axis); stroke-width: 1; shape-rendering: crispEdges; }
.chart-svg .reference { stroke: var(--reference); stroke-width: 1.5; stroke-dasharray: 4 4; }
.chart-svg .reference-label { fill: var(--fg); font-size: 12px; font-weight: 600; }
.chart-svg .s1 { fill: var(--series-1); }
.chart-svg .s2 { fill: var(--series-2); }
.chart-svg .s3 { fill: var(--series-3); }
.chart-svg .s4 { fill: var(--series-4); }
.chart-svg .s-other { fill: var(--series-other); }
.chart-svg .line { fill: none; stroke: var(--series-1); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.chart-svg .area { fill: var(--series-1); opacity: 0.12; }
.chart-svg .dot { fill: var(--series-1); stroke: var(--bg); stroke-width: 2; }
.chart-empty { display: grid; place-items: center; min-height: 132px; padding: var(--s-5); border: 1px dashed var(--border-strong); border-radius: var(--r-md); color: var(--fg-muted); font-size: var(--t-small); text-align: center; }
.chart-svg .dot--hollow { fill: var(--bg); stroke: var(--series-1); }
.chart-svg .crosshair { stroke: rgba(242, 242, 237, 0.45); stroke-width: 1; shape-rendering: crispEdges; }
.chart-svg .hit { fill: transparent; cursor: pointer; }
.chart-svg .mark-group { outline: none; cursor: pointer; }
.chart-svg .mark-group .bar { transition: opacity var(--t-state), filter var(--t-state); }
.chart-svg .mark-group:hover .bar, .chart-svg .mark-group.is-hot .bar { filter: brightness(1.18); }
.chart-svg .mark-group:focus-visible .focus-ring { stroke: var(--wl-purple-bright); stroke-width: 2; fill: none; }
.chart-svg .focus-ring { fill: none; stroke: none; }
/* Rows not chosen step back: their bars fade, their words turn grey but stay readable (AA). */
.chart-svg.has-selection .mark-group:not(.is-selected) .bar { opacity: 0.6; }
.chart-svg.has-selection .mark-group:not(.is-selected) .label,
.chart-svg.has-selection .mark-group:not(.is-selected) .value { fill: var(--fg-muted); }
.chart-svg .mark-group.is-selected .label { font-weight: 700; }
.chart--small .chart-svg text { font-size: 12px; }
.chart-footnote { max-width: 70ch; margin-top: var(--s-3); color: var(--fg-muted); font-size: var(--t-caption); line-height: 1.45; }

/* Two figures as bars from zero, each named and valued in words, so nothing needs hovering
   (compareBars). The lead figure is the brand blue; what it is compared with is the grey. */
.pair-bars { display: grid; gap: var(--s-4); margin: 0; padding: 0; list-style: none; }
.pair-bar-words { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 2px 12px; font-size: var(--t-small); line-height: 1.35; }
.pair-bar-label { color: var(--fg-dim); font-weight: 500; }
.pair-bar-note { color: var(--fg-muted); font-weight: 400; }
.pair-bar-value { margin-left: auto; color: var(--fg); font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.pair-bar-track { display: block; height: 12px; margin-top: 6px; }
.pair-bar-fill { display: block; width: calc(var(--value, 0) * 100%); height: 100%; border-radius: 0 var(--r-xs) var(--r-xs) 0; background: var(--series-other); transform-origin: 0 50%; }
.pair-bar[data-tone="lead"] .pair-bar-label { color: var(--fg); font-weight: 600; }
.pair-bar[data-tone="lead"] .pair-bar-fill { background: var(--series-1); }

.legend { display: flex; flex-wrap: wrap; gap: 6px 18px; margin: 0 0 var(--s-4); padding: 0; list-style: none; color: var(--fg-dim); font-size: var(--t-caption); }
.legend li { display: inline-flex; align-items: center; gap: 8px; }
.legend i { display: inline-block; width: 12px; height: 12px; border-radius: 3px; background: var(--series-other); }
.legend .s1 { background: var(--series-1); }
.legend .s2 { background: var(--series-2); }
.legend .s3 { background: var(--series-3); }
.legend .s4 { background: var(--series-4); }
.legend .line-key { width: 16px; height: 0; border-top: 1.5px dashed var(--reference); border-radius: 0; background: none; }

.chart-tip {
  position: absolute;
  z-index: 20;
  left: 0;
  top: 0;
  min-width: 160px;
  max-width: 280px;
  padding: 10px 12px 11px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: rgba(31, 27, 38, 0.97);
  box-shadow: var(--shadow-lg), var(--lit-edge);
  color: var(--fg);
  font-size: var(--t-caption);
  line-height: 1.4;
  pointer-events: none;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 120ms var(--ease-state), transform 120ms var(--ease-state);
}
.chart-tip.is-shown { opacity: 1; transform: none; }
.chart-tip-title { color: var(--fg-muted); font-size: var(--t-micro); font-weight: 600; }
.chart-tip-value { margin-top: 2px; font-size: 18px; font-weight: 700; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
.chart-tip-rows { display: grid; gap: 3px; margin: 8px 0 0; padding: 8px 0 0; border-top: 1px solid var(--border); list-style: none; }
.chart-tip-rows li { display: grid; grid-template-columns: 14px minmax(0, 1fr) auto; gap: 8px; align-items: center; color: var(--fg-dim); }
.chart-tip-rows li b { color: var(--fg); font-weight: 600; font-variant-numeric: tabular-nums; text-align: right; }
.chart-tip-rows i { display: block; width: 14px; height: 2px; border-radius: 1px; background: var(--series-other); }
.chart-tip-rows .s1 { background: var(--series-1); }
.chart-tip-rows .s2 { background: var(--series-2); }
.chart-tip-rows .s3 { background: var(--series-3); }
.chart-tip-rows .s4 { background: var(--series-4); }
.chart-tip-rows .line-key { height: 0; border-top: 1.5px dashed var(--reference); border-radius: 0; background: none; }
.chart-tip-hint { margin-top: 6px; color: var(--fg-faint); font-size: var(--t-micro); }

.chart-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; margin-top: var(--s-3); }
.chart-filter { display: inline-flex; align-items: center; gap: 10px; color: var(--fg-dim); font-size: var(--t-caption); }
.chart-filter button { min-height: 32px; padding: 0 12px; border: 1px solid var(--border-strong); border-radius: var(--r-pill); background: none; color: var(--fg); font-size: var(--t-caption); font-weight: 600; cursor: pointer; }
.chart-filter button:hover { background: var(--wash); }
.chart-filter button:focus-visible { border-radius: var(--r-pill); }
.chart-table > summary { display: inline-flex; align-items: center; gap: 8px; min-height: 32px; color: var(--fg-muted); font-size: var(--t-caption); font-weight: 600; list-style: none; cursor: pointer; }
.chart-table > summary::-webkit-details-marker { display: none; }
.chart-table > summary:hover { color: var(--fg); }
.chart-table > summary .icon { width: 16px; height: 16px; }
/* A chart's table never widens the page: it scrolls inside its own box. width 0 keeps the table
   out of the chart's intrinsic width (so a grid or flex parent never grows to fit it) and
   min-width 100% fills the space the chart has. */
.chart-foot > * { min-width: 0; max-width: 100%; }
.chart-table[open] { flex-basis: 100%; }
.chart-table .table-wrap { width: 0; min-width: 100%; }
.chart-table .data { margin-top: var(--s-2); font-size: var(--t-small); }
.chart-table .data th, .chart-table .data td { padding-top: 8px; padding-bottom: 8px; }

/* ---- Motion -------------------------------------------------------------------------------------
   Movement is added by shared/motion.mjs only when it can run and the person has not asked for
   less. What is on screen when the page opens draws in once; what is further down is drawn in as
   the person scrolls to it: bars grow from the zero line, lines draw, the step line fills to
   where they are. A page that is never scrolled (a printout, a full-page capture) shows
   everything in its final state. */
.motion-reset, .motion-reset * { transition: none !important; }
.moving .pair-bars:not(.is-in) .pair-bar-fill { transform: scaleX(0); }
.moving .pair-bar-fill { transition: transform 900ms var(--ease); }
.moving .pair-bar + .pair-bar .pair-bar-fill { transition-delay: 120ms; }
.moving .chart:not(.is-in) .chart-svg .bar { transform: scaleX(0); }
.moving .chart--columns:not(.is-in) .chart-svg .bar { transform: scaleY(0); }
.moving .chart .chart-svg .bar { transform-box: fill-box; transform-origin: 0 50%; transition: transform 900ms var(--ease) var(--delay, 0ms), opacity var(--t-state), filter var(--t-state); }
.moving .chart--columns .chart-svg .bar { transform-origin: 50% 100%; }
.moving .chart .chart-svg .line:not(.line--partial) { stroke-dasharray: 1; transition: stroke-dashoffset 1200ms var(--ease) 150ms; }
.moving .chart:not(.is-in) .chart-svg .line:not(.line--partial) { stroke-dashoffset: 1; }
.moving .chart.is-in .chart-svg .line { stroke-dashoffset: 0; }
.moving .chart:not(.is-in) .chart-svg .line--partial { opacity: 0; }
.moving .chart .chart-svg .line--partial { transition: opacity 400ms var(--ease) 1100ms; }
.moving .chart:not(.is-in) .chart-svg :is(.value, .value-note, .dot, .area) { opacity: 0; }
.moving .chart .chart-svg :is(.value, .value-note, .dot, .area) { transition: opacity 400ms var(--ease) 650ms; }
.moving .tracker:not(.is-in)::after { transform: scaleX(0); }
.moving .tracker:not(.is-in) .tracker-stage[data-position="done"] .tracker-node { border-color: rgba(242, 242, 237, 0.36); background: var(--wl-ink); }
.moving .tracker:not(.is-in) .tracker-stage[data-position="done"] .tracker-node .icon { opacity: 0; }
.moving .tracker:not(.is-in) .tracker-stage[data-position="current"] .tracker-node { transform: scale(0.6); }
.moving .tracker.is-in .tracker-stage[data-position="done"] .tracker-node { transition-delay: calc(200ms + var(--i, 0) * 150ms); }
.moving .meter:not(.is-in) .meter-fill { transform: scaleX(0); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
}

/* ---- Print ------------------------------------------------------------------------------------- */
@media print {
  @page { margin: 16mm 14mm; }
  :root { color-scheme: light; --bg: #fff; --bg-elev: #fff; --fg: #000; --fg-dim: #222; --fg-muted: #444; --fg-faint: #555; --border: #bbb; --border-strong: #888; --grid: #ddd; --axis: #888; }
  html, body { background: #fff; color: #000; }
  .sections, .person, .skip, .btn, .chart-foot, .chart-tip { display: none !important; }
  .opening::before, .opening::after { content: none; }
  .opening { background: none; }
  .frame { border-bottom: 1px solid #000; }
  .opening-title .line--when { background: none; color: #000; -webkit-text-fill-color: #000; }
  .chart-svg text, .chart-svg .label, .chart-svg .value { fill: #000; }
}

/* ---- Brand on scroll and the foot (Trent, 16 October 2026) -------------------------------------
   Once the brand row has scrolled away, a small Woodlark mark and the client's logo lead the
   sticky row of sections, so both brands stay on screen down a long page (frame.mjs sets
   .is-stuck). The foot is the website's closing band (site.css .cta-band): deep plum with a blue
   bloom top left, a pink bloom bottom right and an oversized Woodlark mark at 5.5%. */
.sections-brand { display: none; }
@media (min-width: 720px) {
  .sections.is-stuck .sections-inner { display: flex; align-items: center; gap: var(--s-4); }
  .sections.is-stuck .sections-brand { display: flex; flex: none; align-items: center; gap: 12px; color: var(--fg); }
  .sections-brand .mark { width: 20px; height: 20px; }
  .sections-brand .client-logo { height: 12px; padding-left: 12px; border-left: 1px solid var(--border-strong); box-sizing: content-box; }
  .sections.is-stuck .pills { flex: 1 1 auto; min-width: 0; margin-left: 0; padding-left: 0; }
}
.foot {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border-top: 0;
  color: rgba(242, 242, 237, 0.78);
  background:
    radial-gradient(70% 120% at 12% 10%, rgba(81, 112, 255, 0.36), transparent 58%),
    radial-gradient(70% 110% at 88% 85%, rgba(255, 102, 196, 0.28), transparent 58%),
    var(--wl-purple-3);
}
.foot::after {
  content: "";
  position: absolute;
  z-index: 0;
  right: -12vw;
  bottom: -24vw;
  width: min(58vw, 760px);
  height: min(58vw, 760px);
  background: url("assets/logo-mark-white.svg") center / contain no-repeat;
  opacity: 0.055;
  pointer-events: none;
}
.foot-inner { position: relative; z-index: 1; padding-block: var(--s-8); }
.foot .mark { color: var(--wl-bone); }
/* The role picker: the client's logo above its own group of people. */
.client-logo--group { height: 18px; margin-bottom: var(--s-3); }

/* ---- Files a client sends (web/shared/upload.mjs) ----------------------------------------------
   The list of files sent, then one panel holding the form. The file picker's button matches the
   portal's quiet button instead of the browser's own grey one. */
.upload-body { display: grid; gap: var(--s-6); }
.upload-panel { display: grid; gap: var(--s-5); max-width: 720px; padding: var(--s-6); border: 1px solid var(--border-strong); border-radius: var(--r-lg); background: rgba(255, 255, 255, 0.03); }
.upload-panel h3 { font-size: var(--t-h3); }
.upload-panel .form { max-width: none; gap: var(--s-5); }
.upload-problem:empty { display: none; }
.input--file { padding: 8px; cursor: pointer; color: var(--fg-dim); font-size: var(--t-small); }
.input--file::file-selector-button { min-height: 36px; margin-right: var(--s-4); padding: 6px 16px; border: 1px solid var(--border-strong); border-radius: var(--r-pill); background: transparent; color: var(--fg); font: inherit; font-weight: 600; cursor: pointer; }
.input--file:hover::file-selector-button { background: var(--wash); border-color: rgba(255, 255, 255, 0.3); }
.send-cell { text-align: right; }
.data--files .file-name { font-weight: 400; overflow-wrap: anywhere; }
@media (max-width: 719px) {
  .upload-panel { padding: var(--s-5); }
  .data--stack tbody td.send-cell > * { grid-column: 1 / -1; justify-self: start; }
  .data--stack tbody td.send-cell::before { display: none; }
}
