
/* ---- 0. THEME TOKENS ---------------------------------------------------------
   The dashboards hard-coded 347 literal colours (rgba(0,212,255,..) cyan,
   rgba(255,255,255,..) overlays, rgba(0,0,0,..) shadows). Those ignored the theme
   entirely, so light mode rendered cyan hairlines and white-on-white washes over a
   blue-accented light page - half-converted and muddy. Those literals are now
   rgba(var(--c-rgb),..) / rgba(var(--wash),..) / rgba(var(--shade),..) and resolve
   per theme from here.
   --c-rgb  accent as an r,g,b triplet   --wash  subtle overlay ON the surface
   --shade  shadow colour                --sunken/--raised  inset + bubble fills */
:root{
  --c-rgb:0,212,255;
  --wash:255,255,255;
  --shade:0,0,0;
  --sunken:rgba(0,0,0,.18);
  --sunken-2:rgba(0,0,0,.30);
  --raised:rgba(255,255,255,.05);
}
/* The boot screen and auth gate are deliberately dark in BOTH themes - they scope
   their own dark vars. Re-pin the triplets so their cyan never follows light mode. */
.boot,#authGate{--c-rgb:0,212,255;--wash:255,255,255;--shade:0,0,0;
  --sunken:rgba(0,0,0,.18);--sunken-2:rgba(0,0,0,.30);--raised:rgba(255,255,255,.05);}

/* ---- LINKS -------------------------------------------------------------------
   There was no bare `a` rule anywhere in the fleet, so any link an author forgot
   to give a class fell through to the browser default #0000EE - 2.09:1 on the
   dark surface, well under the 3:1 floor. It only ever surfaced on /print-status
   ("On board" -> /kevin) because that was the one unclassed link, but the hole
   was fleet-wide and the next unclassed link would have reopened it.
   Pinning to the accent clears both themes: #00d4ff on the dark ground and
   #1d4ed8 on the light one. Underline only on hover/keyboard focus so dense
   tables do not turn into a field of rules. */
a{color:var(--c,#00d4ff);text-decoration:none;}
a:visited{color:var(--c,#00d4ff);}
a:hover,a:focus-visible{text-decoration:underline;}

/* ---- LIGHT THEME -------------------------------------------------------------
   One canonical palette. The 7 pages had drifted into THREE different light token
   sets; this block is injected last so it overrides all of them.
   Design intent: neutral slate structure (blue-tinted hairlines read as muddy on
   white), a stronger blue-700 accent that holds its own against a white surface,
   and AA-contrast semantic colours. */
[data-theme="light"]{
  --bg:#f1f4f8;
  --bg-card:#ffffff;
  --bg-hover:#f7f9fc;
  --c:#1d4ed8;
  --c-rgb:29,78,216;
  --c-border:rgba(15,23,42,0.11);     /* neutral structure, not blue */
  --c-dim:rgba(29,78,216,0.055);
  --c-glow:rgba(29,78,216,0.10);
  --c-glow-txt:none;
  --green:#15803d; --green-dim:rgba(21,128,61,0.075);  --green-bdr:rgba(21,128,61,0.30);
  --red:#b91c1c;   --red-dim:rgba(185,28,28,0.07);     --red-bdr:rgba(185,28,28,0.30);
  --amber:#b45309; --amber-dim:rgba(180,83,9,0.075);   --amber-bdr:rgba(180,83,9,0.32);
  --text:#0f172a; --text-sec:#334155; --text-mute:#5a6b80;
  --wash:15,23,42;                    /* overlays become dark-on-light */
  --shade:15,23,42;
  --sunken:rgba(15,23,42,.035);
  --sunken-2:#ffffff;
  --raised:rgba(15,23,42,.04);
  --label-font:'Inter',system-ui,sans-serif;
  --nav-bg:rgba(255,255,255,0.94);
  --card-shadow:0 1px 2px rgba(15,23,42,.06),0 2px 8px rgba(15,23,42,.05);
  --card-shadow-hover:0 4px 12px rgba(15,23,42,.10),0 2px 6px rgba(15,23,42,.06);
  --radius:8px;
}
/* Light-mode corrections for effects that only ever made sense on a dark surface. */
[data-theme="light"] .nav-link{box-shadow:0 1px 2px rgba(15,23,42,.05);}
[data-theme="light"] .card,[data-theme="light"] .kpi,[data-theme="light"] .panel,
[data-theme="light"] .chart-panel,[data-theme="light"] .needle{box-shadow:var(--card-shadow);}
[data-theme="light"] .scan-line{display:none;}          /* CRT sweep reads as a glitch on white */
[data-theme="light"] .needle::before{opacity:.35;}
[data-theme="light"] .status-pill{box-shadow:none;}
[data-theme="light"] .eb-legend{background:rgba(29,78,216,.045);}
[data-theme="light"] .cfm-send:hover{color:#ffffff;}
[data-theme="light"] .cfm input:checked::after{border-color:#ffffff;}
[data-theme="light"] .needle .n{color:#ffffff;}

/* ---- 0b. CROSS-WORKER VOCABULARY (LIGHT ONLY, LITERALS ONLY) ------------------
   This used to alias the foreign names onto the canonical ones in :root -
   `--txt:var(--text)`. eb-bot's own stylesheet contains the mirror image,
   `--text:var(--txt)`. Together those form a CYCLE, and the CSS spec makes every custom
   property in a cycle invalid at computed-value time: --txt AND --text both collapsed,
   so `color:var(--txt)` fell back to the initial colour and MRP rendered its wordmark
   and every section title near-black on a near-black bar. Nothing errored; the text was
   simply gone. (Ash: "why do these not look like the other dashboards".)

   THE RULE, now standard: this layer never defines a token as an alias of another token.
   It writes LITERALS, and only inside [data-theme="light"], where the layer legitimately
   owns the palette. In dark mode every app keeps its own colours, which were already
   correct - the layer had no business rewriting them. */
[data-theme="light"]{
  --line:rgba(15,23,42,0.11);
  --txt:#0f172a;
  --dim:#5a6b80;
  --accent:#1d4ed8;
  --panel:#ffffff;
  --input-bg:#ffffff;
}

/* ---- 1. CANONICAL TYPE SCALE -------------------------------------------------
   Left column = jarvis-v6 vocabulary, right = the five subpages'. Same role, same
   size, so every dashboard reads identically. */
/* height:auto neutralises the subpages' fixed nav{height:56px} so min-height governs;
   otherwise the two properties fight and the bar sits at an in-between height.
   `.nav` is listed as well as `nav` because the foreign Workers (eb-crm, eb-mail,
   eb-bot MRP) put their rules on a CLASS - `.nav{height:56px}` (0,1,0) beats a bare
   `nav` element selector (0,0,1) no matter how late this file loads, so those three
   apps silently kept a 56px bar while taking the 20px brand text from the rule below.
   That is exactly the "CRM and Mail headers don't match" drift. Matching specificity
   here fixes all three from one place - never by editing 3 more copies. */
/* flex-wrap: the bar now carries 11 link pills plus status badges. Without wrapping,
   a narrow viewport squashes the pills instead of dropping them to a second row. */
nav,.nav{min-height:92px;height:auto;flex-wrap:wrap;row-gap:8px;padding-top:10px;padding-bottom:10px;}
/* font-family/weight are pinned here too: eb-mail had swapped the wordmark to
   var(--brand-font) (Good Times, weight 400) - correct for the STOREFRONT header
   standard, but it made the Mail app the only dashboard in the fleet not wearing
   Orbitron. One suite = one wordmark. Flip this single line if the fleet ever moves
   to Good Times; never fork it per app. */
/* .nexus-* is index's own vocabulary. It was missing from this scale, which is why the
   hub's header never matched the others - Ash: "all the headers on all the dashboards
   should be similar". Same rule, three names. */
/* 16px + no glow. The wordmark was 20px with a 18px white text-shadow, which on a
   near-black bar renders as a grey slab behind the letters - what Ash saw as a
   "background" and asked to make transparent. It is a shadow, not a fill. */
.nav-logo-name,.nav-name,.nexus-name{font-family:var(--label-font);font-weight:900;font-size:16px;letter-spacing:2px;
  background:transparent;text-shadow:none!important;}
/* Sub-line up from 11px (Ash: "increase the font of all the titles under Easi Breezi").
   It labels which dashboard you are on - the second most useful string in the bar and it
   was set smaller than the clock. */
/* Fluid, so the LONGEST sub-line still fits rather than ellipsing (Ash: "we want the
   full name MANUFACTURING COMMAND, it should just fit"). 21 characters at a fixed 14px
   with 2.5px tracking needs ~245px; a 390px bar only offers ~250 once the mark and the
   burger are out. Clamp trades a couple of pixels on a phone for the whole word. */
.nav-logo-sub,.nav-sub,.nexus-sub{font-size:clamp(10.5px,3.1vw,14px);letter-spacing:clamp(1px,0.4vw,2.5px);font-weight:600;}
.sys-badge{font-size:12px;letter-spacing:1.4px;}
/* Clock + date stamp removed fleet-wide (Ash: "remove time and date"). Every device
   showing this already has a clock; it was pure chrome competing with the wordmark. */
.nav-clock,.nexus-clock,#navClock,#liveStamp,.nav-date{display:none!important;}
/* The mark scaled with the old bar heights (32px on the subpages, 30px in the Worker
   apps, 52px on Jarvis). One bar height => one mark size. Gated behind min-width on
   purpose: this layer is injected LAST, so an unconditional rule here would beat the
   pages' own @media shrink rules (jarvis drops the mark to 42/36px on small screens)
   and leave a 44px mark on a phone. Desktop only; below that each page still governs. */
/* One size everywhere and larger (Ash: "Easi Breezi logo bigger"). The old rule only
   grew it above 1100px while a phone rule pinned it to 30px, so the mark was smallest
   exactly where the header has the least else in it. */
.nav-logo-svg{width:46px!important;height:46px!important;}
.section-hdr-label,.section-title{font-size:clamp(20px,2.4vw,32px);letter-spacing:2px;line-height:1.15;}
/* Ash 2026-07-21 ("change heading colours - make better"): a 32px band of accent-blue
   caps, centred between two glowing rules and slowly pulsing, reads as a BANNER - it
   competes with the data underneath and there is one every screen. Structure should be
   quiet, so: label drops to --text, the accent stays where it carries meaning (the icon
   and the rule), the pulse animation stops, and killing the LEFT rule left-aligns the
   label so sections scan down a column instead of each centring on its own axis. */
.section-hdr-label,.section-title{color:var(--text,#f0f6ff);text-shadow:none;animation:none;}
/* Centred again (Ash 2026-07-21: "put these titles in the middle"). I had left-aligned
   them in the same pass that calmed the colour; the colour was the part that was wrong.
   Both rules stay visible so the label is framed rather than pushed to one edge. */
.section-hdr{justify-content:center;}
.section-hdr-line,.section-hdr-line-r{display:block;flex:1;opacity:.5;box-shadow:none;}
.section-title{text-align:center;}
.panel-title,.card-title{font-size:clamp(17px,1.9vw,26px);letter-spacing:1px;line-height:1.2;}
/* Shared .card-title base (2026-09-02) - was hand-pasted byte-identical into 7 pages'
   own <style> blocks (financials/kevin/manufacturing/marketing/ops-health/print-status/
   timesheets), all !important on the same 3 properties for the same reason: pinning
   the card-title look against the clamp() rule right above, which every page inherits
   from here. One copy of that pin, here, instead of 7 that a future edit has to find
   and update in lock-step. color and (on 3 pages) the flex header-row layout are the
   ONLY things that genuinely differ per page - those stay in each page's own <style>,
   deliberately NOT `!important` here, so a page's own rule keeps winning regardless of
   this file's injection order. */
.card-title{font-family:var(--label-font);font-size:8px!important;font-weight:700;letter-spacing:2px!important;line-height:1.4!important;text-transform:uppercase;margin-bottom:14px;padding-bottom:8px;border-bottom:1px solid var(--c-border);}
.chart-title{font-size:15px;}
.kpi-value,.kpi-val{font-size:28px;line-height:1.05;}
.kpi-label{font-size:11px;letter-spacing:1.6px;}
.badge{font-size:11px;}
.hero-desc,.hero-sub{font-size:14px;line-height:1.6;}

/* ---- 1a. CONDENSE-TO-TOP-LINER + STAT TICKER, FLEET-WIDE (2026-09-08+) ------------
   Cascaded here from jarvis-v6's redesign pass (Ash: "all this should be condensed
   until I click on it, and then the side panel comes out - on all of these" - said
   while looking at /manufacturing, confirming the same treatment belongs on every
   dashboard, not just Overview). Two things, both proven live on jarvis-v6 first:

   (a) CONDENSE. .issue-sub and .kpi-sub are already wired into the shared detail
       drawer everywhere (.issue-card has been a fleet-wide AUTO entry for weeks;
       .kpi-grid .card joined it 2026-09-08) and dwRenderNarrative() already falls
       back to reading these exact classes' innerHTML - so hiding them here costs
       nothing per page, the full text still opens in the drawer on click, unchanged.
       Headline/value/priority stay visible always, so every row stays scannable
       collapsed instead of showing a paragraph nobody asked to read yet.
   (b) THE TICKER (.hero-mini-stats/.hms). A small grid of equal-width linked stat
       boxes for a hero's own key numbers (first proven as jarvis-v6's social ticker -
       IG/YouTube/TikTok/etc, each linking straight to the platform). Declared here,
       not per page, so a page only has to add the MARKUP to get an aligned, on-brand
       ticker for free - this is exactly the "one canonical rule instead of seven
       hand-pasted copies" reasoning the shared .card-title rule above already
       established. `.hms`/`.hero-mini-stats` were unique to jarvis-v6 before this
       (checked - zero collisions), so promoting them fleet-wide is a pure add. */
.issue-sub,.kpi-sub{display:none;}
:root{--fs-label:9px;--fs-body:13px;--fs-val-sm:18px;--fs-val-lg:34px;}
.hero-mini-stats{display:grid;grid-template-columns:repeat(6,minmax(88px,1fr));gap:8px;margin-top:14px;}
.hms{background:var(--bg-card);border:1px solid var(--c-border);border-radius:4px;padding:9px 10px;position:relative;
  text-decoration:none;display:block;transition:border-color 0.15s,transform 0.15s;}
a.hms{cursor:pointer;}
a.hms:hover{border-color:var(--c);transform:translateY(-1px);box-shadow:0 3px 10px rgba(var(--c-rgb),0.15);}
.hms::before{content:'';position:absolute;top:-1px;left:-1px;width:6px;height:6px;border-top:1px solid var(--c);border-left:1px solid var(--c);}
.hms-label{font-family:var(--label-font);font-size:var(--fs-label);letter-spacing:0.6px;color:var(--text-mute);text-transform:uppercase;margin-bottom:4px;
  display:flex;align-items:center;gap:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.hms-val{font-size:var(--fs-val-sm);font-weight:800;color:var(--text);font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.hms-val.g{color:var(--green);}
.hms-val.r{color:var(--red);}
.hms-foot{font-size:10px;color:var(--text-mute);margin-top:6px;}
@media(max-width:640px){.hero-mini-stats{grid-template-columns:repeat(3,minmax(0,1fr));}}

/* ROUTINE-NOTE CLAMP (2026-09-08, ops-health's routine-health tables - Ash: "daily
   routines... all says healthy, done... got a load of writing next to it... I don't
   think it needs this unless I click on it"). 2-line clamp + the same .eb-more/.eb-hide
   chevron button the needle cards already use (see Expand-RoutineNoteClamp, build.ps1,
   for the build-time wrapping). display:block on .open, not display:-webkit-box with
   an unclamped line count, because Safari/older engines can drop -webkit-line-clamp
   silently and block is the one value guaranteed to show everything. */
.rn-clamp{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
.rn-clamp.open{display:block;overflow:visible;}

/* ---- 1a-2. DISCLOSURE (.hero-more / .more-toggle), FLEET-WIDE (2026-09-08+) --------
   Was hand-pasted identically into jarvis-v6.html and manufacturing.html (the only two
   pages that had adopted the <details> pattern before today); financials/marketing/
   kevin/ops-health had never adopted it at all. Declared once here now, same reasoning
   as everything else in this block. `.more-toggle` is a plain alias for the identical
   rules - `class="hero-more"` reads oddly on a non-hero card (e.g. a supplier-directory
   paragraph, or manufacturing's COG Scale Curve breakdown), so bespoke long-prose cards
   elsewhere on a page use the alias while the hero's own disclosure keeps its original
   name (both select the same CSS, so nothing about the hero's own usage changes). */
.hero-more,.more-toggle{margin-top:10px;}
.hero-more>summary,.more-toggle>summary{cursor:pointer;list-style:none;display:inline-flex;align-items:center;gap:7px;
  font-family:var(--label-font);font-size:9px;font-weight:700;letter-spacing:1.6px;text-transform:uppercase;
  color:var(--text-mute);border:1px solid var(--c-border);border-radius:4px;padding:7px 11px;}
.hero-more>summary::-webkit-details-marker,.more-toggle>summary::-webkit-details-marker{display:none;}
/* REAL ARROW, not +/- (2026-09-08, Ash: "you need an arrow to click on to say open up" -
   the plus/minus marker this used until today apparently didn't read as "there is more
   here, click me" at a glance). Same rotating-triangle language as .flow-toggle's own
   arrow and the needle-card "More" chevron button below, so one visual grammar means "click to
   expand" everywhere on the fleet, not three different ones. */
.hero-more>summary::before,.more-toggle>summary::before{content:"\25B8";font-size:11px;line-height:1;transition:transform .15s;display:inline-block;}
.hero-more[open]>summary::before,.more-toggle[open]>summary::before{content:"\25B8";transform:rotate(90deg);}
.hero-more>summary:hover,.more-toggle>summary:hover{color:var(--c);border-color:var(--c);}

/* ---- 1b. NAV LINKS + ASK/NOTE DOCK -------------------------------------------
   The link pills are styled here, not per page, because five of the six dashboards
   never had a link bar at all and Jarvis's own `.nav-link` rules are page-local.
   Same class, same look, everywhere - which is the whole point of Ash's "make all
   these look the same so we know that they are buttons". */
.nav-links{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin-left:8px;}
.nav-link{display:inline-flex;align-items:center;gap:5px;text-decoration:none;white-space:nowrap;
  font-family:var(--label-font,inherit);font-size:11px;font-weight:700;letter-spacing:1.4px;
  text-transform:uppercase;padding:8px 13px;border-radius:5px;
  border:1px solid var(--c-border,rgba(0,212,255,.2));background:var(--sunken,rgba(0,0,0,.18));
  color:var(--text-sec,#c2cedc);transition:border-color .15s,color .15s,background .15s;}
.nav-link:hover{border-color:var(--c,#00d4ff);color:var(--c,#00d4ff);background:var(--c-dim,rgba(0,212,255,.07));}
.nav-link.active,.nav-link.on{border-color:var(--c,#00d4ff);color:var(--c,#00d4ff);
  background:var(--c-dim,rgba(0,212,255,.07));box-shadow:inset 0 0 0 1px var(--c-dim,rgba(0,212,255,.07));}
@media(max-width:900px){.nav-link{font-size:10px;padding:8px 10px;letter-spacing:1px;}}

/* Unconditional, not inside a breakpoint: a flex/grid child defaults to min-width:auto
   and refuses to shrink below its content, which is what let Kevin's kanban column render
   572px wide inside a 364px track and get clipped by body{overflow-x:hidden}. It was
   still clipped at 1024px when this lived under max-width:900px, so it applies at every
   size. */
.tb-board>*,.tb-col{min-width:0;}
.tb-col h4,.tb-card,.tb-note,.tb-move,.tb-colhint{overflow-wrap:anywhere;}

/* ---- 1c. PHONE + TABLET (Ash 2026-07-21: "make all the dashboards work on phone and
   tablet in both orientations"). Measured with headless Chromium at 360/390/768/844/1024
   in both orientations rather than guessed - the numbers below are what that found.

   THE NAV WAS THE WHOLE PROBLEM. A 92px bar is right on a desktop and absurd on a phone:
   it was eating 36-41% of a portrait phone and up to 57% of a landscape one, so every
   dashboard opened on a header with a sliver of data under it. On small screens the bar
   goes back to its natural height and the link strip becomes ONE horizontally-scrollable
   row - 11 pills wrapping to four rows is what created the 300px+ header. */
/* THREE-ZONE HEADER (Ash 2026-07-21: "Easi Breezi logo top left of the header and name
   in the middle", and "still no burger" - the hamburger goes).
   Both side zones are flex:1 1 0 so they carry equal weight, which puts the wordmark on
   the true centre line of the bar regardless of how many badges the right side holds.
   Two class vocabularies because index calls its parts .nexus-*; same layout either way. */
.nav-logo,.nexus-logo{flex:1 1 0;min-width:0;}
/* One line each, ellipsised if the name is long. Without this "MARKETING COMMAND" wrapped
   to two lines and made that one bar 87px against everyone else's 69px - the last visible
   difference between the headers. */
/* ABSOLUTE centring, not flex balance (Ash: "title needs to be in the middle").
   The bar is [logo][title][right group][burger] and the burger sits OUTSIDE the
   flex:1/flex:1 pair, so it dragged the title 29px left of the bar's centre - measured,
   not guessed. Anchoring to 50% of the nav is immune to whatever the sides carry.
   pointer-events:none so a wide title can never swallow a tap meant for the burger. */
.eb-nav-title{position:absolute;left:50%;transform:translateX(-50%);
  max-width:calc(100% - 128px);text-align:center;pointer-events:none;}
.eb-nav-title>*{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.nav-right,.nexus-right{flex:1 1 0;justify-content:flex-end;}
/* The link strip is always its own full-width row. It used to sit inline above 1200px,
   which pushed the wordmark off centre on desktop - "in the middle" has to mean the same
   thing at every width. Scrolling, never wrapping: 11 pills stacked four rows deep is
   what made the bar eat a third of a phone screen. */
.nav-links{order:9;width:100%;margin-left:0;flex-wrap:nowrap;overflow-x:auto;
  scrollbar-width:none;-webkit-overflow-scrolling:touch;padding-bottom:2px;}
.nav-links::-webkit-scrollbar{display:none;}
.nav-link{flex:0 0 auto;}
/* Jarvis's own hamburger stays hidden - it drove a page-local menu that no longer
   exists. The fleet-wide one below (.eb-burger) replaces it on EVERY dashboard, which is
   what Ash asked for; two toggles in one bar would fight. */
.nav-toggle,#navToggle{display:none!important;}
/* order:99 = far right of the bar (Ash 2026-07-21: "move the burger to the right").
   The links and the theme toggle now live inside the drawer it opens, so the header is
   just [logo] [name] [burger] on every dashboard. */
.eb-burger{order:99;flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:44px;height:44px;margin-left:2px;padding:0;cursor:pointer;border-radius:6px;
  border:1px solid var(--c-border,rgba(0,212,255,.2));background:var(--sunken,rgba(0,0,0,.18));
  color:var(--c,#00d4ff);transition:border-color .15s,background .15s;}
.eb-burger:hover{border-color:var(--c,#00d4ff);background:var(--c-dim,rgba(0,212,255,.07));}
.eb-burger span{display:block;width:18px;height:2px;border-radius:2px;background:currentColor;
  position:relative;transition:transform .2s,opacity .2s;}
.eb-burger span::before,.eb-burger span::after{content:"";position:absolute;left:0;width:18px;height:2px;
  border-radius:2px;background:currentColor;transition:transform .2s;}
.eb-burger span::before{top:-6px;} .eb-burger span::after{top:6px;}
/* Open state folds the bars into an X, so the button reports the state it controls. */
nav.eb-nav-open .eb-burger span,.nav.eb-nav-open .eb-burger span{background:transparent;}
nav.eb-nav-open .eb-burger span::before,.nav.eb-nav-open .eb-burger span::before{transform:translateY(6px) rotate(45deg);}
nav.eb-nav-open .eb-burger span::after,.nav.eb-nav-open .eb-burger span::after{transform:translateY(-6px) rotate(-45deg);}
/* The in-header link strip is gone entirely - its contents moved into the drawer. */
nav .nav-links,.nav .nav-links{display:none!important;}

/* ---- SLIDE-IN MENU (from the left, opened by the burger on the right) --------
   Everything that is navigation or a preference lives here now, so the bar itself
   carries only identity. A drawer also scales: adding a twelfth dashboard costs a row
   in a list instead of another pill fighting for width in a 390px header. */
.eb-scrim{position:fixed;inset:0;z-index:9500;background:rgba(0,0,0,.55);backdrop-filter:blur(2px);
  opacity:0;pointer-events:none;transition:opacity .2s ease;}
.eb-scrim.open{opacity:1;pointer-events:auto;}
/* Anchored RIGHT (Ash 2026-07-21): the burger is on the right, so a panel flying in
   from the left crossed the whole screen away from the finger that opened it. */
.eb-drawer{position:fixed;top:0;right:0;bottom:0;z-index:9600;width:min(310px,84vw);
  display:flex;flex-direction:column;gap:2px;padding:16px 14px calc(16px + env(safe-area-inset-bottom));
  background:var(--bg-card,#050c16);border-left:1px solid var(--c-border,rgba(0,212,255,.2));
  box-shadow:-8px 0 34px rgba(var(--shade,0,0,0),.45);overflow-y:auto;
  transform:translateX(102%);transition:transform .22s cubic-bezier(.22,1,.36,1);}
.eb-drawer.open{transform:translateX(0);}
.eb-drawer-hd{display:flex;align-items:center;gap:10px;padding:2px 4px 14px;
  border-bottom:1px solid var(--c-border,rgba(0,212,255,.2));margin-bottom:10px;}
.eb-drawer-mark{width:30px;height:30px;flex:0 0 auto;
  background:linear-gradient(135deg,#5BB1FF 0%,#0A84FF 50%,#0066CC 100%);
  -webkit-mask-image:var(--logo-mask);mask-image:var(--logo-mask);
  -webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-size:contain;mask-size:contain;}
.eb-drawer-ttl{font-family:var(--label-font,inherit);font-size:12px;font-weight:900;letter-spacing:2px;
  text-transform:uppercase;color:var(--text,#f0f6ff);}
.eb-drawer-x{margin-left:auto;width:34px;height:34px;border-radius:6px;cursor:pointer;font-size:17px;line-height:1;
  border:1px solid var(--c-border,rgba(0,212,255,.2));background:transparent;color:var(--text-mute,#c2cedc);}
.eb-drawer-x:hover{color:var(--c,#00d4ff);border-color:var(--c,#00d4ff);}
.eb-drawer .nav-link{display:flex;width:100%;justify-content:flex-start;padding:12px 13px;
  font-size:12px;letter-spacing:1.4px;border-radius:6px;}
.eb-drawer-lbl{font-family:var(--label-font,inherit);font-size:9px;letter-spacing:2px;text-transform:uppercase;
  color:var(--text-mute,#c2cedc);margin:14px 4px 6px;}
/* The page's own theme button, relocated - not a copy, so its handler and label stay live. */
.eb-drawer .theme-btn,.eb-drawer .snd-btn{width:100%;justify-content:center;padding:12px;font-size:11px;}
/* Audio toggle out too (Ash 2026-07-21: "remove the audio button, makes it messy").
   Jarvis was the only page with it, so it was also breaking the fleet's header symmetry -
   one dashboard with an extra control next to the theme button. */
#sndBtn{display:none!important;}
@media(max-width:1200px){
  nav,.nav{min-height:0!important;padding-top:8px!important;padding-bottom:8px!important;row-gap:6px;}
}
/* ---- STICKY HEADER (Ash 2026-07-21: "make it stay at the top as I scroll") -------
   Two separate faults, both needed fixing:
   1. Every subpage carries `@media(max-width:1024px){nav{position:static}}` from the
      era when the bar was 300px tall and pinning it would have eaten the screen. At
      63px that reasoning is gone, so the layer re-asserts sticky - with !important,
      because those page rules are equal specificity and land earlier.
   2. Those same media queries set `overflow-x:hidden` on html/body. Hidden on one axis
      forces the other from `visible` to `auto`, which makes the element a SCROLL
      CONTAINER - and a sticky child sticks to its nearest scroll container, not the
      viewport, so it scrolled away regardless of position:sticky. `clip` clips exactly
      the same overflow WITHOUT creating a scroll container (a `visible` sibling axis
      computes to `clip`, not `auto`), which is the whole reason it exists. */
html,body{overflow-x:clip!important;}
nav,.nav{position:sticky!important;top:0;z-index:120;}
/* Under ~520px the status badges are what stack the bar 3 rows deep. They are ambient
   readouts, not controls, and the same numbers are on the page below. */
/* Ambient status pills leave the bar entirely, not just on phones. They are readouts,
   not controls, every page carries a different number of them (which is what made the
   headers different heights), and at tablet width they reached far enough in to sit
   under the centred wordmark. The same numbers are on the page below. */
.sys-badge,#liveStamp,.sys-online,.nav-date{display:none!important;}
@media(max-width:900px){
  nav,.nav{min-height:0!important;padding:8px 12px!important;row-gap:6px;gap:10px;}
  /* NO font-size override here any more. These lines used to shrink the wordmark to 13px
     and the sub-line to 8px on phones, which (a) fought the 20/14 scale Ash asked for and
     (b) only hit .nav-*, never index's .nexus-*, so the hub looked different from every
     other dashboard - the exact "headers should be similar" complaint. The height they
     were buying back is now free: the link strip is collapsed behind the burger and the
     clock is gone, so a closed bar is ~61-69px. */
  /* One scrolling row, not a wrapping block. -webkit-overflow-scrolling keeps the
     momentum flick on iOS; the scrollbar is hidden because it would sit on the pills. */
  .nav-links{order:9;width:100%;margin-left:0;flex-wrap:nowrap;overflow-x:auto;
    scrollbar-width:none;-webkit-overflow-scrolling:touch;padding-bottom:2px;}
  .nav-links::-webkit-scrollbar{display:none;}
  .nav-link{flex:0 0 auto;}
  /* min-width:auto is the default for a flex/grid child, so a nowrap group of badges
     REFUSES to shrink: .nav-right measured 510px inside a 390px bar and .nexus-right
     561px. It never showed as a sliding page because body{overflow-x:hidden} was
     quietly CLIPPING it - the buttons were simply off the right edge, unreachable.
     Same root cause as Kevin's board below. Found with headless Chromium, not by eye. */
  .nav-right,.nexus-right{margin-left:auto;gap:8px;flex-wrap:wrap;justify-content:flex-end;
    min-width:0;max-width:100%;}
  .nav-right>*,.nexus-right>*{min-width:0;}
  .nav-clock{display:none;}          /* the phone already shows the time */
  .sys-badge{font-size:9px;}
  /* Kevin's kanban collapses to one column under 560px, but the column itself was
     572px wide inside a 364px track (min-width:auto again, driven by long unbroken
     card text) and got clipped. */
  .tb-board>*,.tb-col{min-width:0;}
  .tb-col h4,.tb-card,.tb-note,.tb-move,.tb-colhint{overflow-wrap:anywhere;}
  /* Big display numbers ("T-27 DAYS TO SHIP") overran a 360px phone. */
  .hero-ship-val{font-size:24px!important;white-space:normal!important;}
}
/* Landscape phone: ~390px of height total. Anything chrome-ish has to give. */
@media(max-height:520px) and (orientation:landscape){
  nav,.nav{min-height:0!important;padding:6px 12px!important;}   /* stays sticky - it is only 57px here */
  /* Landscape keeps the sub-line - it names the dashboard you are on, and hiding it here
     but not elsewhere is the drift Ash is asking me to stop. Only the ambient badges go. */
  .sys-badge{display:none;}
  .nav-logo-svg{width:26px!important;height:26px!important;}
  .boot-logo{width:74px!important;height:74px!important;}
  .section-hdr-label,.section-title{font-size:17px!important;}
}
/* Touch targets. The audit found 21-53 controls per page under 32px tall - pills,
   theme buttons, note buttons. Only raised for coarse pointers so the desktop density
   is untouched. */
@media(pointer:coarse){
  .nav-link,.theme-btn,.snd-btn,.cfm,.cfm-note-btn,.cfm-send,.eb-dock-tab,.eb-dock-go,.eb-dock-save,.chip{
    min-height:36px;display:inline-flex;align-items:center;justify-content:center;}
  .cfm input{width:20px;height:20px;}
}
/* Any table that is wider than its column must scroll INSIDE its own box. Several
   dashboards wrap their tables already; these are the ones that did not, and without a
   wrapper the only alternatives are a squashed table or a sliding page. */
@media(max-width:900px){
  .sup-table,.camp-table,.econ-table{display:block;overflow-x:auto;-webkit-overflow-scrolling:touch;white-space:nowrap;}
  .kpi-row{grid-template-columns:minmax(0,1fr)!important;}
  .eb-dock{right:12px;bottom:12px;}
  .eb-dock-panel{width:calc(100vw - 24px);}
}

/* Ask/Note dock. z-index sits UNDER the boot overlay (9999) on purpose - it must not
   float over the loading screen - and over everything else. */
.eb-dock{position:fixed;right:20px;bottom:20px;z-index:9000;display:flex;flex-direction:column;
  align-items:flex-end;gap:10px;font-family:Inter,system-ui,sans-serif;
  transition:transform .18s ease;}   /* transform is driven by the keyboard-avoidance fit() */
.eb-dock-panel{overflow-y:auto;}
/* Circular, carrying the EB mark itself (Ash 2026-07-21) - a labelled pill read as a
   banner and took up a phone's width; the logo is the most recognisable object we have
   and needs no words. Same masked-PNG technique as the nav mark, so it inherits
   --logo-mask from whichever app it is mounted in and can never drift from the brand. */
.eb-fab{display:grid;place-items:center;cursor:pointer;width:60px;height:60px;padding:0;
  border-radius:50%;border:1px solid var(--c,#00d4ff);background:var(--bg-card,#050c16);
  box-shadow:0 6px 22px rgba(var(--shade,0,0,0),.35);transition:transform .18s,box-shadow .18s;}
/* Breathing halo (Ash: "a slight glow and light around the ai button"). Driven by
   box-shadow rather than a pseudo-element so it cannot sit above the mark or steal the
   tap, and it rides --c-rgb so the glow follows the theme accent instead of a fixed
   cyan that would look wrong on the light palette. */
@keyframes eb-fab-halo{
  0%,100%{box-shadow:0 6px 22px rgba(var(--shade,0,0,0),.35),0 0 12px 1px rgba(var(--c-rgb,0,212,255),.30);}
  50%{box-shadow:0 8px 26px rgba(var(--shade,0,0,0),.40),0 0 28px 7px rgba(var(--c-rgb,0,212,255),.60);}
}
.eb-fab{animation:eb-fab-halo 3.2s ease-in-out infinite;}
.eb-fab:hover{transform:translateY(-2px) scale(1.04);animation-duration:1.6s;}
@media(prefers-reduced-motion:reduce){.eb-fab{animation:none;box-shadow:0 6px 22px rgba(var(--shade,0,0,0),.35),0 0 16px 3px rgba(var(--c-rgb,0,212,255),.45);}}
.eb-fab:active{transform:scale(.96);}
/* 42 of 60 (70%) - Ash asked for it bigger. The mark's PNG canvas is 263x300, so with
   mask-size:contain the ink is height-bound: a 42px box renders ~37px wide, which still
   leaves a visible ring inside the 60px circle rather than crowding the border. */
.eb-fab-mark{width:42px;height:42px;display:block;pointer-events:none;
  background:linear-gradient(135deg,#5BB1FF 0%,#0A84FF 50%,#0066CC 100%);
  -webkit-mask-image:var(--logo-mask);mask-image:var(--logo-mask);
  -webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-size:contain;mask-size:contain;filter:drop-shadow(0 2px 8px rgba(10,132,255,.45));}
/* Open state: the ring fills so the button reads as "this panel belongs to me". */
.eb-dock.open .eb-fab{background:var(--c-dim,rgba(0,212,255,.07));transform:rotate(-8deg);}
@media(max-width:760px){.eb-fab{width:56px;height:56px;}.eb-fab-mark{width:40px;height:40px;}}
.eb-dock-panel{display:none;width:min(390px,calc(100vw - 32px));border-radius:10px;overflow:hidden;
  background:var(--bg-card,#050c16);border:1px solid var(--c-border,rgba(0,212,255,.2));
  box-shadow:0 16px 44px rgba(var(--shade,0,0,0),.45);}
.eb-dock.open .eb-dock-panel{display:block;}
.eb-dock-tabs{display:flex;border-bottom:1px solid var(--c-border,rgba(0,212,255,.2));}
.eb-dock-tab{flex:1;padding:11px 8px;background:transparent;border:0;cursor:pointer;
  font-family:var(--label-font,inherit);font-size:10px;font-weight:700;letter-spacing:1.4px;
  text-transform:uppercase;color:var(--text-mute,#c2cedc);}
.eb-dock-tab.on{color:var(--c,#00d4ff);background:var(--c-dim,rgba(0,212,255,.07));
  box-shadow:inset 0 -2px 0 var(--c,#00d4ff);}
.eb-dock-body{padding:12px;display:flex;flex-direction:column;gap:9px;}
.eb-dock-input{width:100%;min-height:70px;resize:vertical;border-radius:5px;padding:9px 11px;
  font-family:inherit;font-size:13px;line-height:1.45;
  background:var(--sunken-2,rgba(0,0,0,.3));color:var(--text,#f0f6ff);
  border:1px solid var(--c-border,rgba(0,212,255,.2));}
.eb-dock-input:focus{outline:none;border-color:var(--c,#00d4ff);}
.eb-dock-row{display:flex;gap:8px;}
.eb-dock-go,.eb-dock-save{flex:1;padding:9px 12px;border-radius:5px;cursor:pointer;
  font-family:var(--label-font,inherit);font-size:10px;font-weight:700;letter-spacing:1.4px;text-transform:uppercase;
  border:1px solid var(--c,#00d4ff);background:var(--c-dim,rgba(0,212,255,.07));color:var(--c,#00d4ff);}
.eb-dock-go:hover,.eb-dock-save:hover{background:var(--c,#00d4ff);color:#02121c;}
.eb-dock-go:disabled{opacity:.45;cursor:not-allowed;}
.eb-dock-save{border-color:var(--c-border,rgba(0,212,255,.2));color:var(--text-sec,#c2cedc);background:transparent;}
.eb-dock-out{max-height:230px;overflow:auto;font-size:12.5px;line-height:1.55;white-space:pre-wrap;
  color:var(--text-sec,#c2cedc);background:var(--sunken,rgba(0,0,0,.18));
  border:1px solid var(--c-border,rgba(0,212,255,.2));border-radius:5px;padding:10px 12px;}
/* NO padding override here. This line used to carry `.eb-fab{padding:12px 16px}` from
   the pill version. When the pill became a fixed 54px circle, border-box maths turned
   that into a 20px content box for a 29px mark, so the logo sat 17px from the left edge
   and 8px from the right - visibly off-centre, which is exactly what Ash spotted. The
   circle's size lives in the .eb-fab rule above; nothing here may touch its box. */
@media(max-width:760px){.eb-dock{right:12px;bottom:12px;}}

/* Needle movers lead with the ACTION; the reasoning collapses to two lines behind a
   toggle (Ash: "make the needle movers info small and then a drop down for more
   information"). Three cards each carrying a five-line paragraph is a wall - the point
   of a Top 3 is what to do, with the evidence one tap away. */
/* Title-only by default (Ash: "just the titles need to show in the needle movers and
   then the drop-down if we want"). Was a 2-line clamp; two lines x 3 cards is still a
   paragraph to scan past. A Top 3 answers "what do I do" - the evidence is a tap away. */
.eb-hide{display:none!important;}
.eb-more{display:inline-flex;align-items:center;gap:5px;margin:8px 0 2px;padding:3px 0;
  background:none;border:0;cursor:pointer;font-family:var(--label-font,inherit);
  font-size:9px;font-weight:700;letter-spacing:1.4px;text-transform:uppercase;color:var(--c,#00d4ff);}
.eb-more::after{content:"";width:6px;height:6px;border-right:1.5px solid currentColor;
  border-bottom:1.5px solid currentColor;transform:rotate(45deg) translateY(-1px);transition:transform .18s;}
.eb-more.open::after{transform:rotate(-135deg) translateY(-1px);}
.eb-more:hover{opacity:.75;}

/* ---- 2. BOX RHYTHM -----------------------------------------------------------
   Ash: "all the little boxes should be similar in size". auto-fit + a shared
   min-height makes every tile in a row equal regardless of copy length, and is
   inherently responsive (it collapses on its own, so no breakpoint has to guess). */
.kpi-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;align-items:stretch;}
.kpi{display:flex;flex-direction:column;gap:5px;min-height:130px;justify-content:flex-start;}
.grid2,.grid3,.cards-grid{align-items:stretch;}
.kpi-row>*,.grid2>*,.grid3>*,.cards-grid>*{min-width:0;}
.card,.kpi,.panel,.chart-panel{border-radius:4px;}

/* On a phone every dashboard goes 1-up. The five subpages carry an inherited
   `.kpi-row{grid-template-columns:repeat(2,1fr)!important}` in their own media
   queries, which would otherwise force 2-up here and leave jarvis (1-up) looking
   different from the rest. !important is used ONLY to beat that existing
   !important - it is not the default tool in this layer. */
@media(max-width:760px){
  .kpi-row{grid-template-columns:minmax(0,1fr)!important;}
  .kpi{min-height:0;}
  .needle{min-height:0;}
}

/* ---- 3. BOOT EXIT ------------------------------------------------------------
   The old exit was clip-path:inset(48% 0 48% 0) - it squeezed the whole boot screen
   into a thin line IN THE MIDDLE of the viewport before vanishing. That is the
   "appears in the middle then disappears" Ash reported. Replaced with a calm
   scale-and-dissolve on an ease-out curve. Duration stays under the 500ms JS
   timeout that hides the layer, so the animation always completes before it cuts. */
.boot{transition:opacity .40s cubic-bezier(.22,1,.36,1),transform .40s cubic-bezier(.22,1,.36,1);}
.boot.exit{clip-path:none;opacity:0;transform:scale(1.025);pointer-events:none;}
/* ---- 3b. HERO FOCUS DISCLOSURE ------------------------------------------------
   Ash, twice: /manufacturing "why is there so much information, straight to the
   point" and then the same on jarvis. The hero states the ONE thing that matters
   and everything else folds behind this. Lives in the shared layer, not per page,
   because it was hand-rolled on manufacturing only and jarvis had no styles for it
   at all - the disclosure rendered as a raw browser triangle.
   NOTE the [open] marker: the per-page copy had a mangled character from an old
   ASCII-sanitisation pass, so an opened panel showed literal junk instead of a minus.
   Written here as the CSS escape \2212 (minus), which is ASCII-safe in this file. */
.hero-more{margin-top:10px;}
.hero-more>summary{cursor:pointer;list-style:none;display:inline-flex;align-items:center;gap:7px;
  font-family:var(--label-font,inherit);font-size:9px;font-weight:700;letter-spacing:1.6px;
  text-transform:uppercase;color:var(--text-mute,#c2cedc);
  border:1px solid var(--c-border,rgba(0,212,255,.2));border-radius:4px;padding:7px 11px;}
.hero-more>summary::-webkit-details-marker{display:none;}
.hero-more>summary::before{content:"+";font-size:12px;line-height:1;}
.hero-more[open]>summary::before{content:"\2212";}
.hero-more>summary:hover{color:var(--c,#00d4ff);border-color:var(--c,#00d4ff);}
.hero-more .hero-desc{color:var(--text-sec,#e9eff6);font-size:13px;line-height:1.6;}
/* ---- 4. LEGEND -------------------------------------------------------------
   Ash: "for other people when we bring them on board, we need to make this as
   simple and user-friendly as possible." A new person should not have to be told
   what the colours mean or that the boxes are clickable. Sits under the nav on
   every dashboard, dismissible per-browser so it never nags a daily user. */
.eb-legend{display:flex;align-items:center;flex-wrap:wrap;gap:8px 20px;
  padding:10px 28px;border-bottom:1px solid var(--c-border,rgba(0,212,255,.2));
  background:rgba(var(--c-rgb,0,212,255),.035);font-size:12px;color:var(--text-mute,#c2cedc);}
.eb-legend b{font-family:var(--label-font,inherit);font-size:11px;letter-spacing:1.4px;
  text-transform:uppercase;color:var(--c,#00d4ff);font-weight:700;}
.eb-legend span{display:inline-flex;align-items:center;gap:7px;}
.eb-legend i{width:9px;height:9px;border-radius:50%;display:inline-block;flex-shrink:0;}
.eb-legend .lg{background:var(--green,#22c55e);} .eb-legend .lr{background:var(--red,#ef4444);}
.eb-legend .lw{background:var(--amber,#f59e0b);}
.eb-legend-x{margin-left:auto;cursor:pointer;border:1px solid var(--c-border,rgba(0,212,255,.2));
  background:transparent;color:var(--text-mute,#c2cedc);border-radius:3px;padding:4px 9px;
  font-family:var(--label-font,inherit);font-size:10px;letter-spacing:1px;text-transform:uppercase;}
.eb-legend-x:hover{border-color:var(--c,#00d4ff);color:var(--c,#00d4ff);}
@media(max-width:760px){.eb-legend{padding:9px 13px;font-size:11px;gap:6px 14px;}}
@keyframes eb-rise{from{opacity:0;transform:translateY(12px);}to{opacity:1;transform:none;}}
body.eb-revealed .page,body.eb-revealed #app{animation:eb-rise .55s cubic-bezier(.22,1,.36,1) both;}
@media(prefers-reduced-motion:reduce){
  .boot{transition:opacity .2s linear;}
  .boot.exit{transform:none;}
  body.eb-revealed .page,body.eb-revealed #app{animation:none;}
}
