/* ============================================================
   Meridian component layer — ported from
   docs/design/meridian-concept-v0.1.html (demo data ignored, structure
   and interaction behaviour kept).

   Load order (see docs/design/DISCOVERY.md / head.ejs): meridian-tokens.css,
   existing suite CSS, THIS FILE. Two deliberate scoping choices to protect
   that cascade position:

   1. Real class-name collisions exist between the prototype and the live
      app (.btn, .legend, .muted — checked against theme.css/leave.css/
      report.css/directory.css/audit.css/training.css). Since this file
      loads last, a bare .btn{} here would silently repaint every button
      in the app today, before Phase 3/4 markup exists to intentionally
      opt in. So every component rule below is scoped under .shell — the
      prototype's real outer wrapper class, which no current template
      uses. That makes this whole layer inert dead weight until Phase 3
      adds the rail/shell markup, then it activates exactly as designed.
      (No !important anywhere, and nothing needs it: scoping under one
      ancestor class is enough on its own.)
   2. Typography, focus-visible, reduced-motion and the type-scale/ink
      utilities are deliberately NOT shell-scoped — Phase 2 asks for the
      font and accessibility base to apply everywhere immediately, ahead
      of the shell. .muted/.faint were renamed .ink-muted/.ink-faint to
      dodge the training.css collision.

   Suite-accent adaptation: the prototype paired --accent (primary) with
   --accent-soft as both fill+icon AND as the background under on-soft
   TEXT (nav items, active chips). The colour audit
   (docs/design/COLOR_TOKENS.md) found primary-on-soft fails AA for two
   suites — that's why the token file has a --{suite}-deep slot. Every
   text/icon-on-soft rule below reads var(--accent-deep, var(--accent))
   instead of var(--accent) — falls back to accent if a caller hasn't
   set --accent-deep, but uses it when they have.
   ============================================================ */

/* ---------- self-hosted type — SIL Open Font License 1.1, see public/fonts/OFL.txt ---------- */
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/instrument-sans-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: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/instrument-sans-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;
}

/* ============================================================
   GLOBAL — applies everywhere immediately, not shell-scoped
   ============================================================ */
body{
  font-family:'Instrument Sans',system-ui,-apple-system,'Segoe UI',sans-serif;
  background:var(--canvas);
  color:var(--ink);
  transition:background .3s var(--ease), color .3s var(--ease);
}
h1,h2,h3,h4{
  font-family:'Instrument Sans',system-ui,-apple-system,'Segoe UI',sans-serif;
}

/* Work Package T (continuation) Section 5: no app-wide placeholder rule
   existed anywhere in this codebase before this -- every <input>/
   <textarea> placeholder in every suite fell back to the browser's own
   default grey (measured rgb(117,117,117): 4.61:1 in light mode, a real
   but passing ratio, and 2.43:1 in dark mode, a real failure, since that
   fixed grey never adapts to a dark background). --ink-2 is an existing,
   already-managed token comfortably clearing 4.5:1 in both themes against
   the surfaces this codebase's own inputs actually sit on (7.84:1 light,
   4.99:1 dark, verified via scripts/check-shared-contrast-fixes-local.js)
   -- loads last (meridian.css), so it wins regardless of what any
   individual suite's own stylesheet does or doesn't set. */
::placeholder{color:var(--ink-2);opacity:1}

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

@media (prefers-reduced-motion:reduce){
  /* The one deliberate !important in this file: reduced-motion is a hard
     accessibility requirement, not a specificity contest with a
     component — it must win regardless of what any future rule sets. */
  *{animation-duration:.01ms!important;transition-duration:.01ms!important}
}

.skip-link{
  position:absolute;left:-9999px;top:auto;
  background:var(--ink);color:var(--surface);
  padding:10px 16px;border-radius:0 0 var(--r-chip) 0;
  font-weight:600;font-size:13px;z-index:300;
}
.skip-link:focus{left:0;top:0}

/* ============================================================
   TYPE SCALE
   ============================================================ */
.t-display{font-size:44px;line-height:1.02;font-weight:700;letter-spacing:-.033em}
.t-fig    {font-size:34px;line-height:1;font-weight:700;letter-spacing:-.028em}
.t-fig-sm {font-size:25px;line-height:1;font-weight:700;letter-spacing:-.022em}

/* THE FIGURE ON A DASHBOARD TILE. One size for every suite.
 *
 * Each suite had reached its own: SU Records and HR at --text-2xl (28px),
 * Training at a literal 1.8rem (28.8px), Audits at --text-3xl (36px), Leave
 * at .t-fig-sm (25px) and Compliance at .t-h2 (17px) — a HEADING class, so
 * its dashboard numbers were the size of a section title. Read side by side
 * the suites looked like different products.
 *
 * SU Records is the standard, so 28px is the number. Every suite's own
 * selector is listed HERE rather than corrected in its own stylesheet, so
 * there is one declaration and they cannot drift apart again — the same move
 * the page-title selectors above make.
 *
 * Scoped to .signal__top for Leave and Compliance: .t-h2 and .t-fig-sm are
 * general classes used elsewhere for their own reasons, and only the tile
 * figure is being normalised. Leave's two TEXT tiles ("Nothing booked", the
 * next bank holiday) carry an inline size and keep it, correctly — they are
 * sentences, not figures. */
.t-fig-tile,
.su-tile__value,
.hr-tile__value,
.audit-stat strong,
.training-metric strong,
.suite-card__count strong,
.signal__top .t-h2,
.signal__top .t-fig-sm {font-size:28px;line-height:1.1;font-weight:700;letter-spacing:-.024em}
.t-h1     {font-size:25px;line-height:1.2;font-weight:600;letter-spacing:-.02em}
/* A PAGE TITLE THAT CARRIES NO TYPE CLASS GETS THE BROWSER'S OWN h1 SIZE.
   Nothing in this codebase sets a size for a bare h1 — the scale is opt-in
   through these classes — so an h1 without one falls through to the user
   agent's 2em, which is 32px against this scale's 17px. Meridian HR's page
   heading and the Audits hero were the two places that happened, and they
   read half again the size of Leave, SU Records, Compliance, Training and the
   home page, all of which use .t-h2.

   Training's .training-heading h1 was a third, found 2026-09-29 at 32px on
   the Action Centre, In-Person Training, Reports and the rest.

   The page-title selectors are ADDED TO THE SCALE rather than given
   their own rule, so there is one declaration and they cannot drift back
   apart. check-heading-scale-live.js measures the rendered size on every
   suite's landing page and fails if any one of them differs. */
.t-h2,
.page-heading h1,
.profile-heading h1,
.training-heading h1,
.audit-hero h1 {font-size:17px;line-height:1.3;font-weight:600;letter-spacing:-.012em}

/* A dashboard title with its one-line description BESIDE it (2026-09-21):
   every suite dashboard stacked eyebrow, title, description and date on
   separate lines, pushing the figures down the page. On a narrow screen the
   description wraps under the title rather than squeezing it. */
.title-line{display:flex;flex-wrap:wrap;align-items:baseline;column-gap:14px;row-gap:2px;margin:0}
.title-line > h1{margin:0}
.title-line__desc{margin:0;font-size:13px;line-height:1.45;color:var(--ink-2)}
/* The live filter's announcement ("Results updated."), kept in the header's
   action row so it takes no height of its own. It stays rendered, empty,
   so screen readers still hear it when it changes. */
.live-status{margin:0;font-size:12.5px;line-height:1.45;color:var(--ink-2)}
.t-h3     {font-size:14px;line-height:1.35;font-weight:600;letter-spacing:-.006em}
.t-body   {font-size:14px;line-height:1.55}
.t-sm     {font-size:12.5px;line-height:1.45}
.t-xs     {font-size:11.5px;line-height:1.4}
.ink-muted{color:var(--ink-2)}
.ink-faint{color:var(--ink-3)}

/* ============================================================
   THE SPINE — the one signature device.
   A 3px leading bar that encodes state, not decoration. Global utility
   (no collision, safe to use standalone ahead of the shell).
   ============================================================ */
.spine{
  position:absolute;left:0;top:0;bottom:0;width:3px;
  background:var(--accent);
  border-radius:var(--r-card) 0 0 var(--r-card);
}
.spine--seg{display:flex;flex-direction:column;background:none;overflow:hidden}
.spine--seg i{display:block;flex:1}

/* ============================================================
   APP SHELL — everything below is scoped under .shell and inert
   until Phase 3 adds the wrapper markup.
   ============================================================ */
.shell{
  --suite-canvas:var(--canvas);
  --suite-outline:var(--line);
  /* Every suite must define a primary-button fill. Four did; SU Records,
     Compliance and the no-suite Operations shell did not, so
     .btn--primary fell through to var(--accent) -- which is only ever set
     on a .ribbon. Outside a ribbon that resolved to nothing: a transparent
     button with white text on a near-white canvas (measured 1.11:1, i.e.
     invisible). This neutral slate is the backstop so a shell added later
     cannot reintroduce that. Fills carry white text, so each is chosen
     against white, not against the canvas: 11.68:1 here. */
  --primary-button-fill:#2F3A3F;
  display:flex;min-height:100vh;background:var(--suite-canvas);
}
.shell--leave{--suite-canvas:var(--leave-canvas);--suite-outline:var(--leave-2);--primary-button-fill:#2C4320}
.shell--hr{--suite-canvas:var(--hr-canvas);--suite-outline:var(--hr-2);--primary-button-fill:#572934}
.shell--audits{--suite-canvas:var(--audits-canvas);--suite-outline:var(--audits-2);--primary-button-fill:#3A3700}
.shell--workforce{--suite-canvas:var(--workforce-canvas);--suite-outline:var(--workforce-2);--primary-button-fill:#02305C}
.shell--finance{--suite-canvas:var(--finance-canvas);--suite-outline:var(--finance-2);--primary-button-fill:#072F1D}
.shell--training{--suite-canvas:var(--training-canvas);--suite-outline:var(--training-2);--primary-button-fill:#3B315C}
/* EVERY FILL ABOVE IS THAT SUITE'S OWN -deep VALUE, WRITTEN AS A LITERAL,
   deliberately: the token itself flips lighter in dark mode, which a fill
   carrying white text cannot follow. SU Records is the exception in the other
   direction -- its base and its deep are the same colour, and it is the suite
   the others were levelled against on 2026-09-17.
   check-suite-palette.js asserts each of these equals the -deep token it is
   standing in for, so the literal cannot drift away from the palette. */
.shell--su{--suite-canvas:var(--su-canvas);--suite-outline:var(--su-2);--primary-button-fill:#7C451D}
.shell--policies{--suite-canvas:var(--policies-soft);--suite-outline:var(--policies-2);--primary-button-fill:#333C40}

/* Every authenticated suite uses the whole usable canvas.  Individual
   directory pages previously reintroduced narrow max-width variants, which
   made HR visibly stop short of the SU Records layout.  Content controls may
   still choose a readable width; the page canvas itself is never capped. */
.shell .site-main{width:100%;max-width:none;margin:0}

/* Carry SU Records' clear, accented tile boundary into the shared card
   primitive.  A suite sets --suite-outline once above, so the treatment is
   consistent without forcing SU's copper into HR, Leave, Audits, Training
   or Compliance. */
.shell .site-main .card{border-color:var(--suite-outline)}

/* Suite accents brighten in dark mode for text on the canvas, while primary
   buttons need a dark-enough solid fill for white text. Keep the fill role at
   shell level in both themes: ordinary Leave pages have no local --accent at
   all, while ribbon pages do. meridian.css loads after the suite styles. */

/* ---- left rail ---- */
.shell .rail{
  position:fixed;left:0;top:0;bottom:0;z-index:60;
  width:var(--rail);
  background:var(--surface);
  border-right:1px solid var(--line);
  display:flex;flex-direction:column;
  padding:10px 0;
  transition:width .28s var(--ease);
  overflow:hidden;
}
.shell .rail:hover,.shell .rail:focus-within{width:var(--rail-open);box-shadow:var(--shadow-lift,0 2px 4px rgba(15,31,27,.06),0 18px 40px -18px rgba(15,31,27,.28))}

.shell .rail__brand{
  display:flex;align-items:center;gap:12px;
  height:40px;padding:0 10px;margin-bottom:12px;flex:none;
}
.shell .rail__mark{
  width:44px;height:36px;flex:none;border-radius:9px;padding:1px;
  background:var(--surface);border:1px solid var(--line-2);
  display:grid;place-items:center;overflow:hidden;
}
.shell .rail__mark img{display:block;width:42px;height:20px;object-fit:contain}
.shell .rail__word{
  font-weight:600;font-size:15px;letter-spacing:-.02em;white-space:nowrap;
  opacity:0;transition:opacity .18s var(--ease);
}
.shell .rail:hover .rail__word,.shell .rail:focus-within .rail__word{opacity:1}

.shell .rail__group{padding:0 10px;display:flex;flex-direction:column;gap:2px}
.shell .rail__label{
  padding:16px 8px 6px;font-size:11px;font-weight:600;color:var(--ink-3);
  white-space:nowrap;opacity:0;transition:opacity .18s var(--ease);
  letter-spacing:.01em;
}
.shell .rail:hover .rail__label,.shell .rail:focus-within .rail__label{opacity:1}

.shell .navitem{
  position:relative;display:flex;align-items:center;gap:14px;
  height:44px;padding:0 5px;border-radius:9px;width:100%;
  color:var(--ink-2);transition:background .15s var(--ease),color .15s var(--ease);
  text-align:left;
}
.shell .navitem:hover{background:var(--surface-2);color:var(--ink)}
.shell .navitem__ic{
  width:34px;height:34px;flex:none;border-radius:9px;
  display:grid;place-items:center;
  background:var(--accent-soft);color:var(--accent-deep,var(--accent));
  transition:background .15s var(--ease);
}
.shell .navitem__ic svg{width:21px;height:21px;display:block}
.shell .navitem__tx{
  white-space:nowrap;overflow:hidden;font-weight:500;
  opacity:0;transition:opacity .18s var(--ease);
}
.shell .rail:hover .navitem__tx,.shell .rail:focus-within .navitem__tx{opacity:1}
.shell .navitem__badge{
  margin-left:auto;flex:none;font-size:11px;font-weight:600;
  padding:1px 6px;border-radius:20px;
  background:var(--danger-soft);color:var(--danger);
  opacity:0;transition:opacity .18s var(--ease);
}
.shell .rail:hover .navitem__badge,.shell .rail:focus-within .navitem__badge{opacity:1}
.shell .navitem__badge--test{
  opacity:1;background:#F4C84B;color:#4A3300;
  font-size:10px;font-weight:700;letter-spacing:.04em;
}

.shell .navitem[aria-current="page"]{background:var(--accent-soft);color:var(--accent-deep,var(--accent))}
.shell .navitem[aria-current="page"] .navitem__tx{font-weight:600}
.shell .navitem[aria-current="page"]::before{
  content:"";position:absolute;left:-10px;top:9px;bottom:9px;width:3px;
  background:var(--accent);border-radius:0 3px 3px 0;
}
.shell .navitem--soon{opacity:.5;cursor:default}
.shell .navitem--soon:hover{background:none}

.shell .rail__foot{margin-top:auto;padding:0 10px}

/* ---- top bar ---- */
.shell .main{
  flex:1;margin-left:var(--rail);min-width:0;display:flex;flex-direction:column;
  background:var(--suite-canvas);
}

.shell .topbar{
  position:sticky;top:0;z-index:50;height:var(--bar);flex:none;
  display:flex;align-items:center;gap:14px;
  padding:0 22px;
  border-bottom:1px solid var(--line);
  background:color-mix(in srgb,var(--suite-canvas) 82%,transparent);
  backdrop-filter:saturate(1.6) blur(14px);
  -webkit-backdrop-filter:saturate(1.6) blur(14px);
}
.shell .crumb{display:flex;align-items:center;gap:8px;min-width:0}
.shell .crumb__dot{width:7px;height:7px;border-radius:2px;background:var(--accent);flex:none}
.shell .crumb__here{font-weight:600;letter-spacing:-.012em;white-space:nowrap}
.shell .crumb__sep{color:var(--ink-3)}

.shell .searchbtn{
  margin-left:auto;display:flex;align-items:center;gap:9px;
  height:34px;min-width:290px;padding:0 10px 0 11px;
  border:1px solid var(--line);border-radius:var(--r-chip);
  background:var(--surface);color:var(--ink-3);
  transition:border-color .15s var(--ease),box-shadow .15s var(--ease);
}
.shell .searchbtn:hover{border-color:var(--ink-3);box-shadow:var(--shadow,0 1px 2px rgba(15,31,27,.05),0 8px 24px -12px rgba(15,31,27,.16))}
.shell .searchbtn kbd{
  margin-left:auto;font:inherit;font-size:11px;font-weight:600;
  padding:2px 6px;border-radius:4px;background:var(--sunk);color:var(--ink-2);
}

.shell .iconbtn{
  width:34px;height:34px;flex:none;border-radius:var(--r-chip);
  display:grid;place-items:center;color:var(--ink-2);
  border:1px solid transparent;transition:background .15s,border-color .15s;
}
.shell .iconbtn:hover{background:var(--surface);border-color:var(--line);color:var(--ink)}
.shell .iconbtn svg{width:17px;height:17px}
/* .shell .iconbtn's own display:grid (2 classes) otherwise beats the UA
   [hidden]{display:none} default (attribute selectors are always low-
   specificity) — an author display rule silently un-hides any hidden
   iconbtn, exactly what happened to #themeBtn. 3 classes settles it. */
.shell .iconbtn[hidden]{display:none}

.shell .avatar{
  width:32px;height:32px;flex:none;border-radius:50%;
  background:linear-gradient(150deg,var(--leave),var(--leave-deep,var(--leave)));color:#fff;
  display:grid;place-items:center;font-size:11.5px;font-weight:700;letter-spacing:-.01em;
}

/* Work Package T (continuation) Section B: shared-shell presence strip,
   sitting immediately left of the search field (searchbtn's own
   margin-left:auto already pushes everything from here rightward, so this
   naturally lands in the header's middle band without a margin of its
   own). Same [hidden]-specificity discipline as .iconbtn above -- JS
   toggles the `hidden` attribute on both the row and each popover, so
   every one of these needs its own [hidden] override at matching
   specificity, or the white-on-white-button class of bug recurs here. */
/* The presence strip carries the auto margin that pushes the whole
   right-hand cluster over, so it lands immediately left of the search
   field rather than beside the breadcrumb. .searchbtn keeps an auto
   margin only for the case where the strip is hidden (no one to show),
   so the cluster still sits right in that state. */
.shell .presence-row{display:flex;align-items:center;gap:6px;flex:none;margin-left:auto;margin-right:4px}
.shell .presence-row:not([hidden]) ~ .searchbtn{margin-left:0}
.shell .presence-row[hidden]{display:none}
.shell .presence-avatar{
  position:relative;width:28px;height:28px;flex:none;border-radius:50%;border:1px solid var(--line);
  background:var(--surface);color:var(--ink);display:grid;place-items:center;
  font-size:10.5px;font-weight:700;letter-spacing:-.01em;cursor:pointer;padding:0;
  margin-left:-8px;transition:transform .12s var(--ease),z-index 0s;
}
.shell .presence-avatar:first-child{margin-left:0}
.shell .presence-avatar--self{border-color:var(--ok);box-shadow:0 0 0 1px color-mix(in srgb,var(--ok) 28%,transparent)}
.shell .presence-avatar:hover,.shell .presence-avatar:focus-visible{transform:translateY(-2px);z-index:2}
.shell .presence-avatar--away{opacity:.55}
.shell .presence-avatar--simulated{border-style:dashed;border-color:var(--warn)}
.shell .presence-dot{
  position:absolute;right:-1px;bottom:-1px;width:9px;height:9px;border-radius:50%;
  border:2px solid var(--surface);box-sizing:content-box;
}
.shell .presence-dot--active{background:var(--ok)}
.shell .presence-dot--away{background:transparent;border-color:var(--warn);border-width:2px}
.shell .presence-more-wrap{position:relative}
.shell .presence-more{
  width:28px;height:28px;border-radius:50%;border:1px solid var(--line);background:var(--sunk);
  color:var(--ink-2);font-size:10.5px;font-weight:700;cursor:pointer;margin-left:-8px;
}
.shell .presence-popover{
  position:absolute;top:36px;right:0;z-index:60;display:flex;flex-direction:column;gap:6px;
  padding:10px;border:1px solid var(--line);border-radius:var(--r-card);background:var(--surface);
  box-shadow:0 8px 24px -12px rgba(15,31,27,.28);min-width:160px;
}
.shell .presence-popover[hidden]{display:none}
.shell .presence-popover .presence-avatar{margin-left:0}
/* Click-to-open roster panel. Replaces the per-avatar title= tooltip: a
   tooltip needed a mouse and showed one person at a time, and on a touch
   screen it never appeared at all. The strip is position:relative so this
   anchors under it. */
.shell .presence-row{position:relative}
.shell .presence-panel{
  position:absolute;top:38px;right:0;z-index:60;
  padding:8px;border:1px solid var(--line);border-radius:var(--r-card);background:var(--surface);
  box-shadow:0 10px 28px -12px rgba(15,31,27,.32);min-width:248px;max-width:320px;
  max-height:min(60vh,420px);overflow-y:auto;
}
.shell .presence-panel[hidden]{display:none}
.shell .presence-panel__list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px}
.shell .presence-panel__item{
  display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:8px;
}
.shell .presence-panel__item.is-focus{background:var(--sunk)}
.shell .presence-panel__item .presence-avatar{margin-left:0;cursor:default}
.shell .presence-panel__who{display:flex;flex-direction:column;min-width:0}
.shell .presence-panel__name{
  font-size:13px;font-weight:600;color:var(--ink);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.shell .presence-panel__state{font-size:11.5px;color:var(--ink-3)}
.shell .presence-panel__state--active{color:var(--ok)}

@media (max-width:760px){
  .shell .presence-row{display:none!important}
}

/* ---- page canvas ---- */
/* No max-width: a 1900px cap still left visible dead margin on a large
   monitor (screenshot-confirmed, red-lined by the user against the true
   window edges) -- every page's own content already flexes to fill
   whatever width it's given, so there was never a real line-length reason
   to cap this. .page now always equals .main's full flex width (viewport
   minus the fixed rail), with only the existing small side padding left
   for breathing room. This can only make pages wider, never narrower, so
   any page whose own content is already wider than this is unaffected. */
.shell .page{flex:1;padding:26px 22px 64px;width:100%}
.shell .page__head{display:flex;align-items:flex-end;gap:20px;margin-bottom:22px;flex-wrap:wrap}
.shell .page__head h1{margin:0}
.shell .page__sub{margin:5px 0 0;color:var(--ink-2)}
.shell .page__act{margin-left:auto;display:flex;gap:8px;flex-wrap:wrap}

/* suite ribbon: replaces the old hero banners (Phase 4) */
.shell .ribbon{
  display:flex;align-items:center;gap:12px;
  padding:0 0 16px;margin-bottom:20px;
  border-bottom:1px solid var(--line);
}
.shell .ribbon__ic{
  width:38px;height:38px;flex:none;border-radius:10px;
  background:var(--accent);color:#fff;display:grid;place-items:center;
}
.shell .ribbon__ic svg{width:20px;height:20px}
/* On a phone the title keeps the row beside its icon and the buttons move to
   a line of their own; sharing the row squeezed the title into a column a few
   words wide (2026-09-21). */
@media (max-width:640px){
  .shell .ribbon{flex-wrap:wrap}
  /* !important because every ribbon's title block carries an inline
     style="flex:1", which would otherwise win. */
  .shell .ribbon > div:has(> .title-line),
  .shell .ribbon > .title-line,
  .shell .ribbon > div:has(h1){flex:1 1 calc(100% - 50px) !important}
}
[data-theme="dark"] .shell .ribbon__ic{color:#0E1416}

/* ============================================================
   BUTTONS / CHIPS — Phase 4 page-content vocabulary.
   Scoped under .shell .page, not bare .shell: .page doesn't exist in any
   current template (Phase 4 hasn't restyled a page yet), so this stays
   fully dormant. Bare .shell would reach *every* existing page's own
   content too (the whole body is necessarily inside .shell for the rail
   offset to work) — and .btn is a real, already-used class in production
   CSS (theme.css/directory.css/audit.css/training.css all define it).
   Confirmed the hard way: an early draft used bare .shell .btn and it
   silently repainted the live Audits "Run an audit" button transparent
   (white text on white, from audit.css's own .btn losing the fight).
   ============================================================ */
.shell .page .btn{
  display:inline-flex;align-items:center;gap:7px;height:34px;padding:0 13px;
  border-radius:var(--r-chip);border:1px solid var(--line);
  background:var(--surface);font-weight:500;font-size:13px;
  transition:.15s var(--ease);white-space:nowrap;
}
/* Its own [hidden] override, at THIS selector's specificity. theme.css's
   .btn[hidden] is not enough here: .shell .page .btn is three classes and
   wins, so a hidden .btn inside a suite page stayed visible while doing
   nothing. su-records.css already carries the same guard for one specific
   button and explains the trap; this covers every .btn on every suite page,
   which is where live-filter.js hides an Apply button after binding. */
.shell .page .btn[hidden]{display:none}
.shell .page .btn:hover{border-color:var(--ink-3);box-shadow:var(--shadow,0 1px 2px rgba(15,31,27,.05),0 8px 24px -12px rgba(15,31,27,.16))}
.shell .page .btn svg{width:15px;height:15px}
.shell .page .btn--primary{
  background:var(--primary-button-fill,var(--accent));border-color:transparent;color:#fff;font-weight:600;
}
/* Button fills are not text tokens: retain white text after the shared rule
   wins the cascade in both themes. */
[data-theme="dark"] .shell .page .btn--primary{color:#fff}
.shell .page .btn--primary:hover{filter:brightness(1.08)}
.shell .page .btn--ghost{background:none;border-color:transparent;color:var(--ink-2)}
.shell .page .btn--positive{background:var(--ok);border-color:transparent;color:#fff;font-weight:600}
[data-theme="dark"] .shell .page .btn--positive{color:#0E1416}
.shell .page .btn--positive:hover{filter:brightness(1.08)}
.shell .page .btn--ghost:hover{background:var(--surface);border-color:var(--line);box-shadow:none;color:var(--ink)}

.shell .page .chip{
  display:inline-flex;align-items:center;gap:6px;height:29px;padding:0 9px 0 10px;
  border-radius:var(--r-chip);border:1px solid var(--line);
  background:var(--surface);font-size:12.5px;font-weight:500;
  transition:.15s var(--ease);
}
.shell .page .chip:hover{border-color:var(--ink-3)}
.shell .page .chip--on{background:var(--accent-soft);border-color:transparent;color:var(--accent-deep,var(--accent));font-weight:600}
.shell .page .chip__x{
  width:15px;height:15px;border-radius:3px;display:grid;place-items:center;
  color:currentColor;opacity:.6;font-size:14px;line-height:1;
}
.shell .page .chip__x:hover{opacity:1;background:color-mix(in srgb,currentColor 15%,transparent)}
.shell .page .chip--add{border-style:dashed;color:var(--ink-2)}

/* THE TAG SYSTEM COVERS BOTH PAGE SHELLS (2026-09-18). It was scoped to
   `.shell .page` only, and Meridian HR's and Workforce's rosters sit in
   `.directory-shell` with no `.page` -- so every Employment and Availability
   tag on the Care Workers list rendered as bare inline text, no pill and no
   colour, while the markup said tag. One definition, two scopes, rather than
   a second copy in directory.css to drift from this one.

   The modifiers are exactly these five. `tag--good` was being asked for in
   five templates and never existed; scripts/check-tag-modifiers.js now fails
   on any modifier not defined here. */
.shell .page .tag,
.shell .directory-shell .tag{
  display:inline-flex;align-items:center;gap:5px;
  height:21px;padding:0 7px;border-radius:5px;
  font-size:11.5px;font-weight:600;letter-spacing:-.002em;white-space:nowrap;
}
.shell .page .tag--danger,.shell .directory-shell .tag--danger{background:var(--danger-soft);color:var(--danger)}
.shell .page .tag--warn,  .shell .directory-shell .tag--warn  {background:var(--warn-soft);color:var(--warn)}
/* ok and mute use the STATUS FILLS: on --ok-soft and --sunk an "Active" and
   an "Inactive" tag were the same pale green-grey (228,240,233 against
   231,236,234). */
.shell .page .tag--ok,    .shell .directory-shell .tag--ok    {background:var(--ok-fill);color:var(--ok-ink)}
.shell .page .tag--info,  .shell .directory-shell .tag--info  {background:var(--info-soft);color:var(--info)}
.shell .page .tag--mute,  .shell .directory-shell .tag--mute  {background:var(--neutral-soft);color:var(--ink-2)}

/* ============================================================
   PANELS & GRID
   ============================================================ */
.shell .page .grid{display:grid;gap:14px}
.shell .page .g-2{grid-template-columns:repeat(2,minmax(0,1fr))}
.shell .page .g-3{grid-template-columns:repeat(3,minmax(0,1fr))}
.shell .page .g-4{grid-template-columns:repeat(4,minmax(0,1fr))}
.shell .page .g-main{grid-template-columns:minmax(0,1.62fr) minmax(0,1fr)}
.shell .page .g-flip{grid-template-columns:minmax(0,1fr) minmax(0,1.62fr)}

.shell .page .panel{
  position:relative;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-card);
  overflow:hidden;
}
.shell .page .panel__head{
  display:flex;align-items:center;gap:10px;
  padding:13px 16px;border-bottom:1px solid var(--line-2);
}
.shell .page .panel__head h2,.shell .page .panel__head h3{margin:0}
.shell .page .panel__more{margin-left:auto;font-size:12.5px;font-weight:500;color:var(--ink-2)}
.shell .page .panel__more:hover{color:var(--accent)}
.shell .page .panel__body{padding:16px}
.shell .page .panel__body--flush{padding:0}
.shell .page .panel__foot{
  padding:11px 16px;border-top:1px solid var(--line-2);
  background:var(--surface-2);font-size:12.5px;color:var(--ink-2);
  display:flex;align-items:center;gap:10px;
}

/* ============================================================
   SIGNAL CARD — a number that always shows "of what"
   ============================================================ */
.shell .page .signal{
  position:relative;display:block;width:100%;text-align:left;
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-card);padding:14px 16px 14px 19px;
  transition:.18s var(--ease);
}
.shell .page .signal:hover{border-color:var(--ink-3);box-shadow:var(--shadow-lift,0 2px 4px rgba(15,31,27,.06),0 18px 40px -18px rgba(15,31,27,.28));transform:translateY(-1px)}
.shell .page .signal__top{display:flex;align-items:baseline;gap:8px}
.shell .page .signal__lab{margin:3px 0 10px;color:var(--ink-2);font-size:12.5px}
.shell .page .signal__delta{font-size:12px;font-weight:600;margin-left:0;color:var(--ink-2)}

/* proportion bar: the number's denominator, made visible */
.shell .page .prop{display:flex;height:5px;border-radius:3px;overflow:hidden;background:var(--sunk);gap:1.5px}
.shell .page .prop i{display:block;transition:width .5s var(--ease)}
.shell .page .prop__key{display:flex;gap:12px;margin-top:8px;flex-wrap:wrap}
.shell .page .prop__key span{display:flex;align-items:center;gap:5px;font-size:11.5px;color:var(--ink-2)}
.shell .page .prop__key b{width:7px;height:7px;border-radius:2px;flex:none}

.shell .page .spark{width:100%;height:26px;display:block;overflow:visible}

.shell .page .ring{width:52px;height:52px;flex:none;transform:rotate(-90deg)}
.shell .page .ring circle{fill:none;stroke-width:5;stroke-linecap:round}
.shell .page .ring .bg{stroke:var(--sunk)}
.shell .page .ring .fg{stroke:var(--accent);transition:stroke-dasharray .6s var(--ease)}

/* ============================================================
   ATTENTION QUEUE — grouped by entity, not one row per check
   ============================================================ */
.shell .page .queue{list-style:none;margin:0;padding:0}
.shell .page .queue>li{border-bottom:1px solid var(--line-2)}
.shell .page .queue>li:last-child{border-bottom:0}

.shell .page .qrow{
  position:relative;display:flex;align-items:center;gap:12px;
  width:100%;padding:12px 16px 12px 19px;text-align:left;
  transition:background .14s var(--ease);
}
.shell .page .qrow:hover{background:var(--surface-2)}
.shell .page .qrow__av{
  width:30px;height:30px;flex:none;border-radius:8px;
  background:var(--sunk);color:var(--ink-2);
  display:grid;place-items:center;font-size:11px;font-weight:700;
}
.shell .page .qrow__mid{min-width:0;flex:1}
.shell .page .qrow__t{font-weight:600;letter-spacing:-.008em}
.shell .page .qrow__s{font-size:12.5px;color:var(--ink-2);margin-top:1px}
.shell .page .qrow__chev{color:var(--ink-3);flex:none;transition:transform .22s var(--ease)}
.shell .page .qrow[aria-expanded="true"] .qrow__chev{transform:rotate(90deg)}

.shell .page .qsub{list-style:none;margin:0;padding:0 16px 12px 62px;display:none}
.shell .page .qsub--open{display:block;animation:mer-drop .24s var(--ease)}
@keyframes mer-drop{from{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:none}}
.shell .page .qsub li{
  display:flex;align-items:center;gap:10px;
  padding:7px 10px;border-radius:7px;font-size:13px;
  border:1px solid var(--line-2);margin-bottom:5px;background:var(--surface-2);
}
.shell .page .qsub li:last-child{margin-bottom:0}
.shell .page .qsub__act{margin-left:auto;font-size:12px;font-weight:600;color:var(--accent-deep,var(--accent))}

/* ============================================================
   MODULE CARDS (Operations landing) — live consoles, not launchers
   ============================================================ */
.shell .page .mods{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(276px,1fr))}
.shell .page .mod{
  position:relative;display:flex;flex-direction:column;text-align:left;
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-card);padding:16px 16px 0 19px;overflow:hidden;
  transition:.2s var(--ease);min-height:196px;
}
.shell .page .mod:hover{border-color:var(--accent);box-shadow:var(--shadow-lift,0 2px 4px rgba(15,31,27,.06),0 18px 40px -18px rgba(15,31,27,.28));transform:translateY(-2px)}
.shell .page .mod__top{display:flex;align-items:flex-start;gap:11px;margin-bottom:13px}
.shell .page .mod__ic{
  width:34px;height:34px;flex:none;border-radius:9px;
  background:var(--accent-soft);color:var(--accent-deep,var(--accent));display:grid;place-items:center;
  transition:.2s var(--ease);
}
.shell .page .mod:hover .mod__ic{background:var(--accent);color:#fff}
[data-theme="dark"] .shell .page .mod:hover .mod__ic{color:#0E1416}
.shell .page .mod__ic svg{width:18px;height:18px}
.shell .page .mod__name{font-weight:600;font-size:15px;letter-spacing:-.014em}
.shell .page .mod__desc{font-size:12.5px;color:var(--ink-2);margin-top:1px}
.shell .page .mod__stats{display:flex;gap:20px;margin-bottom:12px}
.shell .page .mod__stat b{display:block;font-size:21px;font-weight:700;letter-spacing:-.022em;line-height:1.1}
.shell .page .mod__stat span{font-size:11.5px;color:var(--ink-2)}
.shell .page .mod__viz{margin:auto -16px 0 -19px;height:44px}
.shell .page .mod--soon{opacity:.62}
.shell .page .mod--soon:hover{transform:none;border-color:var(--line);box-shadow:none}

/* ============================================================
   TIMELINE (Leave)
   ============================================================ */
/* Plain data table. Distinct from .tl below, which is a timeline/Gantt
   table: .tl's header is position:sticky and its first column is a fixed
   210px, both of which are wrong for an ordinary list of rows -- the
   sticky header floated over and hid the first row on the Compliance
   overview. Use .dtable for lists, .tl for timelines. */
.shell .page .dtable{width:100%;border-collapse:collapse;font-size:13px}
.shell .page .dtable th{
  text-align:left;font-weight:600;font-size:11px;letter-spacing:.04em;text-transform:uppercase;
  color:var(--ink-2);padding:8px 10px;border-bottom:1px solid var(--line);white-space:nowrap;
}
.shell .page .dtable td{padding:9px 10px;border-bottom:1px solid var(--line-2);vertical-align:top}
.shell .page .dtable tbody tr:last-child td{border-bottom:0}
.shell .page .dtable tbody tr:hover{background:var(--surface-2)}
.shell .page .dtable .num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.shell .page .dtable__sub{display:block;font-size:11.5px;color:var(--ink-2);margin-top:2px}
.shell .page .dtable .expiry-sort{display:inline-flex;align-items:center;gap:.2rem;color:inherit;text-decoration:none}.shell .page .dtable .expiry-sort:hover,.shell .page .dtable .expiry-sort:focus-visible{color:var(--suite-hr-accent-deep);text-decoration:underline}

.shell .page .tl{width:100%;border-collapse:separate;border-spacing:0;font-size:12.5px}
.shell .page .tl :where(th,td){padding:0;border-bottom:1px solid var(--line-2)}
.shell .page .tl thead th{
  position:sticky;top:var(--bar);z-index:5;background:var(--surface-2);
  font-weight:600;font-size:11px;color:var(--ink-2);
  border-bottom:1px solid var(--line);
}
.shell .page .tl__name{width:210px;min-width:210px;padding:0 12px 0 19px;text-align:left;position:relative}
.shell .page .tl__day{width:30px;text-align:center;padding:5px 0}
.shell .page .tl__day em{display:block;font-style:normal;font-size:10px;color:var(--ink-3);font-weight:500}
.shell .page .tl__day--we{background:var(--sunk)}
.shell .page .tl__day--now{background:var(--accent);color:#fff;border-radius:5px 5px 0 0}
[data-theme="dark"] .shell .page .tl__day--now{color:#0E1416}
.shell .page .tl__day--now em{color:inherit;opacity:.75}
.shell .page .tl tbody tr:hover .tl__name{background:var(--surface-2)}
.shell .page .tl__cell{height:34px;position:relative;border-right:1px solid var(--line-2)}
.shell .page .tl__cell--we{background:var(--sunk)}
.shell .page .tl__cell--now{box-shadow:inset 1.5px 0 0 var(--accent),inset -1.5px 0 0 var(--accent)}
.shell .page .tl__who{display:flex;align-items:center;gap:9px;padding:5px 0}
.shell .page .tl__ini{
  width:24px;height:24px;flex:none;border-radius:50%;
  background:var(--sunk);color:var(--ink-2);
  display:grid;place-items:center;font-size:9.5px;font-weight:700;
}
.shell .page .tl__role{
  font-size:9.5px;font-weight:700;padding:1px 5px;border-radius:4px;
  background:var(--info-soft);color:var(--info);
}
.shell .page .tl__bar{
  position:absolute;top:6px;bottom:6px;left:2px;right:2px;
  border-radius:5px;background:var(--ok);color:#fff;
  display:flex;align-items:center;padding:0 8px;
  font-size:11px;font-weight:600;white-space:nowrap;overflow:hidden;
}
[data-theme="dark"] .shell .page .tl__bar{color:#0E1416}
.shell .page .tl__grouprow td{
  background:var(--surface-2);font-weight:600;padding:7px 19px;
  border-bottom:1px solid var(--line);
}

/* ============================================================
   FINDINGS / GAP LISTS
   ============================================================ */
.shell .page .flist{list-style:none;margin:0;padding:0}
.shell .page .flist li{
  position:relative;display:flex;align-items:center;gap:13px;
  padding:12px 16px 12px 19px;border-bottom:1px solid var(--line-2);
  transition:background .14s var(--ease);
}
.shell .page .flist li:last-child{border-bottom:0}
.shell .page .flist li:hover{background:var(--surface-2)}
.shell .page .flist__q{flex:1;min-width:0;font-weight:500;letter-spacing:-.006em}
/* Both of these are <span>s nested inside their label, and both carry a
   margin-top that an inline box silently ignores -- so the label and its
   sub-line ran together as one word ("Barnet3 non-compliant"). Inherited
   from the concept HTML, and only visible once real data filled them. */
.shell .page .flist__meta{display:block;font-size:12px;color:var(--ink-2);margin-top:2px;font-weight:400}
.shell .page .flist__n{flex:none;text-align:right;font-size:19px;font-weight:700;letter-spacing:-.02em}
.shell .page .flist__n span{display:block;font-size:11px;font-weight:500;color:var(--ink-2);letter-spacing:0}

.shell .page .hbar{display:flex;align-items:center;gap:12px;padding:9px 0}
.shell .page .hbar__lab{width:104px;flex:none;font-size:12.5px;font-weight:500}
.shell .page .hbar__track{flex:1;height:22px;background:var(--sunk);border-radius:5px;overflow:hidden}
/* display:block is load-bearing, not tidiness. The fill is an <i>, so it is
   an inline box by default, and width/height percentages do not apply to a
   non-replaced inline box -- the element computed to 0x0 and every branch
   health bar rendered as an empty grey track. The colour was being set the
   whole time; there was no box to paint it on. */
.shell .page .hbar__fill{display:block;height:100%;background:var(--accent);border-radius:5px;transition:width .5s var(--ease)}
/* Wide enough for "100%" -- 26px clipped the one value that matters most. */
.shell .page .hbar__n{width:36px;flex:none;text-align:right;font-weight:600;font-size:13px}

/* ============================================================
   READINESS LIST (governance)
   ============================================================ */
.shell .page .rdy{list-style:none;margin:0;padding:0}
.shell .page .rdy li{display:flex;gap:12px;padding:12px 16px;border-bottom:1px solid var(--line-2)}
.shell .page .rdy li:last-child{border-bottom:0}
.shell .page .rdy__st{
  width:20px;height:20px;flex:none;border-radius:6px;margin-top:1px;
  display:grid;place-items:center;font-size:11px;font-weight:700;
}
.shell .page .rdy__st--on{background:var(--ok-soft);color:var(--ok)}
.shell .page .rdy__st--wait{background:var(--sunk);color:var(--ink-3)}
.shell .page .rdy__st--off{background:var(--warn-soft);color:var(--warn)}
.shell .page .rdy__t{display:block;font-weight:600;font-size:13px}
.shell .page .rdy__s{display:block;font-size:12.5px;color:var(--ink-2);margin-top:1px}

/* ============================================================
   EMPTY STATES — an invitation, not a full stop.
   Kept at bare .shell (not .shell .page): the command palette's "No
   matches" state uses this today, inside .cmdk__list, which sits
   outside .page. No collision found against it in the audit (unlike
   .btn), so this is safe un-nested.
   ============================================================ */
.shell .empty{padding:26px 20px;text-align:center}
.shell .empty__ic{
  width:38px;height:38px;margin:0 auto 11px;border-radius:11px;
  background:var(--sunk);color:var(--ink-3);display:grid;place-items:center;
}
.shell .empty__t{font-weight:600;margin-bottom:3px}
.shell .empty__s{font-size:12.5px;color:var(--ink-2);max-width:270px;margin:0 auto 13px}

/* ============================================================
   COMMAND PALETTE
   ============================================================ */
/* z-index scale (docs/design/PHASE4_AUDIT.md flagged two numeric ties across
   suites — .topbar/.calendar-tooltip both 50, and .scrim/.hover-card/
   .audit-source-panel all 200 — resolved into one documented scale instead
   of leaving them coincidentally equal; a third tie found 2026-09-04,
   .audit-message/.topbar both 50, added the tier below):
     4-5  sticky in-page content — a banner or table header scrolling
          under the persistent topbar, not shell chrome itself; must sit
          below the tiers above but above the ordinary content it scrolls
          over (leave.css .tl thead th, audit.css .audit-message and
          .audit-builder-savebar)
     50   .topbar (persistent shell chrome, this file)
     60   .rail (persistent shell chrome, this file)
     70   hover/tooltip layer — transient, hover-triggered UI that must sit
          above the content that triggered it but never blocks interaction
          (leave.css .calendar-tooltip, .hover-card)
     150  persistent slide-in overlays the user can leave open while working
          (audit.css .audit-source-panel)
     200  .scrim (command palette) — the true modal ceiling; everything above
          must still yield to it
     300  .skip-link (accessibility, always literally on top) */
.shell .scrim{
  position:fixed;inset:0;z-index:200;display:none;
  background:color-mix(in srgb,var(--ink) 34%,transparent);
  backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px);
  padding:11vh 20px 20px;
}
.shell .scrim--on{display:block;animation:mer-fade .16s var(--ease)}
@keyframes mer-fade{from{opacity:0}to{opacity:1}}

.shell .cmdk{
  max-width:600px;margin:0 auto;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-modal);
  box-shadow:0 32px 80px -20px rgba(0,0,0,.45);
  overflow:hidden;animation:mer-pop .2s var(--ease);
}
@keyframes mer-pop{from{opacity:0;transform:translateY(-10px) scale(.985)}to{opacity:1;transform:none}}
/* Two gaps found in this sweep (2026-09-04): the input never set its own
   color (it inherits normally here, unlike a <select>, but was still
   depending on inheritance reaching the right value rather than being
   explicit), and outline:none on the input removed its focus ring with
   nothing replacing it - the palette had no visible focus indicator at
   all, in either theme. :focus-within on the row gives it one back. */
.shell .cmdk__in{display:flex;align-items:center;gap:11px;padding:15px 17px;border-bottom:1px solid var(--line-2)}
.shell .cmdk__in:focus-within{box-shadow:inset 0 0 0 2px var(--accent,var(--info))}
.shell .cmdk__in svg{width:17px;height:17px;color:var(--ink-3);flex:none}
.shell .cmdk__in input{flex:1;border:0;background:none;outline:none;font-size:15.5px;letter-spacing:-.01em;color:var(--ink)}
.shell .cmdk__in input::placeholder{color:var(--ink-3)}
.shell .cmdk__esc{font-size:11px;font-weight:600;padding:3px 7px;border-radius:4px;background:var(--sunk);color:var(--ink-2)}
.shell .cmdk__list{max-height:min(430px,58vh);overflow-y:auto;padding:7px}
.shell .cmdk__sec{padding:11px 11px 5px;font-size:11px;font-weight:600;color:var(--ink-3)}
.shell .cmdk__it{display:flex;align-items:center;gap:12px;width:100%;padding:9px 11px;border-radius:8px;text-align:left}
.shell .cmdk__it[data-sel="1"]{background:var(--surface-2)}
.shell .cmdk__it[data-sel="1"] .cmdk__go{opacity:1}
.shell .cmdk__ic{
  width:27px;height:27px;flex:none;border-radius:7px;display:grid;place-items:center;
  background:var(--accent-soft);color:var(--accent-deep,var(--accent));
}
.shell .cmdk__ic svg{width:14px;height:14px}
.shell .cmdk__t{font-weight:500;font-size:13.5px}
.shell .cmdk__s{font-size:12px;color:var(--ink-2)}
.shell .cmdk__go{margin-left:auto;font-size:11px;color:var(--ink-3);opacity:0;font-weight:600}
.shell .cmdk__foot{
  display:flex;gap:15px;padding:9px 15px;border-top:1px solid var(--line-2);
  background:var(--surface-2);font-size:11.5px;color:var(--ink-2);
}
.shell .cmdk__foot b{font-weight:600;padding:1px 5px;border-radius:3px;background:var(--sunk);margin-right:4px}

/* ============================================================
   MISC — Phase 4 page-content vocabulary (.shell .page, see the note on
   BUTTONS/CHIPS above). .legend collides with leave.css today, same
   class of risk as .btn; the rest don't collide but are scoped the same
   way for consistency, since none of them are used by the Phase 3 shell
   itself.
   ============================================================ */
.shell .page .countdown{
  position:relative;display:flex;align-items:center;gap:14px;
  padding:14px 18px 14px 21px;border-radius:var(--r-card);
  background:var(--surface);border:1px solid var(--line);
}
.shell .page .countdown__n{font-size:32px;font-weight:700;letter-spacing:-.03em;line-height:1;color:var(--warn)}

.shell .page .legend{display:flex;flex-wrap:wrap;gap:5px}
.shell .page .legend button{
  display:flex;align-items:center;gap:6px;height:26px;padding:0 9px;
  border-radius:20px;font-size:11.5px;font-weight:500;
  border:1px solid var(--line);background:var(--surface);color:var(--ink-2);
  transition:.14s var(--ease);
}
.shell .page .legend button:hover{border-color:var(--ink-3);color:var(--ink)}
.shell .page .legend button[aria-pressed="false"]{opacity:.42}
.shell .page .legend i{width:9px;height:9px;border-radius:3px;flex:none}

.shell .page .seg{display:inline-flex;padding:3px;background:var(--sunk);border-radius:8px;gap:2px}
.shell .page .seg button{
  height:28px;padding:0 13px;border-radius:6px;font-size:12.5px;font-weight:500;
  color:var(--ink-2);transition:.14s var(--ease);
}
.shell .page .seg button:hover{color:var(--ink)}
.shell .page .seg button[aria-pressed="true"]{background:var(--surface);color:var(--ink);font-weight:600;box-shadow:var(--shadow,0 1px 2px rgba(15,31,27,.05),0 8px 24px -12px rgba(15,31,27,.16))}

.shell .page .scroll-x{overflow-x:auto}
.shell .page .pg{display:none}
.shell .page .pg--on{display:block;animation:mer-enter .3s var(--ease)}
@keyframes mer-enter{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}

.shell .page .note{
  display:flex;gap:11px;padding:12px 15px;border-radius:var(--r-card);
  background:var(--info-soft);color:var(--info);font-size:12.5px;line-height:1.5;
}
.shell .page .note svg{width:16px;height:16px;flex:none;margin-top:1px}

/* ---- mobile fallback: rail collapses below 900px (Phase 3 wires the markup) ---- */
@media (max-width:900px){
  .shell .rail{
    top:auto;bottom:0;width:100%;height:var(--bar);flex-direction:row;
    border-right:0;border-top:1px solid var(--line);padding:0;
  }
  .shell .rail:hover,.shell .rail:focus-within{width:100%;box-shadow:none}
  .shell .rail__brand,.shell .rail__label,.shell .rail__foot{display:none}
  .shell .rail__group{flex-direction:row;padding:0;justify-content:space-around;flex:1}
  .shell .navitem{flex-direction:column;height:100%;gap:2px;justify-content:center;width:auto;padding:4px 10px}
  .shell .navitem__tx{opacity:1;font-size:10px}
  .shell .main{margin-left:0;padding-bottom:var(--bar)}
  /* EVERY SUITE REACHABLE ON A PHONE (2026-09-21). The bottom bar held Home
     and every suite in one row with overflow:hidden, so on a 390px screen
     the later suites were cut off with no way to reach them, and Settings
     was not shown at all. The bar now scrolls sideways, one item at a time. */
  .shell .rail{overflow-x:auto;overflow-y:hidden;scroll-snap-type:x proximity;scrollbar-width:none;overscroll-behavior-x:contain}
  .shell .rail::-webkit-scrollbar{display:none}
  .shell .rail__group{flex:1 0 auto}
  .shell .rail__foot{display:flex;flex:none;margin:0;padding:0;border:0}
  .shell .navitem{flex:none;min-width:62px;scroll-snap-align:start;position:relative}
  /* The suite's NAME must show: stacked under the icon, the TEST badge took
     the name's line. It sits small on the icon's corner instead, and the
     invisible "Soon"/"No access" badges stop taking room. */
  .shell .navitem__badge--test{position:absolute;top:3px;right:6px;margin:0;padding:0 4px;font-size:8px;line-height:13px}
  .shell .navitem__badge:not(.navitem__badge--test){display:none}
}

/* THE TOP BAR ON A PHONE (2026-09-21). It was ~410px wide on a 390px
   screen: the avatar ran off the edge and My calendar / Messages overlapped
   the crumb, so every page scrolled sideways. The crumb keeps only the suite
   name, and My calendar and Messages become icon buttons; their names stay
   in aria-label. */
/* Declared BEFORE the phone rule below, which must win over it. */
.shell .tilebtn__ic--phone{display:none}
@media (max-width:760px){
  .shell .topbar{gap:6px;padding:0 12px}
  .shell .crumb{flex:1 1 auto;min-width:0}
  .shell .crumb .ink-faint,.shell .crumb__sep{display:none}
  .shell .crumb__here{overflow:hidden;text-overflow:ellipsis}
  .shell .topbar__mine{margin-left:0;gap:6px;flex:none}
  .shell .tilebtn{min-width:34px;padding:0 8px;justify-content:center;gap:4px}
  .shell .tilebtn__tx{display:none}
  .shell .tilebtn__ic--phone{display:block;width:16px;height:16px;flex:none}
  .shell .searchbtn{margin-left:0;flex:none}
  .shell .profile{flex:none}
}

@media (max-width:1180px){
  .shell .page .g-main,.shell .page .g-flip{grid-template-columns:minmax(0,1fr)}
  .shell .page .g-4{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:760px){
  .shell .page .g-2,.shell .page .g-3,.shell .page .g-4{grid-template-columns:minmax(0,1fr)}
  .shell .searchbtn{min-width:0;width:34px;padding:0}
  .shell .searchbtn span,.shell .searchbtn kbd{display:none}
  .shell .page{padding:18px 14px 60px}
}

/* ============================================================
   .site-nav (theme.css) re-homed inside the shell.
   theme.css's rule centres it with position:absolute, sized and coloured
   for the old .site-header (position:relative, solid suite-colour
   background, near-white text). It's now a sibling of .topbar with no
   positioned ancestor of its own, so position:absolute escaped to the
   viewport, and #E6EDF3 text is close to invisible on the light surface
   behind it. Not a theme.css edit — a same-specificity-beating override
   here, since .shell .site-nav (2 classes) already wins over theme.css's
   bare .site-nav (1 class) without !important.
   ============================================================ */
.shell .site-nav{
  position:static;left:auto;transform:none;
  justify-content:center;flex-wrap:wrap;gap:2px;
  padding:8px 22px;border-bottom:1px solid var(--line);
  background:var(--surface);
}
.shell .nav-link{color:var(--ink-2)}
.shell .nav-link:hover{color:var(--ink);background:var(--surface-2)}
.shell .nav-link--active{color:var(--ink)}
.shell .nav-link--active::after{background:var(--ink)}

/* --- record quality (HR + SU Records dashboards) -----------------------
   Signal totals first, names second. This replaced SU Records' Data Quality
   tab (2026-09-08): the tab only listed people, so "how many have no
   emergency contact" was never a number anybody could watch. */
.shell .page .rq-signals{
  display:flex;flex-wrap:wrap;gap:10px;margin:0;padding:0;list-style:none;
}
.shell .page .rq-signal{
  flex:1 1 190px;min-width:170px;
  padding:11px 13px;border:1px solid var(--line);border-radius:var(--r-card);
  background:var(--surface-2);
}
.shell .page .rq-signal b{display:block;line-height:1.1}
.shell .page .rq-signal__lab{display:block;font-size:12px;color:var(--ink-2);margin-top:3px}
.shell .page .rq-chip{
  display:inline-block;margin:0 5px 5px 0;padding:2px 8px;
  border:1px solid var(--line);border-radius:20px;
  font-size:11.5px;color:var(--ink-2);background:var(--surface-2);
}

/* --- topbar: My calendar + notifications ------------------------------
   Top LEFT, beside the breadcrumb, on every authenticated page. One says
   what is coming up, the other what has just happened. */
.shell .topbar__mine{display:flex;align-items:center;gap:6px;margin-left:14px}
.shell .iconbtn{position:relative}

/* The bell's badge: UNSEEN, cleared the moment notifications are opened. */
.shell .countbadge{
  position:absolute;top:-3px;right:-3px;min-width:16px;height:16px;padding:0 4px;
  display:grid;place-items:center;border-radius:9px;
  background:var(--danger);color:var(--surface);
  font-size:10.5px;font-weight:700;line-height:1;font-variant-numeric:tabular-nums;
}
/* On My calendar the number is "still to do", not "new" — same shape, but it
   must not read as urgent unread mail. */
.shell .countbadge--quiet{position:static;background:var(--sunk);color:var(--ink-2)}

/* The rail badge: OUTSTANDING per suite. Persists until each item is done. */
.shell .navitem__ic{position:relative}
/* The number is --surface, not white (2026-09-29): dark mode's --danger is
   the brightened coral meant for TEXT on the dark canvas, and white on it was
   3.16:1. --surface is #FFFFFF in light mode (unchanged) and the dark card
   colour in dark mode, 5.3:1 on the coral. The bell's badge above, same. */
.shell .navitem__count{
  position:absolute;top:-5px;right:-6px;min-width:15px;height:15px;padding:0 3px;
  display:grid;place-items:center;border-radius:8px;
  background:var(--danger);color:var(--surface);
  font-size:9.5px;font-weight:700;line-height:1;font-variant-numeric:tabular-nums;
}


/* --- notifications list + my calendar --------------------------------- */
.shell .page .ntf-list{list-style:none;margin:0;padding:0}
.shell .page .ntf{
  display:flex;gap:14px;align-items:flex-start;justify-content:space-between;
  padding:13px 16px;border-bottom:1px solid var(--line-2);
}
.shell .page .ntf:last-child{border-bottom:0}
.shell .page .ntf--new{background:var(--surface-2)}
.shell .page .ntf--done{opacity:.62}
.shell .page .ntf--overdue{box-shadow:inset 3px 0 0 var(--danger)}
.shell .page .ntf__body{min-width:0}
.shell .page .ntf__title{display:block;font-weight:600;font-size:13.5px;overflow-wrap:anywhere}
.shell .page .ntf__sub{display:block;font-size:12.5px;color:var(--ink-2);margin-top:2px;overflow-wrap:anywhere}
.shell .page .ntf__meta{display:block;font-size:11.5px;color:var(--ink-3);margin-top:4px}
.shell .page .ntf__action{flex:none}

.shell .page .mycal{
  display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:5px;
}
.shell .page .mycal__dow{
  font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  color:var(--ink-2);padding:0 0 4px 2px;
}
.shell .page .mycal__cell{
  min-height:74px;padding:5px 6px;border:1px solid var(--line-2);border-radius:8px;
  background:var(--surface);display:flex;flex-direction:column;gap:3px;
}
.shell .page .mycal__cell--empty{border-color:transparent;background:transparent}
.shell .page .mycal__cell--today{border-color:var(--accent,var(--ink-2));box-shadow:inset 0 0 0 1px var(--accent,var(--ink-2))}
.shell .page .mycal__num{font-size:11.5px;color:var(--ink-2);font-variant-numeric:tabular-nums}
.shell .page .mycal__entry{
  display:block;font-size:10.5px;line-height:1.3;padding:2px 5px;border-radius:5px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  background:var(--sunk);color:var(--ink-2);
}
/* What is due first, then whose (Review Calendars, 2026-10-09). */
.shell .page .mycal__what{display:block;font-weight:700;overflow:hidden;text-overflow:ellipsis}
.shell .page .mycal__who{display:block;overflow:hidden;text-overflow:ellipsis;opacity:.9}
.shell .page .mycal__entry--overdue{background:var(--danger-soft);color:var(--danger)}
.shell .page .mycal__entry--due{background:var(--warn-soft);color:var(--warn)}
/* A Cycle Planner item with a planned date (docs/hr/cycle-planner): scheduled work is blue there, and here. */
.shell .page .mycal__entry--planned{background:var(--info-soft);color:var(--info)}
/* A leave day is drawn in the Leave calendar's own colour for that type,
   supplied inline as --leave-colour. The fallback keeps the suite tint if a
   type ever arrives without one. */
.shell .page .mycal__entry--leave{
  background:color-mix(in srgb,var(--leave-colour,var(--leave)) 16%,transparent);
  color:var(--leave-colour,var(--leave));
  border-left:3px solid var(--leave-colour,var(--leave));
}

@media (max-width:640px){
  .shell .page .mycal__cell{min-height:56px}
  .shell .page .mycal__entry{font-size:9.5px}
}
.shell .page .assignform{display:flex;gap:6px;align-items:center;margin-top:5px}
.shell .page .assignform select{max-width:170px}

/* --- topbar: notifications, then My calendar (2026-09-08) --------------
   Bell on the left and deliberately the larger of the two: it changes on its
   own and needs to catch the eye. My calendar is a plain text tile to its
   right -- a destination you choose to visit, not an alert. */
.shell .iconbtn--bell{
  width:40px;height:36px;
}
.shell .iconbtn--bell svg{width:19px;height:19px}

.shell .tilebtn{
  display:inline-flex;align-items:center;gap:8px;
  height:34px;padding:0 12px;
  border:1px solid var(--line);border-radius:9px;
  background:var(--surface);color:var(--ink);
  font-size:12.5px;font-weight:500;line-height:1;white-space:nowrap;
  text-decoration:none;position:relative;
  transition:border-color 120ms ease,background-color 120ms ease;
}
.shell .tilebtn:hover{border-color:var(--ink-3);background:var(--surface-2)}
.shell .tilebtn:focus-visible{outline:2px solid var(--info);outline-offset:2px}

@media (max-width:900px){
  .shell .tilebtn{padding:0 9px;font-size:12px}
}

/* ============================================================
   FORM CONTROLS — one modern style for every select, input and
   textarea inside the app shell.

   Before this, nothing in the codebase set `appearance` anywhere, so every
   dropdown and date field rendered in the operating system's own chrome:
   a grey 3D select on Windows next to flat, tokenised cards. .form-field in
   theme.css set a border and padding but never removed the native look, which
   is why the filter bars and the Assign control read as "old school".

   Scoped to .shell so it reaches every authenticated page at once and cannot
   touch the sign-in pages, which have their own deliberate treatment.
   ============================================================ */
:root{
  /* The chevron is a background image, so its colour cannot come from a
     token at paint time -- it is baked into the data URI. Two variants,
     switched with the theme, rather than one grey that is wrong in both. */
  --select-chevron: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' stroke='%23425650' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
}
[data-theme="dark"]{
  --select-chevron: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' stroke='%23A9B6B1' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
}

.shell :where(select, input[type="text"], input[type="search"], input[type="email"],
               input[type="password"], input[type="number"], input[type="date"],
               input[type="month"], input[type="time"], textarea){
  appearance:none;-webkit-appearance:none;
  min-height:34px;
  padding:6px 11px;
  border:1px solid var(--line);
  border-radius:8px;
  background:var(--surface);
  color:var(--ink);
  font-family:inherit;
  font-size:13px;
  line-height:1.35;
  transition:border-color 120ms ease, box-shadow 120ms ease;
}

/* Room for the chevron, and the chevron itself. */
.shell select{
  padding-right:30px;
  background-image:var(--select-chevron);
  background-repeat:no-repeat;
  background-position:right 11px center;
  cursor:pointer;
}

/* The native date picker button is the one piece of OS chrome worth keeping —
   removing it leaves no way to open the picker — but it inherits the page's
   colour so it stops being a black square on a dark card. */
.shell input[type="date"]::-webkit-calendar-picker-indicator{
  cursor:pointer;opacity:.55;filter:var(--date-picker-filter,none);
}
[data-theme="dark"] .shell input[type="date"]::-webkit-calendar-picker-indicator{
  --date-picker-filter:invert(1);
}

/* Dark: every form control on its own surface with an edge at 3:1 (WCAG
   1.4.11), shell or not -- --line was 1.30:1, and a control outside a styled
   container fell back to the browser's grey (SU Records, Compliance).
   Hover and focus below still win. Light is unchanged. */
[data-theme="dark"] :where(select, textarea, input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"])){
  background-color:var(--surface);color:var(--ink);border-color:var(--control-edge);
}

.shell :where(select, input, textarea):hover:not(:disabled){border-color:var(--ink-3)}
.shell :where(select, input, textarea):focus-visible{
  outline:none;
  border-color:var(--accent,var(--info));
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent,var(--info)) 22%,transparent);
}
.shell :where(select, input, textarea):disabled{
  background:var(--sunk);color:var(--ink-3);cursor:not-allowed;
}
.shell textarea{min-height:76px;padding:9px 11px;resize:vertical}

/* The option list is drawn by the OS, not by this stylesheet. Setting both
   colours explicitly is what stops Chromium inventing its own dark-mode
   palette for the open dropdown (the same fault meridian-tokens.css's
   color-scheme declaration exists to prevent). */
.shell select option{background:var(--surface);color:var(--ink)}

/* --- centred table columns --------------------------------------------
   Asked for on the expiry board (2026-09-08): left-aligned columns pushed
   every value hard against its neighbour, so the eye could not tell where one
   column ended and the next began. Centring gives each column its own visual
   lane without inventing separator rules.

   The Assign control is the deliberate exception: its select and button are
   one action, so they stay side by side and are centred as a pair. */
/* Written out rather than with :where(): the base rule .shell .page .dtable th
   sets text-align:left at (0,3,1), and :where() contributes nothing, so a
   :where(th,td) version left the HEADERS aligned left while the body centred --
   worse than either alone. */
.shell .page .dtable--centred th,
.shell .page .dtable--centred td{text-align:center;vertical-align:middle}
.shell .page .dtable--centred .num{text-align:center}
.shell .page .dtable--centred .dtable__sub{text-align:center}
.shell .page .dtable--centred .assignform{justify-content:center;margin-top:0}
.shell .page .dtable--centred .rq-chip{margin:0 3px 4px}
/* Give the columns real room to sit apart in, rather than relying on the
   centring alone on a wide screen. */
.shell .page .dtable--centred th,
.shell .page .dtable--centred td{padding:10px 14px}

/* Reconcile the legacy .form-field controls with the shell's own.
   The base rule above uses :where(), which has ZERO specificity so modules can
   still size their own controls -- but that also let theme.css's older
   `.form-field select` rule keep its 6px radius and legacy --oasis-* colours,
   leaving a filter dropdown visibly different from the Assign dropdown three
   columns to its right. This is specific enough to win, and only inside
   .shell, so the sign-in pages keep their own treatment. */
.shell .form-field :where(select, input:not([type="checkbox"]):not([type="radio"]), textarea){
  border-radius:8px;
  border-color:var(--line);
  background-color:var(--surface);
  color:var(--ink);
  padding:6px 11px;
  font-size:13px;
  min-height:34px;
}
.shell .form-field select{padding-right:30px}
.shell .form-field :where(select, input, textarea):focus{
  outline:none;
  border-color:var(--accent,var(--info));
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent,var(--info)) 22%,transparent);
}
.shell .form-field label{font-size:11.5px;font-weight:600;letter-spacing:.02em;color:var(--ink-2)}

/* The filter bar reads as one row of controls with its action at the end. */
.shell .page .filter-bar{align-items:end}
.shell .page .filter-bar .btn{min-height:34px}

/* --- Bulk-import guidance and the duplicates pop-up -----------------------
   Shared by all three import screens (Meridian staff, SU Records care workers,
   SU Records service users) via partials/import-column-guide.ejs and
   partials/import-duplicates-dialog.ejs. Deliberately NOT scoped to .shell:
   the two SU Records import pages are plain <main> pages that never enter the
   shell, and a .shell prefix here would leave them unstyled. */

.import-guide{margin:var(--space-5) 0;border:1px solid var(--line);
  border-radius:var(--radius);background:var(--surface);overflow:hidden}
.import-guide__summary{padding:var(--space-3) var(--space-4);cursor:pointer;
  font-size:var(--text-sm);font-weight:700;color:var(--ink);background:var(--surface-2)}
.import-guide__summary:focus-visible{outline:2px solid var(--info);outline-offset:-2px}
.import-guide__note{margin:0;padding:var(--space-3) var(--space-4);
  font-size:var(--text-sm);color:var(--ink-2);border-top:1px solid var(--line-2)}
/* Eleven columns of guidance is wider than a phone. It scrolls in its own box
   rather than pushing the page sideways. */
.import-guide__scroll{overflow-x:auto}
/* A min-width, so the guidance column keeps a readable measure and the table
   SCROLLS on a narrow page instead of squeezing "What to enter" into a
   four-words-per-line ribbon -- which is what it did inside SU Records' own
   700px import page. */
/* table-layout:fixed, NOT auto. Under auto layout the widest unbreakable
   thing wins, and the widest unbreakable thing here is an example chip like
   "Oasis Care and Training" -- which squeezed "What to enter" down to about
   four words a line, the one column anybody actually reads. Fixed layout
   honours the percentages instead. The min-width keeps that measure readable
   on a narrow page by letting the table SCROLL rather than compress. */
.import-guide__table{width:100%;min-width:36rem;table-layout:fixed;
  border-collapse:collapse;font-size:var(--text-sm)}
.import-guide__table :where(th,td):nth-child(1){width:20%}
.import-guide__table :where(th,td):nth-child(2){width:13%}
.import-guide__table :where(th,td):nth-child(3){width:45%}
.import-guide__table :where(th,td):nth-child(4){width:22%}
.import-guide__table th,.import-guide__table td{padding:var(--space-2) var(--space-4);
  text-align:left;vertical-align:top;border-top:1px solid var(--line-2)}
.import-guide__table thead th{font-size:var(--text-xs);text-transform:uppercase;
  letter-spacing:.06em;color:var(--ink-2);background:var(--surface-2);white-space:nowrap}
.import-guide__table tbody th{white-space:nowrap;color:var(--ink);font-weight:700}
.import-guide__table td{color:var(--ink-2)}
/* Wraps. An example is there to be read, not to set the column width -- and
   a long one (an email address, an organisation name) is exactly the case
   where nowrap would steal the room from the guidance beside it. */
.import-guide__table code{font-size:.92em;padding:1px 5px;border-radius:var(--radius-sm);
  background:var(--sunk);color:var(--ink);overflow-wrap:anywhere}
.import-guide__flag{display:inline-block;padding:.1rem .45rem;border-radius:var(--radius-sm);
  font-size:var(--text-xs);font-weight:700;letter-spacing:.04em;text-transform:uppercase;
  white-space:nowrap}
.import-guide__flag--required{background:var(--warn-soft);color:var(--warn)}
.import-guide__flag--optional{background:var(--sunk);color:var(--ink-2)}
.import-guide__blank{color:var(--ink-3);font-style:italic}

.import-dupes{width:min(46rem,calc(100vw - 2rem));max-height:calc(100vh - 4rem);padding:0;
  border:1px solid var(--line);border-radius:var(--radius);background:var(--surface);
  color:var(--ink);box-shadow:0 1.5rem 4rem rgba(0,0,0,.3)}
/* Token-derived, never a literal: a fixed dark scrim over a dark-mode page is
   a scrim that does nothing. */
.import-dupes::backdrop{background:color-mix(in srgb,var(--ink) 55%,transparent)}
/* Dark mode: --ink is light there, so the backdrop darkens instead (2026-10-09). */
[data-theme="dark"] .import-dupes::backdrop{background:rgba(0,0,0,.6)}
/* The no-JavaScript state. [open] without showModal() puts the panel in the
   normal flow, where a fixed max-height and a drop shadow read as broken --
   so in that state it is simply a card in the page. */
.import-dupes:not(:modal){margin:var(--space-5) 0;max-height:none;box-shadow:none}
.import-dupes__head{display:flex;align-items:flex-start;justify-content:space-between;
  gap:var(--space-3);padding:var(--space-4) var(--space-5);
  border-bottom:1px solid var(--line);background:var(--warn-soft)}
.import-dupes__head h2{margin:0;font-size:var(--text-base);color:var(--warn)}
.import-dupes__close{border:0;background:transparent;color:var(--warn);cursor:pointer;
  font-size:1.4rem;line-height:1;padding:0 .2rem}
.import-dupes__close:focus-visible{outline:2px solid var(--warn);outline-offset:2px}
.import-dupes__body{padding:var(--space-4) var(--space-5)}
.import-dupes__lede{margin:0 0 var(--space-4);font-size:var(--text-sm);color:var(--ink-2)}
.import-dupes__scroll{overflow:auto;max-height:22rem;border:1px solid var(--line-2);
  border-radius:var(--radius-sm)}
/* separate, NOT collapse. A position:sticky <th> inside a border-collapse
   table makes Chromium reserve a horizontal scrollbar the table does not need
   -- a grey bar under a list that fits perfectly, on every one of these
   pop-ups. Confirmed in the browser, not reasoned about. */
.import-dupes__table{width:100%;border-collapse:separate;border-spacing:0;font-size:var(--text-sm)}
.import-dupes__table th,.import-dupes__table td{padding:var(--space-2) var(--space-3);
  text-align:left;vertical-align:top;border-top:1px solid var(--line-2)}
.import-dupes__table thead th{position:sticky;top:0;border-top:0;background:var(--surface-2);
  font-size:var(--text-xs);text-transform:uppercase;letter-spacing:.06em;color:var(--ink-2)}
.import-dupes__table td{color:var(--ink-2)}
.import-dupes__actions{display:flex;justify-content:flex-end;
  padding:var(--space-3) var(--space-5) var(--space-4);gap:var(--space-3)}
@media(max-width:640px){
  .import-dupes__head,.import-dupes__body{padding-left:var(--space-4);padding-right:var(--space-4)}
  .import-dupes__actions{padding-left:var(--space-4);padding-right:var(--space-4)}}

/* The resolve pop-up: the same frame as the duplicates one, wider, and a list of
   rows each needing a decision. Tokens only, so it follows dark mode. */
.import-resolve{width:min(58rem,calc(100vw - 2rem))}
.import-resolve form{display:flex;flex-direction:column;max-height:inherit}
.import-resolve .import-dupes__body{overflow:auto}
.import-resolve__all{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2) var(--space-3);
  margin:0 0 var(--space-4);font-size:var(--text-sm)}
.import-resolve__all label{font-weight:600;color:var(--ink-2)}
.import-resolve__list{display:flex;flex-direction:column;gap:var(--space-4)}
.import-resolve__row{margin:0;padding:var(--space-3) var(--space-4) var(--space-4);
  border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--surface)}
.import-resolve__row legend{padding:0 var(--space-2);font-weight:600;color:var(--ink)}
.import-resolve__why{font-weight:400;font-size:var(--text-sm);color:var(--ink-2)}
.import-resolve__row .import-dupes__scroll{max-height:14rem;margin:var(--space-2) 0 var(--space-3)}
.import-resolve__changed td,.import-resolve__changed th{background:var(--warn-soft)}
.import-resolve__flag{display:inline-block;margin-left:var(--space-1);padding:0 .35rem;border-radius:999px;
  font-size:var(--text-xs);font-weight:700;color:var(--warn);border:1px solid var(--warn)}
.import-resolve__choices{display:grid;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));gap:var(--space-2) var(--space-3)}
.import-resolve__choices label{display:flex;align-items:flex-start;gap:var(--space-2);font-size:var(--text-sm);cursor:pointer}
.import-resolve__choices input{margin-top:.2rem}
.import-resolve__new-key{display:flex;flex-direction:column;gap:var(--space-1);margin-top:var(--space-3);font-size:var(--text-sm)}
/* [hidden] must still hide: the class sets display, which beats the user agent's rule. */
.import-resolve__new-key[hidden]{display:none}
.import-resolve__new-key input{max-width:20rem}
.import-resolve__hint,.import-resolve__status{margin:0;font-size:var(--text-sm);color:var(--ink-2)}
.import-resolve__status{margin-top:var(--space-3)}

/* --- List toolbar and chip filters (2026-09-21) -------------------------
   One row above a list: the filter on the left, the list's own actions
   (downloads, settings) on the right as real buttons. A status filter is a
   row of toggle chips -- the checkbox is still there, visually hidden but
   focusable, so the form, the keyboard and live-filter.js work unchanged.
   A ticked chip is ink on surface inverted; both tokens swap in dark mode,
   so it reads in either theme and in every suite. */
.list-toolbar{display:flex;align-items:center;justify-content:space-between;gap:var(--space-3) var(--space-4);
  flex-wrap:wrap;margin:0 0 var(--space-4);padding:10px 12px;border:1px solid var(--line);
  border-radius:var(--r-card);background:var(--surface)}
.list-toolbar__actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.list-toolbar__status{margin:0;font-size:var(--text-xs);color:var(--ink-3);min-width:0}
.list-toolbar__status:empty{display:none}
.chip-filter{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0;padding:0;border:0;background:none}
.chip-filter fieldset{display:flex;align-items:center;gap:6px;flex-wrap:wrap;border:0;padding:0;margin:0;min-width:0}
.shell .chip-filter legend{float:left;margin:0 6px 0 0;padding:0;font-size:var(--text-xs);font-weight:600;
  letter-spacing:.04em;text-transform:uppercase;color:var(--ink-3)}
.chip-filter__chip{position:relative;display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 10px 0 12px;
  border:1px solid var(--line);border-radius:999px;background:var(--surface);color:var(--ink-2);
  font-size:var(--text-sm);font-weight:500;cursor:pointer;user-select:none;transition:.15s var(--ease)}
.chip-filter__chip:hover{border-color:var(--ink-3);color:var(--ink)}
.chip-filter__chip input{position:absolute;inset:0;margin:0;opacity:0;cursor:pointer}
.chip-filter__chip:has(input:focus-visible){outline:2px solid var(--ink);outline-offset:2px}
.chip-filter__chip:has(input:checked){background:var(--ink);border-color:var(--ink);color:var(--surface)}
.chip-filter__n{min-width:20px;height:20px;padding:0 6px;border-radius:999px;display:inline-grid;place-items:center;
  font-size:11px;font-weight:600;font-variant-numeric:tabular-nums;background:var(--surface-2);color:var(--ink-2)}
.chip-filter__chip:has(input:checked) .chip-filter__n{background:color-mix(in srgb,var(--surface) 22%,transparent);color:var(--surface)}
.shell .page .list-toolbar .btn svg{width:15px;height:15px}
@media(max-width:600px){.list-toolbar{align-items:flex-start}.list-toolbar__actions{width:100%}}

/* --- Filter panel (2026-09-21) -------------------------------------------
   One look for every list's filters, in every suite: a card holding an even
   grid, each label ABOVE its field, fields one height, the buttons at the end
   of the row. Replaces labels squeezed beside pill-shaped fields, full-width
   stacked boxes and loose label/field pairs. Works with the three shapes the
   forms already use -- .form-field wrappers, a <label> wrapping its control,
   and fieldsets of checkboxes -- so no field changes name or id.
   Specificity: .shell .filter-panel beats each suite's own .filter-bar
   rules (two classes), which is why the selectors carry all three. */
.shell .filter-panel{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));
  gap:12px 14px;align-items:end;padding:14px 16px;margin:0 0 var(--space-4);
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-card);box-shadow:none}
.shell .filter-panel > .form-field,
.shell .filter-panel > label{display:flex;flex-direction:column;align-items:stretch;gap:5px;margin:0;min-width:0;flex:none}
.shell .filter-panel > .form-field > label,
.shell .filter-panel > label{font-size:12px;font-weight:600;letter-spacing:0;text-transform:none;
  color:var(--ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.shell .filter-panel select,
.shell .filter-panel input:not([type=hidden]):not([type=checkbox]):not([type=radio]){
  width:100%;min-width:0;height:36px;padding:0 10px;border:1px solid var(--line);border-radius:8px;
  background-color:var(--surface);color:var(--ink);font-size:13.5px;font-weight:400;box-sizing:border-box}
/* Room for the drop-down arrow (2026-10-09, Yusuf: "Long term and reablement" ran under it): the
   padding above took away the 30px .shell select leaves, on every filtered list. Long text ends in "...". */
.shell .filter-panel select{padding-right:30px;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}
.shell .filter-panel select:focus-visible,
.shell .filter-panel input:focus-visible{outline:2px solid var(--ink);outline-offset:1px}
.shell .filter-panel .check-field,
.shell .filter-panel label.check-field{flex-direction:row;align-items:center;gap:8px;white-space:normal;align-self:center}
/* Buttons (Apply, Reset, Clear) sit together at the end of the grid. */
.shell .filter-panel > .btn,
.shell .filter-panel > button,
.shell .filter-panel > .filter-panel__actions{justify-self:start;align-self:end;height:36px}
.shell .filter-panel__actions{display:flex;gap:8px;align-items:center}
.shell .filter-panel > p,
.shell .filter-panel > .filter-panel__wide{grid-column:1/-1;margin:0}
/* The search box (a name, an employee ID, a job title) is the field people
   type the most into, so it takes two columns of the grid rather than one,
   in every suite's filters (2026-09-25). One column on a narrow screen, where
   the grid itself is one or two columns wide. */
@media (min-width:600px){
  .shell .filter-panel > .form-field:has(> input[type="search"]),
  .shell .filter-panel > .form-field:has(> input[name="q"]),
  .shell .filter-panel > .form-field:has(> input[name="query"]),
  .shell .filter-panel > .form-field:has(> input[name="search"]),
  .shell .filter-panel > label:has(> input[type="search"]),
  .shell .filter-panel > label:has(> input[name="q"]),
  .shell .filter-panel > label:has(> input[name="query"]),
  .shell .filter-panel > label:has(> input[name="search"]){grid-column:span 2}
}

/* An EMPTY live-filter status line reserves no height: it only has something
   to say while a filter is updating. */
.shell p[role="status"]:empty{min-height:0 !important;margin:0 !important;padding:0 !important}
/* A grid wrapper a form already had (Leave's .book-form-grid) joins the
   panel's grid instead of nesting a second one; and a panel inside a card
   (SU Records' roster) lets the card be the box, so there is no box in a box. */
.shell .filter-panel > .book-form-grid{display:contents}
.shell .filter-panel > .book-form-grid > .form-field{display:flex;flex-direction:column;gap:5px;margin:0;min-width:0}
.shell .filter-panel > .book-form-grid > .form-field > label{font-size:12px;font-weight:600;color:var(--ink-2)}
.shell .card > .filter-panel{border:0;padding:0;margin:0;background:none}
/* A list's filters with its pager beside them (2026-10-04): one box, the
   filters on the left, the pager (views/partials/_pager.ejs, place 'top') on
   the right; the pager drops under the filters when there is no room. The
   wrapper is the box, so the panel inside it draws none (no box in a box). */
.shell .list-filters{display:flex;flex-wrap:wrap;align-items:flex-end;gap:12px 24px;padding:14px 16px;margin:0 0 var(--space-4);
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-card)}
.shell .list-filters > .filter-panel{flex:1 1 0;min-width:min(100%,520px);border:0;padding:0;margin:0;background:none}
.shell .list-filters__pager{flex:0 1 auto;margin-left:auto;max-width:100%}
.shell .list-filters__pager:empty{display:none}
.shell .list-filters__pager .list-pager{margin:0;justify-content:flex-end}
/* Above a list (pages with no filter box to sit in): no gap above it. */
.shell .list-pager--top{margin-top:0}

/* --- Settings -> Users (2026-09-21) --------------------------------------
   The page now has .page, so it gets the same gutter and buttons as every
   other page. Its two header buttons stay together on one line; a
   collapsible section says it opens (a chevron that turns); and the closed
   "Add user" section is just its button, not a card around a button. */
.shell .page .platform-settings__actions{display:flex;gap:8px;flex-wrap:wrap;flex:none}
.shell .page .platform-settings__section > summary:not(.btn)::after,
.shell .page .platform-user-card > summary::after{content:"";flex:none;width:9px;height:9px;margin-left:auto;
  border-right:2px solid var(--ink-3);border-bottom:2px solid var(--ink-3);transform:rotate(45deg) translateY(-3px);transition:transform .15s var(--ease)}
.shell .page .platform-settings__section[open] > summary:not(.btn)::after,
.shell .page .platform-user-card[open] > summary::after{transform:rotate(-135deg) translateY(-3px)}
.shell .page .platform-settings__section:not([open]):has(> summary.btn){background:none;border:0;box-shadow:none;padding:0;overflow:visible}
.shell .page .platform-settings__section > summary.btn{display:inline-flex;width:auto;padding:0 16px}
/* Panel spacing and odd children (2026-09-21, from the screenshot review):
   a gap above the panel when a card precedes it; a button that came wrapped
   in a .form-field sits at the start of its cell, not centred in it. */
.shell .filter-panel{margin-top:var(--space-4)}
.shell .card > .filter-panel:first-child,.shell .card > p[role="status"]:empty + .filter-panel{margin-top:0}
.shell .filter-panel > .form-field > .btn,
.shell .filter-panel > .book-form-grid > .form-field > .btn,
.shell .filter-panel > .form-field > button,
.shell .filter-panel > .book-form-grid > .form-field > button{align-self:flex-start;height:36px}
/* Settings -> Users: the structure section's padding came from both the card
   and its summary, making a tall empty band. The summary alone pads it. */
.shell .page .platform-settings__section{padding:0}

/* ONE DROP ZONE LOOK, EVERYWHERE (item 0v, Yusuf 2026-10-12: "all drag and
   drop functions across the platform should look like this (colour and
   theme should be depending on suite)"). The look is HR's document drop: a
   dashed box in the suite's colour, its upload arrow on the left, "Drag files
   here, or choose files" with "choose files" underlined, the limits under it.
   --drop-edge / --drop-fill / --drop-ink are the page's suite (set in
   the page's .shell--<suite>, from partials/header.ejs; neutral elsewhere), so
   one rule draws every zone: the generic one public/js/file-drop.js puts
   above any file input (.file-drop) and the zones with a drop of their own
   (.drop-look on each: HR's document and application drops, the bulk
   imports, SU Records' documents, Imports' boxes, Audits' upload boxes). */
:root{--drop-edge:var(--ink-3);--drop-fill:var(--surface-2);--drop-ink:var(--ink)}
.shell--leave{--drop-edge:var(--leave-2);--drop-fill:var(--leave-soft);--drop-ink:var(--leave)}
.shell--hr{--drop-edge:var(--hr-2);--drop-fill:var(--hr-soft);--drop-ink:var(--hr)}
.shell--su{--drop-edge:var(--su-2);--drop-fill:var(--su-soft);--drop-ink:var(--su)}
.shell--audits{--drop-edge:var(--audits-2);--drop-fill:var(--audits-soft);--drop-ink:var(--audits)}
.shell--training{--drop-edge:var(--training-2);--drop-fill:var(--training-soft);--drop-ink:var(--training)}
.shell--workforce{--drop-edge:var(--workforce-2);--drop-fill:var(--workforce-soft);--drop-ink:var(--workforce)}
.shell--finance{--drop-edge:var(--finance-2);--drop-fill:var(--finance-soft);--drop-ink:var(--finance)}
.shell--policies{--drop-edge:var(--policies-2);--drop-fill:var(--policies-soft);--drop-ink:var(--policies)}
.drop-look,.file-drop{position:relative;display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:var(--space-4);
  width:100%;box-sizing:border-box;margin:0 0 var(--space-2);padding:var(--space-5);text-align:left;
  border:2px dashed var(--drop-edge);border-radius:var(--radius);background:var(--drop-fill);color:var(--drop-ink);cursor:pointer;
  transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease}
.drop-look:hover,.file-drop:hover{box-shadow:0 4px 14px rgb(43 52 64 / 8%)}
.drop-look:focus-visible,.drop-look:focus-within,.file-drop:focus-visible{outline:2px solid var(--drop-ink);outline-offset:2px}
.drop-look.is-dragover,.drop-look.is-dragging,.drop-look.is-over,.drop-look.is-drop-target,.audit-upload-field--over .drop-look,.file-drop--over{
  border-style:solid;background:color-mix(in srgb,var(--drop-edge) 16%,var(--drop-fill));box-shadow:0 0 0 4px color-mix(in srgb,var(--drop-edge) 22%,transparent)}
.drop-look[aria-disabled="true"]{cursor:not-allowed;opacity:.6;box-shadow:none}
.drop-look__icon,.file-drop__icon{width:2.5rem;height:2.5rem;flex:none;color:var(--drop-ink)}
.drop-look__text,.file-drop__text{display:flex;flex:1;min-width:0;flex-direction:column;gap:var(--space-1)}
.drop-look__text strong,.file-drop__text strong{color:var(--drop-ink);font-size:var(--text-base)}
.drop-look__choose,.file-drop__choose{text-decoration:underline;text-underline-offset:2px}
.drop-look__hint,.file-drop__hint{font-size:var(--text-sm);color:var(--ink-2)}
.file-drop__chosen{font-size:var(--text-sm);color:var(--ok);font-weight:600;word-break:break-word}
.file-drop__chosen:empty{display:none}
@media(max-width:560px){.drop-look,.file-drop{flex-direction:column;align-items:flex-start;padding:var(--space-4)}}
.drop-look[hidden],.file-drop[hidden]{display:none}
/* A file input hidden behind its own button (the Messages image picker):
   that button, or the area marked data-file-drop-area, takes the drop
   (standing rule, 2026-10-09). */
.file-drop-area--over{outline:2px dashed var(--drop-edge);outline-offset:2px;background:var(--drop-fill)}

/* ---------------------------------------------------------------------------
   Meridian's calendar for every date field (public/js/date-picker.js, Yusuf
   2026-10-09). The field keeps its size; the calendar button sits inside its
   right edge where the browser's small icon was. The calendar: large days,
   the platform's colours, light and dark (tokens), a sheet on a phone.
   --------------------------------------------------------------------------- */
.dp-field{position:relative;display:inline-flex;align-items:stretch;max-width:100%;vertical-align:middle}
.dp-field--block{display:flex}
.dp-field--full{width:100%}
/* DD/MM/YYYY whatever the browser's language (Yusuf, 2026-10-09): the box a
   person types in is Meridian's; the real date field lies under it unseen,
   holding the value for the form. */
.dp-field > .dp-text{flex:1 1 auto;min-width:0;padding-right:36px;font-variant-numeric:tabular-nums}
.dp-field--full > .dp-text{flex-grow:1}
.dp-field > .dp-native{position:absolute;inset:0;width:100%;height:100%;margin:0;opacity:0;pointer-events:none}
.dp-toggle{position:absolute;top:50%;right:2px;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;padding:0;border:0;border-radius:6px;background:transparent;color:var(--ink-2);cursor:pointer}
.dp-toggle:hover{background:var(--sunk);color:var(--ink)}
.dp-toggle:focus-visible{outline:3px solid var(--info);outline-offset:1px}
.dp-toggle:disabled{opacity:.4;cursor:default}
.dp-toggle[aria-expanded="true"]{background:var(--sunk);color:var(--ink)}
.dp-panel{position:absolute;z-index:2000;width:356px;max-width:calc(100vw - 16px);padding:12px;border:1px solid var(--ink-3);border-radius:12px;background:var(--surface);color:var(--ink);box-shadow:0 12px 32px rgba(0,0,0,.22);font-size:15px}
.dp-panel--sheet{position:fixed;left:0 !important;right:0;bottom:0;top:auto !important;width:auto;max-width:none;border-radius:16px 16px 0 0;padding:16px 16px calc(16px + env(safe-area-inset-bottom))}
.dp-head{display:flex;align-items:center;gap:6px;margin-bottom:8px}
.dp-nav{flex:0 0 44px;height:44px;border:1px solid var(--ink-3);border-radius:8px;background:var(--surface);color:var(--ink);font-size:24px;line-height:1;cursor:pointer}
.dp-nav:hover{background:var(--sunk)}
.dp-nav:disabled{opacity:.35;cursor:default}
.dp-panel .dp-select{flex:1 1 auto;min-width:0;min-height:44px;font-size:15px;font-weight:700}
.dp-grid{width:100%;border-collapse:collapse;table-layout:fixed}
.dp-grid th{padding:4px 0;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--ink-2);text-align:center}
.dp-grid td{padding:2px;text-align:center}
.dp-day{width:100%;min-width:40px;height:44px;border:2px solid transparent;border-radius:8px;background:transparent;color:var(--ink);font:inherit;font-size:16px;cursor:pointer}
.dp-day:hover{background:var(--sunk)}
.dp-day:focus-visible{outline:3px solid var(--info);outline-offset:1px}
.dp-day--weekend{color:var(--ink-2)}
.dp-day--outside{color:var(--ink-3)}
.dp-day--today{border-color:var(--info);font-weight:700}
.dp-day--chosen,.dp-day--chosen:hover{background:var(--ink);color:var(--surface);font-weight:700}
.dp-day:disabled{color:var(--ink-3);text-decoration:line-through;opacity:.55;cursor:not-allowed;background:transparent}
.dp-foot{display:flex;gap:8px;margin-top:10px}
.dp-foot .btn{min-height:40px}
.dp-foot .dp-close{margin-left:auto}
.dp-visually-hidden{position:absolute !important;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* data-file-drop-only (file-drop.js, 2026-10-13): the drop area's own "choose
   files" opens the input; the browser's button under it is hidden, not removed. */
input.file-drop__native--hidden{position:absolute!important;width:1px!important;height:1px!important;min-height:0!important;margin:-1px!important;padding:0!important;border:0!important;overflow:hidden!important;clip:rect(0 0 0 0)!important;white-space:nowrap!important}

/* A link to a missing question lands on it, outlined in red (2026-10-13; public/js/field-attention.js). */
.field-attention{outline:2px solid var(--danger);outline-offset:4px;border-radius:6px;background:var(--danger-soft);scroll-margin-top:calc(var(--bar, 56px) + 24px)}
tr.field-attention>td{background:var(--danger-soft)}
.field-attention__note{margin:0 0 6px;font-size:var(--text-xs, 12px);font-weight:700;color:var(--danger)}
/* The Incomplete page (2026-10-13): what is missing, by tab, each a button to it. */
.incomplete-group{margin-top:18px;padding-top:14px;border-top:1px solid var(--line)}
.incomplete-group__h{display:flex;align-items:center;gap:8px;margin:0;font-size:1rem}
.incomplete-group__n{display:inline-grid;place-items:center;min-width:1.4rem;height:1.4rem;padding:0 .35rem;border-radius:999px;background:var(--danger-soft);color:var(--danger);font-size:.75rem;font-weight:700}
.incomplete-group__why{margin:4px 0 8px}
.incomplete-list{list-style:none;margin:0;padding:0;display:grid;gap:6px;max-width:44rem}
.incomplete-item{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 12px;border:1px solid var(--line);border-left:3px solid var(--danger);border-radius:8px;background:var(--surface, transparent)}
.incomplete-item__label{min-width:0;overflow-wrap:anywhere;color:var(--ink)}
.staff-file-badge__go{flex:none;margin-left:2px}
a.staff-file-badge{text-decoration:none}

/* Meridian's time picker (public/js/time-picker.js, item 0av, 13/10/2026): the
   date picker's field, button and panel, with the hours and the minutes as
   grids of the calendar's large day buttons. */
.tp-panel{width:332px}
.tp-step{margin:0 0 8px;font-weight:700;color:var(--ink)}
.tp-sub{margin:10px 0 6px;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--ink-2)}
.tp-grid{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:4px}
.tp-cell{min-width:0;font-variant-numeric:tabular-nums}

/* Sortable column headings (0ak point 13; public/js/table-sort.js, shared since item 0cx F7 -- SU Records and Meridian Finance): the heading is the button. */
.table-sort__button{all:unset;cursor:pointer;display:inline-flex;align-items:center;gap:.25rem;font:inherit;font-weight:inherit;color:inherit}
.table-sort__button:focus-visible{outline:2px solid var(--ink);outline-offset:2px;border-radius:2px}
.table-sort__button::after{content:"\2195";font-size:.8em;opacity:.45}
th[aria-sort="ascending"] .table-sort__button::after{content:"\2191";opacity:.9}
th[aria-sort="descending"] .table-sort__button::after{content:"\2193";opacity:.9}
