/* ============================================================================
   SCEND Client Portal — shared theme & component kit
   The single source of truth for the client portal's look, feel, and states.
   Every client page links this and composes from these classes. Do NOT define
   page-local :root tokens or re-implement these components (STANDARDS + DESIGN.md).

   Base: black-and-white neutrals · SCEND Teal accent used sparingly · semantic
   colours (green/amber/red) reserved for data state. Light primary, dark supported.
   ============================================================================ */

/* --on-accent is the text colour for anything sitting ON the accent fill, and it must
   be declared in EVERY block that declares --accent — all four of them. It used to be
   set once here, on bare :root, as #fff, and never again. That single value then had to
   serve two different accents (#00B5B6 light, #22C9C9 dark) and it suited neither:
   measured in the browser, white on the default teal is 2.53:1, under half of AA.
   brand_palette() in the database — the same function that colours a custom brand — has
   always returned #1f2430 with contrast 6.12 for that exact colour, so the stylesheet and
   the database were answering the same question differently and the stylesheet was wrong.
   26 of our 27 live brands are on the default teal, so this was every primary button,
   every active nav item and every active tab in the portal (28/08/2026). Keep the pair
   together: an --accent without an --on-accent beside it is the bug coming back. */
/* THE QUIET RAMP AND THE SEMANTIC PILLS — retuned 01/09/2026, computed not eyeballed.
   Audited every foreground/background token pair in both themes: LIGHT had eight under
   AA, dark had three. Light being the worse of the two is backwards — DESIGN.md says
   "Light is primary; dark is tuned, not inverted", and in practice dark got tuned and
   light did not. Body text was always fine; it was the quiet furniture that failed.

   The fix keeps the zinc ramp a RAMP and shifts it one step darker, so hierarchy
   survives (ink > ink-2 > muted > faint, each still visibly lighter than the last):
     light  muted #71717A -> #52525B (4.83 -> 7.73)   faint #A1A1AA -> #66666E (2.56 -> 5.69)
     dark   muted #8A8A93 -> #A1A1AA (5.33 -> 7.11)   faint #57575F -> #8A8A93 (2.55 -> 5.33)
   Simply pushing --faint to 4.5 would have collapsed it into --muted and destroyed the
   four-level hierarchy the type scale depends on; moving both preserves it.

   Both values are checked against BOTH grounds. --muted was already failing on the
   ground (4.40) while passing on a card (4.83) — a pair that looks fine in one place and
   not the other, which is why every pair here is tested twice.

   Light semantic foregrounds darkened against their own soft grounds (dark already
   passed, so dark is untouched):  good 3.49->4.72 · warn 3.25->5.28 · crit 4.00->5.53

   Accent-coloured TEXT (links, .btn.subtle, .btn-link, hover states) now reads
   --accent-ink, not --accent: the accent is a FILL colour and at 2.53:1 on white it was
   never a text colour. --accent-ink also darkened #0A7C7C -> #097474 so .btn.subtle
   clears 4.5 (4.49 -> 4.99) — one token instead of touching --accent-soft, which is a
   surface used in many places.

   Rail section labels #6B6B72 -> #8A8A93 in all four blocks; the rail is dark in both
   themes, so one value serves both (3.72/3.97 -> 5.75/6.14).

   Re-audit after changing anything here — the script is trivial: parse the four blocks,
   pair each foreground with the grounds it actually sits on, require >= 4.5. */
:root{
  --bg:#F4F4F5; --canvas:#FFFFFF;
  --rail:#0B0B0C; --rail-2:#18181B; --rail-ink:#A1A1AA; --rail-ink-strong:#FFFFFF; --rail-lbl:#8A8A93;
  --ink:#111111; --ink-2:#3F3F46; --muted:#52525B; --faint:#66666E;
  --line:#E4E4E7; --line-2:#F1F1F3;
  --accent:#00B5B6; --accent-ink:#097474; --accent-soft:#E3F6F6; --on-accent:#1F2430; --skel-1:#BFEAEA; --skel-2:#EAF9F9;
  --good:#0F7A4B; --good-soft:#E4F4EC;
  --warn:#8A5A12; --warn-soft:#FBF1DF;
  --crit:#B02A34; --crit-soft:#FBE8EA;
  --card-shadow:0 1px 2px rgba(9,9,11,.04), 0 6px 18px rgba(9,9,11,.06);
  --r:16px; --r-sm:10px;
  --ff:-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,"Helvetica Neue",Arial,sans-serif;
  /* motion — the sleekness knobs. Keep transitions short + consistent. */
  --t-fast:120ms; --t:170ms; --ease:cubic-bezier(.4,0,.2,1);
}
@media (prefers-color-scheme:dark){:root:not([data-theme]){
  --bg:#0A0A0B; --canvas:#151518; --rail:#000000; --rail-2:#151518; --rail-ink:#8A8A93; --rail-ink-strong:#FFF; --rail-lbl:#8A8A93;
  --ink:#F4F4F5; --ink-2:#C4C4CC; --muted:#A1A1AA; --faint:#8A8A93; --line:#27272B; --line-2:#1C1C20;
  --accent:#22C9C9; --accent-ink:#7DE6E6; --accent-soft:#0E2E2E; --on-accent:#1F2430; --skel-1:#123433; --skel-2:#1C4645;
  --good:#3FBE84; --good-soft:#123227; --warn:#E0A64A; --warn-soft:#33280F; --crit:#F06B74; --crit-soft:#3A1B20;
  --card-shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 22px rgba(0,0,0,.3);
}}
:root[data-theme="dark"]{
  --bg:#0A0A0B; --canvas:#151518; --rail:#000000; --rail-2:#151518; --rail-ink:#8A8A93; --rail-ink-strong:#FFF; --rail-lbl:#8A8A93;
  --ink:#F4F4F5; --ink-2:#C4C4CC; --muted:#A1A1AA; --faint:#8A8A93; --line:#27272B; --line-2:#1C1C20;
  --accent:#22C9C9; --accent-ink:#7DE6E6; --accent-soft:#0E2E2E; --on-accent:#1F2430; --skel-1:#123433; --skel-2:#1C4645;
  --good:#3FBE84; --good-soft:#123227; --warn:#E0A64A; --warn-soft:#33280F; --crit:#F06B74; --crit-soft:#3A1B20;
  --card-shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 22px rgba(0,0,0,.3);
}
:root[data-theme="light"]{
  --bg:#F4F4F5; --canvas:#FFFFFF; --rail:#0B0B0C; --rail-2:#18181B; --rail-ink:#A1A1AA; --rail-lbl:#8A8A93;
  --ink:#111111; --ink-2:#3F3F46; --muted:#52525B; --faint:#66666E; --line:#E4E4E7; --line-2:#F1F1F3;
  --accent:#00B5B6; --accent-ink:#097474; --accent-soft:#E3F6F6; --on-accent:#1F2430; --skel-1:#BFEAEA; --skel-2:#EAF9F9;
  --good:#0F7A4B; --good-soft:#E4F4EC; --warn:#8A5A12; --warn-soft:#FBF1DF; --crit:#B02A34; --crit-soft:#FBE8EA;
  --card-shadow:0 1px 2px rgba(9,9,11,.04), 0 6px 18px rgba(9,9,11,.06);
}

*{box-sizing:border-box}
[hidden]{display:none!important}
html{color-scheme:light dark}
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--ff);-webkit-font-smoothing:antialiased;line-height:1.5}
a{color:var(--accent-ink);text-decoration:none}
.tnum{font-variant-numeric:tabular-nums}
/* Quiet-text utilities. Both were used across the portal and defined in NEITHER the
   kit nor several of the pages using them — .hint on 12 client pages with 11 separate
   local definitions, .muted similar. Where a page had no copy, text meant to recede
   rendered at full strength. These are the floor: any page that already defines its
   own still wins, so nothing existing changes. */
.muted{color:var(--muted)}
.hint{color:var(--muted);font-size:12px;line-height:1.5;margin:0}
.faint{color:var(--faint)}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}

/* ---- app shell ---- */
.app{display:grid;grid-template-columns:236px 1fr;min-height:100vh}
@media(max-width:860px){
  .app{grid-template-columns:1fr;padding-bottom:70px}
  .app .rail{display:none}
  .topbar{padding:13px 18px}
  .content{padding:22px 18px 36px}
}
/* height:100vh with no overflow rule CLIPPED the rail: on a laptop viewport the last
   nav items and the account button at the foot were simply unreachable, with no way to
   scroll to them. Adding Assistant made the rail one item taller and pushed it over the
   edge at common heights, so this ships with that change rather than after it.
   `margin-top:auto` on .rail-foot still pins the foot down when everything fits, and
   collapses to 0 and scrolls with the list when it doesn't — which is what you want. */
.rail{background:var(--rail);color:var(--rail-ink);padding:22px 16px;display:flex;flex-direction:column;gap:3px;position:sticky;top:0;height:100vh;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.18) transparent}
/* A default light scrollbar on a near-black rail reads as a bug. Keep it faint. */
.rail::-webkit-scrollbar{width:8px}
.rail::-webkit-scrollbar-track{background:transparent}
.rail::-webkit-scrollbar-thumb{background:rgba(255,255,255,.16);border-radius:4px}
.rail::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.28)}
/* Rail wordmark. scend-logo.png is pure black on transparent and the rail is #0B0B0C (light)
   / #000 (dark), so dropped in raw it is black on black — invisible. invert(1) turns pure
   black to pure white and leaves the alpha alone, which beats shipping a second asset that
   would then need keeping in step. 20px tall keeps it clear of the 236px rail's padding. */
.rail .brand{padding:0 8px 18px;flex:none}
.rail .brand img{height:20px;width:auto;display:block;filter:invert(1)}
.brand{display:flex;align-items:center;gap:10px;padding:4px 8px 20px;color:var(--rail-ink-strong)}
.brand .mark{width:30px;height:30px;border-radius:8px;background:var(--accent);display:grid;place-items:center;font-weight:800;font-size:16px;color:var(--on-accent,#fff);letter-spacing:-.5px;overflow:hidden}
.brand b{font-size:16.5px;letter-spacing:-.2px;font-weight:700}
.brand .plat{font-size:9px;letter-spacing:.6px;color:var(--rail-lbl);font-weight:700;text-transform:uppercase;margin-top:1px}
.navlbl{font-size:10.5px;text-transform:uppercase;letter-spacing:.8px;color:var(--rail-lbl);padding:16px 8px 6px;font-weight:700}
.nav{display:flex;align-items:center;gap:11px;padding:9px 10px;border-radius:9px;font-size:13.5px;font-weight:500;color:var(--rail-ink);cursor:pointer;text-decoration:none;transition:background var(--t-fast) var(--ease),color var(--t-fast) var(--ease)}
.nav svg{width:17px;height:17px;stroke:currentColor;fill:none;stroke-width:1.7;flex:none}
.nav:hover{background:rgba(255,255,255,.06);color:#fff}
.nav.on{background:var(--accent);color:var(--on-accent,#fff)}
.rail-foot{margin-top:auto;display:flex;align-items:center;gap:10px;padding:10px 8px;border:none;border-top:1px solid rgba(255,255,255,.08);background:transparent;width:100%;cursor:pointer;font-family:inherit;transition:background var(--t-fast) var(--ease)}
.rail-foot:hover{background:rgba(255,255,255,.05)}
.rail-foot .cog{width:16px;height:16px;stroke:var(--rail-ink);fill:none;stroke-width:1.7;margin-left:auto;flex:none}
.rail-foot:hover .cog{stroke:#fff}
.avatar{width:30px;height:30px;border-radius:50%;background:#26262B;color:#fff;display:grid;place-items:center;font-size:12px;font-weight:700;flex:none}

/* ---- mobile navigation ---- */
.mobile-tabs,.mobile-drawer,.mobile-drawer-backdrop{display:none}
@media(max-width:860px){
  .mobile-tabs{position:fixed;z-index:40;left:0;right:0;bottom:0;height:70px;padding:7px max(8px,env(safe-area-inset-right)) calc(7px + env(safe-area-inset-bottom)) max(8px,env(safe-area-inset-left));display:grid;grid-template-columns:repeat(5,1fr);gap:3px;background:var(--canvas);border-top:1px solid var(--line);box-shadow:0 -6px 20px rgba(9,9,11,.08)}
  .mobile-tab{min-width:0;border:0;background:transparent;color:var(--muted);font:600 10.5px/1 var(--ff);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;border-radius:9px;text-decoration:none;cursor:pointer;padding:4px}
  .mobile-tab svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.7}
  .mobile-tab.on{color:var(--accent-ink);background:var(--accent-soft)}
  .mobile-drawer-backdrop{position:fixed;z-index:2147483099;inset:0;display:block;background:rgba(0,0,0,.42);border:0;padding:0}
  .mobile-drawer{position:fixed;z-index:2147483100;left:12px;right:12px;bottom:82px;display:block;max-height:min(72vh,620px);overflow:auto;background:var(--canvas);border:1px solid var(--line);border-radius:20px;box-shadow:0 18px 55px rgba(0,0,0,.24);padding:10px 14px 14px}
  .mobile-drawer-head{position:sticky;top:-10px;z-index:1;display:flex;align-items:center;gap:12px;padding:14px 4px 10px;background:var(--canvas);border-bottom:1px solid var(--line)}
  .mobile-drawer-head b{font-size:15px}
  .mobile-drawer-close{margin-left:auto}
  .mobile-drawer .navlbl{color:var(--faint);padding-top:14px}
  .mobile-drawer .nav{color:var(--ink-2);padding:11px 9px}
  .mobile-drawer .nav:hover{background:var(--line-2);color:var(--ink)}
  .mobile-drawer .nav.on{background:var(--accent-soft);color:var(--accent-ink)}
  .mobile-drawer .mobile-settings{width:100%;margin-top:12px;justify-content:flex-start}
  .mobile-scroll-hint{display:flex;align-items:center;justify-content:flex-end;gap:5px;margin:0 2px 7px;color:var(--faint);font-size:11px;font-weight:600}
}
@media(max-width:420px){
  .topbar{padding-left:14px;padding-right:14px}
  .content{padding-left:14px;padding-right:14px}
  .fresh{max-width:108px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
}

/* ---- top bar ---- */
.main{min-width:0;display:flex;flex-direction:column}
.topbar{display:flex;align-items:center;gap:16px;padding:16px 32px;border-bottom:1px solid var(--line);background:var(--canvas);position:sticky;top:0;z-index:5}
.crumb{font-size:13px;color:var(--muted)} .crumb b{color:var(--ink);font-weight:600}
.spacer{margin-left:auto}
.fresh{font-size:12px;color:var(--faint)}
.icon-btn{width:36px;height:36px;border-radius:9px;border:1px solid var(--line);background:var(--canvas);display:grid;place-items:center;cursor:pointer;color:var(--ink-2);transition:border-color var(--t-fast) var(--ease),color var(--t-fast) var(--ease)}
.icon-btn:hover{border-color:var(--accent);color:var(--accent-ink)}
.icon-btn svg{width:17px;height:17px;stroke:currentColor;fill:none;stroke-width:1.7}

/* ---- content + type scale ---- */
/* 1680, not 1180. Kyle, 04/09/2026: "make all pages wider if thats what you need to
   do? can't have one odd one out" — the Products table was capped at 1114px of usable
   width whatever the screen, so its columns competed for a fixed budget and every fix
   for one squeezed another.
   1680 is not a new number: orders.html had already overridden .content to exactly this
   and has been live at it, so the odd one out was every OTHER page. That override is
   now removed, which is the point — one width, set once, in the kit. */
.content{padding:28px 32px 44px;max-width:1680px;width:100%;margin:0 auto}
.h1{font-size:25px;font-weight:700;letter-spacing:-.5px;margin:0 0 3px;text-wrap:balance}
.sub{color:var(--muted);font-size:13.5px;margin:0 0 24px}

/* ---- segmented control / tabs ----
   Was defined page-locally in orders.html and inventory.html and nowhere else, so the
   Products tabs shipped as raw browser buttons — grey boxes with hard borders, on a
   page meant to look sellable. A control used on three pages belongs in the kit; DESIGN
   says compose, never invent. One definition here, none in the pages. */
.segs{display:inline-flex;background:var(--canvas);border:1px solid var(--line);border-radius:var(--r-sm);padding:3px;gap:2px}
.segs button{border:none;background:none;font-family:inherit;font-size:12.5px;font-weight:600;color:var(--muted);padding:6px 12px;border-radius:7px;cursor:pointer;transition:background var(--t-fast) var(--ease),color var(--t-fast) var(--ease)}
.segs button.on{background:var(--accent);color:var(--on-accent,#fff)}
.segs button:not(.on):hover{color:var(--ink)}
/* count badge riding inside a tab (Orders uses it for filter counts) */
.segs button .fbadge{display:inline-block;margin-left:6px;font-size:10px;font-weight:700;line-height:1;padding:3px 6px;border-radius:9px;background:var(--warn-soft);color:var(--warn);font-variant-numeric:tabular-nums;vertical-align:1px}
.segs button .fbadge.z{background:var(--line-2);color:var(--muted)}
/* On the ACTIVE tab the badge sits on the accent fill. It used to be a translucent
   WHITE pill (rgba(255,255,255,.22)) carrying white text, which composites to white
   on rgb(153,101,241) — 3.83:1, under AA for text this small, and measurably worse
   than the tab's own label at 5.7:1. Lightening the ground under white text was the
   bug. Darkening it instead is self-correcting across every white-label brand: the
   pill is always darker than the accent the tab already prints white on, so the
   badge can never be less legible than the label beside it. Measured on the branch
   preview as an Aeyla client: 3.83:1 → 5.33:1, clearing AA.

   That measurement also turned up something this rule does NOT fix and must not be
   read as fixing: the active tab's own white LABEL is only 2.98:1 on Aeyla's accent
   (it is 5.7:1 on the darker SCEND default, which is why it reads fine in-house).
   That is the white-label palette, not this component — a brand free to choose a
   pale accent can put white text below AA on every active tab in the portal. Raised
   with Kyle 28/08/2026; fixing it means constraining the brand accent or picking the
   label colour from its luminance, and belongs with the branding work, not here. */
.segs button.on .fbadge{background:rgba(0,0,0,.28);color:#fff}
/* a plain count on a tab, e.g. "Kits 43" — quieter than a badge, same alignment */
.segs button .c{margin-left:6px;font-size:11px;font-weight:700;color:var(--faint);font-variant-numeric:tabular-nums}
.segs button.on .c{color:rgba(255,255,255,.8)}

/* ---- filter chip (.fbtn) ----
   Identical in inventory.html and products.html, byte for byte, and used nowhere else
   in the kit. Promoted so the next page that wants a filter row does not copy it a
   third time. */
.fbtn{font-size:12.5px;font-weight:600;padding:6px 12px;border:1px solid var(--line);border-radius:20px;background:var(--canvas);color:var(--ink-2);cursor:pointer;font-family:inherit;transition:border-color var(--t-fast) var(--ease),background var(--t-fast) var(--ease),color var(--t-fast) var(--ease)}
.fbtn:hover{border-color:var(--accent);color:var(--accent-ink)}
.fbtn.on{background:var(--accent);border-color:var(--accent);color:var(--on-accent,#fff)}
.fbtn .c{margin-left:6px;font-size:11px;color:var(--faint);font-variant-numeric:tabular-nums}
.fbtn.on .c{color:rgba(255,255,255,.85)}

/* ---- inline subscribe control ----
   Wrapper for SCEND_CLIENT.alertChip: an .fbtn, an optional link, and a status
   line. Lives in the kit rather than on four pages because it mounts on four
   pages; the button itself is .fbtn, deliberately reused rather than reinvented.
   --muted for the status line, NOT --faint: --faint measures 2.56:1 against the
   card in light and 2.55:1 in dark, both under the 4.5:1 AA floor for text this
   size. Measured 28/08 on the live kit. */
.alert-chip{display:inline-flex;align-items:center;gap:9px;flex-wrap:wrap}
.alert-chip .msg{font-size:11.5px;color:var(--muted)}

/* ---- label chip ----
   A small static pill. NOTE the name has been doing two jobs: order.html and
   support.html used .chip for this, while reports.html used it for an interactive
   filter — which is .fbtn above, not this. Same word, two components, which is how a
   design system stops being one. This is the label; reports moves to .fbtn. */
.chip{display:inline-block;font-size:10.5px;font-weight:600;padding:1px 7px;border-radius:10px;background:var(--line-2);color:var(--ink-2)}
/* support.html wanted the same pill a shade larger and squarer; a modifier, not a copy */
.chip.sq{font-size:11px;border-radius:6px;padding:1px 8px}

/* ---- horizontal scroll wrapper ----
   Three pages had the same one-liner; guide.html frames it, so that is a modifier
   rather than a fourth copy. */
.tbl-wrap{overflow-x:auto}
.tbl-wrap.framed{border:1px solid var(--line);border-radius:var(--r-sm);margin:12px 0 14px}

/* ---- pager ----
   Was defined SIX different ways across seven pages, because it was never in the kit.
   Three pages meant the same thing (a count on the left, Prev/Next on the right) and
   had drifted apart; three used the name for a different layout entirely. One
   component with modifiers now, rather than six private ones:
     .pager             count left, controls right — the default, inside a card
     .pager.center      a single control, centred (a "show more", not a pager)
     .pager.bare        no top border or card padding, for a pager outside a card
   Controls are .btn like everything else; the old copies styled bare <button> tags,
   which is how one page ended up with browser-default buttons. */
.pager{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;padding:14px 20px 4px;border-top:1px solid var(--line-2)}
.pager.center{justify-content:center}
.pager.bare{padding:12px 0 0;border-top:none}
.pager .pg-info{font-size:12px;color:var(--muted);font-variant-numeric:tabular-nums}
.pager .pg-btns{display:flex;align-items:center;gap:8px}
.pager .btn{padding:6px 12px;font-size:12.5px}
.pager .btn:disabled{opacity:.4;cursor:default}

/* ---- KPI tiles ---- */
.kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:16px}
@media(max-width:900px){.kpis{grid-template-columns:repeat(2,1fr)}}
.kpi{background:var(--canvas);border:1px solid var(--line);border-radius:var(--r);padding:18px 20px;box-shadow:var(--card-shadow)}
.kpi .lbl{font-size:12px;color:var(--muted);font-weight:600;display:flex;align-items:center;gap:7px;margin-bottom:12px}
.kpi .lbl .ic{width:26px;height:26px;border-radius:7px;display:grid;place-items:center;background:var(--accent-soft);color:var(--accent-ink)}
.kpi .lbl .ic svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:1.8}
.kpi .val{font-size:33px;font-weight:750;letter-spacing:-1px;line-height:1}
.kpi .val small{font-size:17px;font-weight:600;color:var(--muted);letter-spacing:0}
.kpi .meta{font-size:12px;margin-top:9px;display:flex;align-items:center;gap:6px;color:var(--muted)}
.trend{font-weight:700;display:inline-flex;align-items:center;gap:3px}
.trend.up{color:var(--good)} .trend.down{color:var(--crit)}

/* ---- cards / layout ---- */
.grid2{display:grid;grid-template-columns:1.7fr 1fr;gap:16px;margin-bottom:16px}
@media(max-width:900px){.grid2{grid-template-columns:1fr}}
.card{background:var(--canvas);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--card-shadow);overflow:hidden}
.card-h{display:flex;align-items:center;justify-content:space-between;padding:17px 20px 0}
.card-h h3{font-size:14px;font-weight:650;margin:0;letter-spacing:-.1px}
.card-h .link{font-size:12.5px;color:var(--accent-ink);font-weight:600}
.card-b{padding:16px 20px 20px}

/* ---- list rows ---- */
.row{display:flex;align-items:center;gap:12px;padding:11px 0;border-top:1px solid var(--line-2)}
.row:first-child{border-top:none}
.row .main2{flex:1;min-width:0}
.row .t{font-size:13.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.row .s{font-size:12px;color:var(--muted)}

/* ---- status pills (semantic only, never the accent) ---- */
.pill{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:650;padding:3px 9px;border-radius:20px}
.pill::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}
.pill.good{color:var(--good);background:var(--good-soft)}
.pill.warn{color:var(--warn);background:var(--warn-soft)}
.pill.crit{color:var(--crit);background:var(--crit-soft)}
.pill.mut{color:var(--muted);background:var(--line-2)}

/* ---- progress track ---- */
.track{height:6px;border-radius:4px;background:var(--line-2);overflow:hidden}
.fill{height:100%;border-radius:4px}

/* ---- buttons ---- */
/* The BASE now carries a look of its own. It set no background or colour, so a bare
   class="btn" — ten of them across four client pages — fell through to the browser's
   default: #EFEFEF, black text, hard border. That is the raw-button look that made the
   portal feel unfinished. Base = the quiet outlined button; .primary/.subtle/.ghost/
   .danger all set their own background and still win on specificity. */
.btn{border:none;border-radius:9px;font-family:inherit;font-size:13px;font-weight:600;padding:9px 15px;cursor:pointer;background:var(--canvas);color:var(--ink);box-shadow:inset 0 0 0 1px var(--line);transition:filter var(--t-fast) var(--ease),background var(--t-fast) var(--ease),border-color var(--t-fast) var(--ease)}
.btn:hover:not(:disabled){box-shadow:inset 0 0 0 1px var(--accent);color:var(--accent-ink)}
/* variants own their surface, so they must clear the base outline */
.btn.primary,.btn.subtle,.btn.danger,.btn.red{box-shadow:none}
.btn.primary:hover,.btn.subtle:hover{color:inherit}
.btn:active{transform:translateY(.5px)}
.btn.primary{background:var(--accent);color:var(--on-accent,#fff)} .btn.primary:hover{filter:brightness(1.06)}
.btn.subtle{background:var(--accent-soft);color:var(--accent-ink)}
.btn.ghost{background:transparent;color:var(--ink);border:1px solid var(--line);box-shadow:none} .btn.ghost:hover{border-color:var(--accent);color:var(--accent-ink)}
.btn-link{border:0;background:transparent;color:var(--accent-ink);font:600 12.5px/1.4 var(--ff);padding:0;cursor:pointer}
/* Variants the pages have been ASKING FOR ALL ALONG. `btn danger` appears on four
   pages, `btn sm` on sixteen, `btn red` on one — and none of the three existed, so
   every one of them rendered as a plain .btn. A destructive control looked exactly
   like an ordinary one (including the Cancel-booking button added 27/08/2026), and
   "small" buttons were full size. Found by listing every class used in markup and
   checking it resolves somewhere; that sweep is worth repeating.
   .red is an alias, not a second look: two names for one thing is how this started. */
.btn.danger,.btn.red{background:var(--crit-soft);color:var(--crit);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--crit) 26%,transparent)}
.btn.danger:hover:not(:disabled),.btn.red:hover:not(:disabled){background:var(--crit);color:#fff;box-shadow:none}
.btn.sm{font-size:12px;padding:6px 11px;border-radius:8px}
.btn:disabled{opacity:.45;cursor:default}
.btn:disabled:hover{filter:none}
/* .skel big — a taller shimmer for a headline figure, used on 12 tiles. */
.skel.big{height:26px;border-radius:8px}
.btn-link:hover{text-decoration:underline}

/* ---- states: loading / empty / error (MANDATORY on every data surface) ---- */
.skel{height:11px;border-radius:6px;margin:8px 0;background:linear-gradient(90deg,var(--skel-1) 25%,var(--skel-2) 42%,var(--skel-1) 58%);background-size:220% 100%;animation:sk 1.2s infinite}
.skel-tile{background:var(--canvas);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--card-shadow);padding:18px 20px;min-height:96px}
@keyframes sk{from{background-position:120% 0}to{background-position:-120% 0}}
.empty{text-align:center;color:var(--muted);font-size:12.5px;padding:22px 14px}
.empty .e-ic{font-size:20px;margin-bottom:6px;opacity:.55}
.error{text-align:center;color:var(--crit);font-size:12.5px;padding:22px 14px}

/* content fades in once data lands — no abrupt swaps (sleekness) */
.fade-in{animation:fade var(--t) var(--ease)}
@keyframes fade{from{opacity:0;transform:translateY(3px)}to{opacity:1;transform:none}}

/* respect reduced motion everywhere */
@media(prefers-reduced-motion:reduce){
  *,.skel,.fade-in{animation:none!important;transition:none!important}
}

/* the "change this" link next to a reporting page's title — plain text, same weight as
   the crumb, so it reads as a sibling of the title rather than another button */
.scend-cfg{margin-left:12px;font-size:12.5px;font-weight:700;color:var(--accent);text-decoration:none;white-space:nowrap}
.scend-cfg:hover{text-decoration:underline}
