/* IBM Plex, vendored locally under OFL 1.1 (see vendor/fonts/OFL.txt).
   Latin subset only, fetched 2026-08-29. Sans is the VARIABLE face - Google
   Fonts serves one file for 400/600/700, which it only does for a variable
   family - so the weight range is declared rather than one face per weight.
   Relative paths: nothing here reaches the network at render time. */
@font-face{font-family:"IBM Plex Sans";src:url("vendor/fonts/ibm-plex-sans-var-latin.woff2") format("woff2");
  font-weight:100 700;font-style:normal;font-display:swap}
@font-face{font-family:"IBM Plex Mono";src:url("vendor/fonts/ibm-plex-mono-400-latin.woff2") format("woff2");
  font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"IBM Plex Mono";src:url("vendor/fonts/ibm-plex-mono-600-latin.woff2") format("woff2");
  font-weight:600;font-style:normal;font-display:swap}

/* ---- THEME TOKENS ---------------------------------------------------------
   LIGHT IS THE DEFAULT (leadership, 2026-09-01). The tokens are still organised
   dark-on-:root with light as the override, and that is deliberate: the DEFAULT
   is set by data-theme="light" living in the <html> tag itself, so a browser
   with JS off or site data blocked still gets it. Moving the light values onto
   :root would make the tokens the default and leave the attribute doing nothing
   - which reads the same and behaves differently the moment somebody picks dark.

   EVERY VALUE BELOW IS MEASURED, not picked by eye. Both palettes clear WCAG AA
   (4.5:1) for text and 3:1 for the two UI-only greys, against the WORST of the
   three backgrounds each is ever placed on. The checker that produced them is
   tests/test_theme_contrast.mjs, which fails the build rather than trusting this
   comment.

   TWO THINGS THAT ARE EASY TO GET WRONG AND ARE DELIBERATE HERE:

   1. --signal and --signal-ink are DIFFERENT TOKENS. The amber is used both as a
      fill (a button, a chip) and as small text (a sort arrow, a term label). On a
      light background those pull in opposite directions: the fill must stay bright
      to own attention, and the text must go dark enough to read. One token cannot
      be both - #F0B429 as text on white is 3.87:1 and fails. Split, not compromised.

   2. DEPTH INVERTS between the themes. In dark, ground is the darkest and a raised
      card is the lightest. In light that order flips: the PAGE is the greyest thing
      and a card is white. Copying the dark ordering into light makes every card
      look like a hole punched in the page.

   --danger was #E5484D and measured 4.28:1 on --raised, which FAILED AA in the
   theme that has been shipping. Corrected to #E65156 - a change nobody will see,
   on the colour that reports things going wrong. */
:root{
  --ground:#0A0E14; --surface:#10151D; --raised:#171E29;
  --line:#232D3B; --line-strong:#33415A;
  --ink:#E8EEF6; --ink-2:#9DAEC2; --ink-3:#5F6F84;
  --signal:#F0B429; --signal-ink:#F0B429; --signal-hover:#FFC94D;
  --measure:#58A6E8; --cap:#8296AD;
  --danger:#E65156; --ok:#4CA76B;
  --on-signal:#141414;            /* text ON the amber fill, both themes */
  --danger-tint:rgba(230,81,86,.08);
  --ok-tint:rgba(76,167,107,.16); --signal-tint:rgba(240,180,41,.16);
  --scrim:rgba(4,7,11,.72);
  --shadow:0 24px 60px rgba(0,0,0,.5);
  --paper:#FFFFFF;                /* things that are white in BOTH themes */
}

[data-theme="light"]{
  --ground:#EDF1F6; --surface:#F7F9FC; --raised:#FFFFFF;
  --line:#D7DFE9; --line-strong:#AEBBCB;
  --ink:#0F151D; --ink-2:#44556A; --ink-3:#5E6F85;
  --signal:#F0B429; --signal-ink:#8A6309; --signal-hover:#D99C12;
  --measure:#0F62A8; --cap:#5C6F86;
  --danger:#B92126; --ok:#2C7A4E;
  --danger-tint:rgba(185,33,38,.07);
  --ok-tint:rgba(44,122,78,.11); --signal-tint:rgba(240,180,41,.20);
  --scrim:rgba(24,32,44,.42);
  --shadow:0 18px 44px rgba(24,40,66,.16);
}
:root{
  --sans:"IBM Plex Sans","Segoe UI Variable","Segoe UI",system-ui,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,"Cascadia Code",Consolas,monospace;
  --hd:48px;
  --hdr-h:48px;   /* the sticky header. TWO rows on a phone - see the 560 query */
}
*{box-sizing:border-box}
/* A class selector beats the user-agent [hidden] rule, so any element given
   display: by a class - .authform, .authform label, .who - stayed VISIBLE while
   carrying the hidden attribute. Caught by looking at the rendered login page:
   both forms and the authenticator field were on screen at once. */
[hidden]{display:none !important}
html,body{margin:0;height:100%}
body{background:var(--ground);color:var(--ink);font-family:var(--sans);
     line-height:1.55;overflow:hidden;-webkit-font-smoothing:antialiased}
a{color:var(--measure)}

header.top{height:var(--hd);background:var(--surface);
  border-bottom:1px solid var(--line);display:flex;align-items:center;
  gap:18px;padding:0 16px;position:sticky;top:0;z-index:20}
.brand{display:flex;align-items:center;gap:10px}
.wordmark{font:700 15px/1 var(--sans);letter-spacing:.14em;color:var(--ink)}
nav.tabs{display:flex;gap:2px;margin-left:10px}
nav.tabs button{appearance:none;background:transparent;border:0;color:var(--ink-2);
  font:600 13.5px/1 var(--sans);padding:0 14px;height:var(--hd);
  border-bottom:2px solid transparent;cursor:pointer}
nav.tabs button:hover:not([disabled]){color:var(--ink)}
nav.tabs button.active{color:var(--ink);border-color:var(--signal)}
.chip{font:600 10px/1 var(--mono);letter-spacing:.08em;padding:5px 8px;
  border-radius:3px;border:1px solid var(--line-strong);color:var(--ink-2)}
.chip.restricted{border-color:var(--danger);color:var(--danger)}

main{height:calc(100dvh - var(--hd))}
.pane{display:none;height:100%}
.pane.active{display:block}
.pane.scroll{overflow:auto}
.pane.frame.active{display:flex;flex-direction:column}
iframe.embed{width:100%;height:100%;border:0;flex:1 1 auto;background:var(--ground)}

.wrap{max-width:960px;margin:0 auto;padding:56px 26px 90px}
h1{font:700 clamp(28px,4vw,44px)/1.08 var(--sans);letter-spacing:-.02em;margin:0;max-width:20ch}
p.deck{color:var(--ink-2);font-size:18px;max-width:62ch;margin:18px 0 0}
.eyebrow{font:600 11px/1 var(--mono);letter-spacing:.2em;text-transform:uppercase;
  color:var(--ink-3);margin:0 0 14px}
.coverage{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));
  gap:1px;background:var(--line);border:1px solid var(--line);margin-top:44px}
.cell{background:var(--surface);padding:16px 18px}
.cell .v{font:600 26px/1.1 var(--mono);font-variant-numeric:tabular-nums;color:var(--ink)}
.cell .v.zero{color:var(--ink-3)}
.cell .k{font:600 11px/1.3 var(--mono);letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-2);margin-top:8px}
.cell .prov{font:400 11px/1.4 var(--sans);color:var(--ink-3);margin-top:5px}
.btnrow{display:flex;gap:10px;margin-top:34px;flex-wrap:wrap}
.btn{background:transparent;color:var(--ink);border:1px solid var(--line-strong);
  font:600 13px/1 var(--sans);padding:11px 16px;border-radius:4px;cursor:pointer}
.btn:hover{border-color:var(--signal);color:var(--signal-ink)}

.controls { display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
            margin: 16px 0; font-size: 14px; }
.controls label { display: inline-flex; align-items: center; gap: 6px; }
.controls input[type="number"] { width: 64px; }
.controls input[type="text"] { width: 104px; }

/* ---- sign-in page (login.html) -------------------------------------------
   Scoped to body.authpage so nothing here can reach the portal shell. The
   portal sets body{overflow:hidden} and a full-height main, which is right for
   a tabbed app and wrong for a centred card, so this scope overrides both. */
body.authpage{overflow:auto}
.authmain{min-height:calc(100dvh - var(--hd));display:flex;align-items:flex-start;
  justify-content:center;padding:56px 20px 90px}
.card{width:100%;max-width:420px;background:var(--surface);
  border:1px solid var(--line);border-radius:6px;overflow:hidden}
.cardtabs{display:flex;border-bottom:1px solid var(--line)}
.cardtabs button{flex:1 1 0;appearance:none;background:transparent;border:0;
  color:var(--ink-2);font:600 13px/1 var(--sans);padding:15px 10px;cursor:pointer;
  border-bottom:2px solid transparent}
.cardtabs button:hover{color:var(--ink)}
.cardtabs button.active{color:var(--ink);border-color:var(--signal)}
.authform{display:flex;flex-direction:column;gap:16px;padding:24px}
.authform h2{font:600 16px/1.3 var(--sans);margin:0}
.authform label{display:flex;flex-direction:column;gap:7px;
  font:600 11px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink-2)}
.authform input{background:var(--ground);border:1px solid var(--line-strong);
  border-radius:4px;color:var(--ink);font:400 15px/1.2 var(--sans);padding:11px 12px;
  text-transform:none;letter-spacing:normal}
.authform input:focus{outline:0;border-color:var(--signal)}
.authform input#signin-code,.authform input#reg-code{font-family:var(--mono);
  letter-spacing:.12em}
/* Cosmetic only - the payload upper-cases the code regardless (auth.mjs), this
   just stops a pasted lower-case code looking like it will be rejected. */
.authform input#reg-code{text-transform:uppercase}
.btn.primary{background:var(--signal);border-color:var(--signal);color:var(--on-signal);
  font-weight:700;padding:12px 16px}
.btn.primary:hover:not([disabled]){background:var(--signal-hover);border-color:var(--signal-hover);color:var(--on-signal)}
.btn.primary[disabled]{opacity:.55;cursor:default}
.hint{font:400 12px/1.5 var(--sans);color:var(--ink-3);margin:0;text-transform:none;
  letter-spacing:normal}
.msg{font:400 13px/1.5 var(--sans);margin:0;min-height:1.2em}
.msg.bad{color:var(--danger)}
.msg.hint{color:var(--ink-2)}
.reasons{margin:0;padding-left:18px;display:flex;flex-direction:column;gap:10px;
  font:400 12.5px/1.55 var(--sans);color:var(--ink-2)}
.secret{font:600 20px/1.4 var(--mono);letter-spacing:.14em;color:var(--signal-ink);
  margin:0;word-break:break-all;user-select:all}
.uri{font:400 11px/1.5 var(--mono);color:var(--ink-2);margin:0;word-break:break-all;
  user-select:all}
.mono{font-family:var(--mono)}

/* ---- who is signed in, in the portal header ----------------------------- */
/* NO margin-left:auto here, deliberately. An auto margin absorbs the free space on a
   flex line BEFORE flex-grow is resolved, so it starved nav.tabs of every spare
   pixel: the strip wrapped to 612px while about 940px sat unused beside it, giving a
   two-row header for no reason at all. nav.tabs carries flex:1 and pushes this block
   to the right on its own. */
.who{display:flex;align-items:center;gap:10px}
.who .email{font:400 12px/1 var(--sans);color:var(--ink-2);max-width:22ch;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.who button{appearance:none;background:transparent;border:1px solid var(--line-strong);
  color:var(--ink-2);font:600 11px/1 var(--sans);padding:7px 10px;border-radius:3px;
  cursor:pointer;white-space:nowrap}
.who button:hover{border-color:var(--signal);color:var(--signal-ink)}

/* The enrolment QR. White plate in BOTH themes: an inverted QR is unreadable to a
   good share of scanners, so the dark theme does not get a dark one. */
.qr{background:var(--paper);padding:10px;border-radius:4px;align-self:flex-start;
  line-height:0}
.qr svg{display:block;max-width:100%;height:auto}
.more{font:400 12px/1.5 var(--sans);color:var(--ink-3)}
.more summary{cursor:pointer;color:var(--ink-2)}

/* ---- the access page (admin/) ------------------------------------------- */
body.adminpage{overflow:auto}
.adminmain{max-width:1080px;margin:0 auto;padding:0 22px 90px}
.adminmain .wrap{padding-bottom:28px}
.panel{background:var(--surface);border:1px solid var(--line);border-radius:6px;
  padding:20px 22px;margin-bottom:22px}
.panel h2{font:600 16px/1.3 var(--sans);margin:0 0 6px}
.panel .hint{margin-bottom:16px;max-width:70ch}
form.inline{display:flex;gap:14px;align-items:flex-end;flex-wrap:wrap;
  margin-bottom:18px}
form.inline label{display:flex;flex-direction:column;gap:7px;
  font:600 11px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink-2)}
form.inline input,form.inline select{background:var(--ground);
  border:1px solid var(--line-strong);border-radius:4px;color:var(--ink);
  font:400 14px/1.2 var(--sans);padding:10px 11px;text-transform:none;
  letter-spacing:normal;min-width:220px}
/* The "From documents" drop zone and its pre-filled confirm form
   (assets.mjs: documentsPane / confirmForm). Tokens only - --ink-3 is the
   same muted-text token .hint already uses. */
.dropzone{border:1px dashed var(--line);padding:1rem;text-align:center;
  cursor:pointer;color:var(--ink-3)}
.dropzone.over{background:var(--signal-tint)}
.filelist{margin:.25rem 0 .5rem;padding-left:1rem}
.fieldrow{display:flex;align-items:flex-end;gap:.5rem}
.prov-chip{font-size:.75rem;padding:.1rem .4rem;border:1px solid var(--line);
  border-radius:2px;color:var(--ink-3);cursor:pointer}
.prov-chip.hand{border-style:dashed}
form.inline input:focus,form.inline select:focus{outline:0;border-color:var(--signal)}
.tablewrap{overflow-x:auto}
table{width:100%;border-collapse:collapse;font:400 13px/1.5 var(--sans)}
th{text-align:left;font:600 10.5px/1 var(--mono);letter-spacing:.08em;
  text-transform:uppercase;color:var(--ink-3);padding:0 12px 9px 0;
  border-bottom:1px solid var(--line);white-space:nowrap}
/* Atomic values NEVER wrap. A date broken as "2026-" / "03-07" is unreadable and
   makes the row twice as tall for no information gained; the same goes for a MW
   figure, a market code, a status badge and "no observations". The padding is
   deliberately unchanged - the row keeps its thickness, it just stops being two
   rows tall.

   The FIRST column is the exception because it is the only genuinely long field
   in every one of these tables (project name, asset name, email), and a name like
   "Rock River 138 kV Substations 1 (6 RICE) and 2 (5 RICE)" cannot be made short.
   It wraps; everything else does not. A table that ends up wider than the pane
   scrolls horizontally inside it, which is already how these panes behave. */
td{padding:10px 12px 10px 0;border-bottom:1px solid var(--line);
  color:var(--ink-2);vertical-align:middle;white-space:nowrap}
td:first-child{white-space:normal;min-width:28ch}

/* The panes that hold a WIDE data table get a wider column than the prose panes.
   Measured: at the 960px prose width the RFS table squeezed the project name to
   140px, so a name wrapped to two or three lines and the row went 41px -> 60px ->
   80px while every other cell sat on one line with room to spare. The fix is
   width for the name, not a smaller row - single-line rows stay 41px.
   Prose keeps its own caps (h1 20ch, .deck 62ch, .prov 90ch below) so widening
   this does not produce unreadable line lengths. */
#pane-rfs .wrap,#pane-headroom .wrap,#pane-markets .wrap{max-width:1480px}
#pane-rfs .prov,#pane-headroom .prov,#pane-markets .prov{max-width:90ch}
/* Narrower windows scroll the table instead of squeezing the name column back. */
#rfs-body,#headroom-body,#markets-body{overflow-x:auto}
/* A provenance note INSIDE a cell sits beside its value, not under it. The
   Markets table appends a <div class="prov"> after the number, and a div is a
   block - so "0" and "source not present" were two lines by construction rather
   than by wrapping, and the row was 60px against 41px everywhere else. Inline
   with a gap says the same thing on one line. Only matches a .prov NESTED in a
   cell; the RFS Evidence column is a td that IS .prov and is untouched. */
td .prov{display:inline;margin-left:8px;font:400 11px/1.4 var(--sans);
  color:var(--ink-3)}
td.mono{font-family:var(--mono);color:var(--ink);letter-spacing:.04em}
td.status{font:600 10.5px/1 var(--mono);letter-spacing:.06em;text-transform:uppercase}
td.status.open{color:var(--ok)}
td.status.used{color:var(--ink-3)}
td.status.revoked{color:var(--danger)}
.btn.small{padding:6px 11px;font-size:12px}
.btn.danger{border-color:var(--line-strong);color:var(--ink-2)}
.btn.danger:hover:not([disabled]){border-color:var(--danger);color:var(--danger)}
.btn.danger.armed{border-color:var(--danger);color:var(--danger);
  background:var(--danger-tint)}

/* ---- the filterable RFS table ------------------------------------------- */
.tablebar{display:flex;gap:14px;align-items:center;flex-wrap:wrap;margin:22px 0 10px}
.tablesearch{flex:0 1 320px;background:var(--ground);border:1px solid var(--line-strong);
  border-radius:4px;color:var(--ink);font:400 14px/1.2 var(--sans);padding:9px 11px}
.tablesearch:focus{outline:0;border-color:var(--signal)}
.tablebar .prov{margin:0;flex:1 1 auto;min-width:16ch}
table.filtertable th.sortable{cursor:pointer;user-select:none}
table.filtertable th.sortable:hover{color:var(--ink)}
th .arrow{color:var(--signal-ink);font-size:9px}
/* The filter row is part of the header, so it stays put when the body scrolls
   and never scrolls away from the column it belongs to. */
table.filtertable thead tr.filters th{padding:0 12px 10px 0;border-bottom:1px solid var(--line)}
table.filtertable thead tr.filters input{width:100%;min-width:9ch;background:var(--ground);
  border:1px solid var(--line);border-radius:3px;color:var(--ink);
  font:400 12px/1.2 var(--sans);padding:6px 8px;text-transform:none;letter-spacing:normal}
table.filtertable thead tr.filters input:focus{outline:0;border-color:var(--signal)}
table.filtertable thead tr.filters input::placeholder{color:var(--ink-3)}

/* --- the RFS explanation layer ------------------------------------------
   One always-visible answer line, then a COLLAPSED panel. The leadership
   chose collapsed so the table stays above the fold: an explainer that
   opens itself defeats the choice. */
.answerline{font:400 17px/1.5 var(--sans);color:var(--ink);max-width:62ch;
  margin:26px 0 10px}
/* ch, not var(--measure): --measure is the LINK COLOUR in this file, despite
   its name. max-width:#58A6E8 is invalid and silently ignored, which ran this
   panel to 1,373px - an unreadable line length. */
details.explainer{border:1px solid var(--line);background:var(--surface);
  max-width:92ch;margin:0 0 6px}
/* The measure is set on the TEXT, not on the box. `ch` resolves against the
   element's OWN font-size, and the box is 16px while the prose inside is 14px -
   so 86ch on the container rendered as 94 characters per line, well past
   readable. Measured, not eyeballed. */
details.explainer p,details.explainer li{max-width:78ch}
/* Its own class, NOT .prov: `#pane-rfs .prov` carries an ID and wins the
   specificity fight, silently restoring the 90ch measure. */
details.explainer p.subtle{font-size:12.5px;color:var(--ink-3)}
details.explainer > summary{cursor:pointer;user-select:none;padding:11px 16px;
  font:600 12px/1.2 var(--mono);letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-2)}
details.explainer > summary:hover{color:var(--ink)}
details.explainer[open] > summary{border-bottom:1px solid var(--line)}
/* No max-width reset here: it has the SAME specificity as the measure rule
   below and came later, so it silently won and every paragraph ran to 113
   characters. Spacing only. */
details.explainer > *:not(summary){margin:0 16px 12px}
details.explainer h3{font:600 11px/1.3 var(--mono);letter-spacing:.06em;
  text-transform:uppercase;color:var(--ink-2);margin:20px 16px 10px}
details.explainer p{font:400 14px/1.6 var(--sans);color:var(--ink-2)}
details.explainer strong{color:var(--ink);font-weight:600}
details.explainer ol{margin:0 16px 12px;padding-left:20px}
details.explainer ol li{font:400 14px/1.6 var(--sans);color:var(--ink-2);
  margin-bottom:7px}
/* The help marker sits INSIDE the sortable header, so it must not push the
   header to a second line - the row height rule the leadership set. */
.colhelp{background:none;border:0;padding:0 0 0 5px;cursor:pointer;
  color:var(--ink-3);font-size:11px;line-height:1;vertical-align:baseline}
.colhelp:hover{color:var(--signal-ink)}
.colhelp:focus-visible{outline:1px solid var(--signal);outline-offset:2px}
/* Where the marker lands you, briefly, so the jump is legible. */
details.explainer p.flash{background:var(--ground);
  box-shadow:-6px 0 0 0 var(--signal);transition:background .2s,box-shadow .2s}

/* ---- RFS hero: explanation BESIDE the headline ---------------------------
   The RFS/Headroom/Markets wrap is 1480px, so a 90ch deck leaves a wide empty
   column on the right. That space now carries what the table MEANS, which is the
   question a first-time reader has before any single row is useful.
   NOTE: --measure is the LINK COLOUR in this file, not a typographic measure.
   Never put it in a width. ch units below resolve against each element OWN
   font-size, so they are set on the element whose size is declared beside them. */
.herogrid{display:grid;gap:34px;align-items:start}
@media (min-width:1100px){
  .herogrid{grid-template-columns:minmax(0,1fr) minmax(0,400px)}
}
.herocard{
  background:var(--raised);border:1px solid var(--line);border-radius:10px;
  padding:22px 24px;font-size:14px;line-height:1.55;color:var(--ink-2);
}
.herocard h2{
  margin:0 0 12px;font:600 11px/1.2 var(--mono);letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-3);
}
.herocard p{margin:0 0 14px;max-width:46ch}
.herocard strong{color:var(--ink);font-weight:600}
.herocard em{color:var(--ink);font-style:italic}
.herocard dl{margin:0;display:grid;grid-template-columns:auto 1fr;
  column-gap:14px;row-gap:9px;align-items:baseline}
.herocard dt{font:600 11px/1.5 var(--mono);letter-spacing:.06em;color:var(--signal-ink);
  white-space:nowrap}
.herocard dd{margin:0;font-size:13px;line-height:1.5}
.herohint{margin:16px 0 0 !important;padding-top:14px;border-top:1px solid var(--line);
  font-size:13px;color:var(--ink-3)}

/* ---- a table cell that answers a question --------------------------------
   A cell that does something must LOOK like it does. Dotted underline rather
   than a link colour, because it is not navigation - it explains in place. */
td.cellaction{cursor:pointer;text-decoration:underline dotted var(--ink-3);
  text-underline-offset:4px}
td.cellaction:hover{color:var(--signal-ink);text-decoration-color:var(--signal)}
td.cellaction:focus-visible{outline:2px solid var(--signal);outline-offset:-2px}

/* ---- the value popup ------------------------------------------------------
   Deliberately NOT a native <dialog>: window.confirm() returns false in this
   app webview without showing anything, so nothing here leans on a native
   modal API. Backdrop click, Escape and a Close button, all explicit. */
.vpop-back{position:fixed;inset:0;z-index:200;background:var(--scrim);
  display:grid;place-items:center;padding:24px}
.vpop-back[hidden]{display:none}
.vpop{background:var(--surface);border:1px solid var(--line-strong);
  border-radius:12px;padding:24px 26px 26px;width:min(560px,100%);
  max-height:82vh;overflow:auto;box-shadow:var(--shadow);
  font-size:14px;line-height:1.6;color:var(--ink-2)}
.vpop-head{display:flex;align-items:center;justify-content:space-between;gap:16px}
.vpop-head h3{margin:0;font:600 12px/1.2 var(--mono);letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-3)}
.vpop-who{margin:14px 0 2px;font:400 12px/1.4 var(--mono);color:var(--ink-3)}
.vpop-val{margin:0 0 16px;font:600 26px/1.2 var(--mono);color:var(--ink);
  letter-spacing:.01em}
.vpop p{margin:0 0 13px;max-width:52ch}
.vpop-basis{margin:16px 0 0 !important;padding-top:14px;
  border-top:1px solid var(--line);font-size:13px}

/* ---- MOBILE ---------------------------------------------------------------

   MEASURED ON A 375px VIEWPORT BEFORE THIS EXISTED: documentElement scrollWidth
   1369 against clientWidth 375 - the page overflowed by 994px - with the nav
   715px wide and the last tab's right edge at x=904 where no thumb can reach it.

   min-width:0 IS THE LOAD-BEARING LINE. A flex item defaults to min-width:auto,
   which means it refuses to shrink below its own content, so the nav stayed 715px
   wide and pushed the document out from the inside.

   The nav used to be a horizontally SCROLLING strip here (overflow-x:auto with a
   hidden scrollbar). The four menus replaced that on 2026-09-02, and the menu
   rule further down sets overflow:visible so a dropdown is not clipped by its own
   parent - which silently overrode the scroll rule anyway. The dead half is gone;
   the half that still works stays.

   And note the measurement: window.innerWidth reported 1369, not 375, because it
   GROWS with the overflow. That is the false-green this workspace already has on
   record - the test has to be documentElement.scrollWidth > clientWidth. */
nav.tabs{min-width:0; flex:1 1 auto; flex-wrap:nowrap}
nav.tabs button{flex:0 0 auto}
.brand{flex:0 0 auto}
.who{flex:0 0 auto;min-width:0}

@media (max-width:760px){
  header.top{gap:8px;padding:0 10px}
  .wordmark{font-size:13px;letter-spacing:.10em}
  nav.tabs{margin-left:4px}
  nav.tabs button{padding:0 11px;font-size:13px}
  /* The signed-in address is the widest thing in the header and the least useful
     on a phone - the reader knows who they are. Sign out STAYS: someone reading
     this on a shared or borrowed phone needs a way out. */
  .who .email{display:none}
  /* MEASURED, because the first version of this fix looked right and was not:
     hiding only the address left .brand at 125px and .who at 203px, which with
     padding and gaps left the nav ELEVEN pixels. The page no longer scrolled
     sideways and the tabs were invisible - a worse bug than the one being fixed,
     and one that a "does the page overflow" check passes cleanly. Both ends of
     the header have to give up width, not just the middle. */
  .who{gap:6px}
  .who button{padding:6px 8px;font-size:10.5px}
  #theme{padding:6px 7px}
  .wrap{padding:26px 16px 64px}
  h1{font-size:30px;line-height:1.14}
  .deck{font-size:15px}
  .herocard{padding:18px 18px 20px}
  .herocard dl{grid-template-columns:1fr;row-gap:2px}
  .herocard dt{margin-top:10px}
  /* The filter bar WRAPS rather than pushing the table wider than the screen.
     The table itself already scrolls inside .tablewrap. */
  .tablebar{flex-wrap:wrap;gap:8px}
  .tablesearch{min-width:0;flex:1 1 100%}
  /* A popup that is 82vh tall on a phone hides the page it is explaining. */
  .vpop{max-height:78vh;padding:20px 20px 22px}
  .vpop-val{font-size:22px}
  .vpop p,.herocard p{max-width:none}
}

@media (max-width:560px){
  /* THE TAB STRIP GETS ITS OWN ROW. Sharing one row with the brand and the
     account controls left it 139px for NINE tabs - it scrolled, which was the
     bug being fixed, but one tab was visible at rest and every other tab cost a
     gesture. On its own row it gets the full width and three or four are visible.
     The wordmark comes BACK at this size for the same reason: row one now holds
     only brand (125) and who (182), which fits 375 with room to spare. */
  header.top{flex-wrap:wrap;height:auto;padding:0 8px;gap:6px}
  .wordmark{display:inline}
  .brand{order:1}
  .who{order:2;margin-left:auto}
  nav.tabs{order:3;flex:1 0 100%;margin-left:0;
    border-top:1px solid var(--line)}
  nav.tabs button{height:42px}
  /* The header is sticky and is now two rows, so the panes must start below the
     taller header rather than under it. */
  .pane.frame{height:calc(100dvh - var(--hd) - 42px)}
}


/* Anything that must scroll sideways does it INSIDE its own box. The page itself
   never does - a horizontally scrolling document on a phone hides the right-hand
   half of every screen and there is no affordance saying so. */
.tablewrap{max-width:100%}   /* overflow-x is already set above */
.pane.scroll{overflow-x:hidden}

/* The brand mark. Both halves were hardcoded in the SVG markup and could not theme:
   on the light ground the grey measured 2.68:1 and the amber 1.64:1. The bar uses
   --signal-ink rather than --signal on purpose - that token is the amber in dark and
   a deep gold in light, which is the whole reason it was split. */
.mark-grid{stroke:var(--cap)}
.mark-bar{fill:var(--signal-ink)}

/* ---- the explainer that gets out of the way -------------------------------

   Leadership, 2026-09-01: it should collapse to its title once you scroll into the
   table, float there, and reopen on a click. The explanation is worth reading on
   arrival and worth having WITHIN REACH afterwards - two different jobs, which is
   why it is one component with two states rather than two components.

   DESIGN DECISION worth stating: a click WINS over the scroll position until the
   reader scrolls back above the table. Auto-collapsing something a reader has just
   deliberately opened is the behaviour that makes these widgets infuriating.

   It pins with position:fixed and portal.js leaves a placeholder of the measured
   height behind it. On desktop the hero column is a fixed 400px so nothing moves,
   but on a phone the card is in the stacked flow and removing it would jump the
   page under the reader mid-scroll. */
.herocard-toggle{
  display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;
  appearance:none;background:transparent;border:0;padding:0;cursor:pointer;
  font:600 11px/1.2 var(--mono);letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-3);text-align:left;margin-bottom:12px}
.herocard-toggle:hover{color:var(--ink-2)}
.herocard-toggle:focus-visible{outline:2px solid var(--signal);outline-offset:3px}
.herocard-chev{width:8px;height:8px;flex:0 0 auto;border-right:1.5px solid currentColor;
  border-bottom:1.5px solid currentColor;transform:rotate(45deg) translate(-2px,-2px);
  transition:transform .18s ease}
.herocard[data-collapsed] .herocard-chev{transform:rotate(-135deg) translate(-2px,-2px)}
.herocard[data-collapsed] .herocard-body{display:none}
.herocard[data-collapsed] .herocard-toggle{margin-bottom:0}

.herocard[data-pinned]{
  position:fixed;top:calc(var(--hdr-h) + 12px);right:26px;width:min(400px,calc(100vw - 52px));
  z-index:30;box-shadow:var(--shadow);max-height:calc(100dvh - var(--hdr-h) - 34px);
  overflow:auto}
/* Collapsed it is a pill, not a panel: it must read as a control the reader can
   reopen, not as a box that has lost its contents. */
.herocard[data-pinned][data-collapsed]{width:auto;padding:10px 14px;overflow:visible}

@media (max-width:560px){
  :root{--hdr-h:91px}   /* 48 header + 42 tab row + 1 border */
  /* Full width under the header rather than a floating card: a 400px panel on a
     375px screen is the whole screen, and a right-aligned pill sits under the thumb. */
  .herocard[data-pinned]{left:8px;right:8px;width:auto;top:calc(var(--hdr-h) + 6px);
    max-height:calc(100dvh - var(--hdr-h) - 20px)}
  .herocard[data-pinned][data-collapsed]{width:auto;padding:11px 14px}
}

/* Holds the space the card left, so the page does not jump under the reader. */
.herocard-space{display:none}
.herocard-space[data-active]{display:block}
#rfs-sentinel{height:1px;margin:0}

/* ---- the tab groups ------------------------------------------------------

   Leadership, 2026-09-02: the long menu head needing horizontal scrolling is bad -
   group it so the full scale shows.

   ⚠ MEASURED, AND IT CORRECTED MY OWN ESTIMATE. I first assumed this was a phone
   problem and built the groups as display:contents above 760px, on the reasoning that
   ten tabs fit a desktop header. They do not - not for the leadership, who is the one
   person who sees all ten plus the Access link. At a 1280px viewport the strip still
   reported scrollWidth > clientWidth with the last tabs unreachable. So the groups are
   real at EVERY width and the strip WRAPS rather than scrolls, everywhere.

   Wrapping by GROUP rather than by button is the point: when the row runs out, a whole
   group moves down together, so the second row is still "Documents", never the tail of
   one group and the head of the next. A horizontal scroll with no affordance is
   invisible to anyone who does not already know it is there; two labelled rows are not. */
nav.tabs{display:flex;align-items:center;gap:2px;min-width:0;margin-left:10px;
  flex:1 1 0;
  /* ⚠ overflow:VISIBLE, and it has to be stated. AN OVERFLOW ANCESTOR CLIPS
     ABSOLUTELY POSITIONED DESCENDANTS: when this element still scrolled
     horizontally, every dropdown opened correctly and was then cut off by its own
     parent - aria-expanded true, hidden false, 190x147 px, and nothing on screen.
     Found by measuring the panel rather than by looking, because a clipped element
     and an unrendered one look identical. */
  overflow:visible}
/* A solo menu button may be a link (the admin page's "Portal"), and a link is
   styled by the UA unless told otherwise. */
nav.tabs a.menubtn{text-decoration:none;display:inline-flex;align-items:center}
.menu{position:relative;flex:0 0 auto}
nav.tabs .menubtn{height:34px;padding:0 13px;font-size:13.5px;border-radius:7px;
  border:1px solid transparent;background:transparent;color:var(--ink-2);
  cursor:pointer;font-family:inherit;white-space:nowrap}
nav.tabs .menubtn:hover{color:var(--ink);background:var(--raised)}
nav.tabs .menubtn.active{color:var(--ink);background:var(--raised);
  border-color:var(--line-strong)}
/* A menu with a panel says so. Without the caret a menu button and a solo button are
   indistinguishable, and the reader learns which is which only by clicking. */
.menu > .menubtn::after{content:"";display:inline-block;margin-left:7px;
  border:4px solid transparent;border-top-color:currentColor;
  transform:translateY(2px);opacity:.6}
.menu > .menubtn[aria-expanded="true"]{color:var(--ink);background:var(--raised);
  border-color:var(--line-strong)}
.menupanel{position:absolute;top:calc(100% + 5px);left:0;z-index:40;min-width:190px;
  padding:5px;border:1px solid var(--line-strong);border-radius:9px;
  /* --shadow, not a hardcoded rgba. A black drop shadow is correct on the light
     ground and INVISIBLE on the dark one, which is why the theme guard refuses raw
     colour outside the token blocks - it caught exactly that here. */
  background:var(--raised);box-shadow:var(--shadow);
  display:flex;flex-direction:column;gap:1px}
.menupanel[hidden]{display:none}
.menupanel button,.menupanel .menulink{appearance:none;background:transparent;
  border:0;border-radius:6px;color:var(--ink-2);cursor:pointer;text-align:left;
  font:inherit;font-size:13.5px;padding:8px 11px;text-decoration:none;display:block;
  white-space:nowrap}
.menupanel button:hover,.menupanel .menulink:hover{background:var(--surface);
  color:var(--ink)}
.menupanel button.active{color:var(--ink);background:var(--surface);font-weight:600}
/* The active item carries a bar on its LEFT, where a vertical list reads, rather
   than the bottom border the old horizontal strip used. */
.menupanel button.active{box-shadow:inset 2px 0 0 var(--signal)}
header.top{height:auto;min-height:var(--hd)}

@media (max-width:760px){
  nav.tabs{gap:1px;margin-left:4px}
  nav.tabs .menubtn{height:32px;padding:0 10px;font-size:12.5px}
  /* The panel is anchored to the NAV, not the button, so a menu near the right edge
     cannot open off-screen on a phone. */
  .menupanel{left:0;right:auto;min-width:min(240px,calc(100vw - 24px))}
}

/* ⚠ RE-STATED AFTER the base rule above, and that is the whole point.

   The phone layout at line ~477 gives nav.tabs its own full-width row
   (order:3; flex:1 0 100%). A MEDIA QUERY ADDS NO SPECIFICITY, so the plain
   `nav.tabs{...flex:1 1 0}` written above it silently won on source order and the
   phone header went back to a 234px horizontal overflow with three broken rows.
   Measured, not guessed. This is the same equal-specificity-later-wins trap that
   once ran a paragraph to 113 characters in this very file.

   The fix is ORDER, not force: no !important, just the phone rules restated after
   the base they have to beat. */
@media (max-width:560px){
  nav.tabs{order:3;flex:1 0 100%;margin-left:0;border-top:1px solid var(--line);
    padding:3px 0;overflow:visible}
  /* 7px, measured. At 9px the five menus came to 370px against 359px of row and the
     Admin caret was clipped at the right edge - visible only by comparing scrollWidth
     with clientWidth, since 11px of overflow looks like nothing. */
  nav.tabs .menubtn{height:32px;padding:0 7px;font-size:12px}
  /* Five short menu buttons fit a phone row where eleven tabs never could - which is
     the layout half of why the leadership asked for menus. The own-row rule STAYS
     anyway: it is cheap, it is already proven at 375px, and it is what stops a longer
     menu name in future from reintroducing the overflow. */
}

/* ---- audience gate -------------------------------------------------------
   Static markup that is provenance rather than grid fact: the source list, the
   licence clauses, the capture caveats. portal/audience.mjs owns the rule and the
   reasoning; this is only the half that applies to markup the renderers do not build.

   `!important` on purpose, and it is the one place in this file that earns it. This
   is a visibility GATE, not layout - the same category as the `[hidden]` rule - and
   it has to beat any `display` a later component rule sets on the same element. The
   alternative, relying on source order, is exactly what broke the mobile nav on
   2026-09-01, and a gate that loses a cascade fight shows the leadership's internal
   licence wall to a client.

   Presentational only. The licence strings are authored constants inside shipped JS
   and readable in devtools; the real gate on internal data is the BACKEND refusing
   /coverage.json to a non-leadership session. */
:root[data-audience="client"] [data-internal] { display: none !important; }

/* ---- Map it -----------------------------------------------------------------
   The button lives in the shared table bar; this is only its refusal line. */
.mapit-note {
  margin: 0 auto; max-width: 1100px; padding: 8px 14px;
  border-left: 3px solid var(--signal); background: var(--raised);
  color: var(--ink); font-size: 13px;
}
.mapit-note[hidden] { display: none; }
.btn.mapit { margin-left: 4px; }

/* ---- Home -------------------------------------------------------------------
   The signed-in landing page. Inside the gate by the leadership's choice: a public
   page would have needed a Caddy carve-out and put a new surface in front of
   licence-gated data for no gain. */
.homehero{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:34px;
  align-items:start;margin-bottom:34px}
@media (max-width:900px){.homehero{grid-template-columns:1fr;gap:22px}}

/* THE MARK, TAUGHT ONCE. It is the product thesis in one glyph and at 22px in the
   header nobody can decode it - "so small, I don't get it". Here it is large enough
   to read, with the sentence that explains it. Taught once beats self-evident. */
.markteach{margin:0;padding:20px 22px 18px;border:1px solid var(--line);
  border-radius:12px;background:var(--raised)}
.markteach svg{display:block;width:100%;height:auto;max-width:260px;margin:0 auto 14px}
.markteach .mark-grid{stroke:var(--cap)}
.markteach .mark-bar{fill:var(--signal)}
/* The gap gets its own hairline, because the gap is the point and an empty space
   reads as nothing rather than as a measurement. */
.markteach .mark-gap{stroke:var(--ink-3)}
.markteach figcaption{font-size:12.5px;line-height:1.55;color:var(--ink-2)}
.markteach figcaption strong{color:var(--ink)}

/* Today across the markets, and the changelog. Both are built by portal.js. */
.homegrid{display:grid;gap:10px;grid-template-columns:repeat(auto-fill,minmax(210px,1fr))}
.homecard{border:1px solid var(--line);border-radius:10px;padding:13px 15px;
  background:var(--raised)}
.homecard h3{margin:0 0 3px;font:600 13.5px/1.3 inherit;color:var(--ink)}
.homecard .big{font:600 21px/1.15 inherit;color:var(--ink);letter-spacing:-.01em}
.homecard .sub{font-size:11.5px;color:var(--ink-2);margin-top:3px}
/* THE BASIS, ON EVERY CARD. Ontario and Alberta publish an operator series; most US
   markets publish monthly or annual. Those are different measurements and a card that
   does not say which it is holding invites the reader to compare them as equals. */
.homecard .basis{display:inline-block;margin-top:8px;padding:2px 7px;border-radius:4px;
  font:600 9.5px/1.5 var(--mono);letter-spacing:.07em;text-transform:uppercase;
  background:var(--surface);color:var(--ink-3);border:1px solid var(--line)}
.homecard .basis.hourly{color:var(--measure);border-color:var(--measure)}
.homecard.unmeasured .big{color:var(--ink-3)}

.newsfeed{display:flex;flex-direction:column;gap:0}
.newsitem{display:grid;grid-template-columns:96px minmax(0,1fr);gap:14px;
  padding:11px 0;border-top:1px solid var(--line)}
.newsitem:first-child{border-top:0}
.newsitem time{font:600 11px/1.5 var(--mono);color:var(--ink-3);white-space:nowrap}
.newsitem p{margin:0;font-size:13.5px;line-height:1.5;color:var(--ink-2)}
.newsitem b{color:var(--ink);font-weight:600}
@media (max-width:560px){
  .newsitem{grid-template-columns:1fr;gap:2px}
}

/* ---- sign-in, as two steps --------------------------------------------------
   The credential step collapses to one confirmed line once the server has accepted
   the password, so the only live field is the code. Leaving both on screen made the
   reader hunt for what changed and invited them to re-type a password that was never
   wrong. */
.stepdone{display:flex;align-items:center;gap:9px;margin:2px 0 4px;padding:10px 12px;
  border:1px solid var(--line);border-radius:8px;background:var(--surface);
  font-size:13.5px;color:var(--ink)}
.stepdone[hidden]{display:none}
.stepcheck{color:var(--ok-ink,var(--signal));font-weight:700}
.stepdone .linkbtn{margin-left:auto}
.linkbtn{appearance:none;background:none;border:0;padding:0;cursor:pointer;
  font:inherit;font-size:12.5px;color:var(--measure);text-decoration:underline}
.linkbtn:hover{color:var(--ink)}

/* ---- long text in a table cell ----------------------------------------------
   Leadership, 2026-09-02: "the operator field in the tables is very long ... it is
   disturbing the UI ... make it fixed length or fix it somehow so the majority of the
   table shows on a 14 inch screen."

   The cause is real data, not a layout bug. Curated owners carry the whole ownership
   structure because that structure IS the finding - Bruce reads "Ontario Power
   Generation (site/facility owner); leased to and operated by Bruce Power L.P., a
   partnership of TC Energy (~48.4%), OMERS Infrastructure, the Power Workers' Union
   and The Society of United Professionals". One cell like that sets the column width
   for the whole table and pushes every other column off a 1280px screen.

   TRUNCATED IN THE VIEW, NEVER IN THE DATA. The full string stays in the DOM and in
   the cell's title attribute, so it is still searchable, still filterable, still
   copyable and still there on hover. Shortening the underlying value would have
   thrown away the distinction between an owner and an operator, which is the whole
   reason those rows were curated by hand. */
table.filtertable td{max-width:26ch;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
/* The asset name is what a reader scans for, so it gets more room than the rest. */
table.filtertable td[data-col="name"],table.filtertable td[data-col="asset"]{max-width:22ch}
table.filtertable td[data-col="county"],table.filtertable td[data-col="place"]{max-width:14ch}

/* ⚠ THE FILTER INPUT SETS THE COLUMN FLOOR, and that is what actually decided the
   width. Every column carries a filter box at `min-width:9ch`, so fourteen columns
   could not go below roughly 1,150px however tightly the CELLS were clamped -
   measured, after the cell clamps were already in. Narrowing the box and the header
   padding is what buys the room; clamping the cells alone bought almost none.

   A 14-inch laptop is ~1280px and the RFS table has fourteen columns, so it will
   still scroll inside .tablewrap at full width. That is the correct behaviour and it
   already worked - the ask was that the MAJORITY show, and that one 200-character
   owner string stop being the reason it does not. */
table.filtertable thead tr.filters input{min-width:6ch}
table.filtertable thead tr.filters th{padding-right:8px}
table.filtertable th,table.filtertable td{padding-right:8px}
/* Ownership is the longest field by far and the least scanned - it is read once, on
   the row you have already chosen. 18ch shows enough to recognise the party. */
table.filtertable td[data-col="operator"],table.filtertable td[data-col="owner"]{
  max-width:18ch}
/* Numbers and short codes must never be clipped: a truncated MW figure is a WRONG
   figure, not a shortened one. */
table.filtertable td[data-col="mw"],table.filtertable td[data-col="iso"],
table.filtertable td[data-col="state"],table.filtertable td[data-col="sessions"],
table.filtertable td[data-col="p50"],table.filtertable td[data-col="p90"],
table.filtertable td[data-col="claimedCod"],table.filtertable td[data-col="evidenceN"],
table.filtertable td[data-col="headroomPctNum"],table.filtertable td[data-col="headroomMwNum"]{
  max-width:none;overflow:visible}

/* The capture calendar. Colour carries meaning here, so it is never the ONLY
   carrier: every cell has a title naming its state in words and the legend spells
   all seven out. The fuel-palette work already measured what happens otherwise --
   a category carried purely by colour failed for roughly one man in twelve. */
/* width:auto is LOAD-BEARING. Line 220 sets table{width:100%} for the data tables, and
   this is not one - it is a fixed-cell grid. Stretched, the week label sat at the far
   left with the seven days crushed against the right edge and ~650px of dead space
   between them, and at 375px the row overflowed the viewport so SATURDAY and SUNDAY
   were unreachable - which cut off the one failed night this page exists to show. */
table.capcal{width:auto;max-width:100%;border-collapse:separate;border-spacing:3px;
  margin:12px 0;font:12px/1.4 var(--mono)}
table.capcal th{font-weight:600;text-align:center;padding:2px 4px;color:var(--ink-3)}
/* ⚠ min-width:0 and border-bottom:0 both UNDO a data-table rule, and the first was
   worth 118px. `td:first-child{min-width:28ch}` exists because a project name cannot
   be made short - but this cell holds an ISO date, and 28ch of IBM Plex Mono measured
   184.8px against 66px of content. That one declaration was the whole overflow.
   The bottom border is the same bleed: a grid of separated cells wants no row rules. */
td.capweek{min-width:0;border-bottom:0;padding:0 10px 0 0;color:var(--ink-3);
  white-space:nowrap}
td.capday{width:32px;height:28px;text-align:center;border:1px solid var(--line);
  border-radius:4px;padding:0;color:var(--ink-3)}
td.capday[tabindex]{cursor:pointer}
td.capday[tabindex]:hover{outline:2px solid var(--signal)}
.cap-clean{background:var(--ok-tint);border-color:var(--ok);color:var(--ink)}
.cap-failed{background:var(--danger-tint);border-color:var(--danger);
  color:var(--danger);font-weight:700}
.cap-unrecorded{background:var(--raised);border-color:var(--line);color:var(--ink-3)}
.cap-empty{background:var(--signal-tint);border-color:var(--signal);color:var(--ink)}
/* A night that did not run is the loudest thing on this page, deliberately: it is
   the only failure no other guard on this portal can see, because every other one
   is keyed on sources and a night that never ran has none to be missing.
   --paper is declared white in BOTH themes, so this stays legible in either. */
.cap-missing{background:var(--danger);border-color:var(--danger);
  color:var(--paper);font-weight:700}
.cap-future,.cap-pre-archive{background:transparent;border-style:dashed;
  border-color:var(--line);color:var(--ink-3)}
.caplegend{font-size:11px;line-height:1.7}
#capture-detail{margin-top:14px}
#capture-detail ul{margin:6px 0 0 18px}
#capture-detail li{margin:3px 0}
@media (max-width:720px){
  table.capcal{font-size:11px;border-spacing:2px}
  td.capday{width:26px;height:26px}
  td.capweek{padding-right:6px}
}

/* ---- the deal book's write surface ----------------------------------------
   The Assets tab is the only tab in this portal a person TYPES into, so it is
   the only one that needed controls at all. Everything here reuses the Access
   page's form vocabulary (.panel, form.inline, .btn, .msg) rather than
   inventing a second one - two form languages in one product is how the map,
   the documents and the Access page each ended up wearing a different era. */

/* The table is as wide as RFS's and was squeezing the site name into the same
   140px that measurement fixed there. */
#pane-assets .wrap{max-width:1480px}
#pane-assets .prov{max-width:90ch}
#assets-body{overflow-x:auto}

/* The editor opens ABOVE the table rather than in a modal. A modal would need a
   focus trap and an escape route, and this app's webview is exactly where a
   blocking dialog silently returned false and killed a button. */
.detailhost:empty{display:none}
.detailhead{display:flex;align-items:baseline;justify-content:space-between;
  gap:16px;margin-bottom:10px}
.detailhead h2{font:600 17px/1.3 var(--sans);margin:0}
.detailhost .panel h3{font:600 13px/1.3 var(--sans);margin:22px 0 4px;
  color:var(--ink)}
.detailhost table{margin:10px 0 14px}
.detailhost td:first-child{min-width:22ch;color:var(--ink-3);
  font:600 10.5px/1.4 var(--mono);letter-spacing:.06em;text-transform:uppercase}
.detailhost details{margin:16px 0}
.detailhost summary{cursor:pointer;color:var(--ink-2);
  font:600 11px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase}

/* One item per block, with its help text and its author, because a checklist
   row that shows a state and hides who attested it is the same number with
   none of the accountability. */
.checkitem{border-top:1px solid var(--line);padding:14px 0 4px}
.checkhead{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.checkhead strong{font:600 13.5px/1.4 var(--sans);color:var(--ink)}
.checkitem .hint{margin:4px 0 8px;max-width:80ch}
.checkitem .prov{margin:2px 0 0;font-size:11px;color:var(--ink-3)}
.checklist form.inline,.funnel form.inline{margin-bottom:6px;align-items:flex-start}

/* The evidence box. Deliberately roomy: what goes in it is a document name and
   a date, and a one-line input invites "yes". */
form.inline textarea{background:var(--ground);border:1px solid var(--line-strong);
  border-radius:4px;color:var(--ink);font:400 13px/1.5 var(--sans);
  padding:9px 11px;text-transform:none;letter-spacing:normal;min-width:340px;
  resize:vertical}
form.inline textarea:focus{outline:0;border-color:var(--signal)}
form.inline label .hint{font:400 11px/1.4 var(--sans);color:var(--ink-3);
  text-transform:none;letter-spacing:normal;max-width:34ch}

.funnel .reasons{margin-top:12px;font:400 12px/1.6 var(--sans);color:var(--ink-2)}

@media (max-width:720px){
  form.inline textarea{min-width:0;width:100%}
  .detailhead{flex-direction:column;align-items:flex-start;gap:8px}
}
