/* MediHarbor Ops — the app shell. Loads AFTER ops.css and only does shell things: sidebar
   tree, topbar, workspace, home. Every colour, radius and type size comes from ops.css tokens;
   nothing here invents its own.

   WHAT WAS REMOVED, so it does not creep back in:
     · an animated two-stop radial gradient on <body> — decoration, and it drifted under a table
     · backdrop-filter on the topbar, cards and sidebar — frosted glass over dense data costs
       contrast and buys nothing
     · box-shadow on every card — shadow now means "floats above the page", and a card does not
     · an 18px radius on everything from a pill to a panel
     · emoji as navigation icons
   The sidebar is 216px (was 236) and the whole layout sits on a 4px baseline. */

.app{display:flex;min-height:100vh;position:relative;z-index:1}

/* ═══ sidebar ═══════════════════════════════════════════════════════════════════════ */

.side{
  width:240px;
  flex:none;
  background:var(--nav-bg);
  color:var(--nav-item);
  display:flex;
  flex-direction:column;
  position:sticky;
  top:0;
  height:100vh;
  overflow-y:auto;
  overflow-x:hidden;
}
/* Camo, not a grid. The blueprint rule was legible as graph paper and read like a wireframe;
   this is the same asset the rail carried before the restyle, dialled down and desaturated so it
   gives the surface material rather than pattern. soft-light keeps it inside the ink instead of
   sitting on top as texture. */
.side::before{
  content:"";
  position:absolute;
  inset:0;
  pointer-events:none;
  z-index:0;
  background:url(camo-blue.svg);
  background-size:300px 300px;
  opacity:.30;
  mix-blend-mode:soft-light;
}
/* ── DEPTH ON THE RAIL ────────────────────────────────────────────────────────────────────
   The rail was one flat fill with the camo over it, which reads as a panel rather than as a
   surface — and with eleven groups on it now, a flat 100vh column gives the eye no sense of
   where it is. A vertical gradient, DERIVED from the tenant's own accent rather than picked:
   ops-theme.js paints --nav-active-bg per tenant, so MediHarbor and Scotts each deepen toward
   their own colour instead of sharing one hardcoded blue.

   Kept deliberately shallow. This is a wayfinding surface a person stares at all day, not a
   marketing header; the top and bottom differ by a few percent of lightness, which the eye reads
   as depth without ever competing with the labels sitting on it. The camo stays on top at
   soft-light, so the texture rides the gradient instead of fighting it. */
.side{
  background:
    linear-gradient(
      180deg,
      color-mix(in oklch, var(--nav-active-bg, oklch(28% .050 184)) 22%, var(--nav-bg)) 0%,
      var(--nav-bg) 42%,
      color-mix(in oklch, var(--nav-bg) 92%, black) 100%
    );
  color:var(--nav-item);
  box-shadow:inset -1px 0 0 oklch(100% 0 0 / .07);
}
.side>*{position:relative;z-index:1}

/* ── A COLOUR PER FAMILY ──────────────────────────────────────────────────────────────────
   Eleven groups is more than a person holds in their head as a list, so each family carries a
   hue on its header rule. It is the same idea as the coloured column headers on the patient
   board and the same restraint: the HUE IS IN THE RULE, never in the text. A tinted label on a
   dark rail is the pair that fails the 4.5:1 floor for 11px uppercase, which CLAUDE.md is
   explicit about — small tracked caps need more contrast, not less. So the words stay on the
   lightness ladder and the colour lives in a 2px edge that answers to the 3:1 border rule.

   The rule is transparent until the group is open or on the active trail, so a collapsed rail is
   still calm and the colour means "you are here" rather than "decoration". */
/* ── SECTION ROWS ─────────────────────────────────────────────────────────────────────────
   Matched to the PINNED rows above so the rail reads as one system: same 20px gutter, same 30px
   min-height, same 13px body size. The uppercase tracked treatment went with it — small tracked
   caps need MORE contrast than body text, not less (CLAUDE.md), and a tinted label at 11px caps
   on a dark ground is the pair that fails the 4.5:1 floor. Sentence case at 13px/500 carries the
   hierarchy without spending the contrast budget.

   NO SOLID FILLS. The colour lives in a 3px left rule and in the label; the row's ground stays
   transparent at rest, takes the section colour at 10% on hover and 16% when open or on the
   active trail. The rule sits inside the existing gutter, so nothing shifts horizontally when a
   section opens — the twitch the `.side a.on` inset-shadow note already warns about.

   ONE FAMILY, NOT A RAINBOW. Every hue is capped at 55% saturation and pinned to 60% lightness,
   so eleven sections read as a set. --g is the rule; --gt is the label, mixed ~70% toward white.
   The tints are literal hex rather than a live color-mix because mixing toward white in srgb and
   in oklch give visibly different results, and this set is the one measured against the contrast
   gate. The text is never the raw hue.

   Admin is deliberately near-grey: at 250 it was duplicating Today's hue, and two identical
   colours in an eleven-item set is worse than one deliberate grey. */
.side .navgroup>.navtoggle{
  border-left:3px solid transparent;
  padding:5px 20px;
  min-height:30px;
  line-height:20px;
  font-size:13px;
  font-weight:500;
  letter-spacing:.01em;
  text-transform:none;
  transition:background-color .12s ease, color .12s ease, border-left-color .18s ease;
}
.side .navgroup[data-g="work"]>.navtoggle       { --g:hsl(250 55% 60%); --gt:#D5CFF1 }
.side .navgroup[data-g="patients"]>.navtoggle   { --g:hsl(165 55% 60%); --gt:#CFF1E8 }
.side .navgroup[data-g="intake"]>.navtoggle     { --g:hsl(200 55% 60%); --gt:#CFE6F1 }
.side .navgroup[data-g="orders"]>.navtoggle     { --g:hsl(135 55% 60%); --gt:#CFF1D7 }
.side .navgroup[data-g="claims"]>.navtoggle     { --g:hsl(300 55% 60%); --gt:#F1CFF1 }
.side .navgroup[data-g="money"]>.navtoggle      { --g:hsl(95 55% 60%);  --gt:#DDF1CF }
.side .navgroup[data-g="sales"]>.navtoggle      { --g:hsl(30 55% 60%);  --gt:#F1E0CF }
.side .navgroup[data-g="compliance"]>.navtoggle { --g:hsl(20 55% 60%);  --gt:#F1DACF }
.side .navgroup[data-g="training"]>.navtoggle   { --g:hsl(70 55% 60%);  --gt:#EBF1CF }
.side .navgroup[data-g="reference"]>.navtoggle  { --g:hsl(230 55% 60%); --gt:#CFD5F1 }
.side .navgroup[data-g="admin"]>.navtoggle      { --g:hsl(250 12% 62%); --gt:#DADCE3 }

.side .navgroup>.navtoggle{ border-left-color:var(--g); color:var(--gt) }
/* Every rule that owns a ground states its ink — CLAUDE.md rule 1, and the contrast gate
   refused this twice while I was writing it. Hover brightens the label to the same tint the open
   state uses, so hovering previews what opening looks like. */
.side .navgroup>.navtoggle:hover{
  background:color-mix(in srgb, var(--g) 10%, transparent);
  color:var(--gt);
}
.side .navgroup.open>.navtoggle,
.side .navgroup.trail>.navtoggle{
  background:color-mix(in srgb, var(--g) 16%, transparent);
  border-left-color:var(--g);
  color:var(--gt);
}

/* The badge is a COUNT, not a category, so it stops inheriting the section hue and takes a
   neutral pill. A number wearing the family colour reads as another piece of taxonomy. */
.side .navtoggle .cnt{
  background:rgba(255,255,255,.08);
  color:var(--nav-item);
  padding:0 6px;
  border-radius:999px;
}
/* Work waiting still overrides it — that is the one case where a count means something. */
.side .navtoggle .cnt.due{ background:var(--nav-active-bg); color:var(--nav-active) }

/* The children keep the faintest wash so an open section stays visually bounded while scrolling,
   well under the row treatment above. */
.side .navgroup.open>.navkids{
  background:color-mix(in srgb, var(--g) 5%, transparent);
  /* The children are links with their own colour; this states the inherited ink anyway so the
     wash can never be the reason a label goes unreadable. */
  color:var(--nav-item);
}
/* The active row's marker takes its family's hue, so "where am I" and "which part of the system
   is this" are answered by the same rule. */
.side .navgroup>.navkids a.on{box-shadow:inset 2px 0 0 var(--g, var(--accent))}

/* ── THE ONE PIECE OF MOTION ──────────────────────────────────────────────────────────────
   A group opening already animates its height. This adds the colour arriving with it: the rule
   wipes down as the group expands, which ties the two together instead of one snapping while the
   other slides. 180ms, once, on a deliberate action — not ambient movement on a screen somebody
   works in all day. */
@keyframes navRuleIn{ from{ clip-path:inset(0 0 100% 0) } to{ clip-path:inset(0 0 0 0) } }
.side .navgroup.open>.navtoggle{ animation:navRuleIn .18s ease-out }

@media (prefers-reduced-motion:reduce){
  .side .navgroup.open>.navtoggle{animation:none}
  .side .navgroup>.navtoggle{transition:none}
}

/* ═══ the wordmark ═══════════════════════════════════════════════════════════════════
   The product is DMEStudio on every tenant. It sits centred at the head of the rail, the
   tenant's mark above it (the drawn buoy for MediHarbor, the tenant's own logo when the build
   supplies one) and the tenant's full name under it. */
.side .brand{
  display:flex;flex-direction:column;align-items:center;text-align:center;gap:5px;
  padding:18px 16px 16px;
  border-bottom:1px solid oklch(100% 0 0 / .09);
}
.side .brand svg{flex:none;width:28px;height:28px;color:var(--nav-active)}
/* A tenant's own mark, centred above the wordmark — never beside it. This was display:none
   from before the console had tenants ("the wordmark is drawn, not a raster logo"), which
   hid Scotts' seal after the build had put it there. */
.side .brand img{display:block;width:40px;height:40px;object-fit:contain;border-radius:8px;margin:0 auto 2px}
/* THE WORDMARK. A display face at 700, tracked tight, the letters filled by a gradient that
   drifts slowly between a pale tint of the tenant's accent and the rail's brightest white.
   Derived, not hardcoded: --nav-active-bg is the tenant's rail accent (ops-theme.js paints it
   from brand.accent), and MediHarbor's teal is the fallback. The raw accent never appears —
   Scotts' navy is 1.3:1 on this rail — both stops are mixed toward white, so the darkest the
   text ever gets is ~7:1 on --nav-bg. `color` stays set for anything that cannot clip a
   background to text; -webkit-text-fill-color is what actually lets the gradient show. */
.side .brand .bt{
  font-family:"Sora","Space Grotesk","Instrument Sans","Helvetica Neue",Arial,sans-serif;
  font-weight:700;font-size:22px;line-height:1.1;letter-spacing:-.035em;
  color:var(--nav-active);
  background:linear-gradient(100deg,
    color-mix(in oklch, var(--nav-active-bg, oklch(28% .050 184)) 38%, white) 0%,
    var(--nav-active) 50%,
    color-mix(in oklch, var(--nav-active-bg, oklch(28% .050 184)) 38%, white) 100%);
  background-size:220% 100%;background-position:0% 50%;
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;
  animation:mh-wordmark 10s ease-in-out infinite alternate;
}
@keyframes mh-wordmark{from{background-position:0% 50%}to{background-position:100% 50%}}
@media (prefers-reduced-motion:reduce){.side .brand .bt{animation:none;background-position:50% 50%}}
.side .brand .bs{font-size:11px;color:var(--nav-section);letter-spacing:.05em;text-transform:uppercase;line-height:1.4}

/* ═══ WHERE DOES A RECORD GO? ═══════════════════════════════════════════════════════════
   A permanent line in the rail naming the system this console writes new records to. It is
   here, under the tenant's name, because it is the same KIND of fact — whose console this is,
   and where its records land — and because somebody staring at a screen wondering where a
   patient went should not have to open a settings panel to find out.

   Four states, four colours, and every one of them sets a background AND a colour in the same
   rule: a component that owns its surface owns its text (CLAUDE.md). The border is derived from
   currentColor rather than being a fixed alpha-white, so it clears the 3:1 non-text floor in
   every state instead of only in the brightest one.

   Sentence case at 11px, not the 11px uppercase +0.06em used for section labels: this names a
   live STATE that changes, not a static heading, and tracking destroys word shape at this size
   on a dark ground. Hidden entirely in the ordinary state — see the note in native-app.html. */
.side .modechip{
  display:inline-flex;align-items:center;gap:5px;
  margin-top:2px;padding:3px 9px;border-radius:999px;
  font-size:11px;font-weight:600;line-height:1.45;letter-spacing:0;
  background:oklch(100% 0 0 / .07);
  color:oklch(88% .012 250);
  border:1px solid color-mix(in oklch, currentColor 45%, transparent);
  cursor:help;
}
.side .modechip.parallel{background:oklch(78% .095 85 / .15);color:oklch(86% .085 85)}
.side .modechip.native{background:oklch(80% .095 155 / .15);color:oklch(82% .095 155)}
.side .modechip.blocked{background:oklch(78% .105 25 / .18);color:oklch(80% .105 25)}

.side .navlabel{
  font-size:11px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--nav-section);padding:16px 20px 6px;font-weight:600;
}
.side nav{padding:0;display:flex;flex-direction:column}

/* One vertical axis: every nav row, the brand and the footer share the same 20px gutter as the
   workspace beside them. The active marker is a 2px rule inside that gutter, not an inset pill,
   so nothing shifts horizontally when a row becomes active. */
.side a{
  display:flex;align-items:center;gap:9px;
  padding:5px 20px;
  min-height:30px;
  color:var(--nav-item);
  text-decoration:none;
  font-size:13px;
  font-weight:400;
  line-height:20px;
  transition:background-color .1s,color .1s;
}
/* 16px line icons, 1.5px, currentColor — so they ride the lightness ladder automatically
   instead of needing their own colour rules. A rail with no icons is markedly slower to scan;
   the previous pass removed the emoji and put nothing back, which was half a job. */
.side a svg{flex:none;width:16px;height:16px;opacity:.85;margin-top:1px;align-self:flex-start}
/* NO TRUNCATION. Measured 2026-08-13: 17 of 70 labels were clipped at 216px, worst
   "Transmission Dashboard" needing 236px once its 44px indent is counted. A nav label that
   reads "Transmission Dashbo…" costs more than the pixels it saves. The rail is wider, the
   deep indent is tighter, and anything still too long wraps instead of being cut. */
.side a .lbl{flex:1;min-width:0;white-space:normal;overflow-wrap:break-word;hyphens:none}
.side a:hover{background:oklch(100% 0 0 / .04);color:var(--nav-item-hover)}
.side a:hover svg{opacity:1}
/* inset shadow, NOT border-left: a border changes the box and shifts the label 2px on state
   change, which reads as a twitch every time you navigate. */
.side a.on{background:var(--nav-active-bg);color:var(--nav-active);font-weight:500;box-shadow:inset 3px 0 0 var(--nav-marker,var(--accent))}
.side a.on svg{opacity:1}
.side .ext{color:var(--nav-section);font-size:12px}
.side .spacer{flex:1}
.side .foot{
  padding:12px 20px;font-size:11px;line-height:1.55;
  color:var(--nav-section);
  border-top:1px solid oklch(100% 0 0 / .09);
}

/* ── collapsible tree ───────────────────────────────────────────────────────────────
   Structure is unchanged (the router and MHNav both depend on .navgroup/.navtoggle/.navkids
   and on grid-template-rows for the open animation) — only the skin moved onto the tokens. */
.side .navtoggle{
  display:flex;align-items:center;gap:8px;width:100%;
  padding:7px 20px;border:0;background:transparent;
  color:var(--nav-section);
  font:inherit;font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  text-align:left;cursor:pointer;
  border-left:2px solid transparent;
  transition:background .1s,color .1s;
}
.side .navtoggle:hover{background:oklch(100% 0 0 / .04);color:var(--nav-item-hover)}
/* A FOCUS RING ON THE RAIL IS MEASURED AGAINST THE RAIL, NOT THE PAGE.
   --accent-hi is a page colour: teal at 3.69:1 here before, and once the accent family follows
   the tenant it becomes the tenant's own navy at 2.41:1 (Scotts) — under the 3:1 floor a focus
   ring has to clear. --nav-active is the rail's brightest member, 11.5:1, and is what every
   other "you are here" mark in this rail already uses. */
.side .navtoggle:focus-visible{outline:2px solid var(--nav-active);outline-offset:-2px}
.side .navtoggle .ic{display:none}
.side .navtoggle .gt{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.side .navtoggle .cnt{
  flex:none;font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:11px;font-weight:400;color:var(--nav-section);letter-spacing:0;
}
/* A count is a size by default. It only earns colour when it represents work waiting. */
.side .navtoggle .cnt.due{
  color:var(--nav-active);background:var(--nav-active-bg);
  padding:0 5px;border-radius:var(--r-badge);
}
.side .navtoggle .chev{flex:none;font-size:9px;opacity:.4;transition:transform .16s ease}
.side .navgroup.open>.navtoggle .chev{transform:rotate(90deg)}
.side .navgroup.trail>.navtoggle{color:var(--nav-item-hover)}
.side .navgroup.trail>.navtoggle .cnt{color:var(--nav-item)}

.side .navkids{display:grid;grid-template-rows:0fr;transition:grid-template-rows .16s ease}
.side .navgroup.open>.navkids{grid-template-rows:1fr}
.side .navkids>*{overflow:hidden;min-height:0;padding-top:0;padding-bottom:0;transition:padding .16s ease}
.side .navgroup.open>.navkids>*{padding-top:2px;padding-bottom:6px}

/* Children indent by text, not by a moved gutter — the left rule stays on one axis. */
.side .navkids nav a{padding-left:28px}
.side .navgroup.sub>.navtoggle{padding-left:28px;font-size:11px;color:rgba(255,255,255,.42)}
.side .navgroup.sub>.navtoggle:hover{color:#fff}
.side .navgroup.sub .navkids nav a{padding-left:38px}

@media (prefers-reduced-motion:reduce){
  .side .navkids,.side .navtoggle .chev{transition:none}
}

/* ═══ workspace ═════════════════════════════════════════════════════════════════════ */

.appmain{flex:1;min-width:0;display:flex;flex-direction:column}

.apptop{
  position:sticky;top:0;z-index:20;
  background:var(--paper);color:var(--ink-1,var(--ink));
  border-bottom:1px solid var(--line);
  display:flex;align-items:center;gap:12px;
  padding:0 20px;
  height:52px;flex:0 0 52px;
}
.apptop .menu{display:none;background:none;border:0;font-size:18px;color:var(--ink-2);cursor:pointer;padding:4px}
.apptop h1{font-size:15px;margin:0;font-weight:600;letter-spacing:-.01em}
.apptop .sub{font-size:11px;color:var(--ink-3)}
.apptop .right{margin-left:auto;display:flex;align-items:center;gap:12px}
.apptop .clock{
  text-align:right;font-size:11px;color:var(--ink-3);line-height:1.3;
  font-family:var(--mono);font-variant-numeric:tabular-nums;
}
.apptop .clock b{display:block;font-size:12px;font-weight:500;color:var(--ink-2)}
.apptop .who{
  width:26px;height:26px;border-radius:var(--r-control);
  background:var(--structural-12);color:var(--structural);
  display:flex;align-items:center;justify-content:center;
  font-weight:600;font-size:11px;letter-spacing:.02em;
}
.appbody{padding:20px 20px 64px;max-width:1180px;width:100%}
.app-body .appbody{padding-bottom:72px}

/* ═══ home ══════════════════════════════════════════════════════════════════════════ */

/* ── THE HOME PAGE, ONE LENS PER JOB ───────────────────────────────────────────────────
   Structure versus action (see console-accent-rationing): the lens you are on is where-you-are,
   so it takes --structural; the one button on the page that is "the thing to press" (the first
   needs-you row) is the accent; good / watch / act-now are the semantic family and never borrow
   either. Every surface here sets its background and its colour in the same rule. State is never
   colour alone: each chip carries a word, and each tile a note. */
.hd-head{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 24px;align-items:end;margin:4px 0 18px}
.hd-hello{min-width:0}
.hd-greet{font-size:22px;font-weight:600;letter-spacing:-.015em;line-height:1.2;margin:0;color:var(--ink-1)}
.hd-date{margin:4px 0 0;font-size:13px;color:var(--ink-3)}
.hd-lensbar{display:flex;flex-direction:column;align-items:flex-end;gap:6px}
/* The segmented control's outline is what identifies it as ONE control (WCAG 1.4.11, 3:1 against
   the page): --line-strong measured 2.13:1 on Scotts paper, the secondary-button line clears it. */
.hd-lenses{display:inline-flex;gap:2px;padding:3px;border:1px solid var(--btn-2-line,var(--line-strong));border-radius:var(--r-float);
  background:var(--panel);color:var(--ink-2)}
.hd-lenses[hidden]{display:none}
.hd-lens{appearance:none;-webkit-appearance:none;border:0;border-radius:4px;background:transparent;color:var(--ink-2);
  font:500 13px/1 var(--sans);min-height:32px;padding:0 14px;cursor:pointer;
  transition:background-color var(--exit),color var(--exit)}
.hd-lens:hover{background:var(--tint-2);color:var(--ink-1)}
.hd-lens[aria-checked="true"]{background:var(--structural);color:var(--on-structural);font-weight:600}
.hd-lens:focus-visible{outline:2px solid var(--structural);outline-offset:2px}
.hd-meta{display:flex;align-items:center;gap:8px;font-size:11.5px;color:var(--ink-3)}
.hd-refresh.btn--secondary{height:28px;padding:0 10px;font-size:12px}
.hd-refresh[disabled]{opacity:.55;cursor:progress}
.hd-lensdesc{grid-column:1 / -1;margin:0;font-size:13px;line-height:1.5;color:var(--ink-2);max-width:78ch}
.hd-alert{margin:0 0 14px;padding:10px 12px;border:1px solid var(--warn-line);border-radius:var(--r-control);
  background:var(--warn-bg);color:var(--ink-1);font-size:13px;line-height:1.5}

.hd-sec{font-size:13px;font-weight:600;margin:0 0 8px;color:var(--ink-1)}
.hd-secnote{font-weight:400;color:var(--ink-3)}
.hd-yday{margin:0 0 16px}
.hd-ydaygrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:8px}
.hd-y{display:flex;flex-direction:column;gap:3px;padding:10px 12px;border:1px solid var(--line);border-radius:var(--r-control);
  background:var(--panel);color:var(--ink-1);text-decoration:none}
.hd-y:hover{background:var(--tint);color:var(--ink-1);text-decoration:none;border-color:var(--line-strong)}
.hd-yv{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:20px;font-weight:600;line-height:1.1;color:var(--ink-1)}
.hd-yl{font-size:12px;font-weight:500;color:var(--ink-2)}
.hd-yd{font-size:11.5px;color:var(--ink-3);display:flex;align-items:center;gap:6px;flex-wrap:wrap}

.hd-tiles{display:grid;grid-template-columns:repeat(var(--hd-cols,4),minmax(0,1fr));gap:12px;margin:0 0 16px}
.hd-tile{position:relative;display:flex;flex-direction:column;gap:6px;min-height:118px;padding:14px 16px 14px 15px;
  border:1px solid var(--line);border-left:3px solid var(--line-strong);border-radius:var(--r-control);
  background:var(--panel);color:var(--ink-1);text-decoration:none;
  transition:background-color var(--exit),border-color var(--exit)}
.hd-tile:hover{background:var(--tint);color:var(--ink-1);text-decoration:none;border-color:var(--line-strong)}
.hd-tile:focus-visible{outline:2px solid var(--structural);outline-offset:2px}
.hd-tile.is-good{border-left-color:var(--ok)}
.hd-tile.is-warn{border-left-color:var(--warn)}
.hd-tile.is-critical{border-left-color:var(--bad)}
.hd-tile.is-neutral{border-left-color:var(--structural-40)}
.hd-tile.is-loading .hd-tv{color:var(--ink-4)}
.hd-trow{display:flex;align-items:center;justify-content:space-between;gap:8px}
.hd-tl{font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3)}
.hd-tv{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:26px;font-weight:600;line-height:1.1;
  letter-spacing:-.02em;color:var(--ink-1)}
.hd-tn{font-size:12px;line-height:1.45;color:var(--ink-3)}

.hd-chip{display:inline-flex;align-items:center;gap:5px;flex:none;font-size:11px;font-weight:600;line-height:1.2;
  padding:2px 7px;border-radius:var(--r-badge);border:1px solid var(--idle-line);background:var(--idle-bg);color:var(--idle);white-space:nowrap}
.hd-chip::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
.hd-chip.good{border-color:var(--ok-line);background:var(--ok-bg);color:var(--ok)}
.hd-chip.warn{border-color:var(--warn-line);background:var(--warn-bg);color:var(--warn)}
.hd-chip.critical{border-color:var(--bad-line);background:var(--bad-bg);color:var(--bad)}

.hd-main{display:grid;grid-template-columns:minmax(0,1.75fr) minmax(0,1fr);gap:16px;align-items:start}
.hd-cardhead{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin:0 0 4px}
.hd-cardhead h2{margin:0}
.hd-count{font-family:var(--mono);font-size:12px;color:var(--ink-3)}
.hd-needlist{list-style:none;margin:0;padding:0}
.hd-need{display:grid;grid-template-columns:64px minmax(0,1fr) auto auto;gap:10px;align-items:center;
  padding:10px 0;border-top:1px solid var(--line)}
.hd-need:first-child{border-top:0}
.hd-sev{font-size:11px;font-weight:600;letter-spacing:.02em;color:var(--ink-3)}
.hd-sev.critical{color:var(--bad)}
.hd-sev.warn{color:var(--warn)}
.hd-needmain{display:flex;flex-direction:column;gap:2px;min-width:0;text-decoration:none;color:var(--ink-1);border-radius:var(--r-control)}
.hd-needmain:hover{text-decoration:none;color:var(--ink-1)}
.hd-needmain:hover .hd-needl{text-decoration:underline;text-underline-offset:2px}
.hd-needl{font-size:13.5px;font-weight:600;color:var(--ink-1);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hd-needw{font-size:12px;line-height:1.4;color:var(--ink-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hd-go{text-decoration:none}
.hd-go:hover{text-decoration:none}
.hd-dismiss{font-size:12px}
.hd-empty{list-style:none;display:flex;align-items:center;gap:8px;padding:10px 0;font-size:13px;color:var(--ink-3)}
.hd-hiddenrow{display:flex;align-items:center;gap:6px;margin-top:6px;padding-top:8px;border-top:1px solid var(--line);font-size:12px;color:var(--ink-3)}
.hd-hiddenrow[hidden]{display:none}
.hd-hiddenrow .btn--ghost{height:26px;font-size:12px}

.hd-side h2{margin-bottom:8px}
.hd-spark{margin:0 0 14px}
.hd-spark figcaption{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-bottom:4px}
.hd-spl{font-size:12px;font-weight:600;color:var(--ink-2)}
.hd-spv{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:15px;font-weight:600;color:var(--ink-1)}
.hd-spark svg{display:block;width:100%;height:48px;overflow:visible}
.hd-spa{fill:var(--structural-08);stroke:none}
.hd-spline{fill:none;stroke:var(--structural);stroke-width:1.75;stroke-linejoin:round;stroke-linecap:round;vector-effect:non-scaling-stroke}
.hd-spline.today{stroke-dasharray:3 3}
.hd-spdot{fill:var(--panel);stroke:var(--structural);stroke-width:1.75;vector-effect:non-scaling-stroke}
.hd-spfoot{display:flex;justify-content:space-between;gap:8px;margin-top:3px;font-size:11px;color:var(--ink-3)}

.hd-bar{margin:4px 0 14px;padding-top:12px;border-top:1px solid var(--line)}
.hd-barh{display:flex;justify-content:space-between;gap:8px;margin-bottom:6px;font-size:12px;font-weight:600;color:var(--ink-2)}
.hd-barh span:last-child{font-family:var(--mono);font-weight:500;color:var(--ink-3)}
.hd-bartrack{display:flex;gap:2px;height:10px;border-radius:var(--r-badge);overflow:hidden;background:var(--tint-2);color:var(--ink-1)}
.hd-seg{display:block;min-width:3px;flex-basis:0;height:0;border-top:10px solid var(--idle)}
.hd-legend{list-style:none;margin:8px 0 0;padding:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(118px,1fr));gap:4px 10px;font-size:11.5px;color:var(--ink-2)}
.hd-legend a{color:var(--ink-2);text-decoration:none}
.hd-legend a:hover{color:var(--ink-1);text-decoration:underline}
.hd-legend b{font-family:var(--mono);font-weight:600;color:var(--ink-1)}
/* Age and readiness are ORDERED, so each ramp steps from the good colour through structural to
   the critical colour it ends on. Lead sources are the console's validated categorical scale
   (--src-*). Swatches and segments are painted as BORDERS, not backgrounds: they carry no text,
   and a background is a surface that would owe a text colour it never has. */
.hd-sw{display:inline-block;width:0;height:0;margin-right:5px;border:4px solid var(--idle);border-radius:2px;vertical-align:0}
.a1,.r5{border-color:var(--ok)}
.a2,.r4{border-color:var(--structural-40)}
.a3,.r3{border-color:var(--structural)}
.a4,.r2{border-color:var(--warn)}
.a5,.r1{border-color:var(--bad)}
.r0{border-color:var(--idle)}
.s1{border-color:var(--src-meta)} .s2{border-color:var(--src-phone)} .s3{border-color:var(--src-web)}

@media (max-width:1100px){ .hd-tiles{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))} }
@media (max-width:900px){
  .hd-head{grid-template-columns:1fr}
  .hd-lensbar{align-items:flex-start}
  .hd-lenses{max-width:100%;overflow-x:auto}
  .hd-main{grid-template-columns:1fr}
}
@media (max-width:560px){
  /* A phone: severity, then the row, then its two actions side by side under it. */
  .hd-need{grid-template-columns:auto auto minmax(0,1fr);row-gap:6px}
  .hd-sev,.hd-needmain{grid-column:1 / -1}
  .hd-needl,.hd-needw{white-space:normal}
  .hd-tiles{grid-template-columns:repeat(2,minmax(0,1fr))}
  .hd-trow{flex-direction:column;align-items:flex-start;gap:4px}
  .hd-tv{font-size:22px}
}
@media (prefers-reduced-motion:reduce){ .hd-tile,.hd-lens{transition:none} }
@media (forced-colors:active){
  .hd-tile{border-left-width:3px}
  .hd-chip::before,.hd-sw,.hd-seg{forced-color-adjust:none}
}

.card2{
  background:var(--panel);color:var(--ink-1,var(--ink));
  border:1px solid var(--line);
  border-radius:var(--r-control);
  padding:16px;
  margin-bottom:16px;
  box-shadow:none;
}
.card2 h2,.sec-h{font-size:15px;font-weight:600;color:var(--ink-1);margin:0 0 12px;letter-spacing:-.008em}

.launch{display:grid;grid-template-columns:repeat(auto-fill,minmax(212px,1fr));gap:1px;background:var(--panel);color:var(--ink-1,var(--ink));border:1px solid var(--line);border-radius:var(--r-control);overflow:hidden}
.launch a{
  box-shadow:0 0 0 1px var(--line); /* hairlines from the cells, so an unfilled row stays white */
  display:block;background:var(--panel);padding:14px 16px;
  text-decoration:none;color:var(--ink-1);
  transition:background .1s;
}
.launch a:hover{background:var(--tint);color:var(--ink-1,var(--ink));text-decoration:none}
.launch .t{font-weight:600;font-size:13px;display:flex;align-items:center;gap:8px;letter-spacing:-.005em}
.launch .d{font-size:12px;color:var(--ink-3);margin-top:4px;line-height:1.5}

/* .btn and .pill live in ops.css; these keep the old class names working on this page. */
.pill{font-size:11px;font-weight:600;padding:1px 6px;border-radius:var(--r-badge);border:1px solid transparent}
.pill.g{background:var(--ok-bg);color:var(--ok);border-color:var(--ok-line)}
.pill.a{background:var(--warn-bg);color:var(--warn);border-color:var(--warn-line)}
.pill.r{background:var(--bad-bg);color:var(--bad);border-color:var(--bad-line)}

.appfoot{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px;
  margin-top:24px;padding-top:14px;border-top:1px solid var(--line);
  font-size:11px;color:var(--ink-4);
}
.appfoot .fl{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
.appfoot a{color:var(--ink-3);text-decoration:none}
.appfoot a:hover{color:var(--accent)}
.appfoot .sep{color:var(--line-strong)}
.appfoot .stamp{font-family:var(--mono);font-variant-numeric:tabular-nums}

@media(max-width:900px){
  .side{position:fixed;left:0;top:0;z-index:60;transform:translateX(-100%);transition:transform .18s;box-shadow:var(--lift)}
  body.navopen .side{transform:none}
  /* .app is position:relative;z-index:1 — a stacking context — so the drawer's z-index:60 only
     counted inside it, and the page-level dimmer (z 55) sat ON TOP of the open drawer: it showed,
     but no link could be tapped. While the drawer is open, .app stops being a context. */
  body.navopen .app{z-index:auto}
  body.navopen::after{content:"";position:fixed;inset:0;background:rgba(15,27,45,.4);z-index:55}
  .apptop .menu{display:block}
  .appbody{padding:16px}
}


/* ═══ PINNED MODULES + QUICK COLLAPSE ══════════════════════════════════════════════
   A 70-item tree is only navigable if you can flatten it in one gesture or skip it entirely.
   Five is the cap on purpose: a pin rail that holds everything is just the nav again. */

.pinbar{border-bottom:1px solid oklch(100% 0 0 / .09);padding:8px 0 10px}
.pinbar[hidden]{display:none}
.pinhd{
  font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--nav-section);padding:6px 20px 4px;
}
.pinbar nav a.pinned{padding-left:20px}
/* Says why the click did nothing, instead of the click doing nothing.
   --warn is a theme token and this sits on the rail, which is dark in both themes: in light
   theme it measured 2.85:1 on #09121c. --nav-warn is the rail's own member of that hue, 9.4:1. */
.pinbar.full .pinhd::after{
  content:" — five is the limit";
  color:var(--nav-warn,var(--warn));letter-spacing:0;text-transform:none;font-weight:400;
}

.navctl{display:flex;gap:6px;padding:8px 20px 4px}
.navctl:has(.denswitch){padding-bottom:2px}
.navctl button{
  flex:1;
  background:transparent;border:1px solid oklch(100% 0 0 / .14);
  color:var(--nav-section);
  font:inherit;font-size:11px;font-weight:500;
  padding:4px 6px;border-radius:var(--r-control);cursor:pointer;
  min-height:24px;
  transition:background-color .1s,color .1s,border-color .1s;
}
.navctl button:hover{background:oklch(100% 0 0 / .05);color:var(--nav-item-hover);border-color:oklch(100% 0 0 / .24)}
.navctl button:focus-visible{outline:2px solid var(--nav-active);outline-offset:1px}

/* The pin control rests invisible and arrives on hover or keyboard focus — the same rule the
   table's row actions follow, so the rail reads calm at rest. An already-pinned item keeps it
   visible, because that is state rather than an affordance. */
.side nav a{position:relative}
.pintog{
  margin-left:auto;flex:none;
  background:transparent;border:0;color:var(--nav-section);
  font-size:11px;line-height:1;cursor:pointer;
  padding:3px 4px;border-radius:var(--r-badge);
  min-width:24px;min-height:24px;
  opacity:0;transition:opacity .12s,color .1s,background-color .1s;
}
.side nav a:hover .pintog,
.side nav a:focus-within .pintog,
.pintog:focus-visible{opacity:1}
/* Pinned is a state on the RAIL, so it takes a rail colour: --accent-hi measured 3.69:1 here
   as teal and would be 2.41:1 as Scotts' navy, on an 11px glyph that needs 4.5:1. */
.pintog.on{opacity:1;color:var(--nav-item-hover)}
.pintog:hover{background:oklch(100% 0 0 / .08);color:var(--nav-active)}
@media (prefers-reduced-motion:reduce){.pintog{transition:none}}
/* The reload control sits on the CURRENT link only, always visible there: it is the one honest
   answer to "why does this page look old" now that frames are kept alive. Same footprint as the
   pin so the two read as one family; it takes the pinned colour because it is state of the rail. */
.reloadtog{
  margin-left:auto;flex:none;
  background:transparent;border:0;color:var(--nav-item-hover);
  font-size:13px;line-height:1;cursor:pointer;
  padding:3px 4px;border-radius:var(--r-badge);
  min-width:24px;min-height:24px;opacity:.85;
  transition:opacity .12s,color .1s,background-color .1s,transform .6s;
}
.reloadtog + .pintog{margin-left:0}
.reloadtog:hover,.reloadtog:focus-visible{opacity:1;background:oklch(100% 0 0 / .08);color:var(--nav-active)}
.reloadtog.spin{transform:rotate(360deg)}
.reloadtog.refused{color:var(--danger,#e5484d);opacity:1}
@media (prefers-reduced-motion:reduce){.reloadtog{transition:none}.reloadtog.spin{transform:none}}


/* ═══ RECENTS DOCK ════════════════════════════════════════════════════════════════
   Low-key by construction: a 26px strip on the page edge, muted, no shadow, collapsed to a
   single row until you reach for it. It answers "where was I" — which the nav does not.
   If it ever competes with the content for attention it has failed. */
/* FIXED, not sticky. Sticky needs a scroll container to stick within; in tool mode .app is
   height:100vh with overflow:hidden and the iframe takes the remaining space, so there is no
   scroll context and the dock landed wherever flow put it — which read as it jumping to the
   top. Pinned to the viewport it is in the same place on every page, which is the entire point
   of a dock. */
.dock{
  position:fixed;left:240px;right:0;bottom:0;z-index:30;
  display:flex;align-items:center;gap:var(--space-3);
  height:26px;
  padding:0 var(--space-4) 0 var(--space-3);
  border-top:1px solid var(--line);
  background:var(--paper);
  color:var(--ink-2);
  font-size:11px;
  transition:height .14s var(--ease-out);
}
.dock.open{height:auto;min-height:26px;padding-top:6px;padding-bottom:6px;align-items:flex-start}

.dock-tab{
  flex:none;background:none;border:0;cursor:pointer;
  font:inherit;font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-3);padding:4px 2px;min-height:24px;
}
.dock-tab:hover{color:var(--ink-1)}
.dock-tab::after{content:" ▸";font-size:9px;opacity:.6}
.dock.open .dock-tab::after{content:" ▾"}

/* Collapsed: one line, clipped. Open: wraps to as many rows as it needs. */
/* Chips keep their natural width. Letting them shrink turned "Outreach CRM" into "Outreac"
   and the whole point of a recents list is reading it at a glance — a stub is worse than not
   showing the item. Collapsed, the strip scrolls; open, it wraps. */
.dock-strip{
  flex:1;min-width:0;display:flex;gap:var(--space-2);
  overflow-x:auto;overflow-y:hidden;white-space:nowrap;align-items:center;
  scrollbar-width:none;
}
.dock-strip::-webkit-scrollbar{display:none}
.dock-item{flex:0 0 auto}
.dock.open .dock-strip{flex-wrap:wrap;white-space:normal;overflow:visible}

.dock-item{
  display:inline-flex;align-items:center;gap:5px;flex:0 0 auto;
  padding:2px 7px;border-radius:var(--r-badge);
  border:1px solid var(--line);
  background:var(--panel);color:var(--ink-2);
  text-decoration:none;font-size:11px;line-height:18px;
  max-width:none;
  transition:background-color var(--exit),border-color var(--exit),color var(--exit);
}
.dock-item:hover{background:var(--tint);border-color:var(--line-strong);color:var(--ink-1);text-decoration:none}
.dock-item .dk{
  font-family:var(--mono);font-size:9px;letter-spacing:.04em;
  color:var(--ink-4);
}
/* A record is a thing you were working ON, so it reads slightly warmer than a module. */
.dock-item.rec{border-color:var(--accent-20);color:var(--accent)}
.dock-item.rec .dk{color:var(--accent)}

.dock-clear{
  flex:none;background:none;border:0;cursor:pointer;
  font:inherit;font-size:11px;color:var(--ink-4);padding:4px 2px;min-height:24px;
}
.dock-clear:hover{color:var(--bad)}
.dock-empty{color:var(--ink-4);font-size:11px}

@media (prefers-reduced-motion:reduce){.dock{transition:none}}
@media(max-width:700px){.dock-item{max-width:130px}}


/* ═══ NAV GROUP HUES — SUPERSEDED ════════════════════════════════════════════════
   This block is gone, and it is worth saying what it did rather than deleting it silently.

   It painted `linear-gradient(90deg, var(--g), transparent)` across EVERY section row, with
   --g set per group to a ~20% alpha colour. That was a reasonable treatment for the old nav.
   Two things then happened to it.

   First, the rail was regrouped: its selectors name pipeline, marketing, bflow, phone, content
   and ops, and none of those groups exist any more. Most of the block had quietly stopped
   matching anything.

   Second, and this is the part that actually broke: the section rows further up now define --g
   too, as a FULL-STRENGTH hue for a 3px rule. Same variable name, completely different
   contract. This block kept painting with it, so every section got a fully saturated bar across
   it — the rail rendered as a row of solid colour blocks with unreadable labels. A variable
   name is an interface, and two rules disagreeing about what --g means is the whole failure.

   The treatment that replaced it is above: a 3px left rule in the section colour, a label mixed
   toward white, and a ground that stays transparent until hover or open. No fills. */
.side .navgroup.sub > .navtoggle{background-image:none;box-shadow:none}

@media (forced-colors:active){
  .side .navgroup > .navtoggle{background-image:none;border-left-color:currentColor}
}

/* Room for the dock so it never covers the last row of anything. */
.appbody{padding-bottom:64px}
body.toolmode .toolframe{margin-bottom:26px}
body.toolmode .dock.open ~ .toolframe{margin-bottom:0}
@media(max-width:900px){ .dock{left:0} }


/* ── pin reordering ──────────────────────────────────────────────────────────────── */
.pinbar nav a.pinned{cursor:grab}
.pinbar nav a.pinned:active{cursor:grabbing}
.pinbar nav a.dragging{opacity:.4}
.pinbar nav a.over{box-shadow:inset 0 2px 0 var(--accent)}

/* ── recents bar: colour that means something ────────────────────────────────────
   The dock was uniformly grey, which made a record you were working ON look identical to a
   page you glanced at. Records take the accent; modules take the hue of the section they live
   in, so the strip reads as a map of where you have been rather than a list of words. */
.dock{background:linear-gradient(to top, var(--panel), var(--paper) 70%);color:var(--ink-2)}
.dock-tab{color:var(--accent)}
.dock-item{border-color:var(--line-strong)}
.dock-item[data-sec="pipeline"] {border-left:2px solid oklch(72% .085 265); color:var(--ink-1)}
.dock-item[data-sec="intake"]   {border-left:2px solid oklch(72% .085 184); color:var(--ink-1)}
.dock-item[data-sec="marketing"]{border-left:2px solid oklch(72% .085 85);  color:var(--ink-1)}
.dock-item[data-sec="reference"]{border-left:2px solid oklch(72% .085 155); color:var(--ink-1)}
.dock-item[data-sec="bflow"]    {border-left:2px solid oklch(72% .085 320); color:var(--ink-1)}
.dock-item[data-sec="phone"]    {border-left:2px solid oklch(72% .085 205); color:var(--ink-1)}
.dock-item[data-sec="content"]  {border-left:2px solid oklch(72% .085 25);  color:var(--ink-1)}
.dock-item[data-sec="ops"]      {border-left:2px solid oklch(72% .085 145); color:var(--ink-1)}
/* A record is a thing you were working ON — it gets the accent fill, not just an edge. */
.dock-item.rec{background:var(--accent-08);border-color:var(--accent-20);color:var(--accent)}
.dock-item.rec:hover{background:var(--accent-12);border-color:var(--accent);color:var(--accent)}
.dock-item.rec .dk{color:var(--accent);font-weight:600}


.denlbl{font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--nav-section);align-self:center;flex:none}

.dock-item[data-sec="home"]{border-left:2px solid var(--ink-3)}
