/* ==========================================================================
   GrainCorp Request Portal

   Design system: Geist (Vercel), followed properly rather than borrowed from.
   That means a neutral grey scale doing almost all the work, one blue as the
   only chromatic accent, black as the primary action, 1px borders instead of
   shadows, a single 4px spacing scale and tight radii.

   Why no violet. Spark violet #5F01D3 was carrying identity that the two
   logos in the sidebar already carry, and it forced every tint, hover and
   active state to be invented. Geist blue is the accent this system is drawn
   around, so states come for free and the palette stops fighting itself.
   Brand still reads as Spark; it reads through the marks, not the buttons.

   Accessibility. Every text-on-background pair below is measured, not
   eyeballed, and clears WCAG AA 4.5:1. The lowest is 4.54:1. The old scale
   put meta text at 3.54:1, which failed, and that text is everywhere.

   Speed. Geist Sans and Geist Mono are self-hosted as variable woff2 next to
   this file. That removes a render-blocking third-party stylesheet and two
   extra DNS and TLS handshakes from every page load.
   ========================================================================== */

/* --- Self-hosted Geist. Variable, latin + latin-ext, 84KB total. -------- */
@font-face {
  font-family: "Geist"; font-style: normal; font-weight: 400 600; font-display: swap;
  src: url("../fonts/geist-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Geist"; font-style: normal; font-weight: 400 600; font-display: swap;
  src: url("../fonts/geist-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Geist Mono"; font-style: normal; font-weight: 400 500; font-display: swap;
  src: url("../fonts/geist-mono-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Geist Mono"; font-style: normal; font-weight: 400 500; font-display: swap;
  src: url("../fonts/geist-mono-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  color-scheme: light dark;

  /* --- Neutral scale. Geist grey, monotonic, no tint. ------------------
     n-500 is the lightest grey allowed to carry text: 4.54:1 on white. --- */
  --n-50:   #fafafa;
  --n-100:  #f5f5f5;
  --n-200:  #ebebeb;
  --n-300:  #e5e5e5;
  --n-400:  #d4d4d4;
  --n-500:  #767676;
  --n-600:  #666666;
  --n-700:  #4d4d4d;
  --n-800:  #333333;
  --n-900:  #262626;
  --n-1000: #171717;

  /* --- Geist blue. The only chromatic accent in the system. ----------- */
  --blue-100: #f0f7ff;
  --blue-200: #e1f0ff;
  --blue-400: #a3ccff;
  --blue-600: #0070f3;
  --blue-700: #0068d6;
  --blue-800: #0058b8;

  /* --- Semantic ------------------------------------------------------- */
  --bg:            var(--n-100);
  --surface:       #ffffff;
  --surface-sunk:  var(--n-50);
  --border:        var(--n-300);
  --border-strong: var(--n-400);
  --text:          var(--n-1000);
  --text-soft:     var(--n-700);
  --text-faint:    var(--n-600);
  --text-invert:   #ffffff;

  --accent:       var(--blue-700);   /* links, active states, 5.31:1      */
  --accent-hover: var(--blue-800);
  --accent-soft:  var(--blue-400);   /* borders and rules, never text     */
  --accent-wash:  var(--blue-100);
  --accent-ring:  var(--blue-600);   /* focus rings and fills             */
  --accent-ink:   var(--blue-700);   /* accent text on an accent wash     */

  /* Primary action is black, the way Geist does it. 17.9:1. */
  --solid:       var(--n-1000);
  --solid-hover: #000000;
  --solid-text:  #ffffff;

  --ok:      #0c7a41; --ok-bg:   #eafaf1; --ok-line: #b8e6cd;
  --warn:    #a35200; --warn-bg: #fff6e5; --warn-line: #f3ddb2;
  --err:     #c92a2a; --err-bg:  #fff0f0; --err-line: #f5c2c2;
  --err-ink: #a11d1d;

  /* --- Spacing. One 4px scale, used everywhere. ----------------------- */
  --s1: .25rem;  --s2: .5rem;   --s3: .75rem;  --s4: 1rem;
  --s5: 1.25rem; --s6: 1.5rem;  --s8: 2rem;    --s10: 2.5rem;
  --s12: 3rem;   --s16: 4rem;

  /* --- Radii. Geist is tight. ---------------------------------------- */
  --r-sm: 4px; --r: 6px; --r-lg: 8px; --r-xl: 12px; --r-full: 999px;

  /* --- Type ----------------------------------------------------------- */
  --font: "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --t-xs: .75rem;   --t-sm: .8125rem; --t-base: .875rem; --t-md: .9375rem;
  --t-lg: 1.125rem; --t-xl: 1.375rem; --t-2xl: 1.75rem;  --t-3xl: 2.125rem;

  /* --- Elevation. Borders first. Shadow only for things that float. --- */
  --shadow-pop: 0 1px 1px rgba(0,0,0,.04), 0 8px 24px -8px rgba(0,0,0,.16);

  /* Custom curves. The built-in CSS easings are too weak to read as
     intentional, and ease-in is never right for UI: it delays the first
     movement, which is the exact moment the user is watching. */
  --ease-out:    cubic-bezier(.23, 1, .32, 1);      /* entering, exiting     */
  --ease-in-out: cubic-bezier(.77, 0, .175, 1);     /* moving on screen      */
  --ease:        var(--ease-out);                   /* the default           */

  /* Durations, from the frequency of the thing being animated. Press
     feedback has to beat perception; a dialog can afford a fifth of a
     second. Nothing in the interface goes over 250ms. */
  --t-press: 110ms;
  --t-hover: 100ms;
  --t-in:    220ms;
  --t-out:   150ms;   /* exit is always faster than enter */
  --wrap: 1180px;
  --side-w: 15rem;   /* the sidebar                                      */
  --stick: 0px;      /* what sticky things must clear. Set on mobile.    */
}

/* --- Dark. Geist ships both, and the tokens above are the only thing
       that changes, so nothing downstream needs to know. --------------- */
@media (prefers-color-scheme: dark) {
  :root {
    --n-50:   #0a0a0a;
    --n-100:  #0a0a0a;
    --n-200:  #1f1f1f;
    --n-300:  #2e2e2e;
    --n-400:  #3d3d3d;
    --n-500:  #8f8f8f;
    --n-600:  #a1a1a1;
    --n-700:  #c4c4c4;
    --n-800:  #dcdcdc;
    --n-900:  #e6e6e6;
    --n-1000: #ededed;

    --blue-100: #0d1f33;
    --blue-200: #10263f;
    --blue-400: #1f4f80;
    --blue-600: #0072f5;
    --blue-700: #52a8ff;
    --blue-800: #8ec5ff;

    --surface:      #131313;
    --surface-sunk: #0f0f0f;
    --text-invert:  #0a0a0a;

    --solid:       #ededed;
    --solid-hover: #ffffff;
    --solid-text:  #0a0a0a;

    --ok:      #4ec98a; --ok-bg:   #0d2318; --ok-line: #1d4a32;
    --warn:    #f0b45e; --warn-bg: #241a08; --warn-line: #4d3812;
    --err:     #ff7a7a; --err-bg:  #2a1414; --err-line: #5c2626;
    --err-ink: #ff9d9d;

    --shadow-pop: 0 1px 1px rgba(0,0,0,.4), 0 8px 24px -8px rgba(0,0,0,.7);
  }

  /* Spark has a white cut of its wordmark and the markup swaps to it. The
     GrainCorp mark does not, and inverting a client logo is not ours to do,
     so it sits on a white chip with its supplied colours intact. */
  .brand__client {
    background: #fff; border-radius: var(--r-sm);
    padding: 3px 6px; box-sizing: content-box;
  }
  .brand__rule { background: var(--n-500); }
}

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

/* The hidden attribute has to win.
   The browser hides [hidden] with a UA rule, which any author `display`
   declaration beats. That is not academic here: app.js hides the fallback
   "Move" button on a task card with `el.hidden = true`, and `.btn { display:
   inline-flex }` put it straight back. The board then had a select that
   auto-submits on change AND a Move button, so changing the select reloaded
   the page and the button you were reaching for submitted the reloaded,
   unchanged value. It read as "Move does not work" when the move itself was
   fine. Same bug was in the draft note and the form's outstanding-answers
   line; this covers all of them and any future one. */
[hidden] { display: none !important; }

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 400 var(--t-base)/1.55 var(--font);
  font-feature-settings: "cv11", "ss01";
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--font);
  font-weight: 600;
  letter-spacing: -.015em;
  line-height: 1.2;
  color: var(--text);
}
h1 { font-size: var(--t-2xl); letter-spacing: -.025em; }
h2 { font-size: var(--t-lg); }
h3 { font-size: var(--t-base); margin-top: var(--s6); }
p { margin: 0 0 var(--s4); }
p:last-child { margin-bottom: 0; }

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

.mono { font-family: var(--mono); font-size: .9em; font-variant-ligatures: none; }

.vh {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.skip { position: absolute; left: -9999px; }
.skip:focus {
  left: var(--s2); top: var(--s2); z-index: 100;
  background: var(--solid); color: var(--solid-text);
  padding: var(--s2) var(--s4); border-radius: var(--r);
}

:where(a, button, input, select, textarea, summary):focus-visible {
  outline: 2px solid var(--accent-ring);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* ==========================================================================
   Shell. Sidebar left, content right.

   The nav moved out of the header because it had outgrown it: seven items
   plus two logos plus a bell plus an avatar on one 56px line, wrapping on
   anything under a laptop. Down the side it has room to be grouped, it stops
   competing with the page title, and the content column gets the full width
   back for tables, which is what this portal is mostly made of.

   Under 64rem the same markup becomes a top strip with a scrollable nav row.
   No JavaScript, no toggle, nothing to trap focus in.
   ========================================================================== */
.shell { min-height: 100vh; }

.side {
  background: var(--surface);
  border-right: 1px solid var(--border);
  display: flex; flex-direction: column;
}

.side__top { padding: var(--s4) var(--s4) var(--s3); }
.brand { display: flex; align-items: center; gap: var(--s3); text-decoration: none; }
.brand__spark { height: 20px; width: auto; display: block; }
.brand__rule { width: 1px; height: 18px; background: var(--border-strong); }
.brand__client { height: 22px; width: auto; display: block; }
.brand__name {
  display: block; font-size: var(--t-xs); font-weight: 500; color: var(--text-faint);
  letter-spacing: .04em; text-transform: uppercase; margin: var(--s3) 0 0; padding: 0 2px;
}

.side__nav { flex: 1; padding: var(--s2) var(--s3) var(--s4); overflow-y: auto; }
.side__group {
  font-size: var(--t-xs); font-weight: 550; color: var(--text-faint);
  letter-spacing: .06em; text-transform: uppercase;
  margin: var(--s5) 0 var(--s2); padding: 0 var(--s2);
}
.side__group:first-child { margin-top: var(--s2); }

.nav__link {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s2); border-radius: var(--r);
  color: var(--text-soft); text-decoration: none;
  font-size: var(--t-base); font-weight: 450; line-height: 1.3;
  transition: background .1s var(--ease), color .1s var(--ease);
}
.nav__link:active { transform: scale(.985); }
@media (hover: hover) and (pointer: fine) {
  .nav__link:hover { background: var(--n-200); color: var(--text); }
}
.nav__link.is-active { background: var(--n-200); color: var(--text); font-weight: 550; }
.nav__link.is-active .nav__icon { color: var(--accent); }
.nav__icon { width: 17px; height: 17px; flex-shrink: 0; color: var(--text-faint); }
.nav__count {
  margin-left: auto; font-size: var(--t-xs); font-weight: 550;
  background: var(--accent-ring); color: #fff;
  border-radius: var(--r-full); padding: 0 5px; line-height: 16px; min-width: 16px; text-align: center;
}

.side__foot { border-top: 1px solid var(--border); padding: var(--s3); }

/* The account menu opens upward from the foot of the sidebar. */
.usermenu { position: relative; }
.usermenu__trigger {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s2); border-radius: var(--r); width: 100%;
}
.usermenu__trigger::-webkit-details-marker { display: none; }
.usermenu__trigger { transition: background var(--t-hover) var(--ease); }
@media (hover: hover) and (pointer: fine) { .usermenu__trigger:hover { background: var(--n-200); } }
.usermenu__who { min-width: 0; }
.usermenu__who b { display: block; font-size: var(--t-sm); font-weight: 550; color: var(--text); }
.usermenu__who span { display: block; font-size: var(--t-xs); color: var(--text-faint); }
.usermenu__who b, .usermenu__who span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.usermenu__caret { margin-left: auto; color: var(--text-faint); flex-shrink: 0; transition: transform .12s var(--ease); }
.usermenu[open] .usermenu__caret { transform: rotate(180deg); }
.usermenu__trigger:active .avatar { transform: scale(.94); }
.avatar { transition: transform var(--t-press) var(--ease); }
.usermenu__panel {
  position: absolute; left: 0; right: 0; bottom: calc(100% + var(--s2)); z-index: 30;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-pop);
  padding: var(--s4); min-width: 13rem;
}
.usermenu__name { font-weight: 550; margin: 0; }
.usermenu__meta, .usermenu__role { font-size: var(--t-sm); color: var(--text-faint); margin: 2px 0 0; word-break: break-word; }
.usermenu__panel a { display: block; margin-top: var(--s3); font-size: var(--t-base); }
.usermenu__panel form { margin-top: var(--s2); }

.avatar {
  width: 28px; height: 28px; border-radius: var(--r-full);
  background: var(--solid); color: var(--solid-text);
  display: inline-grid; place-items: center; flex-shrink: 0;
  font: 550 var(--t-xs)/1 var(--font); letter-spacing: 0;
}
.avatar--sm { width: 24px; height: 24px; font-size: 10px; }
.avatar--xs { width: 20px; height: 20px; font-size: 9px; }

/* --- Desktop: the sidebar is fixed and the content clears it. --------- */
@media (min-width: 64rem) {
  .side { position: fixed; inset: 0 auto 0 0; width: var(--side-w); z-index: 20; }
  .side + .shell__main { margin-left: var(--side-w); }
}

/* --- Under 64rem: the same nav becomes a scrollable top strip. -------- */
@media (max-width: 63.99rem) {
  :root { --stick: 97px; }
  .side {
    position: sticky; top: 0; z-index: 20;
    border-right: 0; border-bottom: 1px solid var(--border);
    /* Content scrolls under this, so it reads as a layer rather than a lid. */
    background: color-mix(in srgb, var(--surface) 82%, transparent);
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    backdrop-filter: blur(20px) saturate(180%);
  }
  /* A scroll edge instead of only a hard rule: the content fades out as it
     passes beneath, rather than being guillotined by a 1px line. */
  .side::after {
    content: ""; position: absolute; left: 0; right: 0; top: 100%; height: 12px;
    background: linear-gradient(to bottom, color-mix(in srgb, var(--bg) 70%, transparent), transparent);
    pointer-events: none;
  }
  .side__top {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--s4); height: 52px; padding: 0 var(--s4);
  }
  .brand__name { display: none; }
  .side__nav {
    display: flex; gap: var(--s1); align-items: center;
    height: 44px; padding: 0 var(--s3); flex: none;
    overflow-x: auto; overflow-y: hidden;
    border-top: 1px solid var(--border);
    scrollbar-width: none;
  }
  .side__nav::-webkit-scrollbar { display: none; }
  .side__group { display: none; }
  .nav__link { white-space: nowrap; padding: var(--s2) var(--s3); }
  .side__foot {
    position: absolute; top: 0; right: var(--s4);
    height: 52px; display: flex; align-items: center;
    border-top: 0; padding: 0;
  }
  .usermenu__who, .usermenu__caret { display: none; }
  .usermenu__trigger { padding: 0; }
  .usermenu__panel { left: auto; right: 0; bottom: auto; top: calc(100% + var(--s2)); width: 15rem; }
}

.wrap { max-width: var(--wrap); margin: 0 auto; padding: var(--s8) var(--s6) var(--s16); }
@media (max-width: 40rem) { .wrap { padding: var(--s5) var(--s4) var(--s12); } }

.body--bare { background: var(--bg); display: grid; place-items: start center; min-height: 100vh; }
.bare { width: 100%; max-width: 27rem; padding: var(--s12) var(--s5); }

.foot {
  max-width: var(--wrap); margin: 0 auto;
  padding: var(--s6) var(--s6) var(--s10);
  color: var(--text-faint); font-size: var(--t-sm);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s4); flex-wrap: wrap;
  border-top: 1px solid var(--border);
}
.foot p { margin: 0; }
.foot__note { font-style: italic; }
.foot__logo { height: 16px; width: auto; opacity: .6; }

/* Banners sit flush under the bar, full bleed, no rounding. */
.banner { padding: var(--s3) var(--s5); text-align: center; font-size: var(--t-base); border-bottom: 1px solid; }
.banner--quiet   { background: var(--surface); color: var(--text-soft); border-color: var(--border); }
.banner--warn    { background: var(--warn-bg); color: var(--warn); border-color: var(--warn-line); }
.banner--preview { background: var(--err-bg); color: var(--err-ink); border-color: var(--err-line); }
.banner strong { font-weight: 600; }
.banner a { color: inherit; }
/* ==========================================================================
   Surfaces. One card, three variants. Borders, not shadows.
   ========================================================================== */
.panel {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--s5); margin-bottom: var(--s4);
}
.panel--center { text-align: center; padding: var(--s12) var(--s6); }
.panel--narrow { max-width: 32rem; margin-inline: auto; }
.panel--accent { border-color: var(--accent-soft); background: var(--accent-wash); }
.panel--flag   { border-color: var(--warn-line); background: var(--warn-bg); }
.panel__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s4); margin-bottom: var(--s4); }
.panel__head h2 { margin: 0; }
.panel__head a { font-size: var(--t-sm); }
.panel__sub { color: var(--text-faint); font-size: var(--t-sm); margin: var(--s2) 0 var(--s4); }
.panel__head + .panel__sub { margin-top: calc(var(--s2) * -1); }

.cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); }
.detail { display: grid; grid-template-columns: minmax(0,1fr) 21rem; gap: var(--s4); align-items: start; }
.detail__side { position: sticky; top: calc(var(--stick) + var(--s4)); }
@media (max-width: 60rem) {
  .cols, .detail { grid-template-columns: 1fr; }
  .detail__side { position: static; }
}

/* --- Stat cards. Deliberately not identical: the retainer one is wider --- */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: var(--s3); margin-bottom: var(--s4); }
.card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  padding: var(--s4) var(--s5); text-decoration: none; color: inherit; display: block;
  transition: border-color var(--t-hover) var(--ease), transform var(--t-press) var(--ease);
}
/* A big surface should barely move. 0.5% reads as a press without squashing. */
.card--stat:active { transform: scale(.995); }
@media (hover: hover) and (pointer: fine) {
  .card--stat:hover { border-color: var(--border-strong); }
}
.card--alert { border-color: var(--err-line); background: var(--err-bg); }
.card--retainer { grid-column: span 2; }
@media (max-width: 44rem) { .card--retainer { grid-column: span 1; } }
.card__label {
  display: flex; align-items: center; gap: var(--s2);
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-faint); margin: 0 0 var(--s2); font-weight: 550;
}
.card__label .ico { color: var(--n-500); }
.card--alert .card__label { color: var(--err); }
.card--alert .card__label .ico { color: var(--err); }
.card__big { font-size: var(--t-3xl); font-weight: 600; letter-spacing: -.03em; margin: 0; line-height: 1; }
.card__of { font-size: var(--t-sm); font-weight: 400; color: var(--text-faint); margin-left: var(--s2); letter-spacing: 0; }
.card__foot { font-size: var(--t-sm); color: var(--text-soft); margin: var(--s3) 0 0; }
.card__foot--muted { color: var(--text-faint); }

.meter { height: 4px; background: var(--n-300); border-radius: var(--r-full); overflow: hidden; margin-top: var(--s3); }
.meter span { display: block; height: 100%; background: var(--solid); border-radius: var(--r-full); }
.meter--over span { background: var(--err); }
.bigfig { font-size: var(--t-xl); font-weight: 600; letter-spacing: -.02em; margin: 0 0 var(--s3); }
.bigfig .sub { font-size: var(--t-sm); font-weight: 400; color: var(--text-faint); }

/* ==========================================================================
   Buttons. One scale: xs, sm, base, lg. One hierarchy: solid, ghost, quiet.
   ========================================================================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  background: var(--solid); color: var(--solid-text); border: 1px solid var(--solid);
  padding: 0 var(--s4); height: 34px; border-radius: var(--r);
  font: 550 var(--t-base)/1 var(--font); letter-spacing: -.005em;
  text-decoration: none; cursor: pointer; white-space: nowrap;
  transition: background var(--t-hover) var(--ease),
              border-color var(--t-hover) var(--ease),
              transform var(--t-press) var(--ease);
}
/* The interface has to be seen to hear the press. Subtle: 3%. */
.btn:active { transform: scale(.97); }
.btn:hover { background: var(--solid-hover); border-color: var(--solid-hover); color: var(--solid-text); }
.btn--ghost { background: transparent; color: var(--text); border-color: var(--border-strong); }
.btn--ghost:hover { background: var(--n-100); border-color: var(--n-500); color: var(--text); }
.btn--ghost.is-on { background: var(--accent-wash); border-color: var(--accent-soft); color: var(--accent); }
.btn--danger { background: var(--err); border-color: var(--err); color: var(--text-invert); }
.btn--danger:hover { background: var(--err-ink); border-color: var(--err-ink); }
.btn--lg { height: 40px; padding: 0 var(--s5); font-size: var(--t-md); }
.btn--sm { height: 30px; padding: 0 var(--s3); font-size: var(--t-sm); }
.btn--xs { height: 26px; padding: 0 var(--s2); font-size: var(--t-xs); }
.btn--full { width: 100%; }

.linkbtn {
  background: none; border: 0; padding: 0; cursor: pointer; color: var(--accent);
  font: 450 var(--t-sm)/1.4 var(--font); text-decoration: underline; text-underline-offset: 2px;
}
.linkbtn--danger { color: var(--err); }
.inlineform { display: inline; }
.dangerzone { text-align: center; padding: var(--s2) 0 var(--s4); }

/* ==========================================================================
   Forms. One field pattern, one control height.
   ========================================================================== */
.form__section { margin-bottom: var(--s8); padding-bottom: var(--s6); border-bottom: 1px solid var(--border); }
.form__section:last-of-type { border-bottom: 0; padding-bottom: 0; }
.form__legend { font-size: var(--t-md); font-weight: 600; margin-bottom: var(--s4); letter-spacing: -.01em; }
.form__row { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap; }
.form__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); flex-wrap: wrap; padding-top: var(--s2); }
.form__ref { font-size: var(--t-sm); color: var(--text-faint); margin: 0; }
.form__ref strong { font-family: var(--mono); color: var(--text); font-weight: 500; }
.form__privacy { font-size: var(--t-sm); color: var(--text-faint); margin-top: var(--s6); padding-top: var(--s4); border-top: 1px solid var(--border); }
.form--tight .field { margin-bottom: var(--s3); }
.form--row { display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; margin-bottom: var(--s2); }
.form--row input[type=text] { width: 8rem; }

.field { margin-bottom: var(--s4); }
.field > label { display: block; font-weight: 500; font-size: var(--t-base); margin-bottom: var(--s2); }
.req { color: var(--text-faint); font-weight: 400; font-size: var(--t-xs); margin-left: var(--s1); }
.field__hint { font-size: var(--t-sm); color: var(--text-faint); margin: var(--s2) 0 0; }
.field__error { font-size: var(--t-sm); color: var(--err); margin: var(--s2) 0 0; font-weight: 450; }
.has-error input, .has-error select, .has-error textarea { border-color: var(--err); }

input[type=text], input[type=email], input[type=password], input[type=number],
input[type=date], input[type=search], select, textarea {
  width: 100%; padding: 0 var(--s3); height: 34px;
  border: 1px solid var(--border-strong); border-radius: var(--r);
  font: 400 var(--t-base)/1 var(--font); color: var(--text); background: var(--surface);
  transition: border-color .12s var(--ease), box-shadow .12s var(--ease);
}
textarea { height: auto; min-height: 5.5rem; padding: var(--s2) var(--s3); line-height: 1.55; resize: vertical; }
input::placeholder, textarea::placeholder { color: var(--n-500); }
input:hover, select:hover, textarea:hover { border-color: var(--n-500); }
input:focus, select:focus, textarea:focus {
  outline: 0; border-color: var(--accent-ring);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-ring) 22%, transparent);
}
input[type=file] {
  width: 100%; padding: var(--s2); font: 400 var(--t-sm)/1.4 var(--font);
  border: 1px dashed var(--border-strong); border-radius: var(--r);
  background: var(--surface-sunk); color: var(--text-soft);
}
input[type=file]:hover { border-color: var(--accent-soft); }
select {
  appearance: none; padding-right: var(--s8);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23767676' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--s3) center;
}

.grid { display: grid; gap: var(--s4); }
.grid--2 { grid-template-columns: 1fr 1fr; }
.grid--3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 44rem) { .grid--2, .grid--3 { grid-template-columns: 1fr; } }
.hp { position: absolute; left: -9999px; }

/* --- Checkboxes ----------------------------------------------------- */
.checkgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: var(--s2); }
.checkstack { display: grid; gap: var(--s2); }
.check {
  display: flex; gap: var(--s3); align-items: flex-start;
  padding: var(--s3); border: 1px solid var(--border); border-radius: var(--r);
  cursor: pointer; background: var(--surface);
  transition: border-color var(--t-hover) var(--ease),
              background var(--t-hover) var(--ease),
              transform var(--t-press) var(--ease);
}
.check:active { transform: scale(.99); }
@media (hover: hover) and (pointer: fine) {
  .check:hover { border-color: var(--accent-soft); }
}
.check input { margin: 2px 0 0; accent-color: var(--accent-ring); width: 15px; height: 15px; flex-shrink: 0; }
.check:has(input:checked) { border-color: var(--accent-ring); background: var(--blue-200); }
.check__body { display: block; }
.check__label { display: block; font-size: var(--t-base); font-weight: 450; }
.check__hint { display: block; font-size: var(--t-xs); color: var(--text-faint); margin-top: 2px; }
.check--inline { border: 0; padding: 0; background: none; display: inline-flex; align-items: center; }
.check--inline:hover { background: none; }
.check--inline .check__label { font-weight: 400; font-size: var(--t-sm); }

/* ==========================================================================
   Feedback, badges, tables
   ========================================================================== */
.callout { border: 1px solid; border-radius: var(--r-lg); padding: var(--s4); margin-bottom: var(--s4); font-size: var(--t-base); }
.callout strong { display: block; margin-bottom: 2px; font-weight: 600; }
.callout--error       { background: var(--err-bg);  border-color: var(--err-line);  color: var(--err-ink); }
.callout--warn        { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }
.callout--info        { background: var(--accent-wash); border-color: var(--accent-soft); color: var(--accent-ink); }
.callout--credentials { background: var(--ok-bg);  border-color: var(--ok-line);  color: var(--ok); }
.callout ul { margin: var(--s2) 0 0; padding-left: var(--s5); }
.callout p { margin: var(--s2) 0 0; }

.flashes { margin-bottom: var(--s4); display: grid; gap: var(--s2); }
.flash {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: var(--s3) var(--s4); border: 1px solid; border-radius: var(--r);
  font-size: var(--t-base);
  animation: flash-in .3s var(--ease);
}
@keyframes flash-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.flash--success { background: var(--ok-bg);   border-color: var(--ok-line);   color: var(--ok); }
.flash--error   { background: var(--err-bg);  border-color: var(--err-line);  color: var(--err-ink); }
.flash--warning { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }
.flash--info    { background: var(--accent-wash); border-color: var(--accent-soft); color: var(--accent-ink); }
.flash__body { flex: 1; }
.flash__close { background: none; border: 0; font-size: 1.1rem; line-height: 1; cursor: pointer; color: inherit; opacity: .5; padding: 0; }
.flash__close:hover { opacity: 1; }

/* --- Status pills. Flat, bordered, Geist-style. -------------------- */
.pill {
  display: inline-block; padding: 1px var(--s2); border-radius: var(--r-sm);
  font-size: var(--t-xs); font-weight: 500; white-space: nowrap; border: 1px solid;
  letter-spacing: -.005em;
}
.pill.is-new      { background: var(--n-100); border-color: var(--n-400); color: var(--n-800); }
.pill.is-waiting  { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }
.pill.is-approved { background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok); }
.pill.is-active   { background: var(--accent-wash); border-color: var(--accent-soft); color: var(--accent-ink); }
.pill.is-done     { background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok); }
.pill.is-closed   { background: var(--n-100); border-color: var(--border); color: var(--text-faint); }
.pill.is-held     { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }
.pill.is-declined { background: var(--err-bg); border-color: var(--err-line); color: var(--err); }

.taglist { list-style: none; margin: var(--s2) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--s2); }
.tag {
  background: var(--surface-sunk); border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 1px var(--s2); font-size: var(--t-xs); color: var(--text-soft);
}
.tag--internal { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); font-weight: 500; }
.tag--off { opacity: .5; text-decoration: line-through; }
.count {
  display: inline-block; background: var(--n-200); border-radius: var(--r-sm);
  padding: 0 var(--s2); font-size: var(--t-xs); font-weight: 550; color: var(--text-soft); margin-left: var(--s2);
}
.code { font-family: var(--mono); font-size: var(--t-xs); background: var(--n-200); padding: 1px var(--s1); border-radius: var(--r-sm); word-break: break-word; }
.code--err { background: var(--err-bg); color: var(--err); }
.strong { font-weight: 550; }
.sub { display: block; font-size: var(--t-sm); color: var(--text-faint); }
/* The class also lands on cells themselves, which must stay cells. Blanket
   `td .sub { display: inline }` fixed that by also running the requester name
   on to the end of the request title, which is where it had been reading as
   part of the title. */
td.sub, th.sub { display: table-cell; }
.reqlist__meta .sub { display: inline; }
td.sub, th.sub { display: table-cell; }
.nowrap { white-space: nowrap; }
.is-late { color: var(--err); font-weight: 550; }
.empty { color: var(--text-faint); font-size: var(--t-base); }

/* --- Tables ------------------------------------------------------- */
.tablewrap { overflow-x: auto; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); }
.table { width: 100%; border-collapse: collapse; font-size: var(--t-base); }
.table th {
  text-align: left; padding: var(--s3) var(--s4); background: var(--surface-sunk);
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-faint); font-weight: 550;
  border-bottom: 1px solid var(--border); white-space: nowrap;
}
.table td { padding: var(--s3) var(--s4); border-bottom: 1px solid var(--border); vertical-align: top; }
.table tr:last-child td { border-bottom: 0; }
.table tbody tr { transition: background .1s var(--ease); }
.table tbody tr:hover { background: var(--surface-sunk); }
.row--flag { background: var(--warn-bg); }
.row--flag:hover { background: var(--warn-bg); filter: brightness(.98); }
.row--off { opacity: .55; }
.table--users td { vertical-align: middle; }
.userrow { display: flex; gap: var(--s3); align-items: center; }
.credtable { border-collapse: collapse; margin: var(--s3) 0; font-size: var(--t-base); }
.credtable th { text-align: left; padding: 2px var(--s4) 2px 0; font-weight: 450; color: var(--text-soft); white-space: nowrap; }
.credtable td { padding: 2px 0; }
.metadrop summary { font-size: var(--t-xs); color: var(--accent); cursor: pointer; }
.metadrop ul { margin: var(--s2) 0 0; padding-left: var(--s4); font-size: var(--t-xs); color: var(--text-soft); }
.pager { display: flex; align-items: center; justify-content: center; gap: var(--s5); padding: var(--s4) 0; font-size: var(--t-base); }

/* ==========================================================================
   Lists, comments, board
   ========================================================================== */
.reqlist, .tasklist, .feed, .filelist, .comments, .rolelist { list-style: none; margin: 0; padding: 0; }
.reqlist li {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s4);
  padding: var(--s3) 0; border-bottom: 1px solid var(--border); flex-wrap: wrap;
}
.reqlist li:last-child { border-bottom: 0; padding-bottom: 0; }
.reqlist a { text-decoration: none; display: flex; gap: var(--s3); align-items: baseline; min-width: 0; }
.reqlist__ref { font-family: var(--mono); font-size: var(--t-xs); color: var(--text-faint); flex-shrink: 0; }
.reqlist__title { font-weight: 450; color: var(--text); }
.reqlist a:hover .reqlist__title { color: var(--accent); }
.reqlist__meta { font-size: var(--t-sm); color: var(--text-faint); display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }

.tasklist li { padding: var(--s3) 0; border-bottom: 1px solid var(--border); }
.tasklist li:last-child { border-bottom: 0; padding-bottom: 0; }
.tasklist--full li { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s4); }
.tasklist a { font-weight: 450; text-decoration: none; color: var(--text); }
.tasklist a:hover { color: var(--accent); }
.tasklist__meta { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; font-size: var(--t-sm); color: var(--text-faint); margin-top: 2px; }

.feed li { padding: var(--s2) 0; border-bottom: 1px solid var(--border); font-size: var(--t-sm); display: flex; gap: var(--s2); flex-wrap: wrap; align-items: baseline; }
.feed li:last-child { border-bottom: 0; }
.feed--tight li { padding: var(--s1) 0; font-size: var(--t-xs); }
.feed__who { font-weight: 550; color: var(--text); }
.feed__what { color: var(--text-soft); flex: 1; min-width: 0; }
.feed__ref { font-family: var(--mono); font-size: var(--t-xs); color: var(--text-faint); }
.feed__when { color: var(--text-faint); font-size: var(--t-xs); white-space: nowrap; }

.filelist li { padding: var(--s2) 0; border-bottom: 1px solid var(--border); }
.filelist li:last-child { border-bottom: 0; }
.filelist a { font-weight: 450; }
.rolelist { margin-top: var(--s2); }
.rolelist li { padding: 2px 0; font-size: var(--t-sm); }

/* --- Comments. Internal notes get a warm tint, not a slab border. ---- */
.comments { margin-bottom: var(--s6); }
.comment { padding: var(--s4) 0; border-bottom: 1px solid var(--border); }
.comment:last-child { border-bottom: 0; }
.comment--internal { background: var(--warn-bg); border-radius: var(--r); padding: var(--s4); margin: var(--s2) 0; border-bottom: 0; }
.comment__head { display: flex; align-items: center; gap: var(--s2); margin-bottom: var(--s2); flex-wrap: wrap; }
.comment__who { font-weight: 550; font-size: var(--t-base); }
.comment__when { font-size: var(--t-xs); color: var(--text-faint); margin-left: auto; }

.prose { font-size: var(--t-base); color: var(--text-soft); }
.prose p { margin: 0 0 var(--s3); }
.prose p:last-child { margin-bottom: 0; }
.prose--sm { font-size: var(--t-base); }

.meta { margin: 0; }
.meta dt { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .06em; color: var(--text-faint); font-weight: 550; margin-top: var(--s4); }
.meta dt:first-child { margin-top: 0; }
.meta dd { margin: var(--s1) 0 0; font-size: var(--t-base); }

/* --- Board -------------------------------------------------------- */
.board { display: grid; grid-template-columns: repeat(5, minmax(13rem, 1fr)); gap: var(--s3); overflow-x: auto; padding-bottom: var(--s2); }
.board__col { background: var(--surface-sunk); border: 1px solid var(--border); border-radius: var(--r-lg); padding: var(--s3); min-width: 0; }
.board__head {
  display: flex; align-items: center; justify-content: space-between;
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-faint); font-weight: 550; margin-bottom: var(--s3); padding: 0 2px;
}
.board__empty { font-size: var(--t-xs); color: var(--n-500); text-align: center; padding: var(--s4) 0; margin: 0; }
.tcard { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); padding: var(--s3); margin-bottom: var(--s2); }
.tcard { transition: border-color var(--t-hover) var(--ease); }
@media (hover: hover) and (pointer: fine) { .tcard:hover { border-color: var(--border-strong); } }
.tcard--late { border-left: 2px solid var(--err); }
.tcard__title { display: block; font-size: var(--t-base); font-weight: 450; text-decoration: none; color: var(--text); margin-bottom: var(--s1); }
.tcard__title:hover { color: var(--accent); }
.tcard__ref { font-family: var(--mono); font-size: var(--t-xs); color: var(--text-faint); text-decoration: none; }
.tcard__foot { display: flex; align-items: center; gap: var(--s2); margin-top: var(--s2); flex-wrap: wrap; font-size: var(--t-xs); color: var(--text-faint); }
/* Select plus its no-JavaScript fallback button, side by side. app.js hides
   the button, so this layout is only ever seen when scripting is off, and it
   should look deliberate rather than like a stray control. */
.tcard__move, .inlineform[data-autosubmit] {
  display: flex; gap: var(--s2); align-items: center; margin-top: var(--s2);
}
.tcard__move select, .inlineform[data-autosubmit] select {
  height: 26px; font-size: var(--t-xs); padding: 0 var(--s5) 0 var(--s2);
}
.inlineform[data-autosubmit] { margin-top: 0; }

/* --- Filters, tabs, disclosure ------------------------------------ */
.filters { margin-bottom: var(--s4); }
.filters__row { display: flex; gap: var(--s2); flex-wrap: wrap; align-items: center; }
.filters__row input[type=search] { flex: 1 1 16rem; max-width: 24rem; }
.filters__row select { width: auto; min-width: 9rem; }
.filters__clear { font-size: var(--t-sm); }
.tabs { display: flex; gap: var(--s1); border-bottom: 1px solid var(--border); margin-bottom: var(--s4); flex-wrap: wrap; }
.tabs__tab { padding: var(--s2) var(--s3); text-decoration: none; color: var(--text-soft); font-size: var(--t-base); font-weight: 450; border-bottom: 2px solid transparent; margin-bottom: -1px; }
.tabs__tab { transition: color var(--t-hover) var(--ease); }
@media (hover: hover) and (pointer: fine) { .tabs__tab:hover { color: var(--text); } }
.tabs__tab.is-on { color: var(--text); border-bottom-color: var(--solid); font-weight: 550; }
.adder { margin-top: var(--s4); border-top: 1px solid var(--border); padding-top: var(--s4); }
.adder--panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); padding: var(--s4) var(--s5); margin-bottom: var(--s4); }
.adder > summary { font-size: var(--t-base); font-weight: 550; color: var(--accent); }
.adder[open] > summary { margin-bottom: var(--s4); }

/* ==========================================================================
   Page furniture
   ========================================================================== */
.pagehead { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s4); margin-bottom: var(--s6); flex-wrap: wrap; }
.pagehead__sub { color: var(--text-faint); font-size: var(--t-base); margin: var(--s2) 0 0; display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.pagehead__eyebrow { font-size: var(--t-sm); color: var(--text-faint); margin: 0 0 var(--s1); }
.pagehead__actions { display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; }

/* --- Progress track ----------------------------------------------------
   The pill says the status. This says the journey, which is the question a
   requester actually has: where is my thing up to.

   It was five 9px dots joined by a hairline, in 13px grey. That is the most
   asked question on the screen rendered as the quietest thing on it. It is
   now five filled segments, so the proportion done is legible before any of
   the labels are read, at a glance and from across a desk.

   Five stages, because ten statuses is right for a filter and far too many
   for a track. State is carried by fill, weight and a tick as well as hue. */
.trackwrap { margin: 0 0 var(--s5); }
.track {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--s2);
}
.track__step { min-width: 0; }
.track__bar {
  display: block; height: 6px; border-radius: var(--r-full);
  background: var(--n-300);
}
.track__label {
  display: flex; align-items: center; gap: 3px;
  margin-top: var(--s2); font-size: var(--t-xs); line-height: 1.35;
  color: var(--text-faint);
}
.track__step.is-done .track__bar  { background: var(--n-800); }
.track__step.is-done .track__label { color: var(--text-soft); }
.track__tick { color: var(--ok); }
.track__step.is-now .track__bar {
  background: var(--accent-ring);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-ring) 18%, transparent);
}
.track__step.is-now .track__label { color: var(--text); font-weight: 550; }

/* Off the path is not a sixth stage, so it does not get a segment. The stage
   the request stopped at takes the stop colour, and a line underneath says
   what happened in words. */
.trackwrap--off.is-declined .track__step.is-now .track__bar { background: var(--err); box-shadow: none; }
.trackwrap--off.is-held     .track__step.is-now .track__bar { background: var(--warn); box-shadow: none; }
.trackwrap--off.is-declined .track__step.is-now .track__label { color: var(--err); }
.trackwrap--off.is-held     .track__step.is-now .track__label { color: var(--warn); }
.track__off {
  display: flex; align-items: center; gap: var(--s2);
  margin: var(--s3) 0 0; font-size: var(--t-sm); color: var(--text-soft);
}
.track__off strong { font-weight: 550; }
.trackwrap--off.is-declined .track__off { color: var(--err); }
.trackwrap--off.is-held     .track__off { color: var(--warn); }
.track__note { font-size: var(--t-sm); color: var(--text-faint); margin: var(--s3) 0 0; }

/* --- Activity rows, interleaved with comments -------------------------- */
.activity {
  display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap;
  padding: var(--s2) 0 var(--s2) var(--s5); position: relative;
  font-size: var(--t-sm); color: var(--text-faint);
  border-bottom: 1px solid var(--border);
}
.activity:last-child { border-bottom: 0; }
/* A hairline marker so a system event reads as lighter than a comment. */
.activity::before {
  content: ""; position: absolute; left: var(--s2); top: 50%;
  width: 5px; height: 5px; margin-top: -2px;
  border-radius: var(--r-full); background: var(--border-strong);
}
.activity__who { font-weight: 550; color: var(--text-soft); }
.activity__what { flex: 1; min-width: 0; }
.activity__when { white-space: nowrap; font-size: var(--t-xs); }

/* --- Skeleton ----------------------------------------------------------
   A shape where the content will be, rather than the word "Loading". It
   tells you the panel is arriving and roughly what shape it is. */
.skeleton { padding: var(--s5); }
.skeleton__bar {
  height: 10px; border-radius: var(--r-sm); margin-bottom: var(--s3);
  background: linear-gradient(90deg, var(--n-200) 25%, var(--n-300) 37%, var(--n-200) 63%);
  background-size: 400% 100%;
  animation: skeleton 1.4s ease-in-out infinite;
}
.skeleton__bar--title { height: 16px; width: 62%; margin-bottom: var(--s5); }
.skeleton__bar--short { width: 40%; }
@keyframes skeleton { from { background-position: 100% 50%; } to { background-position: 0 50%; } }
@media (prefers-reduced-motion: reduce) {
  .skeleton__bar { animation: none; background: var(--n-200); }
}

/* Status sits next to the pill it changes, in the header, rather than in a
   panel of its own two screens further down. */
.headform { display: flex; gap: var(--s2); align-items: center; }
.headform select { width: auto; min-width: 10rem; }

/* --- The decision, and the evidence for it, in one place ---------------- */
.decision { border-color: var(--accent-soft); background: var(--accent-wash); margin-bottom: var(--s5); }
.decision__head { margin-bottom: var(--s4); }
.decision__head h2 { margin: 0; }
.decision__head .panel__sub { margin: var(--s1) 0 0; }
.decision__evidence {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: var(--s3) var(--s4);
  font-size: var(--t-base); color: var(--text-soft); margin: 0 0 var(--s4);
}
.decision__evidence strong { color: var(--text); font-weight: 550; }
.decision__evidence--gap { border-color: var(--warn-line); background: var(--warn-bg); color: var(--warn); }
.decision__evidence--gap strong { color: inherit; }
.decision .form__row { gap: var(--s3); }

/* Declining is reversible and routine. A solid red button reads as though it
   destroys something, which trains people to hesitate over the wrong control. */
.btn--decline { color: var(--err); border-color: var(--err-line); }
.btn--decline:hover { background: var(--err-bg); border-color: var(--err); color: var(--err); }


/* --- The public form ------------------------------------------------ */
.formpage { max-width: 46rem; margin: 0 auto; }
.formpage__head { margin-bottom: var(--s6); }
.formpage__eyebrow { display: block; font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .09em; color: var(--accent-ink); font-weight: 600; margin-bottom: var(--s3); }
.formpage__head h1 { font-size: var(--t-3xl); letter-spacing: -.03em; }
.formpage__lede { color: var(--text-soft); font-size: var(--t-md); margin-top: var(--s3); max-width: 38rem; }
.formpage .form { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-xl); padding: var(--s8); }
@media (max-width: 40rem) { .formpage .form { padding: var(--s5); } }

.aihelper { background: var(--accent-wash); border: 1px solid var(--accent-soft); border-radius: var(--r-lg); padding: var(--s4); margin-bottom: var(--s6); }
.aihelper > summary { font-weight: 450; font-size: var(--t-base); color: var(--accent-ink); }
.aihelper[open] > summary { margin-bottom: var(--s3); }
.aihelper__badge { display: inline-block; background: var(--accent-ring); color: #fff; font-size: 10px; font-weight: 600; padding: 1px var(--s2); border-radius: var(--r-sm); margin-right: var(--s2); text-transform: uppercase; letter-spacing: .05em; }
.aihelper__body p { font-size: var(--t-base); color: var(--text-soft); }
.aihelper__prompt {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  padding: var(--s3); font: 400 var(--t-xs)/1.6 var(--mono);
  white-space: pre-wrap; word-break: break-word; max-height: 18rem; overflow-y: auto;
  margin: 0 0 var(--s3); color: var(--text-soft);
}

/* Selected files, listed before submit so nobody wonders if it worked. */
.filepreview { margin: var(--s2) 0 0; padding: 0; list-style: none; display: grid; gap: var(--s1); }
.filepreview li { display: flex; gap: var(--s2); align-items: baseline; font-size: var(--t-sm); color: var(--text-soft); }
.filepreview__size { color: var(--text-faint); font-family: var(--mono); font-size: var(--t-xs); }
.draftnote { font-size: var(--t-sm); color: var(--ok); margin: var(--s3) 0 0; display: flex; align-items: center; gap: var(--s2); }

/* --- Sign in, confirmation ------------------------------------------ */
.loginbox { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-xl); padding: var(--s8); }
.loginbox__brand { text-align: center; margin-bottom: var(--s6); }
.loginbox__logo { height: 26px; width: auto; margin-bottom: var(--s4); }
.loginbox__brand h1 { font-size: var(--t-lg); margin: 0 0 var(--s1); }
.loginbox__brand p { font-size: var(--t-sm); color: var(--text-faint); margin: 0; }
.loginbox__foot { text-align: center; font-size: var(--t-sm); color: var(--text-faint); margin-top: var(--s6); }

.tick { width: 44px; height: 44px; margin: 0 auto var(--s4); border-radius: var(--r-full); background: var(--ok-bg); border: 1px solid var(--ok-line); color: var(--ok); display: grid; place-items: center; font-size: 1.4rem; line-height: 1; }
.thanks__ref { font-size: var(--t-md); margin-top: var(--s3); }
.thanks__ref strong { font-family: var(--mono); color: var(--text); font-weight: 550; }
.nextsteps { text-align: left; margin: var(--s6) 0; padding: var(--s5); background: var(--surface-sunk); border: 1px solid var(--border); border-radius: var(--r-lg); }
.nextsteps h2 { font-size: var(--t-base); margin-bottom: var(--s3); }
.nextsteps ol { margin: 0; padding-left: var(--s5); font-size: var(--t-base); color: var(--text-soft); }
.nextsteps li { margin-bottom: var(--s2); }

/* --- Sortable table headers ---------------------------------------- */
.table th[data-sort] { cursor: pointer; user-select: none; padding-right: var(--s6); }
.table th[data-sort]:hover { color: var(--text); background: var(--n-200); }
.table th[data-sort]::after {
  content: ""; position: absolute; right: var(--s3); top: 50%;
  width: 0; height: 0; margin-top: -2px;
  border-left: 3px solid transparent; border-right: 3px solid transparent;
  border-top: 4px solid var(--n-500); opacity: 0; transition: opacity .12s var(--ease);
}
.table th[data-sort]:hover::after { opacity: .5; }
.table th[aria-sort]::after { opacity: 1; border-top-color: var(--text); }
.table th[aria-sort=ascending]::after { border-top: 0; border-bottom: 4px solid var(--text); }
.table thead th { position: sticky; top: 0; z-index: 1; }

/* --- Style guide ---------------------------------------------------- */
.sg { display: grid; gap: var(--s8); }
.sg__row { display: flex; gap: var(--s3); flex-wrap: wrap; align-items: center; }
.sg__swatches { display: grid; grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr)); gap: var(--s2); }
.sg__swatch { border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; font-size: var(--t-xs); }
.sg__chip { height: 44px; display: block; }
/* A <span> with vertical padding stays inline and its box overlaps whatever
   sits above it, which showed as labels running over the swatch. */
.sg__meta { display: block; padding: var(--s2); background: var(--surface); }
.sg__meta code { font-family: var(--mono); font-size: 10px; color: var(--text-faint); display: block; }
.sg__code {
  background: var(--n-1000); color: var(--surface); border-radius: var(--r);
  padding: var(--s3); font: 400 var(--t-xs)/1.6 var(--mono);
  overflow-x: auto; margin: var(--s3) 0 0; white-space: pre;
}
.sg__scale { display: grid; gap: var(--s2); }
.sg__scale div { background: var(--accent-wash); border-left: 2px solid var(--accent-soft); font-size: var(--t-xs); color: var(--text-soft); padding: 2px var(--s2); }

/* ==========================================================================
   Icons, and the things drawn with them

   Added 25 August. The portal had icons in the sidebar and nowhere else, so
   every other screen was pure text: thirteen tick boxes of similar grey
   words, a Details panel of thirteen stacked uppercase labels, and priority
   hidden inside a dropdown. Icons here are never decoration; each one marks a
   row, a choice or a count that the eye would otherwise have to read to find.
   Every one sits beside its own label, so nothing depends on recognising a
   glyph, and all of them are aria-hidden for the same reason.
   ========================================================================== */
.ico { flex-shrink: 0; vertical-align: -.16em; }

/* A heading icon is a landmark, not a bullet, so it is quieter than the words
   it sits beside. */
.panel h2 { display: flex; align-items: center; gap: var(--s2); }
.panel h2 .count { margin-left: 0; }
.h2ico { color: var(--n-500); }
.field__hint .ico, .form__privacy .ico { color: var(--n-500); margin-right: 4px; }
/* --- One disclosure pattern ---------------------------------------------
   Wherever a summary draws its own mark, the browser's triangle is turned
   off, because a triangle plus an icon is two markers for one control. The
   affordance moves to a chevron on the right that rotates when it opens,
   which is the same in every one of them. */
.adder > summary, .aihelper > summary, .metadrop > summary {
  display: flex; align-items: center; gap: var(--s2);
  cursor: pointer; list-style: none;
}
.adder > summary::-webkit-details-marker,
.aihelper > summary::-webkit-details-marker,
.metadrop > summary::-webkit-details-marker { display: none; }
.disclose { margin-left: auto; color: var(--n-500); transition: transform var(--t-hover) var(--ease); }
details[open] > summary .disclose { transform: rotate(180deg); }

/* --- Priority, drawn rather than spelled ---------------------------------
   Three bars rising left to right, filled to the level: one grey, two blue,
   three amber, three red. The scale is legible without reading, and the word
   is still there beside it, because colour on its own is not a signal
   everyone can receive. Low stays grey deliberately, so the coloured part of
   the scale only covers the three levels that change what happens next. */
.pri {
  display: inline-flex; align-items: center; gap: var(--s2);
  padding: 1px var(--s2); border: 1px solid; border-radius: var(--r-sm);
  font-size: var(--t-xs); font-weight: 500; white-space: nowrap;
  letter-spacing: -.005em;
}
.pri__bars { width: 13px; height: 10px; flex-shrink: 0; }
.pri__bars rect { fill: currentColor; opacity: .22; }
.pri__bars rect.is-on { opacity: 1; }
.pri--compact { border: 0; padding: 0; background: none; }

.pri.is-low     { background: var(--n-100);   border-color: var(--n-400);     color: var(--text-faint); }
.pri.is-normal  { background: var(--accent-wash); border-color: var(--accent-soft); color: var(--accent-ink); }
.pri.is-high    { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }
.pri.is-urgent  { background: var(--err-bg);  border-color: var(--err-line);  color: var(--err); }

/* --- Picking a priority --------------------------------------------------
   Four visible choices rather than a dropdown that hides three quarters of
   its own scale behind a click. Radios underneath, so arrow keys work and the
   control survives JavaScript being off. */
.pripick { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s2); }
.pripick__opt {
  position: relative; display: flex; flex-direction: column; align-items: center;
  gap: var(--s2); padding: var(--s3) var(--s1);
  border: 1px solid var(--border); border-radius: var(--r);
  background: var(--surface); color: var(--text-faint); cursor: pointer;
  transition: border-color var(--t-hover) var(--ease),
              background var(--t-hover) var(--ease),
              color var(--t-hover) var(--ease),
              transform var(--t-press) var(--ease);
}
.pripick__opt:active { transform: scale(.98); }
.pripick__opt input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.pripick .pri__bars { width: 17px; height: 13px; }
.pripick__label { font-size: var(--t-xs); font-weight: 500; line-height: 1; }
@media (hover: hover) and (pointer: fine) {
  .pripick__opt:hover { border-color: var(--border-strong); color: var(--text-soft); }
}
.pripick__opt:has(input:focus-visible) {
  border-color: var(--accent-ring);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-ring) 22%, transparent);
}
.pripick__opt.is-low:has(input:checked)    { border-color: var(--n-500);       background: var(--n-100);         color: var(--text); }
.pripick__opt.is-normal:has(input:checked) { border-color: var(--accent-ring); background: var(--accent-wash);   color: var(--accent-ink); }
.pripick__opt.is-high:has(input:checked)   { border-color: var(--warn);        background: var(--warn-bg);       color: var(--warn); }
.pripick__opt.is-urgent:has(input:checked) { border-color: var(--err);         background: var(--err-bg);        color: var(--err); }

/* --- The header fact bar -------------------------------------------------
   Status, priority and the date, before a word of the description. These are
   the three things that decide whether you read the rest now or later. */
.factbar {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  margin: var(--s3) 0 0;
}
.factbar__by { font-size: var(--t-sm); color: var(--text-faint); }
.datechip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 1px var(--s2); border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface); color: var(--text-soft);
  font-size: var(--t-xs); font-weight: 500; white-space: nowrap;
}
.datechip.is-late { background: var(--err-bg); border-color: var(--err-line); color: var(--err); font-weight: 550; }

/* --- Facts, as rows rather than a stack ----------------------------------
   Thirteen stacked uppercase labels made the sidebar taller than the screen
   and gave the eye nothing to aim at. Label and value on one line, an icon
   per row, hairlines between: same facts, roughly half the height. */
.facts { margin: 0; }
.facts__row {
  display: grid; grid-template-columns: 6.75rem minmax(0, 1fr);
  gap: var(--s3); align-items: baseline; padding: var(--s2) 0;
}
.facts__row + .facts__row { border-top: 1px solid var(--border); }
.facts dt {
  display: flex; align-items: center; gap: var(--s2); margin: 0;
  font-size: var(--t-xs); font-weight: 450; color: var(--text-faint);
}
.facts dt .ico { color: var(--n-500); }
.facts dd { margin: 0; font-size: var(--t-sm); color: var(--text); min-width: 0; overflow-wrap: anywhere; }
/* An email address has no break opportunity in it, so in a 12rem value
   column it either breaks mid-word or overflows. This row gets the full
   width instead. */
.facts__row--wide { grid-template-columns: minmax(0, 1fr); gap: 2px; }
.facts__mail { display: block; font-size: var(--t-xs); margin-top: 1px; }
.facts__note { display: block; font-size: var(--t-xs); color: var(--text-faint); margin-top: 1px; }
.facts--top { margin-top: var(--s4); padding-top: var(--s2); border-top: 1px solid var(--border); }

/* --- Hours, as a proportion ---------------------------------------------
   "Logged so far: 1h" next to an estimate of 1.5h somewhere else is two
   numbers to subtract. One bar answers it. */
.hoursbar { margin-top: var(--s4); padding-top: var(--s4); border-top: 1px solid var(--border); }
.hoursbar__head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s2);
  font-size: var(--t-xs); color: var(--text-faint);
}
.hoursbar__head > span:first-child { display: flex; align-items: center; gap: var(--s2); }
.hoursbar__head .ico { color: var(--n-500); }
.hoursbar__figs strong { color: var(--text); font-size: var(--t-sm); font-weight: 550; }
.hoursbar .meter { margin-top: var(--s2); }
.hoursbar__note { font-size: var(--t-xs); color: var(--text-faint); margin: var(--s2) 0 0; }
.hoursbar.is-over .hoursbar__note { color: var(--err); font-weight: 500; }

/* --- The details editor --------------------------------------------------
   Ordered by how often each field is touched, and the save stays on screen,
   because the panel is taller than most laptops and the button used to
   scroll away under the fold. */
.editform { margin-top: var(--s5); padding-top: var(--s5); border-top: 1px solid var(--border); }
.editform__group {
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .06em;
  font-weight: 550; color: var(--text-faint);
  margin: var(--s5) 0 var(--s3); padding-top: var(--s3); border-top: 1px solid var(--border);
}
.editform__save {
  display: flex; align-items: center; gap: var(--s3);
  margin: var(--s4) calc(var(--s5) * -1) calc(var(--s5) * -1);
  padding: var(--s3) var(--s5);
  background: var(--surface); border-top: 1px solid var(--border);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
}
/* It follows you only once there is something to save. A bar pinned to the
   bottom of the panel from the moment the page loads sits on top of whatever
   field happens to be lowest on screen, which is a real cost paid for a
   button nobody has needed yet. Dirty the form and it starts following. With
   JavaScript off it simply stays where it is, which is also correct. */
.editform.is-dirty .editform__save { position: sticky; bottom: 0; z-index: 2; }
.editform__dirty { font-size: var(--t-xs); color: var(--warn); font-weight: 500; }

/* --- Section leads on the public form ------------------------------------
   Three sections told apart before any of them is read. */
.seclead { display: flex; gap: var(--s3); align-items: flex-start; margin-bottom: var(--s5); }
.seclead__ico {
  display: grid; place-items: center; width: 34px; height: 34px; flex-shrink: 0;
  border: 1px solid var(--border); border-radius: var(--r);
  background: var(--surface-sunk); color: var(--text-soft);
}
.seclead .form__legend { margin: 0; }
.seclead__sub { font-size: var(--t-sm); color: var(--text-faint); margin: 2px 0 0; }

/* --- Tick boxes with a mark each ----------------------------------------
   Thirteen lines of similar grey text is a wall. Thirteen distinct marks is
   a menu you can scan. */
.check--ico { align-items: center; }
.check__ico {
  display: grid; place-items: center; width: 28px; height: 28px; flex-shrink: 0;
  border-radius: var(--r-sm); background: var(--n-200); color: var(--text-soft);
  transition: background var(--t-hover) var(--ease), color var(--t-hover) var(--ease);
}
.check--ico:has(input:checked) .check__ico { background: var(--accent-ring); color: #fff; }

@media (max-width: 40rem) {
  .tasklist--full li { flex-wrap: wrap; }
  .tasklist--full li > div { flex: 1 1 100%; }
  .tasklist--full .inlineform { display: flex; gap: var(--s2); width: 100%; margin-top: var(--s2); }
  .tasklist--full .inlineform select { flex: 1; }
}

/* --- A status dot, the pill's colour without the pill ---------------------
   Six board columns of identical grey uppercase meant knowing which column
   you were in came from counting across. The dot carries the same colour the
   status carries everywhere else, and the word is right beside it. */
.dot { width: 7px; height: 7px; border-radius: var(--r-full); flex-shrink: 0; display: inline-block; }
.dot.is-new      { background: var(--n-500); }
.dot.is-waiting  { background: var(--warn); }
.dot.is-approved { background: var(--ok); }
.dot.is-active   { background: var(--accent-ring); }
.dot.is-done     { background: var(--ok); }
.dot.is-closed   { background: var(--n-400); }
.dot.is-held     { background: var(--warn); }
.dot.is-declined { background: var(--err); }
.board__name { display: flex; align-items: center; gap: var(--s2); min-width: 0; }

.tcard__due, .tcard__est { display: inline-flex; align-items: center; gap: 3px; }
.tcard__due .ico, .tcard__est .ico { color: var(--n-500); }

.tag--ico { display: inline-flex; align-items: center; gap: 5px; }
.tag--ico .ico { color: var(--n-500); }
.minicount { display: inline-flex; align-items: center; gap: 3px; margin-right: var(--s2); }
.minicount .ico { color: var(--n-500); }

/* --- The form's own footer, kept on screen -------------------------------
   The way out of a form this long should never be somewhere you have to go
   looking for, and neither should the news that you missed a field. */
.form__foot--stick {
  position: sticky; bottom: 0; z-index: 3;
  margin: var(--s6) calc(var(--s8) * -1) calc(var(--s8) * -1);
  padding: var(--s4) var(--s8);
  background: var(--surface); border-top: 1px solid var(--border);
  border-radius: 0 0 var(--r-xl) var(--r-xl);
}
.form__footinfo { min-width: 0; }
/* One line of context at a time. While something is still missing, that is
   the more useful of the two; once nothing is, the reference number is. Two
   stacked lines made the bar about 110px tall on a phone, which is a lot of
   a 640px viewport to give to a thing that floats over the field you are
   typing in. */
.form__foot--stick:has(.form__todo:not([hidden])) .form__ref { display: none; }
.form__todo {
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap;
  font-size: var(--t-sm); color: var(--warn); font-weight: 500; margin: var(--s1) 0 0;
}
.form__todo .linkbtn { color: inherit; font-weight: 500; }
.form__foot--stick + .form__privacy { display: flex; gap: var(--s2); align-items: flex-start; }
@media (max-width: 40rem) {
  .form__foot--stick { margin-inline: calc(var(--s5) * -1); padding-inline: var(--s5); }
}

/* --- Two-factor ----------------------------------------------------------
   A six-digit code is a number you are copying off a screen, so it gets the
   mono face, wide tracking and a size you can check at a glance without
   losing your place. */
.codeinput {
  font: 500 1.5rem/1 var(--mono); letter-spacing: .35em;
  text-align: center; height: 52px; padding-left: .35em;
}
.codeinput::placeholder { letter-spacing: .35em; color: var(--n-400); }

.setupkey {
  font-size: var(--t-md); letter-spacing: .12em; word-spacing: .2em;
  background: var(--surface-sunk); border: 1px solid var(--border);
  border-radius: var(--r); padding: var(--s3) var(--s4); margin: 0 0 var(--s3);
  overflow-wrap: anywhere;
}
.setupkey--wrap { font-size: var(--t-xs); letter-spacing: 0; word-spacing: 0; }

/* Recovery codes are a list you copy once and then never look at again, so
   they read as a block of data rather than as prose. */
.codes {
  list-style: none; margin: 0 0 var(--s4); padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: var(--s2);
  font: 400 var(--t-md)/1.5 var(--mono);
}
.codes li {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: var(--s2) var(--s3); text-align: center;
}

.steps { margin: 0; padding-left: var(--s5); display: grid; gap: var(--s5); }
.steps li { padding-left: var(--s1); }
.steps li strong { display: block; margin-bottom: var(--s2); font-weight: 550; }
.steps .field { margin-top: var(--s3); }
.steps em { font-style: normal; font-weight: 500; color: var(--text); }

.plainlist { list-style: none; margin: 0 0 var(--s5); padding: 0; display: grid; gap: var(--s2); }
.plainlist li { display: flex; align-items: flex-start; gap: var(--s2); font-size: var(--t-base); color: var(--text-soft); }
.plainlist .ico { color: var(--ok); margin-top: 3px; }

.loginbox__recovery { text-align: left; margin-top: var(--s5); }
.loginbox__foot { margin: var(--s5) 0 0; font-size: var(--t-sm); }

/* Whether an account has an authenticator, in the people table. */
.usermenu__panel a { display: flex; align-items: center; gap: var(--s2); }
.usermenu__panel a .ico { color: var(--n-500); }
/* An account without a second factor should say so where the account lives. */
.usermenu__flag {
  margin-left: auto; font-size: 10px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .05em; color: var(--warn); background: var(--warn-bg);
  border: 1px solid var(--warn-line); border-radius: var(--r-sm); padding: 0 5px;
}

.twofa { display: inline-flex; align-items: center; gap: 4px; font-size: var(--t-xs); font-weight: 500; white-space: nowrap; }
.twofa.is-on  { color: var(--ok); }
.twofa.is-off { color: var(--text-faint); }

@media print {
  .side, .foot, .filters, .btn, .adder, .dangerzone, .tcard__move, .skip, .banner { display: none !important; }
  .form__foot--stick, .editform__save { position: static; border: 0; }
  body { background: #fff; color: #000; }
  .panel, .tablewrap { border-color: #ccc; break-inside: avoid; }
  .shell__main { margin-left: 0; }
  .detail { grid-template-columns: 1fr; }
}
/* Reduced motion is not no feedback. Opacity and colour stay, because they
   aid comprehension; movement and overshoot go. The dialog still fades, so it
   never simply blinks into existence. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }
  .peek, .peek[open] {
    transform: none !important;
    transition: opacity 140ms var(--ease), overlay 140ms allow-discrete, display 140ms allow-discrete !important;
  }
  .peek::backdrop, .peek[open]::backdrop {
    transition: opacity 140ms var(--ease), overlay 140ms allow-discrete, display 140ms allow-discrete !important;
  }
}

/* Apple treats these as binding as reduced-motion, and they are cheap to
   honour when every surface is already a token. */
@media (prefers-reduced-transparency: reduce) {
  .side { background: var(--surface) !important; backdrop-filter: none !important; }
  .peek::backdrop { backdrop-filter: none !important; }
}
@media (prefers-contrast: more) {
  :root {
    --border:        var(--n-500);
    --border-strong: var(--n-600);
    --text-faint:    var(--n-700);
    --text-soft:     var(--n-800);
  }
  .pill, .tag, .card, .panel { border-width: 1px; }
}

/* ==========================================================================
   Quick view dialog. Native <dialog>, so focus trapping, Escape and the
   backdrop come from the platform rather than a library.
   ========================================================================== */
.peek {
  width: min(46rem, calc(100vw - var(--s8)));
  max-height: min(46rem, calc(100vh - var(--s10)));
  padding: 0; border: 1px solid var(--border); border-radius: var(--r-xl);
  background: var(--surface); color: var(--text);
  box-shadow: var(--shadow-pop);
  overflow: hidden;
}

/* Transitions, not keyframes.

   Keyframes restart from zero when interrupted; transitions retarget from
   wherever the element currently is. Triage means opening and dismissing this
   repeatedly, so grabbing it mid-flight has to work.

   @starting-style plus allow-discrete lets the platform run the exit itself,
   which removed a 140ms setTimeout the JS was using to fake it. That timeout
   was latency sitting directly on the input path.

   Enter 220ms, exit 150ms: the system should answer faster than it offers. */
.peek {
  opacity: 0;
  transform: translateY(6px) scale(.985);
  transition:
    opacity   var(--t-out) var(--ease),
    transform var(--t-out) var(--ease),
    overlay   var(--t-out) var(--ease) allow-discrete,
    display   var(--t-out) var(--ease) allow-discrete;
}
.peek[open] {
  display: flex; flex-direction: column;
  opacity: 1;
  transform: none;
  transition:
    opacity   var(--t-in) var(--ease),
    transform var(--t-in) var(--ease),
    overlay   var(--t-in) var(--ease) allow-discrete,
    display   var(--t-in) var(--ease) allow-discrete;
}
@starting-style {
  .peek[open] { opacity: 0; transform: translateY(6px) scale(.985); }
}

/* Nothing in the world appears from nothing, so it starts at 98.5%, not 0.
   It is a modal rather than a popover, so it scales from its own centre
   instead of from a trigger. */

.peek::backdrop {
  background: rgba(0, 0, 0, .40);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  opacity: 0;
  transition:
    opacity var(--t-out) var(--ease),
    overlay var(--t-out) var(--ease) allow-discrete,
    display var(--t-out) var(--ease) allow-discrete;
}
.peek[open]::backdrop {
  opacity: 1;
  transition:
    opacity var(--t-in) var(--ease),
    overlay var(--t-in) var(--ease) allow-discrete,
    display var(--t-in) var(--ease) allow-discrete;
}
@starting-style { .peek[open]::backdrop { opacity: 0; } }

.peek__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s4);
  padding: var(--s5); border-bottom: 1px solid var(--border);
}
.peek__ref { font-size: var(--t-xs); color: var(--text-faint); margin: 0 0 var(--s1); }
.peek__head h2 { font-size: var(--t-lg); }
.peek__meta { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; font-size: var(--t-sm); color: var(--text-faint); margin: var(--s2) 0 0; }
.peek__close {
  background: none; border: 0; cursor: pointer; color: var(--text-faint);
  font-size: 1.4rem; line-height: 1; padding: 0; width: 28px; height: 28px;
  border-radius: var(--r); flex-shrink: 0;
}
.peek__close:hover { background: var(--n-200); color: var(--text); }

.peek__body { padding: var(--s5); overflow-y: auto; flex: 1; }
.peek__body .callout { margin: 0 0 var(--s4); }
.peek__body h3 { margin-top: var(--s5); }

.peek__facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: var(--s3); margin: var(--s5) 0 0; }
.peek__facts div { border-left: 2px solid var(--border); padding-left: var(--s3); }
.peek__facts dt { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .06em; color: var(--text-faint); font-weight: 550; }
.peek__facts dd { margin: 2px 0 0; font-size: var(--t-base); }

.peek__foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s4);
  padding: var(--s4) var(--s5); border-top: 1px solid var(--border);
  background: var(--surface-sunk); flex-wrap: wrap;
}
.peek__actions { display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; }
.peek__loading { padding: var(--s12); text-align: center; color: var(--text-faint); font-size: var(--t-base); }
.peek__loading::after { content: "..."; }

/* --- Entrance. One staggered reveal, not effects everywhere. --------- */
.enter { animation: enter .26s var(--ease) both; animation-delay: var(--enter-delay, 0ms); }
@keyframes enter { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }

/* Rows hint that they open something. */
.table tbody tr:has([data-peek]) { cursor: pointer; }
.table tbody tr:has([data-peek]):hover .strong { color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .peek, .peek[open] { transform: none; }
  .enter { animation: none; }
}
