/* ==========================================================================
   CRUCIBLE DESIGN SYSTEM  ·  v2 (final-design handoff)  ·  Phase A port
   --------------------------------------------------------------------------
   Faithful adaptation of references/final-design/.../design/assets/crucible.css
   for the Spring Boot + Thymeleaf app. Loaded by NEW (migrated) surfaces only;
   old surfaces keep /css/crucible.css untouched during the migration.

   Changes from the design source, and nothing else:
   1. CDN @import fonts replaced with self-hosted @font-face (/fonts/*.woff2).
   2. DemoBar rules stripped (demo furniture, excluded from product).
   3. A "PRODUCT ADDITIONS" section at the end: the per-page inline keyframes
      the design repeats on every screen (toastIn, drawerIn, heartbeat), the
      .toast/.drawer/.pagebar classes that mirror the design's inline styles,
      theme-toggle icon visibility, and dark-mode fixes for the few hard-coded
      light colours the design left behind (scrollbar thumb, hover borders).
   ========================================================================== */

/* — Self-hosted fonts (CSP 'self'; files shipped in /fonts) —
   Satoshi has no 600 cut on disk; the browser maps 600 → 700 (nearest). */
@font-face{font-family:"Satoshi";font-style:normal;font-weight:400;font-display:swap;src:url("../fonts/satoshi-400.woff2") format("woff2")}
@font-face{font-family:"Satoshi";font-style:normal;font-weight:500;font-display:swap;src:url("../fonts/satoshi-500.woff2") format("woff2")}
@font-face{font-family:"Satoshi";font-style:normal;font-weight:700;font-display:swap;src:url("../fonts/satoshi-700.woff2") format("woff2")}
@font-face{font-family:"Satoshi";font-style:normal;font-weight:900;font-display:swap;src:url("../fonts/satoshi-900.woff2") format("woff2")}
@font-face{font-family:"JetBrains Mono";font-style:normal;font-weight:400;font-display:swap;src:url("../fonts/jetbrains-mono-400.woff2") format("woff2")}
@font-face{font-family:"JetBrains Mono";font-style:normal;font-weight:500;font-display:swap;src:url("../fonts/jetbrains-mono-500.woff2") format("woff2")}
@font-face{font-family:"JetBrains Mono";font-style:normal;font-weight:600;font-display:swap;src:url("../fonts/jetbrains-mono-600.woff2") format("woff2")}
@font-face{font-family:"JetBrains Mono";font-style:normal;font-weight:700;font-display:swap;src:url("../fonts/jetbrains-mono-700.woff2") format("woff2")}
@font-face{font-family:"Space Grotesk";font-style:normal;font-weight:400;font-display:swap;src:url("../fonts/space-grotesk-400.woff2") format("woff2")}
@font-face{font-family:"Space Grotesk";font-style:normal;font-weight:500;font-display:swap;src:url("../fonts/space-grotesk-500.woff2") format("woff2")}
@font-face{font-family:"Space Grotesk";font-style:normal;font-weight:600;font-display:swap;src:url("../fonts/space-grotesk-600.woff2") format("woff2")}
@font-face{font-family:"Space Grotesk";font-style:normal;font-weight:700;font-display:swap;src:url("../fonts/space-grotesk-700.woff2") format("woff2")}

/* ========================================================================== */
/*  1. DESIGN TOKENS                                                           */
/* ========================================================================== */
:root {
  /* — Type families — */
  --font-display: 'Space Grotesk', system-ui, -apple-system, sans-serif; /* headings, numerals, brand */
  --font-body:    'Satoshi', system-ui, -apple-system, sans-serif;       /* body, UI, labels */
  --font-mono:    'JetBrains Mono', ui-monospace, 'SF Mono', monospace;  /* code, IDs, metrics, tags */

  /* — Type scale — */
  --t-hero:    clamp(40px, 5.2vw, 66px);
  --t-h1:      36px;
  --t-h2:      clamp(26px, 3.2vw, 34px);
  --t-h3:      22px;
  --t-h4:      17px;
  --t-lead:    19px;
  --t-body:    16px;
  --t-sm:      14px;
  --t-xs:      12.5px;
  --t-eyebrow: 12.5px;

  /* — Ink (text) — */
  --text:   #1C2030;  /* primary text & headings */
  --text-2: #565B6E;  /* secondary / body copy */
  --text-3: #5E6377;  /* tertiary / meta (AA on white) */
  --text-4: #6E7284;  /* faint / table headers (AA on white) */
  --text-decor: #AFB2C0;  /* decorative only, non-text */
  /* rail ink — its own tokens so each theme can hold AA without moving --text-4 / --accent
     everywhere else. Measured: heads 4.8:1 on white; active 5.5:1 at the gradient's worst stop
     (--accent there was 4.4:1). */
  --nav-head:      #6E7284;
  --nav-active-fg: #3754C8;

  /* — Surfaces — */
  --bg:        #F6F7FB;  /* app canvas / page background */
  --bg-2:      #FBFBFD;  /* subtle raised row hover */
  --surface:   #FFFFFF;  /* cards, panels, bars */
  --surface-2: #F2F3F8;  /* inset wells, chips, hover fills */
  --line:      #E7E8F0;  /* primary borders */
  --line-2:    #EEEFF5;  /* hairline dividers inside cards */

  /* — Brand — */
  --accent: #3E63DD;  /* primary blue — actions, links, focus */
  --violet: #8A5CF5;  /* secondary — webhooks, accents */
  --teal:   #0FB8A6;  /* tertiary — success-ish brand pop */
  --grad:      linear-gradient(105deg, #3E63DD, #8A5CF5 58%, #0FB8A6);
  --grad-soft: linear-gradient(105deg, rgba(62,99,221,.12), rgba(138,92,245,.12) 58%, rgba(15,184,166,.12));

  /* — Client rail: "inside a client" is a zone, not a label. The VIEWING CLIENT card's own
       gradient family (--grad-soft) at a much quieter strength, on a faintly tinted ground, so
       the rail's group cards read as one piece with that card and never like the flat firm rail.
       Contrast (AA, worst gradient stop): heads 5.5:1, items 6.4:1,
       active 5.2:1, count badge 5.3:1 (6.4:1 on the active item). — */
  --crail-ground:    #F3F5FD;  /* the rail's ground behind the cards */
  --crail-card-base: #FFFFFF;  /* opaque base under the card tint */
  --crail-card-tint: linear-gradient(105deg, rgba(62,99,221,.04), rgba(138,92,245,.04) 58%, rgba(15,184,166,.04));
  --crail-line:      #E1E5F6;  /* card border — the context card uses it too */
  --crail-shadow:    0 1px 2px rgba(44,56,130,.06);
  --crail-head:      #5B6391;  /* group title inside a card */
  --crail-hover:     rgba(62,99,221,.07);
  --crail-active:    linear-gradient(105deg, rgba(62,99,221,.13), rgba(138,92,245,.13) 58%, rgba(15,184,166,.13));
  --crail-active-fg: #3754C8;
  --crail-badge:     rgba(62,99,221,.12);
  --crail-badge-fg:  #3554C4;

  /* — Status (fg + tint pairs) — */
  --ok:   #0E7A4B;  --ok-bg:   #E4F6EE;  /* pass / healthy */
  --fail: #C1272D;  --fail-bg: #FCEAEA;  /* fail / error */
  --warn: #8A6100;  --warn-bg: #FBF1DD;  /* warning / degraded */
  --run:  #3554C4;  --run-bg:  #E8ECFB;  /* running / in-progress */
  --idle: #5F6474;  --idle-bg: #EEEFF4;  /* idle / queued / neutral */

  /* — Radii — */
  --r-lg: 16px;  /* cards, large panels */
  --r-md: 12px;  /* inner cards, wells */
  --r-sm: 9px;   /* buttons, inputs, nav items */
  --r-xs: 6px;   /* tags, tiny chips */
  --r-pill: 20px;

  /* — Elevation — */
  --shadow-sm:  0 1px 2px rgba(28,32,48,.04), 0 1px 3px rgba(28,32,48,.06);
  --shadow-md:  0 4px 16px rgba(28,32,48,.06), 0 1px 4px rgba(28,32,48,.05);
  --shadow-lg:  0 18px 50px -12px rgba(28,32,48,.22), 0 6px 18px rgba(28,32,48,.08);
  --shadow-pop: 0 12px 40px -8px rgba(28,32,48,.20);

  /* — Spacing rhythm (use multiples of 4) — */
  --space-1: 4px;  --space-2: 8px;  --space-3: 12px; --space-4: 16px;
  --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-12: 48px;

  /* — Layout — */
  --rail-w: 240px;
  --maxw: 1400px;

  /* — Marketing aliases (so landing-page components read naturally) — */
  --ink:   var(--text);
  --ink-2: var(--text-2);
  --ink-3: var(--text-3);
  --paper:   var(--surface);
  --paper-2: var(--bg);
  --paper-3: var(--surface-2);
}

/* ========================================================================== */
/*  2. RESET + BASE                                                            */
/* ========================================================================== */
* { box-sizing: border-box; margin: 0; padding: 0; }
[hidden] { display: none !important; }
body {
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: 1.55;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
h1,h2,h3,h4,h5 { font-family: var(--font-display); font-weight: 600; letter-spacing: -0.02em; line-height: 1.12; color: var(--text); }
h1 { font-size: var(--t-h1); font-weight: 700; letter-spacing: -0.03em; }
h2 { font-size: var(--t-h2); }
h3 { font-size: var(--t-h3); }
h4 { font-size: var(--t-h4); }
p  { color: var(--text-2); }
a  { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; }
input, select, textarea { font-family: inherit; font-size: inherit; color: inherit; }
::selection { background: rgba(138,92,245,.20); }

/* App pages pin the shell to the viewport (the design sets this per page inline). */
html.crucible-app, html.crucible-app body { height: 100%; overflow: hidden; }

/* scrollbars — slim, neutral */
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb { background: #D7D9E4; border-radius: 8px; border: 3px solid transparent; background-clip: content-box; }
*::-webkit-scrollbar-thumb:hover { background: #C2C5D4; background-clip: content-box; }
*::-webkit-scrollbar-track { background: transparent; }

/* ========================================================================== */
/*  3. TYPOGRAPHY UTILITIES                                                    */
/* ========================================================================== */
.display   { font-family: var(--font-display); font-weight: 700; letter-spacing: -0.035em; line-height: 0.98; }
.hero-type { font-size: var(--t-hero); font-family: var(--font-display); font-weight: 700; letter-spacing: -0.04em; line-height: 0.98; }
/* WHERE THE MONOSPACE FACE BELONGS — one rule, because two were in use and the page showed it.
   .mono is for MACHINE STRINGS: things read or copied character by character, where a slipped
   digit or a swapped 0/O changes what the string means — client codes, task refs, PAN, GSTIN,
   TAN, IFSC, account numbers, file names, URLs, keys. A different typeface is the signal that
   this is an identifier, not prose.
   It is NOT for values a person reads as language or as a quantity. A date ("Today, 09:15"),
   an amount (₹115) and a count (4 / 12) are ordinary field values and belong in the body face
   like every other value in their row — the audit log's When column set in JetBrains Mono next
   to an Action column set in the body face was one table speaking in two voices.
   Those still need their digits to LINE UP down a column, which is what .tnum is for: tabular
   figures in the body face. Alignment without a typeface change. Use .tnum on dates, money,
   counts and percentages; use .mono only on identifiers. */
.mono      { font-family: var(--font-mono); }
.tnum      { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }
.lead      { font-size: var(--t-lead); line-height: 1.5; color: var(--text-2); }
.small     { font-size: var(--t-sm); color: var(--text-2); }
.meta      { font-size: var(--t-xs); color: var(--text-3); }
.eyebrow   { font-family: var(--font-body); font-size: var(--t-eyebrow); font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent); }
.grad-text { background: var(--grad); -webkit-background-clip: text; background-clip: text; color: transparent; }

/* ========================================================================== */
/*  4. BUTTONS                                                                 */
/* ========================================================================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 38px; padding: 0 15px; border-radius: var(--r-sm);
  font-size: 14px; font-weight: 600; color: var(--text);
  background: var(--surface); border: 1px solid var(--line);
  transition: background .12s, border-color .12s, box-shadow .12s, transform .04s;
  white-space: nowrap;
}
.btn:hover  { background: var(--surface-2); border-color: #DBDDE8; }
.btn:active { transform: translateY(.5px); }
.btn svg    { width: 16px; height: 16px; }
.btn.primary { background: var(--accent); color: #fff; border-color: transparent; box-shadow: 0 2px 8px -2px rgba(62,99,221,.5); }
.btn.primary:hover { background: #3556c8; }
.btn.grad    { background: var(--grad); color: #fff; border-color: transparent; box-shadow: 0 4px 14px -3px rgba(62,99,221,.5); }
.btn.grad:hover { filter: brightness(1.05); }
.btn.ghost   { background: transparent; border-color: transparent; color: var(--text-2); }
.btn.ghost:hover { background: var(--surface-2); color: var(--text); }
.btn.danger  { color: var(--fail); }
.btn.danger:hover { background: var(--fail-bg); border-color: rgba(229,72,77,.3); }
.btn.sm   { height: 32px; padding: 0 11px; font-size: 13px; }
.btn.lg   { height: 48px; padding: 0 22px; font-size: 15.5px; border-radius: 11px; }
.btn.icon { width: 38px; padding: 0; flex: none; min-width: 38px; }
.btn.icon.sm { width: 32px; min-width: 32px; }
.btn[disabled] { opacity: .45; cursor: default; pointer-events: none; }

/* ========================================================================== */
/*  5. CARDS / SURFACES                                                        */
/* ========================================================================== */
.card     { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--shadow-sm); }
.card-pad { padding: 20px; }
.card.hover, .card-hover { transition: box-shadow .14s, transform .14s, border-color .14s; }
.card.hover:hover, .card-hover:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); border-color: #DCDEEA; }
.divider  { height: 1px; background: var(--line); margin: 16px 0; border: none; }

/* ========================================================================== */
/*  6. CHIPS · TAGS · STATUS DOTS · BADGES                                     */
/* ========================================================================== */
.chip {
  display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 9px;
  border-radius: var(--r-pill); font-size: 12px; font-weight: 600; font-family: var(--font-body);
  background: var(--surface-2); color: var(--text-2); border: 1px solid transparent; white-space: nowrap;
}
.chip .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.chip.ok   { background: var(--ok-bg);   color: var(--ok); }
.chip.fail { background: var(--fail-bg); color: var(--fail); }
.chip.warn { background: var(--warn-bg); color: var(--warn); }
.chip.run  { background: var(--run-bg);  color: var(--run); }
.chip.idle { background: var(--idle-bg); color: var(--idle); }
.chip.line { background: var(--surface); border-color: var(--line); color: var(--text-2); }
/* The ✕ that removes a chip's value. The glyph is ~10px, which is a fine target for a cursor and
   no target at all for a thumb, so the hit area is grown with a pseudo-element instead of the box:
   the chip keeps its size and the tappable region reaches ~44px on a phone. */
.chip .chip-x {
  position: relative; display: inline-grid; place-items: center;
  width: 16px; height: 16px; margin-left: 3px; border-radius: 5px;
  color: var(--text-4); text-decoration: none; line-height: 1;
}
.chip .chip-x::after { content: ''; position: absolute; inset: -7px -6px; }
.chip .chip-x:hover { background: var(--surface-2); color: var(--text); }
@media (hover: none) { .chip .chip-x::after { inset: -14px -10px; } }

.dot-ok { background: var(--ok); } .dot-fail { background: var(--fail); }
.dot-warn { background: var(--warn); } .dot-run { background: var(--run); } .dot-idle { background: var(--idle); }

.tag {
  display: inline-flex; align-items: center; height: 22px; padding: 0 8px; border-radius: var(--r-xs);
  font-size: 11.5px; font-weight: 600; font-family: var(--font-mono);
  background: var(--surface-2); color: var(--text-3); letter-spacing: -.01em;
}
/* HTTP method badge — color set inline per method (GET #18A368, POST #3E63DD, PUT #8A5CF5, PATCH #E0930A, DELETE #E5484D) */
.method-badge { font-family: var(--font-mono); font-weight: 700; letter-spacing: .02em; padding: 2px 7px; border-radius: 5px; font-size: 11px; }

.pulse { animation: pulse 1.4s ease-in-out infinite; }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }

/* ========================================================================== */
/*  7. FORM CONTROLS                                                           */
/* ========================================================================== */
.input, .field-input {
  width: 100%; height: 38px; padding: 0 12px;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--bg-2);
  font-size: 14px; outline: none; transition: border-color .12s, box-shadow .12s;
}
textarea.input { height: auto; padding: 8px 10px; resize: vertical; }
/* native select chevrons sit flush against the border; draw our own with proper inset */
select.input, select.field-input {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding-right: 34px; cursor: pointer;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%236B7280' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center;
}
select.input::-ms-expand { display: none; }
.input::placeholder, .field-input::placeholder, textarea.input::placeholder { color: var(--text-decor); opacity: 1; }
.input:focus, .field-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(62,99,221,.12); background: var(--surface); }
/* branded keyboard focus ring — accessibility (mouse clicks unaffected) */
:where(a, button, .btn, .nav-item, .seg > *, [tabindex], input, select, textarea):focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-xs);
}
.btn.grad:focus-visible, .btn.primary:focus-visible { outline-color: #fff; }
/* Every rail stands on a light surface in light theme, so a white ring there was invisible. The
   rail's ring is the accent in both themes (5.2:1 on white, 5.7:1 on the dark surface), and a
   destination's ring is drawn INSIDE its row: the list scrolls with overflow-x hidden, which
   clipped an outside ring at the rail's edges. The client rail's cards use the same rule. */
.rail :focus-visible { outline-color: var(--accent); }
.rail .nav-item:focus-visible { outline-offset: -2px; }
/* …except the ops rail, which is #0E1020 in BOTH themes (practice/ops/_shell.html): white stays
   the strongest ring there (17:1, where the accent would be 3.6:1). */
#opsRail :focus-visible { outline-color: #fff; }
.input.mono { font-family: var(--font-mono); font-size: 12px; }
.field { display: flex; flex-direction: column; gap: 5px; }
/* A refusal drawn against the box it is about, not only as a toast in the corner of the screen.
   The same class app.css and auth.css define, so every surface says "what is wrong, and where"
   the same way; the page-level banner stays for refusals that name no field. */
.fld-err { display: block; width: 100%; margin-top: 4px; font-size: 11.5px; font-weight: 600;
           line-height: 1.45; color: var(--fail); }
.input.bad, .field-input.bad { border-color: var(--fail); box-shadow: 0 0 0 3px rgba(220,60,60,.13); }
.field-label { font-size: 11.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--text-3); }

/* segmented control */
.seg { display: inline-flex; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm); padding: 3px; gap: 2px; }
.seg button, .seg a { height: 30px; padding: 0 12px; border-radius: 6px; font-size: 13px; font-weight: 600; color: var(--text-3); display: inline-flex; align-items: center; }
.seg button.on, .seg a.on { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }

/* search field */
.search {
  display: flex; align-items: center; gap: 9px; height: 38px; padding: 0 12px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm); color: var(--text-3);
}
.search input { border: none; background: none; outline: none; width: 100%; font-size: 14px; color: var(--text); }
.kbd { font-family: var(--font-mono); font-size: 11px; background: var(--surface); border: 1px solid var(--line); padding: 1px 6px; border-radius: 5px; color: var(--text-3); }

/* ========================================================================== */
/*  8. TABLE                                                                   */
/* ========================================================================== */
.tbl { width: 100%; border-collapse: collapse; }
/* Header padding: the design's stylesheet also declares `0 14px 11px`, but not one design screen
   renders that — every table overrides it inline with a top pad of 11–16px, `14px 14px 11px` on
   Clients / Tasks / Team / Templates / Import / Performance / Preferences. The screens that ported
   the inline style got it; the ones that leaned on this rule drew the header row jammed against the
   card's top border. Default to the value the design actually renders; the inline paddings that ARE
   in our markup keep winning, so nothing that already matched moves. */
.tbl th { text-align: left; font-size: 11.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--text-4); padding: 14px 14px 11px; border-bottom: 1px solid var(--line); }
.tbl td { padding: 13px 14px; border-bottom: 1px solid var(--line-2); font-size: 14px; vertical-align: middle; }
.tbl tr:last-child td { border-bottom: none; }
.tbl tbody tr { transition: background .1s; cursor: pointer; }
.tbl tbody tr:hover { background: var(--bg-2); }

/* ========================================================================== */
/*  9. METRIC / STAT CARD                                                      */
/* ========================================================================== */
.metric { padding: 18px; }
.metric .m-top { display: flex; align-items: center; justify-content: space-between; }
.metric .m-label { font-size: 13px; color: var(--text-3); font-weight: 600; }
.metric .m-ico { width: 30px; height: 30px; border-radius: 8px; display: grid; place-items: center; } /* set bg/color inline w/ a status tint */
/* Headline metrics keep the display face (they are headings, not field values) but take tabular
   figures, so a column of stacked metric cards lines up instead of wobbling by digit width. */
.metric .m-val { font-family: var(--font-display); font-size: 30px; font-weight: 700; letter-spacing: -.03em; margin-top: 12px; font-variant-numeric: tabular-nums; }
.metric .m-sub { font-size: 12.5px; color: var(--text-3); margin-top: 3px; display: flex; align-items: center; gap: 5px; }
.trend-up { color: var(--ok); font-weight: 700; } .trend-down { color: var(--fail); font-weight: 700; }

/* progress bar */
.bar { height: 6px; border-radius: 6px; background: var(--surface-2); overflow: hidden; }
.bar > span { display: block; height: 100%; border-radius: 6px; background: var(--grad); }

/* mini sparkbars */
.spark { display: flex; align-items: flex-end; gap: 3px; height: 34px; }
.spark > i { flex: 1; border-radius: 2px 2px 0 0; min-height: 2px; background: var(--surface-2); }

/* ========================================================================== */
/*  10. APP SHELL — sidebar rail + topbar                                      */
/* ========================================================================== */
.app { display: grid; grid-template-columns: var(--rail-w) 1fr; height: 100vh; }

.rail { background: var(--surface); border-right: 1px solid var(--line); display: flex; flex-direction: column; padding: 18px 14px; gap: 4px; overflow-y: auto; }
/* The rail's foot — who is signed in, and sign out — stays put while the destinations scroll.
   The two rail shapes are built differently and only one of them was broken:
     · appRail / clientRail wrap their destinations in a flex:1 box that scrolls on its own, so
       their foot already sat outside the scrolling list and never moved.
     · portalRail / empRail have no such wrapper — the destinations are direct children of .rail
       with a flex:1 spacer before the foot. There .rail itself is the scroller, so the foot
       scrolled away with the menu: on the client centre at 900px tall it was rendered 265px
       below the fold and you only met it by scrolling the whole menu to the end.
   Sticky pins it to the bottom of whichever box is the scroller, and is a no-op in the rails
   that never scroll. It needs an opaque base or the destinations show through it.
   The shadow: a sticky box cannot be pushed past its containing block, which ends where the
   rail's CONTENT ends, so bottom:0 leaves the rail's own bottom padding (18px, 16px on the icon
   rail) uncovered and destinations scroll visibly through that strip. A zero-blur shadow paints
   it in the rail's own colour; the scroll container clips the shadow at the rail's edge, so it
   costs nothing elsewhere and changes no layout. */
.rail-foot { flex: none; position: sticky; bottom: 0; z-index: 1; background-color: var(--surface); box-shadow: 0 22px 0 0 var(--surface); }
.brand { display: flex; align-items: center; gap: 11px; padding: 6px 8px 18px; }
.brand-mark { width: 34px; height: 34px; border-radius: 10px; flex: none; background: var(--grad); display: grid; place-items: center; box-shadow: 0 4px 14px -3px rgba(62,99,221,.5); }
.brand-mark svg { width: 20px; height: 20px; }
.brand-name { font-family: var(--font-display); font-weight: 700; font-size: 18px; letter-spacing: -0.03em; }
.brand-sub { font-size: 10.5px; color: var(--text-3); letter-spacing: .08em; text-transform: uppercase; font-weight: 600; margin-top: -2px; }

.nav-group-label { font-size: 10.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--nav-head); padding: 14px 10px 6px; }
.nav-item { display: flex; align-items: center; gap: 11px; padding: 8px 10px; border-radius: var(--r-sm); color: var(--text-2); font-size: 14px; font-weight: 500; width: 100%; text-align: left; transition: background .12s, color .12s; position: relative; white-space: nowrap; }
.nav-item > span { flex: 1; min-width: 0; }
.nav-item svg { width: 18px; height: 18px; flex: none; opacity: .75; }
.nav-item:hover { background: var(--surface-2); color: var(--text); }
.nav-item.active { background: var(--grad-soft); color: var(--nav-active-fg); font-weight: 600; }
.nav-item.active svg { opacity: 1; }
.nav-item.active::before { content: ''; position: absolute; left: -14px; top: 50%; transform: translateY(-50%); width: 3px; height: 20px; border-radius: 0 3px 3px 0; background: var(--grad); }
.nav-badge { margin-left: auto; flex: none; font-family: var(--font-mono); font-size: 11px; font-weight: 600; background: var(--surface-2); color: var(--text-3); padding: 1px 7px; border-radius: var(--r-pill); }
/* `.nav-item > span { flex: 1 }` (0,1,1) outranks `.nav-badge { flex: none }` (0,1,0), so every
   count in every rail — firm, client, portal, employee — grew into a pill running to the rail's
   edge ("This period 11", "Notifications 2"). The badge is a span too; say so at equal weight. */
.nav-item > .nav-badge { flex: none; }
/* the foot IS the My account destination — same "you are here" treatment as a nav item */
.rail-foot.active { background: var(--grad-soft); border-radius: var(--r-sm); }
/* --grad-soft is a translucent gradient; the foot is sticky, so it needs an opaque base under
   it or the destinations scroll visibly through the "My account" highlight. */
.rail-foot.active { background-color: var(--surface); }
.rail-foot.active .rail-foot-info > div:first-child { color: var(--nav-active-fg); }
.nav-item.active .nav-badge { background: rgba(62,99,221,.14); color: var(--nav-active-fg); }

/* ── Inside a client: every group of the rail is a card ──────────────────────────────────────
   The firm rail (appRail) and the client rail (clientRail) used to be the same flat list, so
   nothing but a label said which level you were on. The client rail now has a SHAPE of its own:
   a faintly tinted ground, and each group (Client, Employees, Work, Reports) its own card in the
   VIEWING CLIENT card's gradient family. Scoped to .rail.crail — the firm, portal and employee
   rails are untouched. The foot is sticky and opaque, so it takes the same ground. */
.rail.crail { background: var(--crail-ground); }
.rail.crail .rail-foot { background-color: var(--crail-ground); box-shadow: 0 22px 0 0 var(--crail-ground); }
.crail-group { display: flex; flex-direction: column; gap: 1px; padding: 2px 6px 6px;
  background-color: var(--crail-card-base); background-image: var(--crail-card-tint);
  border: 1px solid var(--crail-line); border-radius: var(--r-md); box-shadow: var(--crail-shadow); }
.crail-group > .nav-group-label { padding: 8px 8px 4px; color: var(--crail-head); }
.crail-group .nav-item { padding: 6px 8px; }
.crail-group .nav-item:hover { background: var(--crail-hover); }
.crail-group .nav-item.active { background: var(--crail-active); color: var(--crail-active-fg); }
/* the "you are here" bar sits on the card's inner edge, not out on the rail's */
.crail-group .nav-item.active::before { left: -7px; height: 18px; }
.crail-group .nav-badge { background: var(--crail-badge); color: var(--crail-badge-fg); }
/* on the active tint the count sits on the card's own base: 6:1 light, 7:1 dark (the tinted badge was 4.4 / 3.7) */
.crail-group .nav-item.active .nav-badge { background: var(--crail-card-base); color: var(--crail-active-fg); }

.avatar { border-radius: 50%; flex: none; display: grid; place-items: center; font-weight: 700; font-family: var(--font-display); background-color: var(--surface-2); color: #fff; } /* initials avatar; a photo may overlay via inline background-image */

/* `backdrop-filter` makes .topbar a STACKING CONTEXT. Left non-positioned it painted in the
   in-flow layer, so everything drawn after it — .scroll, .page, the page-head buttons — covered
   its own positioned children: the user dropdown (z-index 111) rendered *under* "Bulk import",
   "+ Add client" and the row count, and every click in it landed on .page instead of a menu item.
   Positioning the bar lifts the whole group (menu included) into the positioned layer. 55 keeps it
   above all page content while staying below every overlay: drawers (60/61 … 120/121), modals
   (131), the FAB (80), the mobile tab bar (2000) and the rail drawer (2100). */
.topbar { position: relative; z-index: 55; height: 62px; flex: none; border-bottom: 1px solid var(--line); background: rgba(251,251,253,.85); backdrop-filter: blur(10px); display: flex; align-items: center; gap: 16px; padding: 0 26px; }
.crumbs { display: flex; align-items: baseline; gap: 8px; font-size: 14px; color: var(--text-3); min-width: 0; overflow: hidden; }
/* the leaf is the only shrinkable part of the trail: truncate on one line, never wrap */
.crumbs .cur { color: var(--text); font-weight: 600; min-width: 0; white-space: nowrap !important; overflow: hidden; text-overflow: ellipsis; }
.crumbs > *:not(.cur) { flex: none; white-space: nowrap; }

.main { display: flex; flex-direction: column; overflow: hidden; min-width: 0; }
.scroll { flex: 1; overflow-y: auto; overflow-x: hidden; }
.page { padding: 28px 32px 56px; max-width: var(--maxw); margin: 0; }
.page-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 20px; margin-bottom: 24px; }
.page-head > *:first-child { min-width: 0; }
.page-head h1 { white-space: nowrap; }
.page-head p { margin-top: 6px; font-size: 15px; max-width: 560px; }

/* layout helpers */
.kbar { display: flex; gap: 8px; align-items: center; }
.grid { display: grid; gap: 16px; }
.metric-grid { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 1180px) { .metric-grid { grid-template-columns: repeat(2, 1fr); } }
.two-col { display: grid; grid-template-columns: 1fr 360px; gap: 16px; align-items: start; }
@media (max-width: 1100px) { .two-col { grid-template-columns: 1fr; } }

/* ========================================================================== */
/*  11. CODE BLOCK (dark)                                                      */
/* ========================================================================== */
.code-block { background: #0E0E18; color: #C8CBE0; font-family: var(--font-mono); font-size: 12.5px; line-height: 1.75; padding: 18px 20px; border-radius: var(--r-md); overflow: auto; }
.code-block .c-key { color: #A9BEFF; }   /* keys */
.code-block .c-ok  { color: #6DD7B8; }   /* values / pass */
.code-block .c-dim { color: #686C88; }   /* comments */
.code-block .c-err { color: #F0888C; }   /* errors */

/* ========================================================================== */
/*  12. ENTRANCE ANIMATION (transform-only — safe for print/reduced-motion)    */
/* ========================================================================== */
.fade-in { animation: fadeIn .4s cubic-bezier(.2,.7,.2,1) both; }
/* opacity only — a transform here would make .page a containing block for its position:fixed drawers */
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .fade-in { animation: none; } }

/* ========================================================================== */
/*  13. MARKETING / LANDING COMPONENTS                                         */
/*  (Use these on marketing pages; the app uses sections 4–11.)                */
/* ========================================================================== */
.wrap { max-width: 1180px; margin: 0 auto; padding: 0 28px; }

/* sticky translucent nav */
.m-nav { position: sticky; top: 0; z-index: 50; background: rgba(255,255,255,.82); backdrop-filter: blur(14px); border-bottom: 1px solid var(--line); }
.m-nav-inner { display: flex; align-items: center; gap: 28px; height: 70px; }
.m-nav-links { display: flex; align-items: center; gap: 26px; }
.m-nav-links a { font-size: 15px; font-weight: 500; color: var(--text-2); white-space: nowrap; transition: color .12s; }
.m-nav-links a:hover { color: var(--text); }
@media (max-width: 860px) { .m-nav-links { display: none; } }
/* marketing hero & inline multi-column rows restack on tablet/phone */
@media (max-width: 820px) {
  .m-hero-grid, .split, .two-col { grid-template-columns: 1fr !important; }
  .m-hero-grid figure, .m-hero-grid .m-hero-media { order: -1; }
}
@media (max-width: 640px) {
  .m-hero-cards, .stats-band, .fgrid, .tiers { grid-template-columns: 1fr !important; }
}

/* hero */
.m-hero { position: relative; padding: 88px 0 70px; overflow: hidden; }
.m-hero::before { content: ''; position: absolute; top: -180px; left: 50%; transform: translateX(-50%); width: 1100px; height: 620px; background: radial-gradient(ellipse at center, rgba(138,92,245,.16), rgba(62,99,221,.10) 40%, transparent 68%); z-index: -1; }
.m-hero h1 { font-size: clamp(42px, 6vw, 76px); font-weight: 700; letter-spacing: -0.04em; line-height: 0.98; max-width: 920px; }
.m-hero .sub { font-size: clamp(18px, 2.1vw, 21px); color: var(--text-2); max-width: 620px; margin-top: 24px; line-height: 1.5; }
.hero-badge { display: inline-flex; align-items: center; gap: 9px; height: 34px; padding: 0 14px; border-radius: var(--r-pill); background: var(--surface); border: 1px solid var(--line); font-size: 13.5px; font-weight: 600; color: var(--text-2); box-shadow: var(--shadow-sm); }
.hero-badge .pip { width: 7px; height: 7px; border-radius: 50%; background: var(--teal); }

/* section scaffolding */
.section { padding: 96px 0; }
.section-head { max-width: 680px; margin-bottom: 54px; }
.section-head.center { margin-left: auto; margin-right: auto; text-align: center; }
.section-head h2 { font-size: clamp(32px, 4vw, 48px); font-weight: 700; letter-spacing: -0.035em; margin-top: 14px; }
.section-head p { font-size: 19px; color: var(--text-2); margin-top: 18px; line-height: 1.5; }

/* split feature row */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: 64px; align-items: center; }
.split.flip .split-media { order: -1; }
@media (max-width: 900px) { .split { grid-template-columns: 1fr; gap: 36px; } .split.flip .split-media { order: 0; } }
.split h3 { font-size: 30px; letter-spacing: -0.03em; margin: 16px 0; }
.feat-list { margin-top: 26px; display: flex; flex-direction: column; gap: 16px; }
.feat-item { display: flex; gap: 13px; align-items: flex-start; }
.feat-item span { color: var(--text-2); }
.feat-check { width: 24px; height: 24px; border-radius: 7px; background: var(--grad-soft); color: var(--accent); display: grid; place-items: center; flex: none; margin-top: 1px; }
.feat-check svg { width: 14px; height: 14px; }

/* feature grid card */
.fgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
@media (max-width: 900px) { .fgrid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 600px) { .fgrid { grid-template-columns: 1fr; } }
.fcard { padding: 26px; border-radius: var(--r-lg); border: 1px solid var(--line); background: var(--surface); transition: box-shadow .16s, transform .16s; }
.fcard:hover { box-shadow: var(--shadow-lg); transform: translateY(-3px); }
.fcard .fi { width: 44px; height: 44px; border-radius: 12px; display: grid; place-items: center; margin-bottom: 18px; } /* set a status tint bg/color inline */
.fcard h4 { font-size: 18.5px; margin-bottom: 9px; }
.fcard p { font-size: 15px; color: var(--text-2); }

/* dark stats band */
.stats-band { background: var(--text); color: #fff; border-radius: 22px; padding: 52px; display: grid; grid-template-columns: repeat(4,1fr); gap: 30px; position: relative; overflow: hidden; }
.stats-band::before { content: ''; position: absolute; inset: 0; background: radial-gradient(circle at 85% 10%, rgba(138,92,245,.35), transparent 50%), radial-gradient(circle at 10% 90%, rgba(15,184,166,.22), transparent 45%); }
.stat { position: relative; }
.stat .v { font-family: var(--font-display); font-weight: 700; font-size: 46px; letter-spacing: -0.03em; background: linear-gradient(120deg,#fff,#C7D0FF); -webkit-background-clip: text; background-clip: text; color: transparent; }
.stat .l { font-size: 15px; color: #A8ACC4; margin-top: 6px; }
@media (max-width: 820px) { .stats-band { grid-template-columns: 1fr 1fr; gap: 28px; padding: 36px; } }

/* comparison table */
.cmp { border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--shadow-sm); }
.cmp table { width: 100%; border-collapse: collapse; }
.cmp th, .cmp td { padding: 16px 20px; text-align: left; font-size: 15px; border-bottom: 1px solid var(--line); }
.cmp thead th { background: var(--bg); font-family: var(--font-display); font-weight: 600; }
.cmp thead th.us { background: var(--grad-soft); color: var(--accent); }
.cmp td.col-us { background: rgba(62,99,221,.04); }
.cmp .yes { color: var(--ok); font-weight: 700; } .cmp .no { color: var(--text-3); } .cmp .partial { color: var(--warn); font-weight: 600; }
.cmp tbody tr:last-child td { border-bottom: none; }

/* pricing tiers */
.tiers { display: grid; grid-template-columns: repeat(3,1fr); gap: 22px; align-items: stretch; }
/* !important: several pages set their column count inline (repeat(4,1fr) on home/plans);
   without it the tablet collapse loses to the inline style and the body scrolls sideways. */
@media (max-width: 900px) { .tiers { grid-template-columns: 1fr !important; max-width: 440px; margin: 0 auto; } }
.tier { padding: 32px; border-radius: var(--r-lg); border: 1px solid var(--line); background: var(--surface); display: flex; flex-direction: column; min-width: 0; }
/* A grid item defaults to min-width:auto, so the nowrap sublabels ("per firm / month · by client
   count") plus 2x32px padding set a hard floor that pushed the card past a 320px viewport. Let the
   labels wrap and give the padding back on the narrowest phones. */
@media (max-width: 480px) {
  .tier { padding: 24px 18px; }
  .tier .price span, .tier .badge { white-space: normal !important; }
}
.tier.featured { position: relative; box-shadow: var(--shadow-lg); background: linear-gradient(var(--surface),var(--surface)) padding-box, var(--grad) border-box; border: 2px solid transparent; }
.tier .badge { position: absolute; top: -13px; left: 32px; height: 26px; padding: 0 13px; border-radius: var(--r-pill); background: var(--grad); color: #fff; font-size: 12.5px; font-weight: 700; display: grid; place-items: center; }
.tier .price { font-family: var(--font-display); font-weight: 700; font-size: 46px; letter-spacing: -0.03em; margin: 16px 0 4px; }
.tier .price span { font-size: 16px; color: var(--text-3); font-weight: 500; }
.tier ul { list-style: none; margin: 22px 0; display: flex; flex-direction: column; gap: 12px; }
.tier li { display: flex; gap: 10px; align-items: flex-start; font-size: 15px; color: var(--text-2); }
.tier li svg { width: 18px; height: 18px; color: var(--accent); flex: none; margin-top: 2px; }
.tier .btn { margin-top: auto; width: 100%; }

/* dark CTA band */
.cta-band { text-align: center; padding: 84px 40px; border-radius: 24px; background: var(--text); color: #fff; position: relative; overflow: hidden; }
.cta-band::before { content: ''; position: absolute; inset: 0; background: radial-gradient(circle at 50% 0%, rgba(138,92,245,.4), transparent 55%); }
.cta-band > * { position: relative; }
.cta-band h2 { color: #fff; font-size: clamp(32px, 4.5vw, 52px); letter-spacing: -0.035em; }
.cta-band p { color: #B6BAD0; font-size: 19px; margin: 18px auto 32px; max-width: 540px; }

/* footer */
.footer { border-top: 1px solid var(--line); padding: 60px 0 40px; }
.footer-grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: 32px; }
@media (max-width: 900px) { .footer-grid { grid-template-columns: 1fr 1fr 1fr !important; gap: 26px; } }
@media (max-width: 560px) { .footer-grid { grid-template-columns: 1fr 1fr !important; gap: 24px; } }
.footer-col h5 { font-size: 13px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--text-3); margin-bottom: 16px; }
.footer-col a { display: block; font-size: 15px; color: var(--text-2); margin-bottom: 11px; transition: color .12s; }
.footer-col a:hover { color: var(--text); }

/* reveal-on-scroll (pair with an IntersectionObserver that adds .in) */
.reveal { opacity: 0; transform: translateY(20px); transition: opacity .7s cubic-bezier(.2,.7,.2,1), transform .7s cubic-bezier(.2,.7,.2,1); }
.reveal.in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .reveal { opacity: 1; transform: none; transition: none; } }

/* ========================================================================== */
/*  14. RESPONSIVE — tablet & mobile                                           */
/*  Firm-app shells wrap their grid in .app-shell (rail column + main).        */
/*  Below 960px the sidebar collapses to an icon rail; below 640px pages       */
/*  go fluid. Inline widths are overridden with !important on purpose.         */
/* ========================================================================== */
.app-shell { display: grid; grid-template-columns: 248px 1fr; height: 100vh; }

@media (max-width: 960px) {
  .app-shell { grid-template-columns: 66px 1fr !important; }
  .rail .nav-group-label { display: none !important; }
  .rail .brand-name, .rail .brand-sub { display: none !important; }
  .rail { width: 100% !important; padding: 16px 8px !important; align-items: stretch; }
  .rail .brand { justify-content: center; padding: 4px 0 14px; }
  .rail .brand-name, .rail .brand-sub, .rail-foot-info { display: none !important; }
  .rail .nav-group-label { text-align: center; padding: 12px 0 5px; font-size: 0; }
  .rail .nav-group-label::after { content: '•'; font-size: 12px; letter-spacing: 0; }
  .nav-item { justify-content: center; padding: 10px 0; }
  .nav-item > span:not(.nav-badge) { display: none; }
  .nav-item .nav-badge { position: absolute; top: 3px; right: 6px; margin: 0; padding: 0 4px; font-size: 9px; min-width: 0; }
  .nav-item.active::before { left: -8px; }
  .rail-foot { flex-shrink: 0; flex-direction: column !important; align-items: center !important; justify-content: center !important; gap: 8px !important; }
  .rail-foot > * { flex: none !important; min-width: 0; }
  .rail-foot .avatar { margin: 0 auto; }
  /* the client rail's cards survive as icon clusters: still a card per group, just narrow */
  .crail-group { padding: 3px; border-radius: 10px; }
  .crail-group .nav-item { padding: 8px 0; }
  .crail-group .nav-item.active::before { left: -4px; }
}
/* the 66px icon rail only (the phone drawer, <=700px, is full width again): the VIEWING CLIENT
   card was squeezed to 50px and clipped its own words ("VIEWI CLIENT", "View clie") — it becomes
   the client's initials over an icon-only way back to the list */
@media (min-width: 701px) and (max-width: 960px) {
  .crail-card { padding: 8px 0 !important; display: flex; flex-direction: column; align-items: center; border-radius: 12px !important; }
  .crail-card .crail-eyebrow, .crail-card .crail-who, .crail-card .crail-back-label { display: none; }
  .crail-card > div { margin-top: 0 !important; }
  .crail-card .crail-back { margin-top: 8px !important; }
  .crail-card .crail-back .btn { width: 32px !important; padding: 0; }
}

@media (max-width: 640px) {
  .topbar { padding: 0 14px; gap: 10px; height: 56px; }
  .page { padding: 18px 15px 48px; }
  .page-head { flex-direction: column; align-items: flex-start; gap: 10px; }
  .page-head h1 { white-space: normal; font-size: 26px; }
  .page-head p { font-size: 14px; }
  .scroll { overflow-x: auto; }
  .grid[style*="repeat(4"], .grid[style*="repeat(3"] { grid-template-columns: 1fr 1fr !important; }
  .kbar { flex-wrap: wrap; }
  .kbar .search, .kbar input { width: 100% !important; }
  h1 { font-size: 26px; }
  /* tab/segment bars and two-col layouts stack cleanly on phones */
  .seg { flex-wrap: wrap; }
  .two-col { grid-template-columns: 1fr !important; }
  /* content cards that were pinned to a fixed max-width go fluid */
  .card[style*="max-width"] { max-width: 100% !important; }
}

@media (max-width: 460px) {
  /* EVERY multi-column .grid collapses to one column here. This used to name three literal
     values — repeat(3, repeat(4 and "1fr 1fr" — so a grid written with fractional tracks
     (1.5fr 1fr on /firm/dashboard, 1.2fr .8fr and .8fr 1.2fr on the metrics screen) or with
     repeat(5 stayed two-/five-up and spilled: a card's right edge measured 632px inside a
     360px grid at 390px wide.

     An attribute selector cannot count tracks, so the match is by SHAPE, not by an explicit
     list of the values in use — an explicit list is what failed above, and it fails again the
     next time somebody writes 1fr 1.1fr:
       · "fr "        a space directly after an fr track only ever occurs when a SECOND track
                      follows; a lone track ends at the ";" or at the closing quote. Covers
                      1fr 1fr, 1.5fr 1fr, 1.2fr .8fr, .8fr 1.2fr, 1fr 1fr 1fr, 1fr 1.1fr, …
       · repeat(2..9  the counted form. repeat(auto-fit, minmax(min(Npx,100%),1fr)) is
                      deliberately NOT matched — it already collapses itself, and forcing it
                      would be a no-op at best.
     Checked against every element carrying class "grid" in templates/: 0 single-column grids
     matched, 0 multi-column grids missed. GridCollapseTest asserts both halves so this cannot
     silently rot again. */
  .grid[style*="fr "],
  .grid[style*="repeat(2"], .grid[style*="repeat(3"], .grid[style*="repeat(4"],
  .grid[style*="repeat(5"], .grid[style*="repeat(6"], .grid[style*="repeat(7"],
  .grid[style*="repeat(8"], .grid[style*="repeat(9"] { grid-template-columns: 1fr !important; }
}

/* --- topbar never overflows: shrink search, pin the avatar --- */
@media (max-width: 1100px) {
  .topbar { min-width: 0; }
  .topbar > * { min-width: 0; }
  .topbar .seg { min-width: 0; }
  .topbar .search { width: 190px !important; flex: 0 1 190px !important; }
  .topbar .tb-search { width: 190px !important; flex: 0 1 190px !important; }
  /* the user menu must always stay reachable */
  .topbar .tb-user { margin-left: auto; flex: none !important; }
}
@media (max-width: 860px) {
  /* non-essentials drop out; breadcrumbs + search truncate instead of spilling */
  .topbar .tb-hide { display: none !important; }
  .topbar .crumbs { min-width: 0 !important; overflow: hidden !important; text-overflow: ellipsis; white-space: nowrap; flex: 0 1 auto; }
  .topbar .search input { text-overflow: ellipsis; }
  /* search stays typable — it is a live input with its own dropdown, not a modal trigger */
  .topbar .search { width: auto !important; flex: 1 1 120px !important; min-width: 0 !important; }
  .topbar .tb-search { width: auto !important; flex: 1 1 120px !important; min-width: 0 !important; }
  .topbar .search input { min-width: 0 !important; }
  .topbar .kbd { display: none !important; }
}
/* A phone's trail has ~100px beside search and the icons, so "Clients / ABC Traders / Tasks"
   showed "Clients / ABC T" and never the leaf. Keep the one crumb that says where you are:
   with links, only the nearest link (inside a client, the client — the page's own h1 repeats
   the leaf directly below); with no link, only the leaf ("Masters / C…" becomes "Clients").
   Markup untouched: separators and sections are hidden by position, not by class. */
@media (max-width: 700px) {
  .topbar .crumbs > *:has(~ a),
  .topbar .crumbs:has(> a) > .cur,
  .topbar .crumbs:has(> a) > span:has(+ .cur),
  .topbar .crumbs:not(:has(> a)) > *:not(.cur) { display: none; }
  /* the one crumb left is worth more than search's spare width: search keeps a 48px box it can
     never be squeezed below (it was drawn under the Capture button), and a long client name
     takes what remains and ends in an ellipsis */
  .topbar .crumbs { flex: 0 1 auto !important; }
  .topbar .tb-search { flex: 1 0 48px !important; }
  .topbar .crumbs > a { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text) !important; font-weight: 600; }
}


/* Split auth / portal hero panels: hide the decorative side below tablet */
@media (max-width: 820px) {
  .split-shell { grid-template-columns: 1fr !important; }
  .split-shell > .split-aside { display: none !important; }
}

/* --- tablet / phone: data tables, chat split, drawers, dense grids --- */
@media (max-width: 900px) {
  /* every data table scrolls sideways inside its card instead of squashing */
  .card:has(> table.tbl), .card:has(> div > table.tbl) { overflow-x: auto !important; }
  table.tbl { min-width: 640px; }
  /* two-pane chat: thread list on top, conversation below */
  [style*="grid-template-columns:280px 1fr"],
  [style*="grid-template-columns:300px 1fr"],
  [style*="grid-template-columns: 280px 1fr"] {
    grid-template-columns: 1fr !important;
    grid-template-rows: minmax(190px, 34vh) 1fr !important;
  }
  /* right-slide drawers go near-full-width so forms stay usable.
     This used to also list [style*="width:520px"] / 480px / 440px, because the drawers carried
     their geometry inline and no class. Two problems with that: an attribute selector outranks
     .drawer, so the phone sheet rules below could never take effect on the very panels that
     needed them; and it silently missed every drawer built at some other width (400, 430, 460,
     470, 540, 560). They all carry .drawer now, so the class is the whole list. */
  .drawer { width: min(94vw, 480px) !important; }
}
@media (max-width: 760px) {
  /* report launcher + any 2-up card grid becomes single column */
  .grid[style*="repeat(2"] { grid-template-columns: 1fr !important; }
  table.tbl th, table.tbl td { padding: 11px 13px !important; font-size: 12.5px !important; }
}

/* ========================================================================== */
/*  15. DARK MODE — token overrides. Set data-theme="dark" on <html>.          */
/*  Everything token-driven (classes + inline var() styles) flips             */
/*  automatically. Uses :root[data-theme] for specificity over :root.         */
/* ========================================================================== */
:root[data-theme="dark"] {
  --text:   #E7E9F2;
  --text-2: #ABB0C4;
  --text-3: #838899;
  --text-4: #5E6379;
  /* rail ink in dark: heads 4.8:1 on the rail's surface (--text-4 was 2.9:1); active 5.4:1 on
     the active gradient (--accent was 4.2:1) */
  --nav-head:      #80859C;
  --nav-active-fg: #8CA3FF;

  --bg:        #0D0F1A;
  --bg-2:      #1A1D2E;
  --surface:   #161927;
  --surface-2: #212539;
  --line:      #2B2F46;
  --line-2:    #24283C;

  --accent: #6E8BFF;
  --violet: #A588FF;
  --teal:   #22CDB8;
  --grad-soft: linear-gradient(105deg, rgba(110,139,255,.16), rgba(165,136,255,.16) 58%, rgba(34,205,184,.16));
  /* client rail — its own dark values, not the light ones inverted: a ground a shade deeper and
     bluer than the firm rail's surface, cards a step lighter, and brighter ink for the heads and
     the active item (the firm rail's --text-4 heads are 2.9:1 here; these are 5.8:1 / 4.7:1). */
  --crail-ground:    #121629;
  --crail-card-base: #1A1E33;
  --crail-card-tint: linear-gradient(105deg, rgba(110,139,255,.06), rgba(165,136,255,.06) 58%, rgba(34,205,184,.06));
  --crail-line:      #2C3254;
  --crail-shadow:    0 1px 0 rgba(0,0,0,.28);
  --crail-head:      #98A1CC;
  --crail-hover:     rgba(110,139,255,.10);
  --crail-active:    linear-gradient(105deg, rgba(110,139,255,.20), rgba(165,136,255,.20) 58%, rgba(34,205,184,.20));
  --crail-active-fg: #9DB0FF;
  --crail-badge:     rgba(110,139,255,.20);
  --crail-badge-fg:  #B4C2FF;

  --ok:   #34D399;  --ok-bg:   rgba(24,163,104,.18);
  --fail: #FF6B6F;  --fail-bg: rgba(229,72,77,.18);
  --warn: #F0B138;  --warn-bg: rgba(224,147,10,.18);
  --run:  #6E8BFF;  --run-bg:  rgba(62,99,221,.22);
  --idle: #9499AD;  --idle-bg: rgba(148,153,173,.16);

  --shadow:     0 1px 2px rgba(0,0,0,.5);
  --shadow-md:  0 8px 24px rgba(0,0,0,.5);
  --shadow-pop: 0 18px 50px rgba(0,0,0,.6);
  color-scheme: dark;
}
/* common hardcoded light surfaces that should follow the theme */
:root[data-theme="dark"] body { background: var(--bg); color: var(--text); }
:root[data-theme="dark"] .m-nav { background: rgba(13,15,26,.82) !important; border-color: var(--line); }
:root[data-theme="dark"] .topbar { background: rgba(16,19,31,.85); border-bottom-color: var(--line); }
/* frosted sticky headers on legal / emails / portal pages hardcode a near-white rgba */
:root[data-theme="dark"] [style*="rgba(251,251,253"] { background: rgba(16,19,31,.9) !important; }
:root[data-theme="dark"] input,
:root[data-theme="dark"] select,
:root[data-theme="dark"] textarea { background-color: var(--surface-2); color: var(--text); }
/* the background shorthand above must not wipe the select chevron — redraw it, lighter for dark surfaces */
:root[data-theme="dark"] select.input, :root[data-theme="dark"] select.field-input {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%23ABB0C4' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center;
}
:root[data-theme="dark"] input::placeholder,
:root[data-theme="dark"] textarea::placeholder { color: var(--text-4); }
:root[data-theme="dark"] img { filter: brightness(.92); }
/* hard-coded light greys the design source left behind (a colour that survives
   the theme switch is a defect) */
:root[data-theme="dark"] *::-webkit-scrollbar-thumb { background: #2F3450; background-clip: content-box; }
:root[data-theme="dark"] *::-webkit-scrollbar-thumb:hover { background: #3A4160; background-clip: content-box; }
:root[data-theme="dark"] .btn:hover { border-color: #3A4160; }
:root[data-theme="dark"] .card.hover:hover, :root[data-theme="dark"] .card-hover:hover { border-color: #3A4160; }

/* marketing comparison tables: scroll sideways on small screens */
@media (max-width: 900px) {
  .cmp { overflow-x: auto !important; -webkit-overflow-scrolling: touch; }
  .cmp table, table.cmp2 { min-width: 560px; }
  .cmp2-wrap { overflow-x: auto; }
}

/* table.tbl-cards on phones lives in its own file — /css/card-tables.css — because the
   legacy CA shell (crucible.css + app.css) needs the same rules and does not load this
   stylesheet. Both shells link it right after their own. */

/* --- mobile: page furniture around a data table --- */
@media (max-width: 700px) {
  .page-head > div > p { display: none; }
  /* pagination stays comfortable */
  .card > div[style*="border-top"], .card > .pagebar { flex-wrap: wrap; }
}


/* ============ mobile navigation: bottom tab bar + rail as drawer ============ */
.mtabs { display: none; }
.msheet-bg { display: none; }
@media (max-width: 700px) {
  /* the side rail becomes a slide-in drawer, opened from the More tab */
  .app-shell { grid-template-columns: 1fr !important; }
  .app-shell > .rail, aside.rail {
    position: fixed !important; top: 0; bottom: 0; left: 0; width: 262px !important;
    z-index: 2100; transform: translateX(-100%); transition: transform .22s cubic-bezier(.2,.7,.2,1);
    box-shadow: 12px 0 40px rgba(15,17,32,.22);
  }
  html.rail-open aside.rail { transform: none; }
  /* a reduced-motion request opens and closes the drawer in place instead of sliding it */
  @media (prefers-reduced-motion: reduce) { .app-shell > .rail, aside.rail { transition: none !important; } }
  /* restore full labels inside the drawer (they were hidden for the icon rail) */
  aside.rail .nav-item span, aside.rail .nav-group-label,
  aside.rail .brand-name, aside.rail .brand-sub { display: revert !important; }
  /* the 960px icon rail blanks the group label to font-size:0 and draws a '•' in its place.
     display:revert alone left the drawer showing four bullets where MASTERS / WORK /
     COMMUNICATION / INSIGHTS belong — undo the whole icon-rail treatment, not just display. */
  aside.rail .nav-group-label { font-size: 10.5px !important; letter-spacing: .12em; text-align: left !important; padding: 14px 10px 6px !important; }
  aside.rail .nav-group-label::after { content: none !important; }
  /* same for the foot: the drawer is 262px wide, so the signed-in name and the "My account"
     line belong back on one row — and without them there is nothing for active to mark. */
  aside.rail .rail-foot-info { display: revert !important; }
  aside.rail .rail-foot { flex-direction: row !important; align-items: center !important; justify-content: flex-start !important; gap: 10px !important; }
  aside.rail .rail-foot > a { flex: 1 !important; min-width: 0; }
  aside.rail .nav-item { justify-content: flex-start !important; padding: 9px 12px !important; }
  /* …and the icon rail's corner badge: back in line at the end of its row, where the desktop
     rail draws it, instead of floating over the item's top-right corner. */
  aside.rail .nav-item .nav-badge { position: static; margin-left: auto; padding: 1px 7px; font-size: 11px; }
  /* the client rail's cards keep their own, tighter rhythm inside the drawer */
  aside.rail .crail-group { padding: 2px 6px 6px; border-radius: var(--r-md); }
  aside.rail .crail-group > .nav-group-label { padding: 8px 8px 4px !important; }
  aside.rail .crail-group .nav-item { padding: 8px 10px !important; }
  aside.rail .crail-group .nav-item.active::before { left: -7px; }

  .msheet-bg { display: block; position: fixed; inset: 0; background: rgba(15,17,32,.45); z-index: 2050; }
  .msheet-bg[hidden] { display: none !important; }

  /* bottom tab bar */
  .mtabs {
    display: grid; grid-template-columns: repeat(5, 1fr);
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 2000;
    background: var(--surface); border-top: 1px solid var(--line);
    padding-bottom: env(safe-area-inset-bottom, 0);
    box-shadow: 0 -4px 20px rgba(15,17,32,.07);
  }
  .mtabs .mtab {
    position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px; padding: 8px 2px 7px; min-height: 56px;
    border: none; background: none; cursor: pointer;
    color: var(--text-3); text-decoration: none; font-family: var(--font-body);
  }
  .mtabs .mtab svg { width: 21px; height: 21px; }
  .mtabs .mtab span { font-size: 10.5px; font-weight: 600; letter-spacing: .01em; }
  .mtabs .mtab.on { color: var(--accent); }
  .mtabs .mtab.on::before { content: ''; position: absolute; top: 0; left: 22%; right: 22%; height: 2.5px; border-radius: 0 0 3px 3px; background: var(--accent); }
  .mtabs .mtdot { position: absolute; top: 7px; right: 50%; margin-right: -16px; width: 7px; height: 7px; border-radius: 50%; background: var(--fail); }
  /* inside a client the bar stands on the client rail's ground, and the client's tab wears the
     client's own initials — the phone's version of the desktop's card-shaped rail */
  .mtabs.mtabs-client { background-color: var(--crail-card-base); background-image: var(--grad-soft); border-top-color: var(--crail-line); }
  .mtabs .mtab-client .avatar { width: 22px; height: 22px; font-size: 9px; letter-spacing: 0; box-shadow: 0 0 0 1.5px var(--crail-card-base); }
  .mtabs .mtab-client.on .avatar { box-shadow: 0 0 0 1.5px var(--crail-card-base), 0 0 0 3px var(--accent); }

  /* content clears the tab bar */
  .scroll { padding-bottom: 64px; }
  .page { padding-right: 15px !important; }
}

/* mobile pagination: count on its own line, controls as one centred wrapping row */
@media (max-width: 700px) {
  .card > div[style*="border-top"]:has(> .meta), .card > .pagebar {
    display: flex; flex-direction: row; flex-wrap: wrap;
    justify-content: center; align-items: center;
    gap: 7px; padding: 13px 14px 15px !important;
  }
  /* the count spans the full width and leads */
  .card > div[style*="border-top"]:has(> .meta) > .meta, .card > .pagebar > .meta {
    order: 1; flex: 1 0 100%; text-align: center; font-size: 12px; margin-bottom: 2px;
  }
  .card > div[style*="border-top"]:has(> .meta) > *, .card > .pagebar > * { order: 2; }
  /* kill the desktop flex spacer — including the shared fragment's OWN spacer, which carries
     class="pg-spacer" and so never matched div:not([class]). Left alive it is a flex:1 1 0% item
     in a wrapping row: it ate the free space mid-row and broke the page numbers across two lines
     at an arbitrary point (…| 5 6 7 8 / 9 17 Next), and took a line of its own besides. */
  .card > div[style*="border-top"]:has(> .meta) > div:not([class]):empty,
  .card > .pagebar > div:not([class]):empty,
  .card > .pagebar > .pg-spacer { display: none; }
  .card > div[style*="border-top"]:has(> .meta) > .btn.sm, .card > .pagebar > .btn.sm {
    flex: none; min-width: 40px; height: 34px;
  }
}

/* wide data tables must always be reachable — never clipped by the card */
.card:has(> table.tbl), .card:has(> div > table.tbl) { overflow-x: auto !important; }
@media (max-width: 1100px) { table.tbl { min-width: 980px; } }

/* Phase 1 · customer Workspace: thread + checklist stack on tablet and below */
@media (max-width: 1000px) {
  .ws-grid { grid-template-columns: 1fr !important; }
}

/* Client persona toggle: stays visible in the topbar (it is the only switcher on that surface),
   compacting rather than disappearing, and only yields once the bottom tab bar takes over. */
.topbar .tb-persona { flex: none; }
@media (max-width: 1000px) {
  .topbar .tb-persona button { padding: 0 8px; font-size: 11.5px; }
}
@media (max-width: 700px) { .topbar .tb-persona { display: none; } }

/* marketing header: let its children shrink rather than overflow. The one "Log in" (.mn-login)
   is never hidden at any width: the header always shows exactly one Log in and one Get started. */
.m-nav-inner > * { min-width: 0; }
.m-nav-links { min-width: 0; flex-shrink: 1; }
.m-nav .mn-search span, .m-nav .mn-search .mn-kbd { display: none !important; }
.m-nav-inner { gap: 16px; }
.m-nav .m-nav-links { gap: 17px; }
@media (max-width: 1240px) {
  .m-nav .btn.ghost:not(.mn-login) { display: none !important; }
  .m-nav-inner { gap: 13px; }
  .m-nav .m-nav-links { gap: 12px; }
  .m-nav-links a.mn-link { font-size: 14px; }
}
@media (max-width: 1040px) {
  .m-nav .mn-search span { display: none !important; }
  .m-nav .btn.ghost:not(.mn-login) { display: none !important; }
  .m-nav-links { gap: 14px; }
  .m-nav-links a.mn-link { font-size: 14px; }
}

/* journey split chat: stack thread list above conversation on narrow screens */
@media (max-width: 900px) {
  .jsplit { grid-template-columns: 1fr !important; grid-template-rows: minmax(150px, 26vh) 1fr !important; }
  .jsplit > div:first-child { border-right: none !important; border-bottom: 1px solid var(--line); }
}

/* ========================================================================== */
/*  16. PRODUCT ADDITIONS — the pieces every design page carried inline        */
/* ========================================================================== */

/* Keyframes the design repeats per page (kept byte-identical to the source) */
@keyframes toastIn  { from { transform: translateY(14px); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes drawerIn { from { transform: translateX(30px); opacity: 0; } to { transform: none; opacity: 1; } }
/* heartbeat — the pulse ring on the current step of the individual journey (design: v2pulse) */
@keyframes heartbeat {
  0%   { box-shadow: 0 0 0 0 rgba(62,99,221,.42); }
  70%  { box-shadow: 0 0 0 11px rgba(62,99,221,0); }
  100% { box-shadow: 0 0 0 0 rgba(62,99,221,0); }
}
.heartbeat { animation: heartbeat 2.2s infinite; }
@media (prefers-reduced-motion: reduce) {
  .heartbeat { animation: none; }
  .toast { animation: none; }
  /* !important: every drawer carries `animation:drawerIn …` in its inline style, so
     without it a reduced-motion request loses to the markup and the panel still slides. */
  .drawer { animation: none !important; }
}

/* Toast — mirrors the design's inline toast (fixed, bottom-right, dark pill) */
.toast {
  position: fixed; right: 24px; bottom: 24px; z-index: 80;
  background: var(--text); color: var(--bg);
  border-radius: 12px; padding: 12px 18px; font-size: 14px; font-weight: 500;
  box-shadow: var(--shadow-pop);
  animation: toastIn .2s cubic-bezier(.2,.7,.2,1) both;
  max-width: 420px;
}
:root[data-theme="dark"] .toast { background: var(--surface-2); color: var(--text); border: 1px solid var(--line); }

/* Drawer scaffold — mirrors the design's right-slide panel (440px, near-full on phones).
   .drawer / .drawer-bg is the ONE selector family every side panel in the app joins;
   the phone bottom-sheet block below is what that membership buys. native.css repeats
   the same conversion for the handful of surfaces that also load it. */
.drawer-bg { position: fixed; inset: 0; background: rgba(15,17,32,.42); z-index: 120; }
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; width: 440px; max-width: 94vw;
  background: var(--surface); z-index: 121;
  box-shadow: -12px 0 40px rgba(15,17,32,.18);
  display: flex; flex-direction: column;
  animation: drawerIn .22s cubic-bezier(.2,.7,.2,1) both;
}
.drawer-head { display: flex; align-items: flex-start; gap: 10px; padding: 18px 20px; border-bottom: 1px solid var(--line); flex: none; }
.drawer-title { font-family: var(--font-display); font-weight: 700; font-size: 17px; }
.drawer-body { flex: 1; overflow-y: auto; padding: 18px 20px; }
.drawer-foot { padding: 14px 20px; border-top: 1px solid var(--line); display: flex; gap: 8px; flex: none; }

/* ── Centred modal card — the OTHER member of the .drawer-bg family ────────────────
   Not every overlay is a side panel: a short confirm or a one-field capture is a card
   in the middle of the screen. Those still use .drawer-bg as their backdrop, and so
   they must be lifted with it. Three of them (portal Uploads, the public upload page,
   the client People revoke confirm) carried a bespoke inline `z-index:131` instead of
   joining a class. The phone rule below raises .drawer-bg to 2200 to clear the 2000
   tab bar; an inline 131 is not raised with it, so on a phone the backdrop painted OVER
   the card and a hit-test at the centre of the card's own primary button returned the
   backdrop — the modal opened and could not be used or dismissed (portal 72.8).
   Join this class instead of inventing another number. */
.modal-card {
  position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 420px; max-width: min(92vw, 460px); max-height: 88vh; overflow-y: auto;
  background: var(--surface); border-radius: 16px; box-shadow: var(--shadow-pop);
  padding: 22px 24px; z-index: 131;
}

/* ── Phone: a right-hand drawer is a bottom sheet, and it sits ABOVE the tab bar ──
   Two reasons every declaration here is !important:
   1. each screen picks its own desktop drawer width, so the geometry lives in the
      element's inline style — a plain rule would lose to it and the panel would stay
      a desktop side panel on a phone;
   2. .mtabs is z-index 2000. A drawer left on the scaffold's z-index 121 renders
      UNDERNEATH the phone tab bar, which swallows the drawer's own Close control and
      leaves the user with no way out but a reload.
   Clearing 2050 (.msheet-bg) and 2000 (.mtabs) is therefore load-bearing, not polish. */
@media (max-width: 700px) {
  .drawer-bg { z-index: 2200 !important; }
  /* Lifted WITH its backdrop, for exactly the reason above. The card stays centred — it is
     not a sheet — but it must sit above .drawer-bg (2200) and .mtabs (2000) or it cannot be
     tapped. !important because these cards carry their geometry inline. */
  .modal-card { z-index: 2201 !important; max-width: calc(100vw - 28px) !important; }
  .drawer {
    top: auto !important; bottom: 0 !important; left: 0 !important; right: 0 !important;
    width: auto !important; max-width: none !important;
    height: auto !important; max-height: 88vh !important;
    border-radius: 20px 20px 0 0 !important;
    border-left: none !important;
    z-index: 2201 !important;
    box-shadow: 0 -12px 40px rgba(15, 17, 32, .24) !important;
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
  /* A sheet rises; it does not slide in from a right edge it no longer has. !important beats the
     inline `animation:drawerIn`, and the no-preference guard keeps the reduced-motion rule above
     (which would otherwise be overruled by this !important) in charge when motion is unwanted.
     Deliberately NO fill-mode: `both` holds the FROM frame whenever the animation does not run,
     which parks the sheet 46px low and pushes its Close button off the bottom of the screen. With
     the default fill the resting position is the element's own — an animation that never plays
     costs an entrance, not the way out. */
  @media (prefers-reduced-motion: no-preference) {
    .drawer { animation: sheetIn .26s cubic-bezier(.22, 1, .36, 1) !important; }
  }
  /* the grab handle every native sheet has */
  .drawer::before {
    content: ''; position: absolute; left: 50%; top: 7px; transform: translateX(-50%);
    width: 38px; height: 4px; border-radius: 2px; background: var(--line); z-index: 3;
  }
  /* clear the handle, whether the first child is the scaffold head or bespoke markup */
  .drawer > *:first-child { padding-top: 16px !important; }
}
@keyframes sheetIn {
  from { transform: translateY(46px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

/* Sortable-header helper — the design's inline th button, as classes.
   crucible.js decorates th[data-sort-key] with .sort-btn and the arrow span. */
.tbl th .sort-btn {
  background: none; border: none; padding: 0; margin: 0; font: inherit; color: inherit;
  text-transform: inherit; letter-spacing: inherit; cursor: pointer;
  display: inline-flex; align-items: center; gap: 5px;
}
.tbl th .sort-btn.on { color: var(--accent); }
.tbl th .sort-arrow { font-size: 10px; opacity: .4; }
.tbl th .sort-btn.on .sort-arrow { opacity: 1; }


/* Pagination bar — the design's inline footer row, as a class (inline border-top
   stays on the markup so the design's mobile :has() rules also match) */
.pagebar { display: flex; align-items: center; gap: 8px; padding: 13px 16px; border-top: 1px solid var(--line); }
.pagebar .pg-spacer { flex: 1; }
.pagebar .btn.sm.pg-n { min-width: 32px; }
/* The gap mark between page numbers. It looks exactly like a .meta and deliberately is NOT one:
   the phone rule below lays every .meta in this bar out as the full-width count line, which threw
   the "…" onto a line of its own from ten pages up. */
.pagebar .pg-gap { font-size: var(--t-xs); color: var(--text-3); }

/* The page you are ON. Every numbered pager in the app — the shared paginationBar fragment and
   the hand-rolled ones on the dashboard, Tasks, Allocation, Billing, Import, Referrals, Support,
   Performance, Receivables, Ops and the journey papers — marks its current link with BOTH
   aria-current="page" (so a screen reader says "current page", which no amount of colour does)
   and .pg-on. Styling hangs off the attribute, so a pager can never look current without
   announcing it, or announce it without looking current. */
.btn.pg-on,
.btn[aria-current="page"] { background: var(--accent); color: #fff; border-color: transparent; min-width: 32px; }
.btn.pg-on:hover,
.btn[aria-current="page"]:hover { background: var(--accent); color: #fff; }

/* Theme toggle — same convention as the rest of the app: moon in light, sun in dark */
.theme-toggle { width: 38px; height: 38px; display: grid; place-items: center; border-radius: var(--r-sm); border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer; }
.theme-toggle:hover { background: var(--surface-2); color: var(--text); }
.theme-toggle .i-sun { display: none; }
.theme-toggle .i-moon { display: inline; }
:root[data-theme="dark"] .theme-toggle .i-sun { display: inline; }
:root[data-theme="dark"] .theme-toggle .i-moon { display: none; }

/* Row-click navigation affordance */
tr[data-href] { cursor: pointer; }

/* native.css opt-in hooks: .m-only elements are phone-only. native.css reverts
   this with display:revert !important inside its max-width:700px block. */
.m-only { display: none; }

/* Topbar user-menu dropdown (markup carries the design's inline styles; this only
   provides the hover state that the DC runtime applied via style-hover) */
.menu-item { display: flex; align-items: center; gap: 10px; padding: 10px 16px; text-decoration: none; color: inherit; font-size: 13.5px; width: 100%; background: none; border: none; text-align: left; cursor: pointer; }
.menu-item:hover { background: var(--bg-2); }
.menu-item.danger { color: var(--fail); border-top: 1px solid var(--line-2); }
.crumbs a:hover { color: var(--text); }
