/* ============================================================================
   JF Ops Ecosystem - shared design system (single source of truth).
   Every screen imports this; do not redefine tokens or core components locally.
   Grounded in the JF 2026 brand (Manrope, Charcoal/White/Teal) + MI dashboard family.
   ============================================================================ */

:root {
  /* lead palette */
  --jf-white:#FFFFFF; --jf-charcoal:#212121; --jf-teal:#47FFCE;
  /* support palette */
  --jf-stone:#F3F3F3; --jf-slate:#4B4B4B; --jf-lime:#D4FF8B; --jf-sage:#6BA09D;
  --jf-border:#E5E7EB; --jf-grey:#9CA3AF;
  --jf-purple:#675FEC;
  /* Status / RAG. Retained for utilisation + forecast heatmaps - brand guidelines §7.6.
     These had NO home here, so every dashboard declared its own copy. All pages already agreed on
     the values (checked 2026-07-16, one distinct value each), so this is their canonical home, not
     a new opinion. NB --warn (#d68100, RAG orange) and --jf-warn (#E9C56A, soft amber banner) are
     genuinely different tokens, not a duplicate - don't "tidy" one into the other. */
  --good:#0f8a4e; --warn:#d68100; --bad:#c13030; --info:#1f6feb;
  --jf-warn:#E9C56A; --jf-warn-bg:#FFF6DD;
  /* type */
  --jf-font:"Manrope",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif;
  --jf-font-display:"Söhne Breit","Manrope",Impact,"Helvetica Neue",Arial,sans-serif;
  --jf-mono:"JetBrains Mono",ui-monospace,"SF Mono",Menlo,monospace;
  --mono:"SF Mono","Fira Code",ui-monospace,monospace;
  /* ── Synonyms, NOT new values ────────────────────────────────────────────────────────────────
     Pages coined these names before ops.css carried the tokens, then each re-declared them in a
     local :root - which silently overrode this file, so a palette change here would never have
     reached them. Same values, kept so the existing pages work without a rename sweep.
     Prefer the short name (--jf-grey / --jf-font / --jf-border) in new code. */
  --jf-grey-mid:#9CA3AF;      /* == --jf-grey */
  --jf-font-body:"Manrope",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;  /* == --jf-font */
  --jf-border-2026:#E5E7EB;   /* == --jf-border */
  /* scale */
  --r:12px; --r-sm:8px; --r-lg:14px;
  --sp-1:6px; --sp-2:10px; --sp-3:14px; --sp-4:18px; --sp-5:24px; --sp-6:32px;
  /* one elevation scale, used everywhere (soft border + layered shadow) */
  --bd-soft:1px solid rgba(17,17,26,.07);
  --shadow:0 1px 3px rgba(0,0,0,.06);
  --shadow-1:0 1px 2px rgba(16,24,40,.05);
  --shadow-2:0 1px 2px rgba(16,24,40,.04),0 6px 16px rgba(16,24,40,.05);
  --shadow-3:0 2px 4px rgba(16,24,40,.05),0 12px 28px rgba(16,24,40,.10);
  /* Canonical page-width system - two tiers + one shared gutter, so every page's content
     aligns. --wrap = standard (reading / forms / registers); --wrap-wide = data-dense
     dashboards (tables, heatmaps, grids). Page-width containers must use one of these + --gutter. */
  --wrap:1200px; --wrap-wide:1440px; --gutter:28px;
  /* motion - one restrained system: fast, purposeful, quiet. Every transition reads these,
     so the whole product eases with one hand. --ease = standard, --ease-out = decel (entrances). */
  --ease:cubic-bezier(.4,0,.2,1); --ease-out:cubic-bezier(.16,1,.3,1);
  --dur-1:120ms; --dur-2:200ms; --dur-3:320ms;
}

*{box-sizing:border-box;margin:0;padding:0}
body{font-family:var(--jf-font);color:var(--jf-charcoal);background:var(--jf-stone);font-size:15px;line-height:1.55;-webkit-font-smoothing:antialiased}
a{color:inherit}
.wrap{max-width:var(--wrap);margin:0 auto;padding:0 var(--gutter)}
.wrap.wide,.wrap-wide{max-width:var(--wrap-wide)}

/* ---- masthead (nav.js injects the nav into this) ---- */
.masthead{display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:13px 24px;border-bottom:2px solid var(--jf-teal);background:var(--jf-white);
  position:sticky;top:0;z-index:50}

/* ---- shared page header: title left, Prototype pill right (the MI family pattern) ---- */
.pghead{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}
.proto-pill{flex:none;font-size:11px;font-weight:700;color:var(--jf-slate);background:var(--jf-white);
  border:1px solid var(--jf-border);border-radius:100px;padding:6px 13px;white-space:nowrap;cursor:default}

/* ---- hero / page header ---- */
.hero{background:var(--jf-charcoal);color:#fff;padding:30px 24px}
.hero .proto-pill{background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.25);color:#fff}
.hero h1{font-size:26px;font-weight:800;letter-spacing:-.02em;line-height:1.12}
.hero p{color:#CFCFCF;font-size:15px;margin-top:8px;max-width:82ch}

/* ---- section header (number + title + lead). No eyebrow/uppercase kicker. ---- */
.section{padding:var(--sp-6) 0;border-bottom:1px solid var(--jf-border)}
.section h2{font-size:24px;font-weight:700;letter-spacing:-.02em;margin-bottom:var(--sp-3)}
.section .lead{font-size:17px;color:var(--jf-slate);max-width:72ch}

/* ---- buttons ---- */
.btn{font-family:var(--jf-font);font-size:14px;font-weight:700;border:none;border-radius:var(--r-sm);
  padding:11px 22px;cursor:pointer;background:var(--jf-teal);color:var(--jf-charcoal);line-height:1;position:relative;
  transition:filter var(--dur-1) var(--ease),transform var(--dur-1) var(--ease),box-shadow var(--dur-1) var(--ease)}
.btn:hover{filter:brightness(.95)}
.btn:active{transform:translateY(1px)}
.btn:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(71,255,206,.5)}
.btn.secondary{background:var(--jf-stone);color:var(--jf-charcoal);border:1px solid var(--jf-border)}
.btn.dark{background:var(--jf-charcoal);color:#fff}
.btn.done{background:var(--jf-sage);color:#fff;cursor:default}
.btn:disabled{opacity:.6;cursor:default}
.btn.sm{font-size:12px;padding:6px 12px;border-radius:7px}
/* in-button loading: keeps width/position, swaps the label for a spinner (async saves). Add .loading. */
.btn.loading{color:transparent!important;pointer-events:none}
.btn.loading::after{content:'';position:absolute;top:50%;left:50%;width:15px;height:15px;margin:-8px 0 0 -8px;
  border:2px solid rgba(33,33,33,.35);border-top-color:var(--jf-charcoal);border-radius:50%;animation:btn-spin .6s linear infinite}
.btn.dark.loading::after,.btn.done.loading::after{border-color:rgba(255,255,255,.4);border-top-color:#fff}
@keyframes btn-spin{to{transform:rotate(360deg)}}

/* ---- cards / panels ---- */
.card,.panel{background:var(--jf-white);border:var(--bd-soft);border-radius:var(--r);box-shadow:var(--shadow-2)}
.card{padding:var(--sp-5)}
.panel{overflow:hidden}
.panel-h{padding:13px 16px;border-bottom:1px solid var(--jf-border);font-size:13px;font-weight:700;
  letter-spacing:0;text-transform:none;color:var(--jf-charcoal)}

/* ---- KPI / scorecard ---- */
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:1px;
  background:var(--jf-border);border:1px solid var(--jf-border);border-radius:var(--r);overflow:hidden}
.kpi{background:var(--jf-white);padding:16px 18px}
.kpi .n{font-size:24px;font-weight:800;letter-spacing:-.02em}
.kpi .c{font-size:12px;color:var(--jf-slate);margin-top:5px}

/* ---- tables: LEFT-aligned headers+cells, tabular-nums, numeric cols right ---- */
/* NOTE: no overflow:hidden here. It captures the sticky-positioning context, so any table with
   sticky headers or frozen first columns (utilisation, revenue, finance grids) would lose its
   pinning. border-collapse:collapse already suppresses corner rounding, so nothing to clip. */
.tbl{width:100%;border-collapse:collapse;font-size:13.5px;background:var(--jf-white);
  border:1px solid var(--jf-border);border-radius:var(--r)}
.tbl thead th{background:var(--jf-charcoal);color:#fff;text-align:left;text-transform:none;font-weight:700;padding:11px 14px;white-space:nowrap}
.tbl th.num,.tbl td.num{text-align:right;font-variant-numeric:tabular-nums}
.tbl tbody td{padding:9px 14px;border-bottom:1px solid var(--jf-border);vertical-align:middle}
.tbl tbody tr:nth-child(even) td{background:var(--jf-stone)}
/* row hover - the single biggest "alive" tell on a data table; a quiet teal wash that beats the zebra */
.tbl tbody tr{transition:background var(--dur-1) var(--ease)}
.tbl tbody tr:hover td{background:rgba(71,255,206,.08)}
/* opt-in sticky header: add .tbl.sticky (needs a scroll container). Header stays as you scroll a long table */
.tbl.sticky thead th{position:sticky;top:0;z-index:2}
/* opt-in sortable columns: add class .sort to a th, toggle .asc/.desc from JS. Caret shows sort state */
.tbl th.sort{cursor:pointer;user-select:none;transition:background var(--dur-1) var(--ease)}
.tbl th.sort:hover{background:#2c2c2c}
.tbl th.sort::after{content:'';display:inline-block;width:0;height:0;margin-left:7px;vertical-align:middle;
  border-left:4px solid transparent;border-right:4px solid transparent;border-top:5px solid currentColor;opacity:.35;transition:opacity var(--dur-1) var(--ease),transform var(--dur-1) var(--ease)}
.tbl th.sort.asc::after{transform:rotate(180deg);opacity:1}
.tbl th.sort.desc::after{opacity:1}
/* ---- tiered .tbl modifiers: one shared identity (charcoal header, teal accents, tabular-nums,
   tokens) across the MI dashboards; specialized structure kept. Add alongside .tbl on the table. ---- */
/* P&L / financial statement: numeric columns right, .lbl column left, total rules, no zebra */
.tbl.pnl thead th{text-align:right;text-transform:uppercase;font-size:11.5px;letter-spacing:.03em}
.tbl.pnl thead th.lbl,.tbl.pnl td.lbl{text-align:left}
.tbl.pnl td{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.tbl.pnl td.lbl{font-weight:600}
.tbl.pnl tbody tr:nth-child(even) td{background:transparent}
.tbl.pnl tbody tr:hover td{background:rgba(71,255,206,.06)}
.tbl.pnl tr.total td,.tbl.pnl tr.total-row td,.tbl.pnl tr.grand td{border-top:2px solid var(--jf-charcoal);font-weight:800}
/* Matrix / heatmap: page-set cell colouring survives (base zebra + row hover off, cells centred) */
.tbl.matrix tbody tr:nth-child(even) td{background:transparent}
.tbl.matrix tbody tr:hover td{background:transparent}
.tbl.matrix td{text-align:center;font-variant-numeric:tabular-nums}
.tbl.matrix td.lbl,.tbl.matrix th.lbl,.tbl.matrix tbody th{text-align:left}
/* Editable grid (pipeline): base look kept, quiet teal affordance (never purple) */
.tbl.editable tbody tr:hover td{background:rgba(71,255,206,.06)}
/* Financial table, .num convention (numeric cells carry .num, labels left by default): charcoal
   header + right-aligned numerics come from base .tbl; .fin just removes the zebra (statements
   with total rows read cleaner without it). No .lbl needed - labels stay left naturally. */
.tbl.fin tbody tr:nth-child(even) td{background:transparent}
.tbl.fin tbody tr:hover td{background:rgba(71,255,206,.06)}

/* ---- tags / chips ---- */
.tag{display:inline-block;font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.04em;
  padding:3px 9px;border-radius:100px;background:var(--jf-stone);color:var(--jf-slate);white-space:nowrap}
.tag.teal{background:var(--jf-teal);color:var(--jf-charcoal)}
.tag.lime{background:var(--jf-lime);color:var(--jf-charcoal)}
.tag.sage{background:var(--jf-sage);color:#fff}
.tag.dark{background:var(--jf-charcoal);color:#fff}
.tag.amber{background:#f3c969;color:var(--jf-charcoal)}
.tag.slate{background:var(--jf-stone);color:var(--jf-slate);border:1px solid var(--jf-border)}

/* ---- rate config form (client page) ---- */
.rateform{display:grid;grid-template-columns:repeat(4,1fr) auto;gap:12px;align-items:end}
@media(max-width:760px){.rateform{grid-template-columns:1fr 1fr}}
.rateform label{display:flex;flex-direction:column;gap:5px;font-size:11.5px;font-weight:700;color:var(--jf-slate);text-transform:uppercase;letter-spacing:.03em}
.rateform input{font-family:var(--jf-font);font-size:14px;font-weight:600;color:var(--jf-charcoal);padding:9px 11px;border:1px solid var(--jf-border);border-radius:var(--r-sm);background:#fff}
.rateform input:focus{outline:none;border-color:var(--jf-sage)}
.rateform .btn{white-space:nowrap}
.chip{display:inline-block;font-size:11px;font-weight:700;background:var(--jf-stone);color:var(--jf-slate);
  padding:3px 9px;border-radius:100px;margin-right:6px}

/* ---- form fields ---- */
.field{font-family:var(--jf-font);font-size:14px;padding:9px 11px;border:1px solid var(--jf-border);
  border-radius:var(--r-sm);background:var(--jf-white);color:var(--jf-charcoal)}
.field:focus{outline:none;border-color:var(--jf-teal);box-shadow:0 0 0 2px rgba(71,255,206,.3)}
label.lbl{font-size:12px;font-weight:700;color:var(--jf-slate);display:block;margin-bottom:5px}

/* ---- callout / note ---- */
.callout{border-left:3px solid var(--jf-teal);background:var(--jf-stone);padding:13px 18px;border-radius:0 var(--r-sm) var(--r-sm) 0;font-size:14px}

/* ---- empty state ---- */
.empty{padding:60px 30px;text-align:center;color:var(--jf-grey)}
.empty .big{font-size:17px;color:var(--jf-slate);font-weight:600;margin-bottom:6px}

/* ---- toast (+ undo action) ---- */
.toast{position:fixed;bottom:20px;left:50%;background:var(--jf-charcoal);color:#fff;
  padding:10px 12px 10px 18px;border-radius:100px;font-size:13px;font-weight:600;z-index:100;
  display:flex;align-items:center;gap:12px;box-shadow:0 4px 16px rgba(0,0,0,.18);
  opacity:0;transform:translateX(-50%) translateY(8px);transition:opacity var(--dur-2) var(--ease),transform var(--dur-2) var(--ease-out)}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
/* status dot: opsToast(msg,{type:'ok'|'err'}) tints it. Neutral (no type) = quiet, no dot. */
.toast .tdot{width:7px;height:7px;border-radius:50%;flex:none;display:none}
.toast.ok .tdot,.toast.err .tdot{display:block}
.toast.ok .tdot{background:var(--jf-teal)}
.toast.err .tdot{background:#FF6B6B}
.toast .undo{background:var(--jf-teal);color:var(--jf-charcoal);border:none;border-radius:100px;font-family:inherit;
  font-weight:800;font-size:12px;padding:5px 13px;cursor:pointer}
.toast .undo:hover{filter:brightness(.95)}

/* ---- skeleton (inline loading placeholder, for partial async regions) ---- */
.skel{background:linear-gradient(90deg,#ececec 25%,#f6f6f6 37%,#ececec 63%);background-size:400% 100%;
  animation:skel 1.4s ease infinite;border-radius:6px;color:transparent!important}
@keyframes skel{0%{background-position:100% 0}100%{background-position:-100% 0}}

/* ---- JF Horizon loading screen (the MI dashboard family loader) ----
   Soft stone backdrop, the Horizon always visible in charcoal, with a teal wave travelling down
   the lines (a "moving toward the horizon" feel) + a quiet Loading caption. */
.ops-loading{position:fixed;inset:0;background:var(--jf-stone);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:18px;z-index:200}
.ops-horizon{width:132px;height:62px}
.ops-horizon line{stroke:var(--jf-charcoal);stroke-width:2.4;stroke-linecap:round;opacity:.5;animation:ops-h 1.6s ease-in-out infinite}
.ops-horizon line:nth-child(2){animation-delay:.12s}.ops-horizon line:nth-child(3){animation-delay:.24s}
.ops-horizon line:nth-child(4){animation-delay:.36s}.ops-horizon line:nth-child(5){animation-delay:.48s}
@keyframes ops-h{0%,100%{stroke:var(--jf-charcoal);opacity:.5}50%{stroke:var(--jf-teal);opacity:1}}
.ops-loading::after{content:'Loading';font-family:Manrope,system-ui,sans-serif;font-size:11px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:var(--jf-grey)}

/* ---- full-page skeleton (route-level loading silhouette; the page's shape loads before its data,
   instead of a centred spinner). Generic chrome + KPI cards + table reads as every Ops page. ---- */
.ops-skel{position:fixed;inset:0;background:var(--jf-stone);z-index:200;overflow:hidden}
.ops-skel-top{height:57px;background:#fff;border-bottom:1px solid #ECECEE;display:flex;align-items:center;gap:14px;padding:0 22px}
.ops-skel-body{max-width:1180px;margin:0 auto;padding:30px 22px}
.ops-skel-cards{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin:22px 0 26px}
.ops-skel-card{background:#fff;border:1px solid #ECECEE;border-radius:14px;padding:18px;display:flex;flex-direction:column;gap:13px}
.ops-skel-table{background:#fff;border:1px solid #ECECEE;border-radius:14px;overflow:hidden}
.ops-skel-row{display:grid;grid-template-columns:2.4fr 1fr 1fr 1fr 1fr;gap:18px;padding:14px 20px;align-items:center;border-top:1px solid #F1F1F2}
.ops-skel-row:first-child{border-top:none;background:#FAFAFA}
.ops-skel .skel{height:13px;display:block}
.ops-skel-avatar{border-radius:50%!important}
@media(max-width:900px){.ops-skel-cards{grid-template-columns:repeat(2,1fr)}.ops-skel-row{grid-template-columns:2fr 1fr 1fr}.ops-skel-row .skel:nth-child(4),.ops-skel-row .skel:nth-child(5){display:none}}
@media(prefers-reduced-motion:reduce){.skel{animation:none}}

.r{text-align:right;font-variant-numeric:tabular-nums}
.muted{color:var(--jf-grey)}

/* ============ INTERACTION PATTERNS (Layer 3) ============ */

/* object links - a Client name or a Job id is ALWAYS navigable (the cross-link rule) */
.opslink{color:var(--jf-charcoal);text-decoration:none;font-weight:600;border-bottom:1.5px solid rgba(71,255,206,.55);cursor:pointer}
.opslink:hover{border-bottom-color:var(--jf-teal);background:rgba(71,255,206,.12)}
.opslink.job{font-family:var(--mono);font-size:.95em}

/* "what's next" continuity banner - carries the lifecycle forward on every screen */
.whatsnext{display:flex;align-items:center;gap:10px;flex-wrap:wrap;background:#EAFBF5;border:1px solid #B7F5E4;border-radius:var(--r-sm);padding:11px 16px;font-size:13.5px}
.whatsnext .wn-k{font-weight:800;font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--jf-sage)}
.whatsnext a{color:var(--jf-charcoal);font-weight:700;border-bottom:1.5px solid var(--jf-teal);text-decoration:none}

/* scope / filter bar - one bar, same place, on every data screen */
.scopebar{display:flex;gap:10px;flex-wrap:wrap;align-items:center;background:var(--jf-white);border:1px solid var(--jf-border);border-radius:var(--r);padding:12px 14px}
.scopebar .grow{flex:1;min-width:180px}
.scopebar .reset{margin-left:auto;font-size:12px;color:var(--jf-grey);background:none;border:none;cursor:pointer;font-weight:600}

/* master-detail - list + detail, the shared shape for Win Gate / RevTrack / Clients */
.md{display:grid;grid-template-columns:320px 1fr;gap:16px;align-items:start}
@media(max-width:820px){.md{grid-template-columns:1fr}}
.md-list{max-height:480px;overflow:auto}
.md-item{padding:11px 14px;border-bottom:1px solid var(--jf-border);cursor:pointer}
.md-item:hover{background:var(--jf-stone)}
.md-item.sel{background:#EAFBF5;border-left:3px solid var(--jf-teal)}

/* state messages */
.state{display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:600;padding:8px 12px;border-radius:var(--r-sm)}
.state.ok{background:#EAFBF5;color:#0f7a5f}
.state.err{background:#FDECEC;color:#b42318}

/* ============ POLISH LAYER (Layer 4): micro-interactions ============ */

/* tooltip - one component for the whole product. Add data-tip="..." to any element (not inside an
   overflow:hidden container, e.g. NOT the rail). Delayed appear so it never nags; quiet fade + rise.
   data-tip-pos="right" flips it beside the element (for left-edge controls). */
[data-tip]{position:relative}
[data-tip]::after{content:attr(data-tip);position:absolute;bottom:calc(100% + 8px);left:50%;
  transform:translateX(-50%) translateY(4px);background:var(--jf-charcoal);color:#fff;font-size:11.5px;font-weight:600;
  line-height:1.3;letter-spacing:.01em;white-space:nowrap;padding:6px 9px;border-radius:7px;box-shadow:var(--shadow-2);
  opacity:0;pointer-events:none;z-index:140;transition:opacity var(--dur-2) var(--ease),transform var(--dur-2) var(--ease-out);transition-delay:0s}
[data-tip]:hover::after,[data-tip]:focus-visible::after{opacity:1;transform:translateX(-50%) translateY(0);transition-delay:.35s}
[data-tip=""]::after{display:none}
[data-tip][data-tip-pos="right"]::after{bottom:auto;left:calc(100% + 10px);top:50%;transform:translateY(-50%) translateX(-4px)}
[data-tip][data-tip-pos="right"]:hover::after,[data-tip][data-tip-pos="right"]:focus-visible::after{transform:translateY(-50%) translateX(0)}
[data-tip][data-tip-pos="left"]::after{bottom:auto;right:calc(100% + 10px);left:auto;top:50%;transform:translateY(-50%) translateX(4px)}
[data-tip][data-tip-pos="left"]:hover::after,[data-tip][data-tip-pos="left"]:focus-visible::after{transform:translateY(-50%) translateX(0)}

/* focus / maximise mode - hides the rail + top bar so the active tool takes the full viewport
   (WordPress-style distraction-free). Driven by body.shell-maximised (set in shell.js). The chrome
   is already CSS-var + body-class driven, so this is a natural extension: rail slides left, bar slides up. */
body.has-appshell.shell-maximised{padding-left:0!important;padding-top:52px!important}
body.has-appshell.shell-maximised .aps-rail{transform:translateX(-100%)}
body.has-appshell.shell-maximised .aps-top{transform:translateY(-100%)}
/* floating restore affordance, top-left, only while maximised */
.aps-restore{position:fixed;top:12px;left:12px;z-index:86;display:inline-flex;align-items:center;gap:7px;
  background:var(--jf-charcoal);color:#fff;border:none;border-radius:100px;padding:8px 14px;cursor:pointer;
  font:800 12px/1 var(--jf-font);box-shadow:var(--shadow-3);
  opacity:0;visibility:hidden;transform:translateY(-8px);transition:opacity var(--dur-2) var(--ease),transform var(--dur-2) var(--ease-out),visibility var(--dur-2)}
.aps-restore svg{width:15px;height:15px}
.aps-restore:hover{filter:brightness(1.22)}
body.shell-maximised .aps-restore{opacity:1;visibility:visible;transform:none}
/* in focus mode, page hero + scorecard sections marked .fs-hide collapse away so the tool's
   primary surface (board / table / grid) takes the whole viewport. Tag any such block .fs-hide. */
body.shell-maximised .fs-hide{display:none!important}

/* accessibility - honour reduced-motion everywhere: near-instant, no shimmer, no slides */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important;scroll-behavior:auto!important}
}

/* ---- ops-modal: the ONE confirm dialog (replaces native confirm() across the OS) ----
   Use via boot.js window.opsConfirm({title, body, confirmLabel, danger}) -> Promise<bool>.
   Charcoal title, Manrope, teal primary (or danger red), backdrop click / Escape = cancel. */
.ops-modal-back{position:fixed;inset:0;z-index:1000;background:rgba(33,33,33,.45);display:flex;align-items:center;justify-content:center;padding:20px;opacity:0;transition:opacity .15s}
.ops-modal-back.show{opacity:1}
.ops-modal{background:#fff;border-radius:14px;box-shadow:0 18px 48px rgba(0,0,0,.22);max-width:440px;width:100%;padding:22px 24px 18px;transform:translateY(6px) scale(.98);transition:transform .15s}
.ops-modal-back.show .ops-modal{transform:none}
.ops-modal h3{margin:0 0 8px;font-size:16px;font-weight:800;letter-spacing:-.01em;color:var(--jf-charcoal)}
.ops-modal .om-body{font-size:13.5px;line-height:1.55;color:var(--jf-slate);white-space:pre-line}
/* .wide: the same ONE modal, sized for a table rather than a sentence (the headcount drill-down
   lists people). white-space:pre-line is right for a typed message but collapses table markup,
   so .wide resets it. Scrolls internally - a 40-person department must not push the page. */
.ops-modal.wide{max-width:920px}
.ops-modal.wide .om-body{white-space:normal;max-height:60vh;overflow-y:auto}
.ops-modal .om-body b{color:var(--jf-charcoal)}
.ops-modal .om-chip{display:inline-block;font-size:11px;font-weight:800;color:#0a6b54;background:rgba(71,255,206,.22);border-radius:5px;padding:1px 7px;margin-right:6px}
.ops-modal .om-actions{display:flex;justify-content:flex-end;gap:9px;margin-top:18px}
.ops-modal .om-actions button{font:inherit;font-size:13px;font-weight:700;border-radius:9px;padding:9px 16px;cursor:pointer}
.ops-modal .om-cancel{border:1px solid var(--jf-border);background:#fff;color:var(--jf-slate)}
.ops-modal .om-cancel:hover{border-color:var(--jf-charcoal);color:var(--jf-charcoal)}
.ops-modal .om-go{border:none;background:var(--jf-teal);color:var(--jf-charcoal)}
.ops-modal .om-go:hover{filter:brightness(.95)}
.ops-modal .om-go.danger{background:#B42318;color:#fff}
