/* TDBFP Sentinel — single source of truth for colour, type and spacing.
   Tabs must style through these custom properties; never add raw hex values. */

:root{
  --ground:#F4F5F3; --surface:#FFFFFF; --surface-2:#EBEEEC; --surface-3:#E2E7E4;
  --line:#D3D9D6; --line-soft:#E3E7E5;
  --ink:#14201F; --ink-2:#3D4D4B; --ink-3:#66766F;
  --accent:#0F5F63; --accent-soft:#DCEBEB; --accent-ink:#0F5F63; --on-accent:#FFFFFF;
  --copper:#C2410C; --copper-soft:#F6E3D8;
  --ok:#15803D; --ok-soft:#DCEFE1;
  --watch:#B45309; --watch-soft:#F7E7CE;
  --crit:#9F1239; --crit-soft:#F6DDE3;
  --info:#0F5F63; --info-soft:#DCEBEB;
  --shadow:0 1px 2px rgba(20,32,31,.06),0 8px 24px -14px rgba(20,32,31,.18);
  --scrim:rgba(20,32,31,.46);
  --shadow-lift:0 18px 50px -12px rgba(20,32,31,.4);
  --radius:5px;
  --f-display:"IBM Plex Serif",Georgia,serif;
  --f-body:"IBM Plex Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
  --f-mono:"IBM Plex Mono",ui-monospace,Consolas,monospace;
  --topbar-h:58px; --tabs-h:44px;
  color-scheme:light;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --ground:#0E1517; --surface:#151F21; --surface-2:#1C282A; --surface-3:#243133;
    --line:#2C3B3D; --line-soft:#22302F;
    --ink:#E6EEEC; --ink-2:#AEC0BD; --ink-3:#7C918D;
    --accent:#4FBFC4; --accent-soft:#143134; --accent-ink:#4FBFC4; --on-accent:#08201F;
    --copper:#F08A4B; --copper-soft:#3A2317;
    --ok:#4ADE80; --ok-soft:#13301F;
    --watch:#FBBF24; --watch-soft:#33270D;
    --crit:#FB7185; --crit-soft:#361620;
    --info:#4FBFC4; --info-soft:#143134;
    --shadow:0 1px 2px rgba(0,0,0,.4),0 8px 24px -14px rgba(0,0,0,.7);
    --scrim:rgba(0,0,0,.62);
    --shadow-lift:0 18px 50px -12px rgba(0,0,0,.8);
    color-scheme:dark;
  }
}
:root[data-theme="dark"]{
  --ground:#0E1517; --surface:#151F21; --surface-2:#1C282A; --surface-3:#243133;
  --line:#2C3B3D; --line-soft:#22302F;
  --ink:#E6EEEC; --ink-2:#AEC0BD; --ink-3:#7C918D;
  --accent:#4FBFC4; --accent-soft:#143134; --accent-ink:#4FBFC4; --on-accent:#08201F;
  --copper:#F08A4B; --copper-soft:#3A2317;
  --ok:#4ADE80; --ok-soft:#13301F;
  --watch:#FBBF24; --watch-soft:#33270D;
  --crit:#FB7185; --crit-soft:#361620;
  --info:#4FBFC4; --info-soft:#143134;
  --shadow:0 1px 2px rgba(0,0,0,.4),0 8px 24px -14px rgba(0,0,0,.7);
  --scrim:rgba(0,0,0,.62);
  --shadow-lift:0 18px 50px -12px rgba(0,0,0,.8);
  color-scheme:dark;
}

*{box-sizing:border-box}
html,body{margin:0;padding:0;height:100%}
body{background:var(--ground);color:var(--ink);font-family:var(--f-body);font-size:14px;line-height:1.55;-webkit-font-smoothing:antialiased}
#app{min-height:100%;display:flex;flex-direction:column}
h1,h2,h3,h4{margin:0;font-family:var(--f-body);font-weight:600;line-height:1.25;text-wrap:balance}
p{margin:0}
button,select,input,textarea{font:inherit;color:inherit}
a{color:var(--accent)}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:3px}

.mono{font-family:var(--f-mono)}
.num{font-variant-numeric:tabular-nums;font-family:var(--f-mono)}
.muted{color:var(--ink-3)}
.eyebrow{font-family:var(--f-mono);font-size:.66rem;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3);font-weight:500}

/* ---------- topbar ---------- */
.topbar{height:var(--topbar-h);flex:0 0 auto;display:flex;align-items:center;gap:1.5rem;
  padding:0 1.1rem;background:var(--surface);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:40}
.brand{display:flex;align-items:center;gap:.6rem;min-width:0}
.brand-mark{width:22px;height:22px;border-radius:3px;flex:0 0 auto;
  background:linear-gradient(135deg,var(--accent) 0%,var(--copper) 130%)}
.brand-name{font-family:var(--f-display);font-size:.98rem;font-weight:600;letter-spacing:-.01em;white-space:nowrap}
.brand-sub{font-size:.68rem;color:var(--ink-3);white-space:nowrap}
.asset-picker{display:flex;align-items:center;gap:.55rem;min-width:0;flex:1 1 auto}
.asset-picker label{font-family:var(--f-mono);font-size:.66rem;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3)}
.asset-stat{font-family:var(--f-mono);font-size:.7rem;color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.topbar-right{display:flex;align-items:center;gap:.6rem;flex:0 0 auto}
.icon-btn{background:var(--surface-2);border:1px solid var(--line);border-radius:var(--radius);
  width:30px;height:30px;cursor:pointer;color:var(--ink-2);display:grid;place-items:center;font-size:.9rem}
.icon-btn:hover{background:var(--surface-3);color:var(--ink)}

select,input[type=text],input[type=date],input[type=datetime-local],input[type=number],textarea{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
  padding:.36rem .55rem;color:var(--ink);max-width:100%}
select:hover{border-color:var(--ink-3)}
textarea{resize:vertical;min-height:70px;font-family:var(--f-body)}

/* ---------- tabs ---------- */
.tabs{height:var(--tabs-h);flex:0 0 auto;display:flex;align-items:stretch;gap:0;padding:0 1.1rem;
  background:var(--surface);border-bottom:1px solid var(--line);overflow-x:auto;position:sticky;top:var(--topbar-h);z-index:39}
.tab{appearance:none;background:none;border:0;border-bottom:2px solid transparent;padding:0 .9rem;
  cursor:pointer;color:var(--ink-3);font-size:.85rem;font-weight:500;white-space:nowrap;letter-spacing:-.005em}
.tab:hover{color:var(--ink)}
.tab[aria-selected="true"]{color:var(--accent-ink);border-bottom-color:var(--accent);font-weight:600}

/* ---------- view ---------- */
.view{flex:1 1 auto;padding:1.25rem 1.1rem 3rem;max-width:1560px;width:100%;margin:0 auto}
.page-head{display:flex;align-items:flex-end;justify-content:space-between;gap:1rem;flex-wrap:wrap;margin-bottom:1.1rem}
.page-head h2{font-size:1.12rem;letter-spacing:-.01em}
.page-head p{font-size:.82rem;color:var(--ink-3);margin-top:.15rem;max-width:70ch}
.toolbar{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}

/* ---------- panels ---------- */
.panel{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius)}
.panel-head{display:flex;align-items:center;justify-content:space-between;gap:.75rem;
  padding:.7rem .95rem;border-bottom:1px solid var(--line-soft);flex-wrap:wrap}
.panel-head h3{font-size:.88rem}
.panel-head .sub{font-size:.74rem;color:var(--ink-3)}
.panel-body{padding:.95rem}
.panel-body.flush{padding:0}

.grid{display:grid;gap:.85rem}
.g2{grid-template-columns:1fr}
.g3{grid-template-columns:1fr}
.g4{grid-template-columns:repeat(2,minmax(0,1fr))}
@media(min-width:760px){.g2{grid-template-columns:repeat(2,minmax(0,1fr))}
  .g3{grid-template-columns:repeat(3,minmax(0,1fr))}
  .g4{grid-template-columns:repeat(4,minmax(0,1fr))}}
@media(min-width:1180px){.g2-wide{grid-template-columns:minmax(0,2fr) minmax(0,1fr)}}

/* ---------- stat tiles ---------- */
.tile{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:.8rem .9rem;
  display:flex;flex-direction:column;gap:.3rem;min-width:0}
.tile .eyebrow{font-size:.62rem}
.tile-val{font-family:var(--f-mono);font-size:1.5rem;font-weight:500;letter-spacing:-.02em;line-height:1.1;
  font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tile-sub{font-size:.72rem;color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tile.sev-ok .tile-val{color:var(--ok)}
.tile.sev-watch .tile-val{color:var(--watch)}
.tile.sev-crit .tile-val{color:var(--crit)}
.tile.sev-accent .tile-val{color:var(--accent-ink)}

/* severity stripe on the left edge, used for findings and health rows */
.stripe{border-left:3px solid var(--line)}
.stripe.sev-ok{border-left-color:var(--ok)}
.stripe.sev-info{border-left-color:var(--info)}
.stripe.sev-watch{border-left-color:var(--watch)}
.stripe.sev-warning{border-left-color:var(--watch)}
.stripe.sev-critical,.stripe.sev-crit{border-left-color:var(--crit)}

/* ---------- pills & badges ---------- */
.pill{font-family:var(--f-mono);font-size:.63rem;letter-spacing:.07em;text-transform:uppercase;font-weight:600;
  padding:.2em .5em;border-radius:3px;white-space:nowrap;display:inline-block}
.pill-ok{background:var(--ok-soft);color:var(--ok)}
.pill-info{background:var(--info-soft);color:var(--info)}
.pill-watch{background:var(--watch-soft);color:var(--watch)}
.pill-warning{background:var(--watch-soft);color:var(--watch)}
.pill-crit,.pill-critical{background:var(--crit-soft);color:var(--crit)}
.pill-muted{background:var(--surface-2);color:var(--ink-3)}
.badge{font-family:var(--f-mono);font-size:.68rem;padding:.22em .5em;border-radius:3px;
  background:var(--surface-2);border:1px solid var(--line);color:var(--ink-2);white-space:nowrap}
.tag-chip{font-family:var(--f-mono);font-size:.72rem;background:var(--surface-2);border:1px solid var(--line-soft);
  padding:.08em .38em;border-radius:3px;color:var(--ink-2);white-space:nowrap}

/* ---------- buttons ---------- */
.btn{appearance:none;border:1px solid var(--line);background:var(--surface);color:var(--ink);
  padding:.4rem .8rem;border-radius:var(--radius);cursor:pointer;font-size:.82rem;font-weight:500;white-space:nowrap}
.btn:hover:not(:disabled){background:var(--surface-2);border-color:var(--ink-3)}
.btn:disabled{opacity:.5;cursor:not-allowed}
.btn-primary{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
.btn-primary:hover:not(:disabled){filter:brightness(1.08);background:var(--accent);border-color:var(--accent)}
.btn-danger{color:var(--crit);border-color:var(--crit)}
.btn-danger:hover:not(:disabled){background:var(--crit-soft)}
.btn-sm{padding:.24rem .55rem;font-size:.75rem}

/* Square icon-only button, e.g. the "expand chart" affordance in a panel corner.
   Sized to the same 26px as a .btn-sm's height so the two can sit side by side. */
.icon-btn{appearance:none;display:inline-flex;align-items:center;justify-content:center;
  width:26px;height:26px;padding:0;flex:0 0 auto;border:1px solid var(--line);
  border-radius:3px;background:var(--surface);color:var(--ink-3);cursor:pointer}
.icon-btn:hover:not(:disabled){background:var(--surface-2);border-color:var(--ink-3);color:var(--ink)}
.icon-btn:disabled{opacity:.4;cursor:not-allowed}
.icon-btn svg{display:block;width:14px;height:14px}

/* ---------- fullscreen chart modal ---------- */
/* Sits above the toasts (z-index 100): a toast firing while the user has a chart
   open for close reading must not land on top of it. */
.modal-backdrop{position:fixed;inset:0;z-index:200;display:flex;
  padding:min(3vh,26px) min(3vw,26px);background:var(--scrim)}
.modal-sheet{flex:1 1 auto;display:flex;flex-direction:column;min-width:0;min-height:0;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow-lift)}
.modal-head{display:flex;align-items:center;justify-content:space-between;gap:.75rem;
  padding:.7rem .95rem;border-bottom:1px solid var(--line-soft)}
.modal-head h3{font-size:.9rem}
.modal-body{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:.35rem;
  padding:.85rem .95rem}
/* The chart is the point of the modal, so it takes the room and the caption the
   remainder — no min-height here, unlike the inline pane, because the sheet is
   always tall enough by construction. */
.modal-body .chart-fill{min-height:0}

/* ---------- tables ---------- */
.tscroll{overflow-x:auto}
/* No blanket min-width. A 520px floor forced a sideways scrollbar onto every
   table narrower than that, which is every table once panels sit in columns.
   Tables now shrink to their container; the few that genuinely cannot compress
   opt in with `.wide` and get the scrollbar only where it is warranted. */
table.data{border-collapse:collapse;width:100%;font-size:.8rem;min-width:0}
table.data.wide{min-width:520px}
table.data th{text-align:left;font-family:var(--f-mono);font-size:.63rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--ink-3);font-weight:500;padding:.55rem .7rem;border-bottom:1px solid var(--line);white-space:nowrap;
  background:var(--surface);position:sticky;top:0;z-index:1}
table.data td{padding:.45rem .7rem;border-bottom:1px solid var(--line-soft);color:var(--ink-2);vertical-align:middle}
table.data tbody tr:last-child td{border-bottom:0}
table.data tbody tr:hover td{background:var(--surface-2)}
table.data td.n{font-family:var(--f-mono);text-align:right;font-variant-numeric:tabular-nums;color:var(--ink);white-space:nowrap}
table.data td strong{color:var(--ink);font-weight:600}

/* ---------- chart containers ---------- */
.chart{width:100%;height:300px;min-height:180px}
.chart-sm{height:200px}
.chart-lg{height:420px}

/* ---------- misc ---------- */
.empty{padding:2.4rem 1rem;text-align:center;color:var(--ink-3);font-size:.85rem}
.empty strong{display:block;color:var(--ink);font-size:.95rem;margin-bottom:.3rem;font-weight:600}
.spinner{width:15px;height:15px;border:2px solid var(--line);border-top-color:var(--accent);
  border-radius:50%;display:inline-block;animation:spin .7s linear infinite;vertical-align:-2px}
@keyframes spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.spinner{animation-duration:2.5s}*{transition:none!important}}
.loading-row{display:flex;align-items:center;gap:.5rem;color:var(--ink-3);font-size:.82rem;padding:1.2rem}

.note{border-left:3px solid var(--accent);background:var(--accent-soft);padding:.7rem .9rem;
  border-radius:0 var(--radius) var(--radius) 0;font-size:.82rem;color:var(--ink-2)}
.note.warn{border-left-color:var(--watch);background:var(--watch-soft)}
.note.crit{border-left-color:var(--crit);background:var(--crit-soft)}
.note strong{color:var(--ink)}

.field{display:flex;flex-direction:column;gap:.28rem;min-width:0}
.field label{font-family:var(--f-mono);font-size:.66rem;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3)}
.field .hint{font-size:.72rem;color:var(--ink-3)}

.toasts{position:fixed;right:1rem;bottom:1rem;display:flex;flex-direction:column;gap:.5rem;z-index:100;max-width:min(420px,90vw)}
.toast{background:var(--surface);border:1px solid var(--line);border-left:3px solid var(--accent);
  border-radius:var(--radius);padding:.6rem .8rem;box-shadow:var(--shadow);font-size:.82rem;color:var(--ink-2)}
.toast strong{display:block;color:var(--ink);font-weight:600;margin-bottom:.1rem}
.toast.err{border-left-color:var(--crit)}
.toast.ok{border-left-color:var(--ok)}

/* progress / meter bar used by protection margins and health */
.meter{height:6px;background:var(--surface-3);border-radius:3px;overflow:hidden;min-width:60px}
.meter>span{display:block;height:100%;background:var(--accent);border-radius:3px}
.meter.sev-ok>span{background:var(--ok)}
.meter.sev-watch>span{background:var(--watch)}
.meter.sev-crit>span{background:var(--crit)}

/* =====================================================================
   CONTRACT-V2.md §5 — shared classes for the run/train-mode feature.
   Defined once here so dashboard/run/train/prediction/models/analysis
   all draw from the same visual language. Every colour below resolves
   through the existing custom properties — no new colour system.
   ===================================================================== */

/* ---------- mode switch: compact segmented control, topbar ---------- */
.mode-switch{display:flex;align-items:center;gap:0;background:var(--surface-2);
  border:1px solid var(--line);border-radius:var(--radius);padding:2px}
.mode-btn{appearance:none;border:0;background:none;color:var(--ink-2);cursor:pointer;
  font-family:var(--f-mono);font-size:.68rem;letter-spacing:.08em;text-transform:uppercase;
  font-weight:600;padding:.32rem .75rem;border-radius:calc(var(--radius) - 1px);white-space:nowrap}
.mode-btn:hover{color:var(--ink)}
/* Selection is driven off aria-pressed, not a plain class, so it stays keyboard/AT-honest. */
.mode-btn[aria-pressed="true"]{background:var(--accent);color:var(--on-accent)}

/* ---------- verdict banner: the headline conclusion of a run ---------- */
.verdict{display:flex;align-items:flex-start;gap:1rem;padding:1.15rem 1.35rem;
  background:var(--surface);border:1px solid var(--line);border-left:5px solid var(--line);
  border-radius:var(--radius)}
.verdict-icon{flex:0 0 auto;width:40px;height:40px;border-radius:50%;
  background:var(--surface-2);color:var(--ink-2);display:grid;place-items:center;font-size:1.2rem}
.verdict-main{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:.4rem}
.verdict-head{font-family:var(--f-display);font-size:1.2rem;font-weight:600;
  letter-spacing:-.01em;color:var(--ink)}
.verdict-sub{font-size:.86rem;color:var(--ink-2)}
.verdict-metrics{display:flex;flex-wrap:wrap;gap:1.5rem;margin-top:.55rem}

.verdict.sev-ok{border-left-color:var(--ok);background:var(--ok-soft)}
.verdict.sev-ok .verdict-icon{background:var(--surface);color:var(--ok)}
.verdict.sev-watch{border-left-color:var(--watch);background:var(--watch-soft)}
.verdict.sev-watch .verdict-icon{background:var(--surface);color:var(--watch)}
.verdict.sev-warning{border-left-color:var(--watch);background:var(--watch-soft)}
.verdict.sev-warning .verdict-icon{background:var(--surface);color:var(--watch)}
.verdict.sev-critical{border-left-color:var(--crit);background:var(--crit-soft)}
.verdict.sev-critical .verdict-icon{background:var(--surface);color:var(--crit)}

/* ---------- filter bar above the all-parameters table ---------- */
.filterbar{display:flex;align-items:center;gap:1.2rem;flex-wrap:wrap;
  padding:.7rem .95rem;border-bottom:1px solid var(--line-soft)}
.filter-group{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap}
.filter-group .eyebrow{margin-right:.2rem}
.chip-toggle{appearance:none;border:1px solid var(--line);background:var(--surface);color:var(--ink-2);
  font-family:var(--f-mono);font-size:.72rem;padding:.22rem .58rem;border-radius:3px;cursor:pointer;
  white-space:nowrap}
.chip-toggle:hover{border-color:var(--ink-3);color:var(--ink)}
.chip-toggle.on{background:var(--accent-soft);border-color:var(--accent);color:var(--accent-ink)}

/* ---------- flagged rows in the all-parameters table ---------- */
/* Inset box-shadow (not border) so it doesn't disturb cell padding, and so it
   layers cleanly with the existing tbody tr:hover td background swap below. */
table.data tr.row-flag td:first-child{box-shadow:inset 3px 0 0 0 var(--line)}
table.data tr.row-flag.sev-watch td:first-child{box-shadow:inset 3px 0 0 0 var(--watch)}
table.data tr.row-flag.sev-warning td:first-child{box-shadow:inset 3px 0 0 0 var(--watch)}
table.data tr.row-flag.sev-critical td:first-child{box-shadow:inset 3px 0 0 0 var(--crit)}

/* ---------- inline magnitude bar inside a table cell (z-score) ---------- */
.cellbar{position:relative;display:inline-block;width:64px;height:5px;vertical-align:middle;
  background:var(--surface-3);border-radius:3px;overflow:hidden}
.cellbar-fill{display:block;height:100%;background:var(--accent);border-radius:3px}
.cellbar.sev-watch .cellbar-fill{background:var(--watch)}
.cellbar.sev-warning .cellbar-fill{background:var(--watch)}
.cellbar.sev-critical .cellbar-fill{background:var(--crit)}

/* ---------- observations feed — items pair with the existing .stripe.sev-* ---------- */
.obs-list{display:flex;flex-direction:column;gap:.55rem}
.obs-item{display:flex;flex-direction:column;gap:.25rem;padding:.6rem .8rem}

/* ---------- compact key/value list for evidence blocks ---------- */
.kv{display:flex;flex-direction:column;gap:.32rem}
.kv-row{display:flex;align-items:baseline;gap:.6rem;font-size:.8rem}
.kv-k{flex:0 0 auto;min-width:130px;font-family:var(--f-mono);font-size:.66rem;
  letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}
.kv-v{flex:1 1 auto;min-width:0;color:var(--ink-2);font-family:var(--f-mono);
  font-variant-numeric:tabular-nums;word-break:break-word}

/* ---------- file drop target — Data Upload today, Run/Train reuse it ---------- */
.dropzone{border:2px dashed var(--line);border-radius:var(--radius);padding:2rem 1rem;
  text-align:center;cursor:pointer;background:var(--surface-2)}
.dropzone.drag{border-color:var(--accent)}

/* ---------- train-vs-test metric comparison (Train tab) ---------- */
.metric-cmp{display:grid;grid-template-columns:1fr;gap:.9rem}
@media(min-width:760px){.metric-cmp{grid-template-columns:repeat(2,minmax(0,1fr))}}

/* ---------- run history list ---------- */
.runlist{display:flex;flex-direction:column;gap:.4rem}
.runlist-item{display:flex;align-items:center;justify-content:space-between;gap:.75rem;
  padding:.55rem .8rem;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);cursor:pointer}
.runlist-item:hover{border-color:var(--ink-3)}
.runlist-item.on{border-color:var(--accent);background:var(--accent-soft)}

/* ===================================================================
   Density layer
   -------------------------------------------------------------------
   A control-room screen should answer its question without scrolling.
   Stacking full-width panels turned the dashboard into a metre of page,
   so panels now flow into columns and any list that can grow without
   bound scrolls inside its own panel instead of pushing the page down.
   =================================================================== */

/* Numeric column headers must sit over their right-aligned values. The base
   `table.data th` rule left-aligns every header, which put "% OF ALARM" at the
   far left of a column of right-aligned numbers. */
table.data th.n{text-align:right}

/* Tighter table for panels sharing a row. Cuts horizontal padding roughly in
   half, which buys back enough width for a six-column protection table to fit
   without a sideways scrollbar. */
table.data.dense th,table.data.dense td{padding:.34rem .45rem}
table.data.dense{font-size:.76rem}

/* Long labels truncate rather than wrapping to four lines. The full text stays
   reachable via the cell's title attribute, so nothing is actually lost - a
   protection name wrapping over four rows costs more than it explains. */
table.data td.clip{max-width:0;width:40%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Panel columns on an explicit track count rather than `auto-fit`.
   `auto-fit` derives the number of columns from a min width, which means the
   track count is unknowable from CSS and `grid-column: span 2` lands somewhere
   different at every viewport. An explicit count makes spanning predictable, so
   a panel can be given the width its content actually needs.

   `align-items:start` is the other load-bearing part: without it every panel in
   a row stretches to match the tallest, which produced the huge empty "Latest
   run" and "Active findings" boxes.

   Two tracks is the ceiling. A third made every panel a third of the viewport -
   narrower than a six-column table or a paragraph of finding detail wants - and
   it makes an even 50/50 pairing impossible to express. */
.cols{display:grid;gap:.85rem;align-items:start;grid-template-columns:minmax(0,1fr)}
@media(min-width:900px){.cols{grid-template-columns:repeat(2,minmax(0,1fr))}}

/* Retained as an explicit alias: a tab that says `cols cols-wide` is declaring
   "these panels need a readable measure", which is worth stating even though
   the track count now matches `.cols`. */
.cols-wide{grid-template-columns:minmax(0,1fr)}
@media(min-width:900px){.cols-wide{grid-template-columns:repeat(2,minmax(0,1fr))}}

/* Tile strips want many small cells, not the panel track count. */
.cols-narrow{grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}
@media(min-width:900px){.cols-narrow{grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}}
@media(min-width:1500px){.cols-narrow{grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}}

/* Width opt-ins for panels whose content does not fit one track: a six-column
   table, a chart, a block of prose. `span-2` only engages once a second track
   exists, so a phone-width layout can never overflow. */
.span-all{grid-column:1 / -1}
@media(min-width:900px){.span-2{grid-column:span 2}}

/* Height-capped, internally scrolling panel bodies. A findings list or a
   protection table has no natural length limit, so it gets a viewport-relative
   cap and scrolls in place rather than filling the page.
   Not scoped to .panel-body: a master/detail split needs the same cap on its
   list column, which is a plain div inside the body rather than the body itself. */
.scroll-sm,.scroll-md,.scroll-lg{overflow-y:auto;overscroll-behavior:contain}
.scroll-sm{max-height:min(240px,32vh)}
.scroll-md{max-height:min(360px,42vh)}
.scroll-lg{max-height:min(520px,58vh)}

/* Keep table headers visible while the body scrolls under them. */
.panel-body[class*="scroll-"] table.data th{position:sticky;top:0;z-index:1;
  background:var(--surface-2)}

/* A restrained scrollbar. The default Windows bar is heavy enough to read as a
   UI element in its own right, which fights the panel borders. */
.panel-body[class*="scroll-"]{scrollbar-width:thin;scrollbar-color:var(--line) transparent}
.panel-body[class*="scroll-"]::-webkit-scrollbar{width:9px;height:9px}
.panel-body[class*="scroll-"]::-webkit-scrollbar-track{background:transparent}
.panel-body[class*="scroll-"]::-webkit-scrollbar-thumb{background:var(--line);
  border-radius:6px;border:2px solid var(--surface)}
.panel-body[class*="scroll-"]:hover::-webkit-scrollbar-thumb{background:var(--ink-3)}

/* Compact metric row — the table-shaped replacement for full-width health cards.
   Label, magnitude bar, value, status: four columns on one 26px line.
   The last two tracks are FIXED widths, not `auto`. Each row is its own grid
   container, so `auto` sized each row's value and pill columns independently -
   an "82 WATCH" row and a "100 OK" row resolved to different widths, and the
   meter column started at a different x on every line. Fixed trailing tracks
   are what makes the column edges line up down the panel. */
.mrow{display:grid;grid-template-columns:minmax(72px,1fr) minmax(44px,1.3fr) 2.6rem 5rem;
  align-items:center;gap:.5rem;padding:.28rem .55rem;border-bottom:1px solid var(--line-soft);
  font-size:.78rem;min-height:26px}
/* Status cell sits hard right so the pills form a clean edge regardless of label length. */
.mrow>:last-child{justify-self:end}
.mrow:last-child{border-bottom:0}
.mrow.clickable{cursor:pointer}
.mrow.clickable:hover{background:var(--surface-2)}
.mrow-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink-2)}
.mrow-val{font-family:var(--f-mono);font-variant-numeric:tabular-nums;text-align:right;
  color:var(--ink);font-size:.8rem}
.mrow .meter{height:5px}

/* "Lower is better" / "higher is better" direction hint. Severity colour means
   severity here, so a direction cue must be typographic, not coloured. */
.dir-hint{font-family:var(--f-mono);font-size:.6rem;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ink-3);white-space:nowrap}

/* ---------- master/detail split inside a single panel ----------
   A list on the left, the selected item's evidence chart on the right. Stacks
   below 1100px, because two 480px halves are worse than either at full width. */
.split50{display:grid;grid-template-columns:minmax(0,1fr)}
.split50>*{min-width:0}
@media(min-width:1100px){
  .split50{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .split50>*+*{border-left:1px solid var(--line-soft)}
}

/* The detail half of a split. Grid items stretch by default, so this column is
   already as tall as the list beside it; making it a flex column hands every
   pixel the labels don't need to the chart, instead of leaving dead space under
   a fixed-height one. The min-height carries the stacked (narrow) case, where the
   row height comes from content rather than from the list. */
.detail-pane{display:flex;flex-direction:column;min-height:0;padding:.8rem}
.chart-fill{flex:1 1 auto;min-height:260px}

/* A selectable row in such a list. Selection is a background change rather than
   a border, so picking an item cannot shift the layout of its neighbours. */
.pick{cursor:pointer}
.pick:hover{background:var(--surface-2)}
.pick.on{background:var(--accent-soft)}

/* Plain-language explainer under a panel heading. Capped so it stays a caption
   rather than becoming a paragraph of documentation. */
.explain{font-size:.78rem;line-height:1.45;color:var(--ink-3);max-width:62ch}
.panel-body>.explain:first-child{margin-bottom:.6rem}

/* The view's 3rem bottom padding existed to clear the toast stack; with panels
   now in columns it is dead space at the end of every page. */
.view{padding-bottom:1.5rem}

/* ---------- recommendations (dashboard, paired with Subsystem health) ----------
   One card per recommendation: priority + kind + horizon on the first line, the
   evidence ("why") and the response ("action") underneath, tags last. Priority is
   carried by the left stripe so the eye can scan the column without reading. */
.mrow.on{background:var(--accent-soft)}
.mrow.on .mrow-label{color:var(--ink);font-weight:600}
.reco-list{display:flex;flex-direction:column;gap:.55rem;padding:.7rem .8rem .9rem}
.reco{border:1px solid var(--line-soft);border-left-width:3px;border-radius:8px;background:var(--surface);
  padding:.6rem .75rem .65rem}
.reco.pr-high{border-left-color:var(--crit)}
.reco.pr-medium{border-left-color:var(--copper)}
.reco.pr-low{border-left-color:var(--watch)}
.reco.pr-none{border-left-color:var(--ok)}
.reco-head{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;margin-bottom:.35rem}
.reco-head strong{font-size:.86rem;color:var(--ink);line-height:1.3;flex:1 1 200px;min-width:0}
.reco-kind{font-family:var(--f-mono);font-size:.62rem;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-3)}
.reco-horizon{font-family:var(--f-mono);font-size:.66rem;color:var(--ink-3);white-space:nowrap;margin-left:auto}
.pill-copper{background:var(--copper-soft);color:var(--copper)}
.reco-why,.reco-action{font-size:.78rem;line-height:1.5;color:var(--ink-2)}
.reco-why{margin-bottom:.35rem}
.reco-action{display:grid;grid-template-columns:auto 1fr;gap:.45rem;color:var(--ink)}
.reco-action .reco-arrow{color:var(--copper);font-weight:700}
.reco-foot{display:flex;align-items:center;gap:.35rem;flex-wrap:wrap;margin-top:.5rem}
.reco-foot .tag-chip{cursor:pointer}
.reco-foot .tag-chip:hover{border-color:var(--accent);color:var(--accent)}
.reco-foot .btn{margin-left:auto}
.reco-sub{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;padding:.55rem .8rem 0;font-size:.78rem;color:var(--ink-3)}
.reco-sub .spacer{flex:1 1 auto}
