/* Ridgeline Signal app — ported from reference/prototype.html v0.2.
   Brand tokens are the single source of truth (see /docs/brand-tokens.md).

   FONTS ARE SELF-HOSTED — fix register #6d closed. The woff2 files are latin subsets
   served from our own origin; the app makes NO third-party call at runtime, which is what
   a client-facing artefact requires (and what the prototype's Google CDN link violated).
   Both families are SIL Open Font Licence 1.1 — see /data-licences.
   font-display:swap so text renders immediately rather than blocking on the font. */
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/spacegrotesk-600.woff2') format('woff2');
}
@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/sora-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/sora-600.woff2') format('woff2');
}
:root{
  --navy:#0E1B2E; --slate:#3B5067; --cobalt:#3D6BFF; --paper:#F4F6FA;
  --green:#1F8A5B; --rust:#C0492F; --hair:#E3E8F0; --muted:#6F8298;
  /* validated colour-vision-safe SERIES palette (never the brand accent as data) */
  --coal:#B5482E; --gas:#D98A1E; --batt:#3D6BFF; --renew:#1F8A5B;
  --bg:#F4F6FA; --card:#FFFFFF; --ink:#0E1B2E; --ink2:#3B5067; --ink3:#6F8298;
  --line:#E3E8F0; --grid:#EDF1F6; --chip:#EEF2F8; --accent:#3D6BFF;
  --pos:#1F8A5B; --neg:#C0492F;
  --shadow:0 1px 2px rgba(14,27,46,.05),0 8px 24px rgba(14,27,46,.06);
}
html[data-theme="dark"]{
  --bg:#0E1B2E; --card:#16273D; --ink:#F4F6FA; --ink2:#A9B7C8; --ink3:#7E90A6;
  --line:#24374F; --grid:#1B2C44; --chip:#1C2E47; --accent:#6E93FF;
  --coal:#D9603F; --gas:#E6AE42; --batt:#6E93FF; --renew:#3BB07E;
  --pos:#3BB07E; --neg:#E06B4C;
  --shadow:0 1px 2px rgba(0,0,0,.3),0 10px 30px rgba(0,0,0,.35);
}
*{box-sizing:border-box}
html,body{margin:0}
body{background:var(--bg);color:var(--ink);
  font-family:'Sora','Inter',system-ui,-apple-system,Segoe UI,sans-serif;
  font-size:15px;line-height:1.5;-webkit-font-smoothing:antialiased;transition:background .25s,color .25s}
h1,h2,h3,.wm,.tag,.big,.seg,.tab,.prod b{
  font-family:'Space Grotesk','SF Pro Display',system-ui,sans-serif}
a{color:var(--accent);text-decoration:none}
.wrap{max-width:1140px;margin:0 auto;padding:0 22px}

/* header */
header{position:sticky;top:0;z-index:40;background:color-mix(in srgb,var(--bg) 86%,transparent);backdrop-filter:blur(10px);border-bottom:1px solid var(--line)}
/* padding-block only — .bar sits on the same element as .wrap, and a `padding:14px 0`
   shorthand was zeroing .wrap's 22px inline padding (header flush to the viewport edge
   below ~1180px; UI review 19 Jul 2026). */
.bar{display:flex;align-items:center;gap:18px;padding-block:14px}
.wm{font-weight:600;font-size:19px;letter-spacing:-.022em;color:var(--ink);display:flex;align-items:center;gap:10px}
.wm .mk{width:24px;height:24px;flex:0 0 auto}
.prod{display:flex;flex-direction:column;gap:2px;border-left:1px solid var(--line);padding-left:16px}
.prod b{font-weight:600;font-size:14px;letter-spacing:.02em}
.prod span{font-size:11px;color:var(--ink3)}
.grow{flex:1}
.snap{font-size:11.5px;color:var(--ink3);text-align:right;line-height:1.35}
.snap b{color:var(--ink2);font-weight:600}
.tbtn{border:1px solid var(--line);background:var(--card);color:var(--ink2);width:38px;height:34px;border-radius:9px;cursor:pointer;font-size:15px;display:grid;place-items:center}
.tbtn:hover{border-color:var(--accent);color:var(--accent)}

/* freshness strip (pipeline-health preview) */
.fresh{display:flex;align-items:center;gap:10px;flex-wrap:wrap;font-size:12px;color:var(--ink3);padding:10px 0 0}
.fresh .dot{width:8px;height:8px;border-radius:50%;background:var(--pos)}
.fresh .stale{background:var(--gas)}
.fresh a{margin-left:auto}

/* lens switcher */
.lensrow{display:flex;align-items:center;gap:14px;padding:16px 0 4px;flex-wrap:wrap}
.lenslab{font-size:11px;text-transform:uppercase;letter-spacing:.16em;color:var(--ink3);font-weight:600}
.seg{display:flex;background:var(--chip);border:1px solid var(--line);border-radius:12px;padding:4px;gap:2px;flex-wrap:wrap}
.seg button{border:0;background:transparent;color:var(--ink2);font-weight:600;font-size:13px;padding:8px 14px;border-radius:9px;cursor:pointer;display:flex;align-items:center;gap:8px;transition:.15s}
.seg button .cdot{width:9px;height:9px;border-radius:50%}
.seg button[aria-pressed="true"]{background:var(--card);color:var(--ink);box-shadow:var(--shadow)}
.seg button:hover{color:var(--ink)}

/* headline */
.head{margin:16px 0 8px;background:linear-gradient(180deg,var(--card),color-mix(in srgb,var(--card) 92%,var(--bg)));border:1px solid var(--line);border-left:4px solid var(--accent);border-radius:14px;padding:18px 20px;box-shadow:var(--shadow)}
.head .k{font-size:11px;text-transform:uppercase;letter-spacing:.18em;font-weight:600;margin-bottom:6px;color:var(--accent)}
.head h2{margin:0;font-size:20px;line-height:1.32;font-weight:600;letter-spacing:.01em}
.head h2 em{font-style:normal;color:var(--accent)}

/* kpi tiles */
.kpis{display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin:14px 0}
.kpi{background:var(--card);border:1px solid var(--line);border-radius:13px;padding:13px 14px;box-shadow:var(--shadow);min-width:0}
.kpi .lab{font-size:10.5px;text-transform:uppercase;letter-spacing:.09em;color:var(--ink3);font-weight:600;line-height:1.3;min-height:26px}
.kpi .big{font-weight:600;font-size:23px;letter-spacing:-.01em;margin:5px 0 3px;color:var(--ink)}
.kpi .sub{font-size:11.5px;color:var(--ink3);display:flex;align-items:center;gap:5px}
.kpi.na .big{color:var(--ink3);font-weight:500}

/* module card */
.mod{background:var(--card);border:1px solid var(--line);border-radius:16px;padding:20px;margin:16px 0;box-shadow:var(--shadow)}
.mod-h{display:flex;align-items:flex-start;gap:14px;margin-bottom:6px}
.mod-h .n{font-weight:700;font-size:12px;color:var(--accent);background:var(--chip);border:1px solid var(--line);width:28px;height:28px;border-radius:8px;display:grid;place-items:center;flex:0 0 auto}
.mod-h h3{margin:0;font-size:17px;font-weight:600;letter-spacing:.01em}
.mod-h p{margin:3px 0 0;font-size:12.5px;color:var(--ink3);max-width:66ch}
.tabs{display:flex;gap:2px;background:var(--chip);border:1px solid var(--line);border-radius:9px;padding:3px}
.tab{border:0;background:transparent;color:var(--ink3);font-size:11.5px;font-weight:600;padding:5px 10px;border-radius:6px;cursor:pointer}
.tab[aria-pressed="true"]{background:var(--card);color:var(--ink);box-shadow:var(--shadow)}

/* PROVENANCE CHIP (fix register #7 — source + as-at + method, at point of use) */
.prov{display:inline-flex;align-items:center;gap:8px;margin-top:12px;padding:6px 10px 6px 8px;background:var(--chip);border:1px solid var(--line);border-radius:999px;font-size:11.5px;color:var(--ink2)}
.prov .badge{font-family:'Space Grotesk',system-ui,sans-serif;font-weight:600;font-size:10px;letter-spacing:.06em;text-transform:uppercase;padding:2px 7px;border-radius:999px}
.prov .badge.sourced{background:color-mix(in srgb,var(--pos) 16%,transparent);color:var(--pos)}
.prov .badge.derived{background:color-mix(in srgb,var(--accent) 16%,transparent);color:var(--accent)}
.prov .badge.heuristic{background:color-mix(in srgb,var(--gas) 20%,transparent);color:var(--gas)}
/* `sample` = illustrative parameters, NOT market data. Deliberately the loudest badge:
   a sample number sitting unmarked beside sourced ones is the defect the review called
   self-refuting, so it must be impossible to mistake at a glance. */
.prov .badge.sample{background:color-mix(in srgb,var(--rust) 18%,transparent);color:var(--rust);
  border:1px dashed color-mix(in srgb,var(--rust) 55%,transparent)}
.prov.sample-chip{border-color:color-mix(in srgb,var(--rust) 35%,transparent)}
.tv td.est{color:var(--gas)}
.prov .src{font-weight:600;color:var(--ink)}
.prov .asat{color:var(--ink3)}
.prov a{color:var(--ink3);border-bottom:1px dotted var(--ink3)}

/* chart */
.chartwrap{position:relative;margin-top:12px}
svg.chart{display:block;width:100%;height:auto;overflow:visible}
.axlab{font-size:10.5px;fill:var(--ink3);font-family:'Sora',system-ui,sans-serif}
.gridln{stroke:var(--grid);stroke-width:1}
.legend{display:flex;flex-wrap:wrap;gap:14px;margin-top:10px;font-size:12px;color:var(--ink2)}
.legend span{display:inline-flex;align-items:center;gap:6px}
.legend i.ln{height:3px;width:16px;border-radius:2px;display:inline-block}
.readout{display:flex;flex-wrap:wrap;gap:10px;margin-top:12px}
.pill{background:var(--chip);border:1px solid var(--line);border-radius:999px;padding:6px 13px;font-size:12.5px;color:var(--ink2)}
.pill b{font-family:'Space Grotesk',system-ui,sans-serif;font-weight:600;color:var(--ink)}
.pill.good b{color:var(--pos)} .pill.bad b{color:var(--neg)}
.action{margin-top:12px;font-size:12.5px;color:var(--ink2);padding-left:12px;border-left:2px solid var(--accent)}
.action b{color:var(--ink)}

/* table */
.tv{width:100%;border-collapse:collapse;margin-top:12px;font-size:12.5px;display:none}
.tv.show{display:table}
.tv th,.tv td{text-align:right;padding:7px 10px;border-bottom:1px solid var(--line)}
.tv th:first-child,.tv td:first-child{text-align:left}
.tv th{color:var(--ink3);font-weight:600;text-transform:uppercase;font-size:10.5px;letter-spacing:.06em}
.tv td b{font-family:'Space Grotesk',system-ui,sans-serif}

/* tooltip */
#tt{position:fixed;z-index:80;pointer-events:none;background:var(--navy);color:#fff;font-size:12px;border-radius:8px;padding:8px 10px;box-shadow:0 8px 24px rgba(0,0,0,.35);opacity:0;transition:opacity .1s;max-width:240px;line-height:1.4}
html[data-theme="dark"] #tt{background:#000}
#tt b{font-family:'Space Grotesk',system-ui,sans-serif}

footer{margin:30px 0 50px;padding-top:22px;border-top:1px solid var(--line);font-size:12px;color:var(--ink3)}
footer h4{font-family:'Space Grotesk',system-ui,sans-serif;font-size:11px;text-transform:uppercase;letter-spacing:.14em;color:var(--ink2);margin:0 0 7px}
.fgrid{display:grid;grid-template-columns:1.3fr 1fr;gap:26px;margin-bottom:20px}
.foot-b{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;border-top:1px solid var(--line);padding-top:14px}
.badge2{font-size:10.5px;text-transform:uppercase;letter-spacing:.14em;color:var(--ink3)}
@media(max-width:920px){.kpis{grid-template-columns:repeat(3,1fr)}.fgrid{grid-template-columns:1fr}}
@media(max-width:560px){.kpis{grid-template-columns:repeat(2,1fr)}}

/* Methodology panel (backcast predicted-vs-actual) */
/* [hidden] is a UA-stylesheet rule, so ANY author `display` here beats it and the panel
   renders on load — open and empty, because #paneBody is only filled by openPanel().
   This one line is what kept the methodology dialog shut. Global guard below stops the
   same trap being set again by the next component that pairs `hidden` with a display. */
[hidden]{display:none !important}
.pane-bg{position:fixed;inset:0;background:rgba(10,12,16,.62);display:flex;align-items:flex-start;justify-content:center;overflow-y:auto;padding:40px 16px;z-index:50}
.pane{position:relative;background:var(--bg);border:1px solid var(--line);border-radius:10px;max-width:820px;width:100%;padding:30px 34px 34px}
.pane-x{position:absolute;top:10px;right:14px;background:none;border:0;color:var(--ink3);font-size:24px;line-height:1;cursor:pointer}
.pane-x:hover{color:var(--ink)}
.pane h3{margin:0 0 12px;font-size:19px}
.pane h4{margin:22px 0 6px;font-size:13px;text-transform:uppercase;letter-spacing:.12em;color:var(--ink2)}
.pane p{margin:0 0 10px;font-size:13.5px;line-height:1.6}
.pane ul{margin:0;padding-left:18px;font-size:13px;line-height:1.6}
.pane li{margin-bottom:7px}
/* .tv is display:none by default because elsewhere it is a toggled table view; inside the
   methodology panel it is always shown. */
.pane .tv{width:100%;font-size:12.5px;margin:6px 0 4px;display:table}
.pane .sub{color:var(--ink3);font-size:12px}
.proj .est{font-size:10.5px;text-transform:uppercase;letter-spacing:.1em;color:var(--ink3);white-space:nowrap}
.proj{display:flex;justify-content:space-between;align-items:center;gap:12px}

/* ---------- early-access gate ---------- */
/* Modules 2-5 are removed from the DOM flow when gated — not merely blurred. A blurred
   overlay still ships the numbers to the browser; this ships nothing. */
body.is-gated #m2, body.is-gated #m3, body.is-gated #m4, body.is-gated #m5 { display: none; }
.gate {
  margin: 28px auto 60px;
  background: var(--paper, #f6f8fc);
  border: 1px solid var(--line);
  border-left: 3px solid var(--cobalt, #3D6BFF);
  border-radius: 0 12px 12px 0;
  padding: 30px 34px 34px;
  max-width: 720px;
}
.gate-eyebrow {
  display: inline-block; font-size: 10.5px; text-transform: uppercase;
  letter-spacing: .16em; font-weight: 600; color: #fff;
  background: var(--cobalt, #3D6BFF); border-radius: 999px;
  padding: 3px 11px; margin: 0 0 12px;
}
.gate h2 { margin: 0 0 10px; font-size: 21px; }
.gate-line { font-size: 14px; line-height: 1.65; margin: 0 0 10px; }
.gate-note { color: var(--ink3); font-size: 13px; }
.gate-fields { display: grid; grid-template-columns: 1fr; gap: 4px; margin: 18px 0 16px; }
.gate-fields label { font-size: 12px; font-weight: 600; color: var(--ink2); margin-top: 8px; }
.gate-opt { font-weight: 400; color: var(--ink3); text-transform: none; letter-spacing: 0; }
.gate-fields input {
  font: inherit; font-size: 14px; padding: 9px 12px;
  border: 1px solid var(--line); border-radius: 8px; background: #fff; color: var(--ink);
}
.gate-fields input:focus-visible,
#gateBtn:focus-visible { outline: 2px solid var(--cobalt, #3D6BFF); outline-offset: 2px; }
#gateBtn { cursor: pointer; border: 0; }
#gateBtn[disabled] { opacity: .6; cursor: progress; }
.gate-msg { color: var(--coal, #b3261e); font-size: 13px; margin: 10px 0 0; min-height: 1em; }
@media (min-width: 620px) {
  .gate-fields { grid-template-columns: max-content 1fr; align-items: center; }
  .gate-fields label { margin-top: 0; }
}

/* ---------- short-form disclaimers (single-sourced from disclaimers.js) ----------
   Muted but legible: --ink2 (slate) on paper is ~7.7:1 light / ~8.8:1 dark — comfortably
   WCAG AA. Deliberately NOT --ink3 (#6F8298), which is only ~3.65:1 and fails AA for body
   text: a disclaimer nobody can read protects nobody. Never cobalt. */
.disclaimer{
  max-width:78ch;margin:26px auto 40px;padding:14px 16px;
  font-size:13px;line-height:1.55;color:var(--ink2);
  background:var(--card);border:1px solid var(--line);border-radius:10px;
}
.disclaimer-compact{
  margin:6px 0 0;padding:0 0 10px;
  font-size:12.5px;line-height:1.5;color:var(--ink2);
}
@media (max-width:420px){
  .disclaimer{margin:18px auto 28px;padding:12px 13px;font-size:12.5px}
  .disclaimer-compact{font-size:12px}
}

/* ---------- chart skeletons (UI review 19 Jul 2026) ----------
   Module SVGs reserve their space via viewBox but sit empty until app.js fetches data
   and injects markup — which read as large blank white boxes ("broken"), not "loading".
   An :empty chart gets a quiet shimmer; the moment app.js writes any child node the
   selector stops matching and the skeleton disappears. No JS involved, dark-theme safe. */
svg.chart:empty{
  background:linear-gradient(100deg,var(--grid) 35%,var(--chip) 50%,var(--grid) 65%);
  background-size:280% 100%;
  border-radius:10px;
  animation:sig-skeleton 1.5s ease-in-out infinite;
}
@keyframes sig-skeleton{
  0%{background-position:120% 0}
  100%{background-position:-60% 0}
}
@media (prefers-reduced-motion:reduce){
  svg.chart:empty{animation:none;background:var(--grid)}
}
