/* GENERATED from lib/brand/steel.css — edit THERE, then run: node field-portal/build-brand.mjs */
/* steel.css — the Clean Steel standard: KSG's ratified design language as importable tokens + recipes.
   SPEC: docs/DESIGN.md + the living mockups docs/design/clean-steel-sidebyside.html (structure + both token
   sets, extracted verbatim) and docs/design/clean-steel-accents.html (the accent audition). When this file and
   those mockups disagree, the mockups win.

   TWO SIBLING MODES, ONE TOKEN SET: every color lives in html[data-theme="dark"] / html[data-theme="light"].
   A page that sets NO data-theme gets dark (:root falls back to the dark set) — today's behavior, unchanged,
   so nothing live shifts until a surface explicitly opts in. The toggle flips data-theme only — never a second
   stylesheet, never a layout change (DESIGN.md "Two sibling modes").

   SOFT FURNITURE, HARD EQUIPMENT: `.card` is the soft surface (10px radius, line border, a faint shadow — the
   ONE permitted depth cue). The safety `.tag`, status `.st` chips, the `.cb` checkbox, and the KEYO block label
   stay SQUARED (border-radius:0) even inside the soft world — they read as equipment, not software. `.tag`
   itself sits OUTSIDE the card system entirely, on the ground, in every consumer.

   TYPE: condensed stencil caps (.cond) live ONLY in the load-bearing skeleton — the stripe wordmark, .secbar
   titles, .nav labels, the tag header, the KEYO block label, and the sparing .edge line. Body/buttons/labels
   are calm, normal-weight system sans. Monospace tabular numerals (.mono) stay everywhere, unchanged.

   NEVER A FILL (DESIGN.md "Key Maroon"): --maroon and --amber are BORDER/TEXT colors on interactive
   controls (.btn.primary, .nav a.on, .st.wait) — never background:var(--maroon)/var(--amber) outside the
   fleet's own named exceptions (.stripe, .tag .head, .keyo .k, .prog i, .cb.done). --amber2 is the secondary
   amber tone (text accents only, e.g. a machine-unit number) — two portals had already independently minted
   this exact value under a page-local --accent2 name before it was promoted here (ui/clean-steel-accent-
   foundation, 2026-07-30); rename a portal-local equivalent to var(--amber2) rather than keeping its own copy.
   lib/brand/no-fill-guard.mjs is the reusable test a migrated portal's OWN suite adopts to enforce this.

   ⚠ TWO MAROONS, AND THEY ARE NOT INTERCHANGEABLE (red team 2026-08-03, pilot-blocking):
     --maroon      #6f1d27 in BOTH modes — the FILL/identity maroon. Correct only where it is a BACKGROUND
                   carrying light text (.stripe, .keyo .k) or where the ground is light.
     --maroon-ink  the maroon used as TEXT or as a BORDER. Per-mode, exactly like --amber2: #6f1d27 in
                   light (unchanged), #d98891 in dark.
   WHY: --maroon was chosen against the LIGHT ground and copied verbatim into the dark token set, where
   #6f1d27 on --panel #323237 is 1.14:1 — WCAG AA wants 4.5:1. mechanic-portal's ONLY sign-in button
   ('Text me a code') is a .btn.primary, so on a dark-mode iPad the pilot's first screen had a phone field
   and an invisible button. The active-tab underline (.nav a.on) had the identical 1.14:1 problem.
   The fix is a second TONE, never a fill — filling the button would break the rule above.
   lib/brand/contrast-guard.mjs is the sibling guard that fails CI when any token pair drops below its
   minimum in EITHER mode, so this class of bug cannot come back silently.

   ONE STRIPE COLOR PER APP (David, ratified 2026-09-14 — docs/DESIGN.md "App colors"). The stripe is no
   longer maroon-by-definition: it paints --app, and each app sets --app / --app-ink / --app-cur / --app-line
   to its ratified pair. The defaults below are Field's (maroon + the warm white ink the stripe has always
   used), so a surface that sets nothing renders exactly as it does today. Inside an app the one-painted-
   accent-per-screen rule is unchanged — the stripe is that accent.
     --app        the painted stripe FILL. Its role is a background under --app-ink; it is NOT a line or
                  text tone, exactly as --maroon is not --maroon-ink. Never paint it anywhere but the stripe.
     --app-ink    the ink ON the stripe (wordmark, tabs, chips).
     --app-cur    the current-tab pill behind .stripe .tabs .cur. It defaults to var(--app-ink) and the
                  current tab INVERTS onto it — the pill is the ink, the text is the stripe. That is not a
                  style preference, it is the only form that is correct for both families: a translucent
                  white pill lightens the very ground its own white text sits on (measured on the ratified
                  Safety green: white on a .22 white pill is 3.49:1, and light mode could afford at most
                  alpha .08 — not a highlight, a rumour), while an opaque NEAR-white pill would put yellow
                  text on white for Equipment. Inverting makes the pair --app on --app-ink, which is the
                  SAME ratio the guard already enforces as --app-ink on --app: correct for all nine apps,
                  for free, guaranteed by an assertion that already exists. A portal overrides it only to
                  soften the pill, never to make it translucent.
     --app-line   the app color as a 3px LINE on --navbg (the phone bottom nav's current item). A SEPARATE
                  tone for the same reason --maroon-ink exists: measured against --navbg, every one of the
                  nine ratified stripe colors fails WCAG 1.4.11 (3.0:1) in one of the two modes — Field's
                  own #6f1d27 is 1.39:1 on the dark nav, the identical 2026-08-03 bug. It defaults to
                  var(--maroon-ink), so .nav a.on is unchanged today.

   Copy into a portal's public/ via its build-brand.mjs (the house pattern). Shares variable names with the
   retired light lib/brand/theme.css ON PURPOSE (historical) — a surface imports steel.css alone, never both. */

:root, html[data-theme="dark"] {
  --bg:#2a2a2e; --panel:#323237; --sechead:#2e2e33; --line:#46464d; --hair:#3b3b41;
  --maroon:#6f1d27; --maroon-ink:#d98891; --ink:#dcdad5; --muted:#a09e97; --dim:#7b796f;
  --ok:#5aa47b; --red:#c44a41; --amber:#cf9245; --amber2:#E9B45A;
  --ok-ink:#6cbb8f; --red-ink:#e8807a;   /* the TEXT/BORDER tones of ok+red — see the two-maroons note above */
  /* the app stripe (ratified 2026-09-14) — defaults are FIELD's, so an un-adopted surface is unchanged */
  --app:#6f1d27; --app-ink:#f2e9e9; --app-cur:var(--app-ink); --app-line:var(--maroon-ink);
  /* the safety tag's danger red — its OWN token, never --red and never maroon: Field's stripe IS maroon,
     so a tag that reads maroon-adjacent no longer reads as danger. Paired ink, because #FF6B6B carries
     dark ink (5.58:1) and drowns white (2.54:1). */
  --danger-tag:#FF6B6B; --danger-tag-ink:#1C2530;
  --tagbg:#332a2a; --tagrule:#5c4340; --progtrack:#4a4a50; --btnline:#6e6e75; --navbg:#242428;
  --shadow:0 1px 2px rgba(0,0,0,.25), 0 3px 10px rgba(0,0,0,.18);
  --hover:#383840;  /* pointer-hover surface for desktop Steel pages (one shared hover gray — don't mint per-surface) */
}
html[data-theme="light"] {
  --bg:#EEF1F4; --panel:#FBFCFD; --sechead:#F3F5F8; --line:#D4DAE0; --hair:#E4E8ED;
  --maroon:#6f1d27; --maroon-ink:#6f1d27; --ink:#1C2530; --muted:#5A6675; --dim:#8A93A0;
  --ok:#2E8B57; --red:#B23B3B; --amber:#C77F22; --amber2:#A8651A;
  /* --ok-ink is DARKER than --ok here: --ok #2E8B57 is only 3.74:1 on --bg, so the light mode had its own
     (smaller, un-reported) version of the same bug. --red-ink keeps --red's value — that one already passed. */
  --ok-ink:#277A4C; --red-ink:#B23B3B;
  --app:#6f1d27; --app-ink:#f2e9e9; --app-cur:var(--app-ink); --app-line:var(--maroon-ink);
  --danger-tag:#C1121F; --danger-tag-ink:#fbf3f2;
  --tagbg:#FDF6F5; --tagrule:#D9B8B4; --progtrack:#E0E4E9; --btnline:#B8C0C9; --navbg:#FBFCFD;
  --shadow:0 1px 2px rgba(20,30,45,.06), 0 2px 8px rgba(20,30,45,.05);
  --hover:#EDF0F3;
}

:root{
  --sans:-apple-system,"SF Pro Text","Segoe UI",Roboto,sans-serif;
  --steel-cond:"Arial Narrow",sans-serif;
  --steel-mono:"SF Mono",Consolas,monospace;
}

body.steel{margin:0;background:var(--bg);color:var(--ink);font-family:var(--sans);}

/* THE hidden ATTRIBUTE MEANS HIDDEN (David's ruling, 2026-09-21).
   The browser's own [hidden]{display:none} is a USER-AGENT rule with no specificity, so ANY author rule that
   sets display beats it — and then an element is hidden and drawn at the same time. That killed the CM Desk:
   <div class="mcdrawer" hidden> against .mcdrawer{position:fixed;inset:0;z-index:60;display:flex}, so a closed,
   empty, invisible drawer sat over the whole page and swallowed every click. Nobody could use cm.ksg.tools from
   2026-09-14 (41c85876) to 2026-09-21 (#1266), and it did not look broken — it looked flat.
   A repo-wide sweep (docs/HIDDEN-AND-OVERLAY-SCAN.md) found twelve elements in that state across four surfaces.
   !important IS NOT OPTIONAL: this sheet is linked FIRST and each surface's own sheet after it, and [hidden] is
   class-level specificity — a plain rule here would lose to every `.thing{display:flex}` downstream and would
   not have fixed the drawer. It must also beat `.panel.active{display:flex}` (0,2,0), which cm-portal has.
   expenses-portal and shopdesk-portal carried this exact line locally before it moved here; theirs are now
   redundant and kept only as a local belt.
   TO SHOW AN ELEMENT, CLEAR THE ATTRIBUTE — a class or an inline display will no longer reveal it. */
[hidden] { display: none !important; }

/* the two display voices */
.cond{font-family:var(--steel-cond);letter-spacing:.1em;text-transform:uppercase;font-weight:800;}
.mono{font-family:var(--steel-mono);font-variant-numeric:tabular-nums;}

/* shared buttons — CALM, bordered, generalizing .keyo .talk's existing shape. .btn.primary is the SAME
   bordered shape with the maroon edge — a border/text color is not a fill (.nav a.on already sets this
   precedent); background:var(--maroon) here would be a SECOND painted accent on the screen, forbidden. */
.btn{border:1px solid var(--btnline);background:transparent;color:var(--ink);font:inherit;font-size:13px;
  font-weight:600;padding:8px 13px;border-radius:7px;cursor:pointer;}
.btn:hover{border-color:var(--dim);}
.btn.primary{border-color:var(--maroon-ink);color:var(--maroon-ink);}
.btn.primary:hover{background:var(--sechead);}

/* header: the APP stripe, painted steel (ONE painted accent per screen — see DESIGN.md "App colors").
   The .co small ink went from a baked rgba(242,233,233,.6) to var(--app-ink) at opacity .6 so it follows the
   app: for a text-only element the two composite to the same pixels, so the default (Field) render is
   unchanged. .stripe.chrome is Dispatch's ratified variant — two inset hairlines, FLAT, never a gradient. */
.stripe{height:46px;background:var(--app);display:flex;align-items:flex-end;padding:0 18px 7px;}
.stripe.chrome{box-shadow:inset 0 1px 0 rgba(255,255,255,.7), inset 0 -1px 0 rgba(0,0,0,.18);}
.stripe .co{font-size:14px;color:var(--app-ink);letter-spacing:.16em;}
.stripe .co small{color:var(--app-ink);opacity:.6;letter-spacing:.3em;font-size:8.5px;margin-left:8px;}

/* THE STATIC STRIPE NAV (lib/brand/README.md "The stripe markup contract"). Every tab on every page, the
   current one lit — never a "go elsewhere" list that rearranges under you (safety-portal/public/safety-nav.js,
   David 2026-09-13). Every selector is scoped UNDER .stripe on purpose: bare .tabs/.cur/.chips would silently
   restyle page-local classes that happen to share the name (the trap #959 hit with .tag/.foot/.btn/.row). */
.stripe .tabs{display:flex;align-items:flex-end;gap:2px;margin-left:22px;}
.stripe .tabs a,.stripe .tabs .cur{color:var(--app-ink);text-decoration:none;font-size:10px;
  letter-spacing:.12em;padding:5px 10px;border-radius:5px 5px 0 0;white-space:nowrap;}
/* NOTHING ON A STRIPE IS DIMMED WITH ALPHA (the finding from the first two adoptions, safety-portal
   2026-09-14 and equipment-desk the same day). The tabs used to carry opacity:.68 and the chips .85 —
   numbers chosen against Field's near-black maroon, where white has 9.36:1 to spend. A mid-tone stripe has
   no alpha budget at all: on the ratified Safety green full white is only 5.32:1, so .68 measured 3.58:1
   and .85 measured 4.32:1, both under AA. Hierarchy on a stripe is SIZE, SHAPE and the pill — never alpha.
   Hover is an underline for the same reason: any tint light enough to see is too light to carry the ink. */
.stripe .tabs a:hover{text-decoration:underline;text-underline-offset:3px;}
.stripe .tabs .cur{background:var(--app-cur);color:var(--app);cursor:default;}
.stripe .chips{margin-left:auto;display:flex;align-items:baseline;gap:14px;color:var(--app-ink);
  font-size:10px;}
.hd{padding:12px 18px;border-bottom:1px solid var(--line);display:flex;align-items:baseline;background:var(--panel);}
.hd h1{margin:0;font-size:17px;font-weight:700;letter-spacing:.01em;}
.hd .role{color:var(--muted);font-size:11.5px;margin-left:8px;}
.hd .sync{margin-left:auto;font-size:10px;color:var(--muted);text-align:right;}
.hd .sync b{color:var(--ok);font-weight:700;}

/* the card: soft furniture — the one permitted depth cue, and it stays faint */
.card{background:var(--panel);border:1px solid var(--line);border-radius:10px;box-shadow:var(--shadow);
  margin-top:16px;overflow:hidden;}

/* section bars — plan-sheet title blocks, now a CARD HEADER: condensed caps left, monospace meta right */
.secbar{display:flex;align-items:baseline;background:var(--sechead);border-bottom:1px solid var(--line);
  padding:9px 16px;}
.secbar h2{margin:0;font-size:11px;color:var(--ink);}
.secbar i{font-style:normal;margin-left:auto;font-size:9.5px;color:var(--dim);letter-spacing:.08em;}

/* the physical safety tag (DO NOT OPERATE) — hard equipment: SQUARED even inside the soft world, and it
   sits OUTSIDE the card system entirely, on the ground (never wrapped in .card). */
.tag{margin:16px 16px 0;border:2px solid var(--danger-tag);background:var(--tagbg);border-radius:0;}
.tag .head{background:var(--danger-tag);color:var(--danger-tag-ink);text-align:center;padding:6px;font-size:13px;letter-spacing:.22em;}
.tag .b{padding:10px 13px;display:flex;justify-content:space-between;align-items:baseline;}
.tag .b b{font-size:13.5px;} .tag .b small{color:var(--muted);font-size:10.5px;}
.tag .meta{border-top:1px dashed var(--tagrule);padding:7px 13px;font-size:10px;color:var(--muted);display:flex;justify-content:space-between;}

/* rows: flat, ruled, dense — the density lives INSIDE the card */
.row{display:flex;align-items:center;gap:12px;padding:13px 16px;border-bottom:1px solid var(--hair);}
.card .row:last-child{border-bottom:none;}
.row .t{flex:1;min-width:0;} .row .t b{font-size:13.5px;font-weight:600;display:block;}
.row .t small{color:var(--muted);font-size:11.5px;}

/* status chips: flat bordered TEXT — hard equipment, SQUARED even inside a card */
.st{font-size:9.5px;font-weight:800;letter-spacing:.08em;padding:2px 7px;border:1px solid;border-radius:0;}
.st.ok{color:var(--ok);border-color:var(--ok);}
.st.wait{color:var(--amber);border-color:var(--amber);}
.st.crew{color:var(--muted);border-color:var(--muted);}
.st.bad{color:var(--red);border-color:var(--red);}

/* square checkbox — hard equipment, SQUARED */
.cb{width:20px;height:20px;border:2px solid var(--dim);flex:none;border-radius:0;}
.cb.done{border-color:var(--ok);background:var(--ok);position:relative;}
.cb.done:after{content:"✓";position:absolute;inset:0;display:grid;place-items:center;color:#fff;font-size:12px;font-weight:900;}

/* crew roster table */
table.crew{width:calc(100% - 32px);margin:2px 16px;border-collapse:collapse;}
.crew td{padding:8px 0;border-bottom:1px solid var(--hair);font-size:12.5px;}
.crew tr:last-child td{border-bottom:none;}
.crew .in{color:var(--ok);font-weight:700;font-size:10px;letter-spacing:.06em;}
.crew .out{color:var(--red);font-weight:700;font-size:10px;letter-spacing:.06em;}
.crew td:last-child{text-align:right;}
.foot{padding:7px 16px 10px;font-size:9.5px;color:var(--dim);}

/* flat progress bar (no animation) */
.prog{height:3px;background:var(--progtrack);margin-top:7px;position:relative;}
.prog i{position:absolute;inset:0 40% 0 0;background:var(--amber);}
.eta{color:var(--amber);font-weight:700;}

/* KEYO: a radio on the dash — flat maroon block label (SQUARED — it's equipment) + calm text + a soft
   action button. Never an orb. In Clean Steel the panel is a .card; the block label stays squared. */
.keyo{display:flex;align-items:center;gap:12px;padding:13px 15px;border-left:4px solid var(--maroon-ink);}
.keyo .k{font-size:12px;letter-spacing:.18em;color:#f2e9e9;background:var(--maroon);padding:4px 8px;font-weight:800;border-radius:0;}
.keyo .t b{font-size:12.5px;display:block;font-weight:600;} .keyo .t small{color:var(--muted);font-size:10.5px;}
.keyo .talk{margin-left:auto;border:1px solid var(--btnline);background:transparent;color:var(--ink);
  font-size:10.5px;letter-spacing:.06em;padding:8px 13px;font-weight:600;border-radius:7px;cursor:pointer;}

/* the sparing K.E.Y. E.D.G.E. moment line — at most once per screen */
.edge{text-align:center;color:var(--dim);font-size:9.5px;letter-spacing:.3em;margin:20px 0 4px;}

/* nav: flat labeled tabs; active = the 3px APP top border (this screen's one accent, when the stripe isn't
   it). --app-line, not --app: see the header note — the ratified stripe fills do not read as a 3px line. */
.nav{background:var(--navbg);border-top:1px solid var(--line);display:flex;}
.nav a{flex:1;text-align:center;color:var(--dim);font-size:10px;text-decoration:none;font-weight:800;
  letter-spacing:.12em;padding:13px 0 6px;border-top:3px solid transparent;}
.nav a.on{color:var(--ink);border-top-color:var(--app-line);}
