/* ── Stacked Out's own typefaces ──────────────────────────────────────────
   The app was running the system stack (-apple-system / Segoe / Roboto), which
   is why it read as generic: nothing on screen was Stacked Out's. These are the
   brand's real faces, self-hosted as woff2 (116KB total) exactly as the brand
   does elsewhere -- no CDN, so no font-CDN CSP problem and no third-party
   request. Clash Display carries headings and the big numbers, which the brand
   treats as its punctuation; Satoshi carries everything you read. swap keeps
   text visible while they load, so a slow site van signal never blanks a
   field. */
@font-face{font-family:'Clash Display';src:url('../fonts/clash-display-600.woff2') format('woff2');font-weight:600;font-style:normal;font-display:swap;}
@font-face{font-family:'Clash Display';src:url('../fonts/clash-display-700.woff2') format('woff2');font-weight:700;font-style:normal;font-display:swap;}
@font-face{font-family:'Satoshi';src:url('../fonts/satoshi-400.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap;}
@font-face{font-family:'Satoshi';src:url('../fonts/satoshi-500.woff2') format('woff2');font-weight:500;font-style:normal;font-display:swap;}
@font-face{font-family:'Satoshi';src:url('../fonts/satoshi-700.woff2') format('woff2');font-weight:700;font-style:normal;font-display:swap;}

/* StackedOut Certificates — theme (18 Sep 2026).
   Jamie's ruling 18 Sep 05:35: stay DARK for now, make the type stand out.
   Structure follows the iCertifi Cloud Pro pattern (rounded cards, grey input
   tiles with the unit on the right, label above every field, tap tiles, one
   accent used only for the selected state and the primary action, pill
   bottom bar) rendered on a dark surface. Every colour in web/app/*.css comes
   from the tokens below — never hex. Report/PDF paper is produced by the
   backend templates and is not affected. */
:root,
html,
body,
body:not(.theme-light) {
  --so-page: #08090b;        /* brand charcoal ground */
  --so-panel: #161a22;       /* neutrals carry a faint cool tint, not dead grey */
  --so-panel-2: #232936;
  --so-field: #0e0f13;
  --so-field-line: #5a6070;   /* 3.05:1 on the field: an input now has a real edge */
  --so-line: #4b5160;         /* was #30333d = 1.34:1 on the card, i.e. invisible */
  --so-line-strong: #646b7c;  /* 3.15:1: for true boundaries and focus */
  --so-text: #f5f6f8;        /* brand off-white: pure #fff glares on site at night */
  --so-muted: #9aa1ab;       /* brand muted */
  --so-label: #9aa1ab;
  --so-accent: #2ad4c4;      /* action: a teal that is not the default cyan and not yellow */
  --so-accent-press: #4ce6d7;
  --so-accent-ink: #06201d;  /* deep ink on teal */
  --so-accent-soft: rgba(42, 212, 196, 0.13);
  --so-ok: #4ade80;
  --so-bad: #ff6b60;
  --so-warn: #3a3418;
  --so-shadow: 0 1px 0 rgba(255, 255, 255, 0.04) inset, 0 8px 24px rgba(0, 0, 0, 0.35);
  --so-new: #7aa2ff;         /* new / incoming / links: a cool blue, distinct from the action */
  --so-display: 'Clash Display', 'Satoshi', system-ui, sans-serif;
  /* See the note at the type scale: Clash's own space is too narrow. */
  --so-display-word: .11em;
  --so-r: 18px;
  --so-r-s: 12px;
  --so-font: 'Satoshi', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;

  /* legacy tokens used by source/css/style.css */
  --bg: var(--so-page); --bg2: var(--so-page); --panel: var(--so-panel); --panel2: var(--so-panel-2);
  --line: var(--so-line); --text: var(--so-text); --muted: var(--so-muted);
  --amber: var(--so-accent); --amber-dk: var(--so-accent-press);
  --yel: var(--so-accent); --yel-ink: var(--so-accent-ink);
  --cyan: var(--so-accent); --green: var(--so-ok); --red: var(--so-bad); --red-ink: var(--so-bad);
  --radius: var(--so-r-s);

  /* board workspace / app shell tokens */
  --bw-page: var(--so-page); --bw-surface: var(--so-panel); --bw-ink: var(--so-text);
  --bw-ink-2: var(--so-muted); --bw-line: var(--so-line); --bw-field: var(--so-field);
  --bw-accent: var(--so-accent); --bw-accent-ink: var(--so-accent-ink);
  --bw-r: var(--so-r); --bw-r-s: var(--so-r-s);

  /* design-c tokens */
  --c-surface: var(--so-page); --c-panel: var(--so-panel); --c-line: var(--so-line);
  --c-line-strong: var(--so-line-strong); --c-ink: var(--so-text); --c-mut: var(--so-muted);
  --c-fill-action: var(--so-accent); --c-fill-action-press: var(--so-accent-press);
  --c-action-ink: var(--so-accent-ink); --c-frame: var(--so-panel); --c-frame-ink: var(--so-text);
  --c-ink-money: var(--so-accent);

  --so-gutter: 12px; --so-card-pad: 18px;
  color-scheme: dark;
}
/* ⚠ THESE NEVER APPLIED, AND THAT IS WHY THE DESKTOP FELT CRAMPED.
   --so-gutter and --so-card-pad are declared above on `body:not(.theme-light)`,
   not on :root. A custom property set on BODY beats the same property
   inherited from HTML, so redefining them on :root here could never win and
   every width -- 1280, 1440, anything -- used the phone's 12px gutter.
   Measured at 1440: content sat at x=31 (12 gutter + 18 pad + 1 border) when
   the intended figure is 40, and the settings pane padded to 12px when it
   asked for the gutter.
   Jamie, 12 Oct 2026: "everything is pushed right up to boundaries
   everywhere?" -- on a 1440 screen it was, by 28px per side.
   Declared on the same element as the originals so they actually take, with
   :root kept alongside for anything that reads them above body. */
@media (min-width: 700px) {
  :root, body, body:not(.theme-light) { --so-gutter: 28px; --so-card-pad: 22px; }
}
@media (min-width: 1100px) {
  :root, body, body:not(.theme-light) { --so-gutter: 40px; }
}

html, body { background: var(--so-page) !important; color: var(--so-text); }
body, .fa-shell, .fc-shell, .so-page, .proto-home, .product-login, .product-startup {
  font-family: var(--so-font) !important;
  -webkit-font-smoothing: antialiased;
}
.fa-shell, .fc-shell { font-size: 17px; line-height: 1.3; font-weight: 500; background: var(--so-page); padding-top: 0; }
body.bw-world .fc-body { padding: 12px 12px 110px; background: var(--so-page); }

/* ── Cards ─────────────────────────────────────────────────────────────── */
.so-page, .so-group, .panel, .card, .fc-card, .wf-section, .pc-view, .so-empty, .commercial-card,
#proto-sheet, #proto-dialog, .product-login, .flow-card, .flow-panel, .bw-panel, .bw-section,
details, fieldset,
body:has(.fc-shell) .fc-body > .fc-card {
  background: var(--so-panel);
  color: var(--so-text);
  border: 1px solid var(--so-line);
  border-radius: var(--so-r);
  box-shadow: var(--so-shadow);
}
body:has(.fc-shell) .fc-body > .fc-card { padding: 16px 16px 18px; margin: 0 0 18px; overflow: visible; }
.fc-card details, .fc-card fieldset { box-shadow: none; border-radius: var(--so-r-s); background: transparent; border: 0; }
/* Card title: cyan marker + rule underneath so a section reads as a section.
   THE RULE IS FULL-BLEED ONLY WHERE THERE IS PADDING TO CANCEL. The underline
   is pulled out to the card's edges with `margin: 0 -16px` + `padding: 0 16px`,
   which is right for the phone shell's cards -- they get `padding: 16px` on the
   line above, and the negative margin simply undoes it.
   It was applied to EVERY .fc-card h4, and `section.fc-card.wf-section` on the
   desktop never gets that padding: it sits flush at the full viewport width. So
   on the desktop EIC the two declaration headings measured 1310px inside a
   1280px section, from -15 to 1295, and the WHOLE PAGE scrolled sideways.
   Measured on all seven types at 390 and 1280: EIC desktop was the only one
   that overflowed, and only on these headings.
   Split, so the bleed belongs to the padded card and everything else gets the
   same underline drawn at its own content edge. */
.fc-card h4 {
  margin: 0 0 16px; padding: 0 0 12px; font-size: 20px; font-weight: 700; letter-spacing: -0.01em; color: var(--so-text);
  border-bottom: 1px solid var(--so-line); display: flex; align-items: center; gap: 10px;
}
body:has(.fc-shell) .fc-body > .fc-card > h4 {
  margin: 0 -16px 16px; padding: 0 16px 12px 16px;
}
.fc-card h4::before, body:has(.fc-shell) .fc-body > .fc-card > h4::before {
  content: ''; width: 4px; height: 20px; border-radius: 2px; background: var(--so-accent); flex: 0 0 auto;
}
/* Sub-box inside a card (customer picker etc.): tinted, edged, clearly nested */
.certcust, .fc-card .fc-sub, .fc-card fieldset {
  background: var(--so-panel-2) !important; border: 1px solid var(--so-line-strong) !important; border-radius: var(--so-r-s); padding: 14px; margin: 0 0 16px;
}
.certcust-head { margin: 0 0 6px; font-size: 16px; font-weight: 700; color: var(--so-text); }
.certcust-note { margin: 0 0 12px; font-size: 14px; color: var(--so-muted); line-height: 1.45; }
.certcust-acts { display: flex; flex-wrap: wrap; gap: 8px; }
#certs-body .certcust .btn, .fc-shell .certcust .btn:not(.btn-amber), .certcust-acts .btn { background: var(--so-field) !important; border: 1px solid var(--so-line-strong) !important; color: var(--so-text) !important; border-radius: 999px !important; padding: 0 18px; }
/* Circuit-sheet groups get the same title treatment as cards */
.bw-group { border: 1px solid var(--so-line) !important; }
.bw-group > h3 { margin: 0 -16px 16px !important; padding: 0 16px 12px; font-size: 20px !important; font-weight: 700 !important; border-bottom: 1px solid var(--so-line); display: flex; align-items: center; gap: 10px; }
.bw-group > h3::before { content: ''; width: 4px; height: 20px; border-radius: 2px; background: var(--so-accent); flex: 0 0 auto; }
.fc-card summary { font-size: 15px; font-weight: 600; color: var(--so-accent); padding: 8px 0; cursor: pointer; }
.fc-hint, .wf-muted, .muted, small, .small, .so-note, .so-pagesub, .so-grouplabel, .proto-eyebrow, .commercial-status {
  color: var(--so-muted); font-size: 14px; line-height: 1.45;
}

/* ── Fields: bold-ish label above, grey tile, unit small on the right ──── */
.fc-grid { gap: 14px 12px; }
body:has(.fc-shell) .fc-body > .fc-card .fc-grid { gap: 14px 12px; }
.fc-f { gap: 6px; font-size: 14px; font-weight: 600; color: var(--so-label); }
.fc-f > span { font-size: 14px; font-weight: 600; color: var(--so-label); line-height: 1.2; letter-spacing: 0.02em; }
.fc-f > span i { font-style: normal; font-weight: 500; color: var(--so-muted); }
/* Fields = dark inset wells with an edge; buttons/tiles = raised lighter grey */
input, select, textarea, .fc-f input, .fc-f select, .fc-f textarea, .wf-field input, .wf-field select {
  background: var(--so-field) !important;
  color: var(--so-text) !important;
  border: 1px solid var(--so-field-line) !important;
  border-radius: var(--so-r-s) !important;
  min-height: 52px;
  padding: 10px 14px;
  font-family: var(--so-font);
  font-size: 19px !important;
  font-weight: 600;
  box-shadow: none;
}
select { -webkit-appearance: none; appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%23b9bcc5' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E") !important;
  background-repeat: no-repeat !important; background-position: right 14px center !important; padding-right: 36px; }
input::placeholder, textarea::placeholder { color: #8a8d96; font-weight: 400; font-size: 16px; }
input:focus, select:focus, textarea:focus { outline: 2px solid var(--so-accent); outline-offset: 0; }
input[type="checkbox"], input[type="radio"] { min-height: 22px; accent-color: var(--so-accent); }
/* Field read-back lines only when a value really does not fit its box.
 *
 * DESCENDANT, not direct child. `full-value.js` puts `fv-long` on the field
 * HOST it finds by walking up (`hostOf`, up to 4 levels: a LABEL, an `fc-f`,
 * a `td`...) but inserts the readback as the CONTROL's next sibling. Where a
 * control sits in a wrapper — `span.fc-inwrap`, which the circuit sheet uses —
 * the readback is a grandchild of the host, so `.fv-long > .fv-readback` never
 * matched and the line above kept it hidden.
 * Measured at 320x568 on the circuit sheet: `#fck-desc` held the 63-character
 * designation "First-floor rear workshop sockets and external maintenance
 * store" in a 244px control with 636px of content, and its readback carried the
 * full string at `display:none`. The value was in the DOM and unreadable, which
 * is the exact failure INFORMATION_VISIBILITY_REQUIREMENTS.md forbids.
 *
 * The two deliberate exceptions below still win and are unaffected: `.obs-row`
 * (line ~354) is later in this file at equal specificity, and the `td.fv-long`
 * rule (line ~369) is more specific. */
.fv-readback { display: none !important; }
.fv-long .fv-readback { display: block !important; font-size: 14px; font-weight: 600; color: var(--so-muted); border-inline-start: 3px solid var(--so-accent); background: transparent; }
label { color: var(--so-text); }
.wf-field { font-size: 14.5px; font-weight: 600; color: var(--so-label); }

/* ── Tap tiles (segmented controls): grey tiles, selected one fills cyan ─ */
.fc-seg { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.fc-seg button {
  /* 44px is the floor, not 0. `min-width: 0` let the 1fr tracks collapse to whatever
     the content box allowed: measured 43.0156px for the inspection ✓/N/A/LIM group at
     390px, one pixel under the minimum touch target (WCAG 2.5.5, Apple HIG). .fc-insp
     sizes to its content, so a floor here widens the group rather than overflowing it
     -- three tiles need 156px and four need 212px inside a 338px card. .bw-sheet's own
     56px floor is more specific and unaffected. */
  min-height: 52px; min-width: 44px; padding: 8px 6px;
  border-radius: var(--so-r-s); border: 1px solid var(--so-line-strong);
  background: var(--so-panel-2); color: var(--so-text);
  font-family: var(--so-font); font-size: 17px; font-weight: 600; line-height: 1.15;
  overflow-wrap: anywhere;
}
.fc-seg button.on, .fc-seg button[aria-pressed="true"] {
  background: var(--so-accent) !important; border-color: var(--so-accent) !important;
  color: var(--so-accent-ink) !important; font-weight: 700;
}
.fc-seg-sm { grid-template-columns: repeat(4, minmax(0, 1fr)); }
/* two-answer rows (✓ / N/A) sit as two tiles, not a stretched pair */
.fc-seg:has(> button:nth-child(2):last-child) { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* ── Buttons ───────────────────────────────────────────────────────────── */
.btn, .so-btn, .pc-btn, button.btn {
  min-height: 48px; border-radius: 999px; border: 0;
  background: var(--so-panel-2); color: var(--so-text);
  font-family: var(--so-font); font-size: 16px; font-weight: 600;
}
.btn:hover, .so-btn:hover { background: #2c2e36; }
.btn-amber, .so-btn-lead, .btn-primary, button[type="submit"].btn {
  background: var(--so-accent) !important; color: var(--so-accent-ink) !important; border-color: var(--so-accent) !important; font-weight: 700;
}
.btn-amber:hover, .so-btn-lead:hover { background: var(--so-accent-press) !important; }
.btn[disabled], .so-btn[disabled], button[disabled] { opacity: 0.5; }
.wf-actions { display: flex; gap: 10px; flex-wrap: wrap; margin: 12px 0 4px; }
.wf-actions .btn { flex: 1 1 auto; }
a { color: var(--so-accent); }

/* ── Chrome: brand bar, identity row, context strip ────────────────────── */
.proto-header { background: var(--so-panel); color: var(--so-text); border-bottom: 1px solid var(--so-line); }
.proto-brand { color: var(--so-text); font-family: var(--so-font); font-weight: 800; letter-spacing: 0.01em; }
.proto-brand span { color: var(--so-accent); }
.proto-brand small { color: var(--so-muted); }
.so-maisie, .bw-ask, .proto-header .btn {
  border-radius: 999px; border: 0; background: var(--so-panel-2); color: var(--so-text); font-weight: 600;
}
.bw-chrome { background: var(--so-panel); border-bottom: 1px solid var(--so-line-strong); box-shadow: none; }
.bw-idrow { align-items: center; padding: 10px 12px 10px; gap: 10px; }
.bw-icon {
  width: 44px; height: 44px; border-radius: 50%; border: 0;
  background: var(--so-panel-2); color: var(--so-text); font-size: 24px; font-weight: 600;
}
.bw-title .bw-stepname, .bw-stepname { font-size: 22px; font-weight: 700; letter-spacing: -0.01em; font-family: var(--so-font); }
.bw-titlesub { display: flex; align-items: center; gap: 8px; }
.bw-idname { font-size: 14px; font-weight: 600; color: var(--so-muted); }
.bw-status { background: var(--so-panel-2); color: var(--so-muted); font-size: 10.5px; }
.bw-status.fc-status-draft { background: var(--so-accent-soft); color: var(--so-accent); }
.bw-context:not(:empty) { background: var(--so-page); border-bottom: 0; }
.bw-ctx-line { padding: 6px 12px; }
.bw-ctx-more { border: 0; background: var(--so-panel); color: var(--so-text); border-radius: 999px; min-height: 32px; box-shadow: var(--so-shadow); }
.bw-context.is-urgent { background: var(--so-warn); }
.bw-context.is-urgent .bw-ctx-more { background: var(--so-panel-2); border: 0; }
#save-state { color: var(--so-muted); min-height: 32px; display: inline-flex; align-items: center; font-size: 13px; }
.bw-seg b { background: var(--so-line-strong); }
.bw-seg b.part { background: var(--so-accent); }
.bw-seg b.done { background: var(--so-ok); }

/* ── Bottom bar: round back, Sections, one cyan Next pill ──────────────── */
body.bw-world .fc-footbar, .fc-footbar {
  background: linear-gradient(rgba(11, 11, 13, 0), var(--so-page) 35%);
  border-top: 0; padding: 14px 12px calc(12px + env(safe-area-inset-bottom, 0px)); gap: 10px; backdrop-filter: none;
}
body.bw-world .fc-footbar .btn, body.bw-world .fc-footbar .bw-sections, .fc-footbar .btn, .fc-footbar .bw-sections {
  min-height: 52px; border-radius: 26px; border: 0; font-size: 16px; font-weight: 600;
  background: var(--so-panel); color: var(--so-text); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
}
body.bw-world .fc-footbar .bw-sections small { font-size: 12.5px; font-weight: 600; color: var(--so-muted); }
body.bw-world .fc-footbar .btn-amber, .fc-footbar .btn-amber {
  background: var(--so-accent) !important; color: var(--so-accent-ink) !important; font-weight: 700; flex: 1 1 auto; box-shadow: none;
}

/* ── Rows, lists, tabs, tables ─────────────────────────────────────────── */
.so-row, .proto-row, .pc-row, .flow-row { background: var(--so-panel); color: var(--so-text); border-color: var(--so-line); min-height: 52px; }
.so-row:hover, .proto-row:hover { background: var(--so-panel-2); }

/* THE PRIMARY ACTION, RESTORED. app-shell.css:162 already paints `.so-row-lead`
   in the accent, but this file loads after it and `.so-row` above has the same
   specificity, so the plain panel background won and the lead row rendered
   identically to every other row. Measured on the running app: "New
   certificate" carried .so-row-lead and still computed rgb(27,29,36), the same
   as "Contractor details". The screen was built to have one obvious next
   action and had silently lost it. This re-states the intent after .so-row,
   so the cascade stops cancelling it. */
.so-row.so-row-lead { background: var(--so-accent); color: var(--so-accent-ink); border-color: var(--so-accent); }
.so-row.so-row-lead:hover { background: var(--so-accent-press); }
.so-row.so-row-lead .so-ico,
.so-row.so-row-lead .so-chev,
.so-row.so-row-lead strong { color: var(--so-accent-ink); }
.so-row.so-row-lead .so-rowtext small { color: color-mix(in oklab, var(--so-accent-ink) 78%, var(--so-accent)); }
.proto-tag, .so-state { background: var(--so-panel-2); color: var(--so-muted); border: 0; border-radius: 999px; padding: 2px 8px; font-size: 11px; }
.so-tabbar, .so-bottomnav, .proto-tabs, nav[role="navigation"] { background: var(--so-panel); border-top: 1px solid var(--so-line); }
.chip-btn.on, .chip-btn[aria-pressed="true"], .tab.active, .tab[aria-pressed="true"] {
  background: var(--so-accent) !important; border-color: var(--so-accent) !important; color: var(--so-accent-ink) !important; font-weight: 700;
}
.chip-btn, .tab { border-radius: 999px; }
table { background: var(--so-panel); color: var(--so-text); border-color: var(--so-line); }
th { background: var(--so-panel-2); color: var(--so-label); border-color: var(--so-line); font-weight: 600; }
td { border-color: var(--so-line); }
tbody tr:nth-child(even) > td { background: #1b1c22; }
tbody tr:hover > td { background: #23262e; }
.proto-notice { background: var(--so-panel); color: var(--so-muted); border-bottom: 1px solid var(--so-line); font-size: 12px; padding: 6px 12px; }
.proto-notice[hidden] { display: none !important; }
.product-startup { background: var(--so-page); color: var(--so-text); }

/* In-app report preview stays on paper. */
.cp-page, .cert-page, .proto-review, .wf-report-cover, .cpx-note { background: #fff !important; color: #16181d !important; }
.proto-review a { color: #16181d; }

/* Desktop editor gutters (kept from the previous theme) */
body:not(:has(.fc-shell)) #certs-body > .product-record-bar,
body:not(:has(.fc-shell)) #certs-body > .cert-toolbar,
body:not(:has(.fc-shell)) #certs-body > .wf-steps,
/* ⚠ `> div:not(...)` IS WHY THE DESKTOP EIC RAN TO THE WINDOW EDGE.
   Audited across all seven certificate types, every step, at 390 and 1440:
   38 screens each. Every EIC step measured its content at x=1 while every
   other type measured 31 -- because the EIC renders top-level
   `<section class="fc-card wf-section">`, and a SECTION is not a DIV, so the
   gutter never reached it. The .panel rule below gutters its own blocks
   correctly, which is why one screen showed two left edges and the CSS looked
   right in isolation.
   Jamie, 12 Oct 2026: "box margins arent the same throughout. Its very hard to
   work from this. doesnt work with my eyes."
   Matched on the elements instead of the tag name, so a section, an article or
   a div all get the same inset and nothing has to remember to be a div. */
body:not(:has(.fc-shell)) #certs-body > :not(.panel):not(.cert-sec):not(.fc-card):not(.wf-section):not(.so-page):not(.proto-home):not(script):not(style) {
  padding-left: var(--so-gutter) !important; padding-right: var(--so-gutter) !important;
}
/* ── THE SETTINGS DIALOG'S RIGHT PANE ──────────────────────────────────────
   Jamie, 12 Oct 2026, screenshotting Settings > Business: "everything is
   pushed right up to boundaries everywhere?"
   Measured at 1440: the dialog carries 24px of its own padding and the nav
   column's buttons are padded, but `.commercial-page` and
   `.commercial-content.pc-view` -- which hold the whole of Business, Team,
   Equipment, Templates, Connections, Plan, Billing, Data and Help -- have
   padding 0, so every heading, field and button in that pane sat hard against
   the pane edge while the nav beside it did not.
   Given the gutter rather than a one-off number, so it tracks the rest of the
   app at every width. */
.commercial-page > .commercial-content, .commercial-content.pc-view {
  padding-left: var(--so-gutter); padding-right: var(--so-gutter);
}

body:not(:has(.fc-shell)) #certs-body > .panel,
body:not(:has(.fc-shell)) #certs-body > .fc-card,
body:not(:has(.fc-shell)) #certs-body > .wf-section,
body:not(:has(.fc-shell)) #certs-body > .cert-sec { margin-left: var(--so-gutter) !important; margin-right: var(--so-gutter) !important; padding: var(--so-card-pad) !important; }

/* ── Carried over from the retired dark-theme.css (layout, not colour) ── */
/* ── Pilot fixes 18 Sep ─────────────────────────────────────────────────────
   1. Signed-out header: before sign-in there is no workspace, so no Maisie
      button, no save chip, no Desktop/On-site switch, no Drafts. Only the
      wordmark and the sign-in card. */
body.product-locked .proto-header > :not(.proto-brand){ display:none !important; }

/* 2. Tablet / desktop home and list pages sat in a 760px strip on the left
      with a darker, empty band to the right (iPad landscape). The page is now
      centred and the body behind it shares the same surface, so there is one
      page, not a column and a hole. */
@media (min-width: 900px){
  body.so-shell .so-page{ margin: 0 auto !important; max-width: 880px; }
  body.so-shell:has(#certs-body > .so-page) #certs-body,
  body.so-shell:has(#certs-body > .so-page) main#view-certs{ background: var(--so-page); }
}

/* ── Maisie's face (sqAiAvatar, app/bootstrap.js) ──────────────────────────── */
/* The real character art, not an icon: a 192px still, and an idle loop where she
   is a presence. She keeps .proto-avatar so the sizing rules already written
   against that class still reach her, but that class draws an amber disc with a
   centred letter in it, and both have to go or they sit behind her. The art is
   square and already has its own background, so it is cropped to the circle
   rather than being given one. object-fit keeps her face centred at any size the
   callers ask for. */
.sq-ava{ display:inline-block; flex:0 0 auto; line-height:0; border-radius:50%;
  overflow:hidden; background:none; padding:0; font-size:0;
  box-shadow:0 0 0 1px var(--so-line); }
.sq-ava img, .sq-ava video{ display:block; width:100%; height:100%; object-fit:cover; border-radius:50%; }
/* The wait: a ring around her, not motion applied to her own artwork. */
.sq-ava.is-think, .mc-pending .sq-ava{ animation:m-think 1.6s ease-in-out infinite; }
@keyframes m-think{
  0%,100%{ box-shadow:0 0 0 0 rgba(240,180,41,.6), 0 0 0 1px var(--so-line); }
  55%    { box-shadow:0 0 0 5px rgba(240,180,41,0), 0 0 0 1px var(--so-line); }
}
@media (prefers-reduced-motion: reduce){
  .sq-ava.is-think, .mc-pending .sq-ava{ animation:none; }
}
/* The controls that carry her are text buttons; she sits on the baseline of
   their label rather than stretching the row. */
.bw-ask, .fc-ask-top, .so-maisie, .proto-header .btn:has(.sq-ava){ display:inline-flex; align-items:center; gap:7px; }

/* ── Maisie chat (app/maisie-chat.js) ─────────────────────────────────────── */
#proto-dialog:has(.mc-sheet){ padding: 0; width: min(560px, calc(100% - 24px)); max-height: 88dvh; border-radius: 18px; overflow: hidden; }
#proto-dialog:has(.mc-sheet) > .proto-close{ display: none; }
.mc-sheet{ display: flex; flex-direction: column; height: min(88dvh, 760px); background: var(--so-panel); color: var(--so-text); }
.mc-head{ display: flex; align-items: center; gap: 12px; padding: 16px 18px; border-bottom: 1px solid var(--so-line); background: var(--so-page); }
.mc-head .proto-avatar{ width: 36px; height: 36px; font-size: 16px; }
.mc-title{ display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.mc-title strong{ font-size: 18px; line-height: 1.1; }
.mc-title small{ font-size: 12px; color: var(--so-muted); margin-top: 3px; }
.mc-close{ width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--so-line); background: var(--so-panel); color: var(--so-text); font-size: 24px; line-height: 1; cursor: pointer; }
.mc-log{ flex: 1 1 auto; min-height: 220px; overflow-y: auto; padding: 18px 18px 6px; display: flex; flex-direction: column; gap: 12px; -webkit-overflow-scrolling: touch; }
.mc-hint{ margin: 4px 0; padding: 16px 18px; border: 1px dashed var(--so-line-strong); border-radius: 12px; color: var(--so-muted); font-size: 14.5px; line-height: 1.5; }
.mc-turn{ max-width: 88%; display: flex; flex-direction: column; gap: 4px; }
.mc-user{ align-self: flex-end; align-items: flex-end; }
.mc-ai{ align-self: flex-start; }
.mc-who{ font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--so-muted); padding: 0 6px; }
.mc-text{ padding: 12px 14px; border-radius: 14px; font-size: 15.5px; line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; }
.mc-user .mc-text{ background: var(--so-accent); color: var(--so-accent-ink); border-bottom-right-radius: 5px; }
.mc-ai .mc-text{ background: var(--so-panel-2); border: 1px solid var(--so-line); border-bottom-left-radius: 5px; }
.mc-pending .mc-text{ color: var(--so-muted); animation: mc-blink 1.1s ease-in-out infinite; }
@keyframes mc-blink{ 0%,100%{ opacity: .45 } 50%{ opacity: 1 } }
.mc-status{ margin: 0; padding: 0 18px; min-height: 18px; font-size: 13px; color: var(--so-bad); }
.mc-status:empty{ min-height: 0; }
.mc-bar{ display: flex; align-items: flex-end; gap: 8px; padding: 10px 14px 12px; border-top: 1px solid var(--so-line); background: var(--so-panel); }
.mc-bar textarea{ flex: 1 1 auto; min-height: 48px; max-height: 140px; padding: 13px 14px; border-radius: 14px; border: 1px solid var(--so-line-strong); background: var(--so-panel-2); color: var(--so-text); font: inherit; font-size: 16px; line-height: 1.35; resize: none; }
.mc-bar textarea:focus{ outline: 2px solid var(--so-accent); outline-offset: 1px; }
.mc-mic{ display: inline-flex; align-items: center; gap: 6px; min-height: 48px; padding: 0 14px; border-radius: 14px; border: 1px solid var(--so-line-strong); background: var(--so-panel-2); color: var(--so-text); font: inherit; font-size: 14px; font-weight: 700; cursor: pointer; white-space: nowrap; }
.mc-mic.is-on{ background: #ff6e61; border-color: #ff6e61; color: #fff; animation: mc-blink 1.4s ease-in-out infinite; }
.mc-send{ width: 48px; height: 48px; border-radius: 14px; border: 0; background: var(--so-accent); color: var(--so-accent-ink); display: inline-grid; place-items: center; cursor: pointer; flex: 0 0 auto; }
.mc-send:disabled, .mc-mic:disabled, .mc-bar textarea:disabled{ opacity: .55; cursor: default; }
.mc-note{ margin: 0; padding: 8px 18px calc(12px + env(safe-area-inset-bottom, 0px)); font-size: 12px; line-height: 1.45; color: var(--so-muted); background: var(--so-panel); }
@media (max-width: 560px){
  #proto-dialog:has(.mc-sheet){ width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh; margin: 0; border-radius: 0; border: 0; }
  .mc-sheet{ height: 100dvh; }
  .mc-head{ padding-top: calc(14px + env(safe-area-inset-top, 0px)); }
  .mc-mic span{ display: none; }
  .mc-mic{ width: 48px; padding: 0; justify-content: center; }
}

/* Toast */
#toast{ background: var(--so-panel-2) !important; color: var(--so-text) !important; border: 1px solid var(--so-line-strong); box-shadow: 0 8px 28px rgba(0,0,0,.5); font-size: 15px; line-height: 1.4; }

/* Legacy !important rules from mobile-polish / flow-ui / product, retired here. */
.proto-phone .fc-shell .btn, .proto-phone #proto-dialog .btn { border-radius: 999px !important; }
.flow-tester-actions .btn { border-radius: 999px !important; }
.flow-tester-actions .btn:first-child { background: var(--so-accent) !important; color: var(--so-accent-ink) !important; border: 0 !important; }
.flow-instrument-details { border-top: 1px solid var(--so-line); }
.fc-insec-b > .fc-secrest { border-radius: 999px !important; }
/* Checks: section header is a clear grey band, system font, bigger */
.fc-insec { border-top: 1px solid var(--so-line-strong) !important; border-radius: 0 !important; }
.fc-insec-h { background: var(--so-panel-2) !important; border-bottom: 1px solid var(--so-line-strong) !important; min-height: 56px; padding: 10px 14px; }
.fc-insec-t { font-family: var(--so-font) !important; font-size: 17px !important; font-weight: 700 !important; color: var(--so-text) !important; }
.fc-insec-h::before { color: var(--so-accent) !important; font-size: 22px !important; }
.fc-insec-b { background: var(--so-panel); }
.fc-insec-n { font-family: var(--so-font) !important; font-size: 13px !important; }
.proto-header > .btn, .proto-home-head > .btn { border-radius: 999px !important; }
body.so-shell .proto-header #save-state { border: 0; background: var(--so-panel-2); color: var(--so-muted); border-radius: 999px; }
body.so-shell .proto-header #save-state[data-state=saved] { background: rgba(74, 222, 128, 0.14); color: var(--so-ok); border: 0; }
body.so-shell .proto-header #save-state[data-state=conflict], body.so-shell .proto-header #save-state[data-state=signed-out] { background: rgba(255, 107, 96, 0.16); color: var(--so-bad); border: 0; }

/* Spacing and small fixes after the first live render */
.fc-card > .fc-f:has(> .fc-seg) { margin-bottom: 14px; }
.fc-card details, .flow-instrument-details { border-radius: 0; }
.fc-f select { font-size: 17px !important; }
.proto-phone .fc-shell .btn:not(.btn-amber), .fc-shell .btn:not(.btn-amber) { background: var(--so-panel-2) !important; color: var(--so-text) !important; border: 0 !important; }
.fc-footbar .btn:not(.btn-amber), body.bw-world .fc-footbar .btn:not(.btn-amber) { background: var(--so-panel) !important; }
.fc-shell .flow-tester-actions .btn:first-child, .proto-phone .fc-shell .flow-tester-actions .btn:first-child { background: var(--so-accent) !important; color: var(--so-accent-ink) !important; }
fieldset { box-shadow: none; border: 1px solid var(--so-line); border-radius: var(--so-r-s); }
/* Provisional assessment strip: quiet tint, not a solid block. */
.wf-assessment, .wf-live-assessment { background: var(--so-accent-soft) !important; border-left: 4px solid var(--so-accent); color: var(--so-text) !important; border-radius: var(--so-r-s); padding: 12px 16px !important; font-size: 14px !important; line-height: 1.45; }
.wf-assessment.wf-bad, .wf-live-assessment.wf-bad { background: rgba(255, 107, 96, 0.16) !important; border-left-color: var(--so-bad); }
.wf-assessment.wf-ok, .wf-live-assessment.wf-ok { background: rgba(74, 222, 128, 0.14) !important; border-left-color: var(--so-ok); }
.wf-live-assessment strong { font-size: 15px !important; color: var(--so-text) !important; }
.wf-live-assessment.wf-bad strong { color: var(--so-bad) !important; }
.wf-live-assessment.wf-ok strong { color: var(--so-ok) !important; }
.wf-live-assessment small { color: var(--so-muted) !important; font-size: 13px !important; display: block; margin-top: 2px; }

/* ── Observations: one finding = one tidy block ─────────────────────────────
   Row 1: number · code · what you found (wide). Row 2: where · regulation.
   Row 3: Maisie · photo · delete. Read-back echoes are off inside a finding. */
.obs-row { display: flex !important; flex-wrap: wrap; gap: 10px 12px; align-items: center; padding: 16px 0 18px; margin: 0 !important; border-bottom: 1px solid var(--so-line); }
.obs-row:last-of-type { border-bottom: 0; }
.obs-row .obs-num { order: 0; flex: 0 0 34px; height: 34px; border-radius: 10px; background: var(--so-panel-2); color: var(--so-text); font-weight: 800; display: inline-flex; align-items: center; justify-content: center; }
.obs-row .obs-code { order: 1; flex: 0 0 104px; width: 104px !important; min-width: 0; font-weight: 800; }
.obs-row .obs-txt { order: 2; flex: 1 1 260px; min-width: 0; width: auto !important; }
.obs-row .obs-loc { order: 3; flex: 0 1 180px; min-width: 0; width: auto !important; margin-inline-start: 46px; }
.obs-row .obs-reg { order: 4; flex: 0 1 120px; min-width: 0; width: auto !important; }
.obs-row > .chip { order: 5; }
.obs-row > .btn, .obs-row > label.btn { order: 6; flex: 0 0 auto; }
.obs-row > .icon-btn { order: 7; flex: 0 0 auto; margin-inline-start: auto; }
.obs-row .fv-readback { display: none !important; }
.obs-row .obs-code.obs-C1, .obs-row .obs-code.obs-C2 { color: var(--so-bad); border-color: var(--so-bad) !important; }
.obs-row .obs-code.obs-C3 { color: #ffd166; border-color: #ffd166 !important; }
.obs-row .obs-code.obs-FI { color: var(--so-accent); border-color: var(--so-accent) !important; }
@media (max-width: 899px) {
  .obs-row .obs-txt { flex-basis: 100%; }
  .obs-row .obs-loc { margin-inline-start: 0; flex-basis: 100%; }
  .obs-row .obs-reg { flex-basis: 100%; }
}
@media (min-width: 900px) {
  .obs-row > .btn:first-of-type { margin-inline-start: 46px; }
}

/* Schedule table: test cells wide enough for a 4-character reading; no echo under a cell. */
.ckt-table td input { min-width: 76px !important; }
.ckt-table td.fv-long > .fv-readback, td.fv-long > .fv-readback { display: none !important; }
.ckt-table td.fv-long, .ckt-table .fv-long { grid-column: auto !important; }
/* Phone landscape: the bottom bar must not eat half the screen. */
@media (max-height: 480px) and (orientation: landscape) {
  .fc-footbar { padding: 6px 12px calc(6px + env(safe-area-inset-bottom, 0px)) !important; }
  .fc-footbar .btn, .fc-footbar .bw-sections { min-height: 44px !important; padding-top: 6px !important; padding-bottom: 6px !important; }
  .fc-footbar .bw-sections small, .fc-footbar .bw-sections span:last-child { display: none; }
}
/* Autosave label: a conflict is never green. */
.autosave.is-conflict, .cert-topbar .autosave.is-conflict { color: var(--so-bad) !important; opacity: 1 !important; }
/* No display font left on the phone editor: everything system */
.fc-schedbar-n, .fc-schedbar-n *, .fc-shell h1, .fc-shell h2, .fc-shell h3, .fc-shell .bw-count, .fc-shell [class*="fa-sheet-h"] { font-family: var(--so-font) !important; }
.fc-schedbar-n { font-size: 20px; font-weight: 700; }

/* ── Schedule table (Table view): readable grid, pinned columns that don't overlap ── */
.bw-table-host .fc-table-scroll { background: var(--so-panel); border: 1px solid var(--so-line-strong); }
.bw-table-host table.ckt-table { border-collapse: separate; border-spacing: 0; background: var(--so-panel); }
.ckt-table thead th { background: var(--so-panel-2) !important; color: var(--so-text) !important; font-size: 13px !important; font-weight: 700; padding: 8px 10px; border-bottom: 1px solid var(--so-line-strong); border-right: 1px solid var(--so-line); white-space: nowrap; }
.ckt-table thead tr:first-child th { color: var(--so-muted) !important; font-weight: 600; letter-spacing: .02em; text-transform: uppercase; font-size: 11.5px !important; background: var(--so-panel) !important; border-bottom: 0; }
.ckt-table tbody td { padding: 6px 6px; border-bottom: 1px solid var(--so-line); border-right: 1px solid var(--so-line); vertical-align: middle; }
.ckt-table thead th:nth-child(-n+3), .ckt-table tbody td:nth-child(-n+3) { background: var(--so-panel) !important; }
.ckt-table thead tr:nth-child(2) th:nth-child(-n+3) { background: var(--so-panel-2) !important; }
.ckt-table tbody tr:nth-child(even) td { background: var(--so-panel-2) !important; }
.ckt-table tbody tr:nth-child(even) td input { background: var(--so-page) !important; }
.ckt-table th:nth-child(2), .ckt-table td:nth-child(2) { width: 210px !important; min-width: 210px !important; max-width: 210px !important; }
.ckt-table th:nth-child(3), .ckt-table td:nth-child(3) { left: 254px !important; box-shadow: 2px 0 0 var(--so-line-strong) !important; }
.ckt-table td:nth-child(1) { text-align: center; font-weight: 700; font-size: 16px; }
.ckt-table td input, .bw-table-host td input { min-width: 76px !important; min-height: 40px !important; font-size: 15px !important; font-weight: 600; border: 1px solid var(--so-field-line) !important; background: var(--so-field) !important; color: var(--so-text) !important; border-radius: 8px; padding: 4px 8px; }
.ckt-table td:nth-child(2) input { width: 100% !important; min-width: 0 !important; }
.ckt-table td input:focus { outline: 2px solid var(--so-accent) !important; outline-offset: 0; }
.ckt-table td input::placeholder { color: var(--so-muted); font-weight: 500; }
.ckt-table .ckt-rescell .ckt-res { font-size: 12px; font-weight: 700; color: var(--so-muted); background: var(--so-panel-2); border: 1px solid var(--so-line-strong); border-radius: 999px; padding: 3px 10px; }
.ckt-table th.ckt-reshead { color: var(--so-muted) !important; }
.ckt-table td .ckt-rm, .ckt-table td button[aria-label*="emove"] { color: var(--so-muted); }
.bw-table-host .ckt-bandbar { background: var(--so-panel); border: 1px solid var(--so-line); }
.bw-table-host .ckt-bandbar .chip-btn { background: var(--so-panel-2); border: 1px solid var(--so-line-strong); color: var(--so-text); }
.bw-table-host .ckt-bandbar .chip-btn.on { background: var(--so-accent); border-color: var(--so-accent); color: var(--so-accent-ink); }
.bw-table-sheet .bw-note { font-size: 14px; color: var(--so-muted); margin: 0 0 10px; }
.ckt-table .ckt-res-ok { color: var(--so-ok); border-color: var(--so-ok); }
.ckt-table .ckt-res-bad { color: var(--so-bad); border-color: var(--so-bad); }
/* Phone landscape: the schedule gets the height — compact head, no explainer */
@media (max-height: 480px) {
  .bw-table-sheet .bw-note { display: none; }
  .bw-table-sheet .bw-head { min-height: 52px; padding: 4px 10px; }
  .bw-table-sheet .bw-sheet-body { padding: 8px 10px; }
  .bw-table-host .ckt-bandbar { padding: 6px 10px; margin-bottom: 8px; gap: 6px; }
  .bw-table-host .ckt-bandbar .chip-btn { min-height: 34px; padding: 4px 12px; font-size: 13px; }
  .ckt-table thead tr:first-child th { padding: 3px 10px; font-size: 10.5px !important; }
  .ckt-table thead th { padding: 6px 10px; }
  .ckt-table tbody td { padding: 4px 6px; }
  .ckt-table td input, .bw-table-host td input { min-height: 38px !important; }
}

/* Section ticks: green, on the card title and the page title once complete */
.so-tick { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 50%; background: var(--so-ok); color: #062a1a; font-size: 14px; font-weight: 800; margin-left: auto; flex: 0 0 auto; }
.fc-card > h4 .so-tick { margin-left: auto; }
.bw-stepname .so-tick { margin-left: 8px; vertical-align: middle; }
.fc-card.so-complete > h4::before { background: var(--so-ok); }

/* THE STEP FOOTER FITS ITS OWN WORDS. board-workspace.css:775 sets
   `white-space:normal; overflow-wrap:anywhere` on these controls. The intent is
   right -- a long section name must not overflow the bar -- but `anywhere`
   permits a break INSIDE a word, so at 375px the back button rendered as
   "Suppl / y" and "Distribution boards" went to two lines. Measured: the bar
   has 351px usable and the three controls wanted 78 + 94 + 159 plus two 10px
   gaps = exactly 351, so they were sitting on the limit.

   Words stay whole; a label with nowhere left to go truncates at a word
   boundary instead of shattering. The sections control keeps its two-line
   label/count stack, which is deliberate. Same three controls, same order,
   same sizes -- this only changes how the text inside them breaks. */
body.bw-world .fc-footbar .btn{
  white-space: nowrap; overflow-wrap: normal; word-break: normal;
  min-width: 0; overflow: hidden; text-overflow: ellipsis;
  padding-inline: 10px;
}
body.bw-world .fc-footbar .bw-sections{ overflow-wrap: normal; word-break: normal; }

/* ══ REFINEMENT LAYER ══════════════════════════════════════════════════════
   Everything below is visual only: colour, type, radius, weight and spacing
   inside boxes that already exist. No element moves, changes order, or gains
   or loses a control.

   The brief: it felt cheap and template-made. The cause was structural, not
   chromatic. One rule above paints .so-page, .so-group, .panel, .card,
   .fc-card, .wf-section, .pc-view, .so-empty, .commercial-card, #proto-sheet,
   #proto-dialog, .product-login, .flow-card, .flow-panel, .bw-panel,
   .bw-section, details AND fieldset with the same fill, the same 1px line, the
   same radius and the same shadow. Fifteen different jobs wearing one costume,
   so a page reads as a stack of identical rounded rectangles and a fieldset
   inside a card becomes a card inside a card. */

/* 1 · A CONTAINER STOPS LOOKING LIKE A CARD JUST FOR BEING A CONTAINER.
      Nested groups keep their spacing and lose the box. Depth now means
      something because only a real card has a card's treatment. */
.so-group fieldset, .so-group details,
.fc-card fieldset, .fc-card details,
.wf-section fieldset, .wf-section details,
.so-group .wf-section, .fc-card .wf-section {
  background: transparent; border: 0; box-shadow: none; border-radius: 0;
  padding-inline: 0;
}
.so-group fieldset > legend, .fc-card fieldset > legend, .wf-section fieldset > legend {
  padding: 0; font-family: var(--so-display); word-spacing: var(--so-display-word); font-weight: 600; font-size: 15px;
  letter-spacing: -.01em; color: var(--so-text);
}
/* A nested group separates with a rule and air, not another border. */
.so-group fieldset + fieldset, .fc-card fieldset + fieldset {
  margin-top: 22px; padding-top: 20px; border-top: 1px solid var(--so-line);
}

/* 2 · RADIUS CARRIES ROLE. One roundness everywhere is the tell. A card is
      generous, a control is crisp, a pill is a pill. */
.so-group, .fc-card, .panel, .card { border-radius: 16px; }
input, textarea, select { border-radius: 10px; }
.so-group > .so-row { border-radius: 0; }
.so-group > .so-row:first-child { border-top-left-radius: 15px; border-top-right-radius: 15px; }
.so-group > .so-row:last-child  { border-bottom-left-radius: 15px; border-bottom-right-radius: 15px; }

/* THE SPACE GLYPH IN CLASH DISPLAY IS TOO NARROW, so every heading set in it
   loses its word gaps: "Board tools" renders as "Boardtools" and "Remove this
   board" as "Removethisboard". Measured in the shipped woff2 against the other
   faces at the same size: Clash 0.154em (600) and 0.140em (700), where Satoshi
   is 0.270em and the system face 0.250em. It is the FONT, not any one screen --
   Jamie reported it as "tools ui page is still old", but it was every heading
   in the display face across the product, and the negative letter-spacing these
   rules carry for tightness subtracts from the gap again.
   word-spacing: .11em puts both weights back at roughly a normal space. It is
   set wherever --so-display is, because the defect travels with the family. */
/* 3 · A TYPE SCALE, IN THE BRAND'S OWN FACES. Clash Display carries headings
      and the counts the brand treats as punctuation; Satoshi carries reading
      text. Steps are deliberate rather than three weights of one size. */
h1, h2, .so-page > h1, .so-h1 {
  font-family: var(--so-display); word-spacing: var(--so-display-word);
  font-weight: 700; letter-spacing: -.035em; line-height: 1.04;
}
h3, h4 { font-family: var(--so-display); word-spacing: var(--so-display-word); font-weight: 600; letter-spacing: -.02em; }
.so-grouplabel {
  font-family: var(--so-font); font-size: 11.5px; font-weight: 700;
  letter-spacing: .09em; text-transform: uppercase; color: var(--so-muted);
  margin: 26px 4px 10px;            /* more above a heading than below it */
}
.so-row .so-rowtext strong { font-weight: 650; letter-spacing: -.012em; }
/* ── LABELS ARE READ, NOT DECODED ──────────────────────────────────────────
   Jamie, 12 Oct 2026: "why doesnt it feel nice to work from?"
   Measured before changing anything: label contrast was 7.65:1 against the
   page, comfortably past the 4.5:1 standard -- so it was never the COLOUR.
   It was 12px UPPERCASE with positive letterspacing. Capitals remove
   word-shape, which is what people actually read by, so a label like
   "CONSTRUCTION · TYPED SIGNATURE · ENTERED BY SIGNER" has to be decoded
   letter by letter instead of recognised at a glance. Every label on a form
   of 24 fields, on eight steps.
   Sentence case at 13px, the tracking relaxed to normal. Still quieter than
   the value it labels -- weight and colour carry that -- but now legible at
   a glance. */
label, .wf-field > span, .certificate-address label,
.cert-sec label, .fc-card label, .wf-section label, .job-cust label {
  font-size: 13px; font-weight: 600; letter-spacing: 0;
  text-transform: none; color: var(--so-label, var(--so-muted));
}
input, textarea, select { font-family: var(--so-font); font-weight: 550; letter-spacing: -.008em; }

/* 4 · SOFT ACTION COLOUR. Full-saturation fills were doing the shouting. A
      desaturated green holds the primary action with light text on it
      (5.4:1) instead of a hard neon block. */
.so-row.so-row-lead, .btn-amber, body.bw-world .fc-footbar .btn-amber {
  background: linear-gradient(180deg, #35796f, #2d675f) !important;
  border: 1px solid #3f8d82 !important;
  color: #f0fbf9 !important;
  box-shadow: 0 1px 0 rgba(255,255,255,.07) inset, 0 6px 18px rgba(0,0,0,.34);
}
.so-row.so-row-lead strong, .so-row.so-row-lead .so-ico, .so-row.so-row-lead .so-chev { color: #f0fbf9 !important; }
.so-row.so-row-lead .so-rowtext small { color: #bcded8 !important; }
.so-row.so-row-lead:hover, .btn-amber:hover { background: linear-gradient(180deg, #3d8a7f, #33756c) !important; }

/* 5 · THE CARD ITSELF, QUIETER. A hairline and real depth instead of a hard
      outline on every box. */
.so-group, .fc-card, .panel, .card {
  border-color: rgba(255,255,255,.055);
  box-shadow: 0 1px 0 rgba(255,255,255,.035) inset, 0 10px 28px rgba(0,0,0,.40);
}

/* ══ FORM SCREENS ══════════════════════════════════════════════════════════
   41 fields on the Supply step alone, every one an identical black box with an
   identical grey label. Nothing told you what kind of value a box wanted, a
   measurement did not look like a measurement, and the unit was an italic <i>
   that read as an accident. Still visual only: nothing moves, nothing is added
   or removed, the grid and field order are untouched. */

/* 1 · A UNIT IS A UNIT, NOT AN ITALIC. `Ze <i>Ω</i>` was rendering in italics
      beside the label as though it were emphasis. It is a measurement unit, so
      it reads as a small upright token held slightly away from the name. */
.fc-f > span > i, label.fc-f span i {
  font-style: normal; font-weight: 650; font-size: 11px;
  letter-spacing: .06em; color: var(--so-muted);
  margin-inline-start: 5px; padding: 1px 5px;
  border: 1px solid var(--so-line); border-radius: 5px;
  /* The chip must not make its own label line taller, or every row holding a
     unit sits 3px below its neighbour -- which is exactly the misalignment it
     was meant to help with. Taken out of the line box so the label measures
     the same with or without it. */
  display: inline-block; line-height: 1; vertical-align: middle;
  position: relative; top: -1px; max-height: 16px;
}
.fc-f > span { line-height: 1.25; min-height: 1.25em; }

/* 2 · READINGS LINE UP. An EICR is read down a column -- Ze, Zs, R1+R2, IR.
      Proportional digits make 1.11 and 0.88 different widths and the eye has
      to re-find the decimal on every row. Tabular figures put every digit on
      the same advance, so a column of results scans straight down. */
input[inputmode="decimal"], input[inputmode="numeric"], input[type="number"],
.fc-ckt-row input, .fc-table input {
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
  letter-spacing: 0;
}

/* 3 · A FIELD IS A WELL YOU CAN FIND WITH GLOVES ON. The fill sat at #0e0f13
      against a #08090b page -- a 2.4-point luminance step, so an input was
      practically a hole in the card. It now sits clearly inside its card, and
      focus is unmistakable rather than a hairline: on site, in daylight, you
      need to see which box you are in. */
.fc-f input, .fc-f textarea, .fc-f select,
.wf-field input, .wf-field textarea, .wf-field select,
.certificate-address input, .certificate-address textarea {
  background: #10131a;
  transition: background .12s ease, border-color .12s ease, box-shadow .12s ease;
}
.fc-f input:hover, .fc-f textarea:hover, .wf-field input:hover { border-color: var(--so-line-strong); }
.fc-f input:focus, .fc-f textarea:focus, .fc-f select:focus,
.wf-field input:focus, .wf-field textarea:focus, .wf-field select:focus,
.certificate-address input:focus, .certificate-address textarea:focus {
  outline: none;
  background: #151a23;
  border-color: #4fb3a6 !important;
  box-shadow: 0 0 0 3px rgba(79,179,166,.20);
}
::placeholder { color: #6b7280; opacity: 1; font-weight: 450; }

/* 4 · LABEL AND FIELD ARE ONE THING; FIELDS ARE SEPARATE THINGS. The grid ran
      a 14px row gap against a 6px label gap, so a label sat almost as close to
      the field above it as to its own. Tightening the pair and opening the
      rows makes each row a unit you read in one go. */
.fc-grid { row-gap: 20px; }
.fc-f { gap: 7px; }
.fc-f > span { line-height: 1.25; }

/* 5 · THE REMAINING HARD FILLS. Selected segments and inline action pills were
      still painting the raw accent as a solid block, which is the "hard full
      colour" that made the screens shout. Same hue, same meaning, same
      position -- just held at a weight that sits in the page instead of
      jumping out of it. Selected state keeps a clear edge so it is still
      obvious which option is chosen. */
.fc-seg button.on, .fc-seg button[aria-pressed="true"],
.chip-btn.on, .chip-btn[aria-pressed="true"],
.tab.active, .tab[aria-pressed="true"] {
  background: linear-gradient(180deg, #2c6a62, #255a53) !important;
  border-color: #3f8d82 !important;
  color: #eafaf7 !important;
  font-weight: 700;
}
.btn.btn-amber, button.btn-amber, .fc-sched, .btn-accent {
  background: linear-gradient(180deg, #35796f, #2d675f) !important;
  border: 1px solid #3f8d82 !important;
  color: #f0fbf9 !important;
}
/* A plain secondary button stops competing: it is a quiet surface with a real
   edge, so the primary action is the only filled thing in view. */
.btn:not(.btn-amber):not(.btn-accent):not(.so-row-lead) {
  background: var(--so-panel-2);
  border: 1px solid var(--so-line);
  color: var(--so-text);
}

/* ══ DISCLOSURES MUST LOOK LIKE DISCLOSURES ═══════════════════════════════
   Jamie, on the Supply step: "its because they are on a dropdown thats not
   apparent its a dropdown. its just a heading with no way to tell it opens
   more information to be filled".

   He is right, and this is a correctness bug rather than a styling one. A
   <summary> only draws its disclosure triangle while it is display:list-item;
   these are display:flex, which removes the marker completely. So "Main
   switch, electrode and the rest" -- which holds the main switch, the earth
   electrode and its Ra -- rendered as an ordinary heading. Fields an engineer
   cannot see are fields that do not get filled, and the certificate goes out
   incomplete. It also fooled me: I probed this screen, could not find the
   electrode inputs, and wrongly reported that we captured no electrode data.

   An explicit chevron, drawn by the summary itself, so it no longer depends on
   a native marker that flex has already suppressed. It rotates on open, and
   the label says there is more underneath. */
.fc-card summary, .fc-more summary, .jl-more summary, .certificate-address-more summary {
  display: flex; align-items: center; gap: 8px;
  position: relative; padding-inline-end: 30px;
}
.fc-card summary::-webkit-details-marker, .fc-more summary::-webkit-details-marker { display: none; }
.fc-card summary::after, .fc-more summary::after,
.jl-more summary::after, .certificate-address-more summary::after {
  content: ""; position: absolute; right: 8px; top: 50%;
  width: 9px; height: 9px; margin-top: -6px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); transform-origin: 70% 70%;
  transition: transform .15s ease; opacity: .85;
}
details[open] > summary::after { transform: rotate(-135deg); }
/* A closed section that holds entries still to make should read as an invitation,
   not as a title someone forgot to style. */
.fc-card summary, .fc-more summary {
  border: 1px solid var(--so-line); border-radius: 10px;
  padding-block: 10px; padding-inline-start: 12px;
  background: var(--so-panel-2);
}
details[open] > summary { margin-bottom: 10px; }
.fc-card summary:hover, .fc-more summary:hover { border-color: var(--so-line-strong); }

/* ══ FIELD ROWS LINE UP ════════════════════════════════════════════════════
   Jamie: "misaligned boxes". In the two-column grid each cell sizes itself, so
   a label that wraps to two lines ("MAIN SWITCH RATED VOLTAGE") pushes its
   input down while its neighbour's one-line label ("ELECTRODE TYPE") leaves
   the input high. Every row with uneven label lengths stepped up and down, and
   a form reading like that looks broken however good the colours are.

   The cell now fills the row height and the control sits at the bottom of it,
   so inputs align across a row whatever the labels do -- one line, two, or
   three. No column count, order or field changes; only where the control sits
   inside the box it already occupied. */
.fc-grid > .fc-f { height: 100%; justify-content: flex-end; }
.fc-grid > .fc-f > span { margin-bottom: auto; }

/* A checkbox is a control, so it gets a control's target. The "installation is
   at the same address" box measured 23x26 -- half the 44px minimum, and it is
   one of the most consequential taps on the job step, because it decides
   whether the installation address follows the customer's. Enlarged with the
   accent on the checked state so it is obvious which way it is set. */
.fc-body input[type="checkbox"], .fc-body input[type="radio"] {
  width: 24px; height: 24px; margin: 10px; accent-color: var(--so-accent);
  flex: 0 0 auto; cursor: pointer;
}
.fc-body label:has(> input[type="checkbox"]),
.fc-body label:has(> input[type="radio"]) { min-height: 44px; align-items: center; cursor: pointer; }

/* ══ THE EDITABLE SCHEDULE ═════════════════════════════════════════════════
   Measured at 844x390 landscape with the table open: all 24 cell inputs were
   38px tall. The table is the one screen where you tap cell after cell after
   cell, so it is the worst place in the app to be under the 44px minimum that
   every other surface already meets -- and it is used one-handed, on site,
   often with gloves. The row grows to suit; nothing is repositioned.

   The sticky first column and sticky header were already right and are left
   exactly as they are. */
.fc-table input, .fc-table select, table.fc-sched input, table.fc-sched select,
.bw-table input, .bw-table select {
  min-height: 44px; font-size: 16px;   /* 16px also stops iOS zooming on focus */
  font-variant-numeric: tabular-nums;
}
.fc-table td, .bw-table td, table.fc-sched td { padding-block: 3px; }
/* The cell you are in should be obvious when the row is one of twenty. */
.fc-table input:focus, .bw-table input:focus, table.fc-sched input:focus {
  outline: none; box-shadow: 0 0 0 3px rgba(79,179,166,.26); border-color: #4fb3a6 !important;
  position: relative; z-index: 2;
}

/* The report picker in the preview bar: which document you are looking at. */
.cpv-reports { display: inline-flex; gap: 6px; margin-inline-end: 10px; flex-wrap: wrap; }
.cpv-reports .btn { min-height: 38px; }
/* The danger report prints as its own document, not as a page of the report. */
.edr h1 { font-family: var(--so-display); word-spacing: var(--so-display-word); letter-spacing: -.03em; margin: 0 0 6px; }
.edr-lede { margin: 0 0 14px; }
.edr-meta { display: grid; grid-template-columns: max-content 1fr; gap: 4px 14px; margin: 0 0 16px; }
.edr-meta dt { font-weight: 700; }
.edr-meta dd { margin: 0; }
.edr-table { width: 100%; border-collapse: collapse; }
.edr-table th, .edr-table td { border: 1px solid var(--so-line); padding: 7px 9px; text-align: left; vertical-align: top; }
.edr-code { font-weight: 800; text-align: center; white-space: nowrap; }
.edr-C1 { color: var(--so-bad); }
.edr-action { margin-top: 14px; font-weight: 650; }
.edr-limit { margin-top: 10px; font-size: 12.5px; color: var(--so-muted); }


/* ── FIELD WIDTH IS INFORMATION ────────────────────────────────────────────
   Measured on the EIC at 1440: 24 fields, median width 1314px, maximum
   1314px, and only FIVE distinct widths between them. A person's name got a
   box wide enough for a paragraph, and a postcode, a name and a signature
   were rendered identically -- so the form could not tell you what it wanted
   until you had read the label, every time, and the eye had to cross the
   whole screen to find nothing at the end of it.
   Jamie: "is it the super wide boxes?" Yes, mostly.
   A field is now sized to what goes IN it, so the shape of the form answers
   the question before the label does. `wide` still means full width -- an
   address or a signature earns it; a date does not. */
@media (min-width: 900px) {
  .fc-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    align-items: end;
  }
  .fc-grid > .fc-f-wide, .fc-grid > .polish-wide { grid-column: 1 / -1; }
  /* A number, a date or a reading is never 1300px of anything. */
  .fc-f input[inputmode="decimal"], .fc-f input[inputmode="numeric"],
  .fc-f input[type="date"] { max-width: 220px; }
  .fc-f-wide input[type="date"] { max-width: 220px; }
}
/* ── ONE ROW HEIGHT ────────────────────────────────────────────────────────
   Measured heights were 49, 51, 52 and 79px. The eye skips down a column on
   rhythm, and four heights break it for no gain. Textareas keep their own
   height -- they are a different kind of thing and should look it. */
.fc-f input, .fc-f select, .set-grid input, .set-grid select {
  min-height: 48px; box-sizing: border-box;
}


/* ── A SHEET WITH A FIXED BAR IN IT MUST SCROLL PAST THE BAR ───────────────
   Jamie, 12 Oct 2026, from a phone: "page scroll doesnt work on settings page
   on mobile".
   Measured at 390x844: the settings dialog holds 1470px of content in an
   844px window and DOES have overflow-y auto -- so it scrolls. But its own
   tab bar is fixed inside it at y=773, and the content had no room reserved
   underneath, so the last ~71px sat permanently behind the bar with nothing
   below it to scroll to. On a phone that reads as "it will not scroll": you
   push, nothing new appears, and the thing you were reaching for is under
   the bar.
   Room is reserved for the bar plus the home indicator, and momentum
   scrolling is asked for explicitly -- without it an overflow container in
   iOS Safari scrolls stiffly enough to feel broken. */
#proto-dialog, #proto-sheet {
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
#proto-dialog:has(.so-tabbar-sheet), #proto-sheet:has(.so-tabbar-sheet) {
  padding-bottom: calc(80px + env(safe-area-inset-bottom, 0px));
}


/* ═══ COLOUR AS A KEY, AND CARDS THAT READ AS CARDS ═══════════════════════
   Jamie, 12 Oct 2026, with his own app beside a competitor's: "there is just
   nothing to separate things by my eye? sections, names - everything merges
   into one ... does it need some colour separation? the tags to be coloured
   as a key?" and "is it the dark mode doing this?"

   Measured on the certificate list at 390:
     · page rgb(8,9,11), card rgb(22,26,34) -- a contrast of 1.14:1. That is
       not a card sitting on a page, it is a smudge. The brighter panel manages
       1.37:1. Both are below what an eye reads as an edge.
     · ZERO hued text anywhere in the list. Certificate type, postcode, status
       and date all render in the SAME grey, so the only way to find an EICR is
       to read every row.
     · four text styles in total, two of them near-identical whites.

   AND YES, THE DARK MODE IS PART OF IT. On a light screen a white card on a
   grey page separates for free, and a shadow reads as "this is on top"
   because we expect light from above. On near-black a shadow is invisible, so
   separation has to be bought with luminance and a real border -- and this
   theme was not paying for it. Dark is not the problem; being timid in dark
   is.

   THE KEY IS THE BIGGER HALF. A colour per certificate type means the type
   column can be SCANNED rather than read. Each colour is a muted ink on a
   tinted ground rather than a saturated fill, because full-strength colour on
   near-black glows and shouts; these have to sit beside each other seven at a
   time without turning the list into a toy. */

/* 1 · A card must look like a card. */
#certs-body .so-group, #certs-body .fc-card, #certs-body .wf-section,
#certs-body .panel, #proto-dialog .so-group {
  /* Measured against the page at rgb(8,9,11). The first attempt here used
     #11151d, which is DARKER than the panel it replaced and took the contrast
     DOWN from 1.14 to 1.09 -- the opposite of the point. A card has to be
     lighter than its page by enough that the edge is seen, not inferred. */
  background: #232b3b;
  border: 1px solid #3d4860;
}
/* On near-black the BORDER does most of the separating -- a shadow is
   invisible and a background step has to be large before the eye calls it an
   edge. So the border is deliberately brighter than a light theme would ever
   need, and the background step supports it rather than carrying it alone. */
/* 2 · Rows inside a group are separated, not stacked into a slab. */
#certs-body .so-group > .so-row + .so-row,
#proto-dialog .so-group > .so-row + .so-row { border-top: 1px solid #1f2633; }

/* 3 · THE KEY. One colour per certificate type, used everywhere that type
      appears, so the colour means the same thing on every screen. */
.so-tag {
  display: inline-block; padding: 2px 8px; margin-right: 6px; border-radius: 999px;
  font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  border: 1px solid transparent; vertical-align: middle;
}
.so-tag-type[data-cert-type="eicr"] { color:#8ab4ff; background:#141d33; border-color:#24355c; }
.so-tag-type[data-cert-type="eic"]  { color:#5fd9b4; background:#0f2620; border-color:#1d4a3d; }
.so-tag-type[data-cert-type="mw"]   { color:#f0b866; background:#2a1f0e; border-color:#50391a; }
.so-tag-type[data-cert-type="pat"]  { color:#c89bf5; background:#211630; border-color:#3e2a57; }
.so-tag-type[data-cert-type="fire"] { color:#ff8f7a; background:#2d1512; border-color:#542722; }
.so-tag-type[data-cert-type="eml"]  { color:#6fd2e8; background:#0e2429; border-color:#1c4650; }
.so-tag-type[data-cert-type="emlp"] { color:#ffa96b; background:#2b1a0d; border-color:#523219; }

/* 4 · State is a colour too, because "is it signed off" is the other question
      asked at a glance. Deliberately a DIFFERENT shape language -- no fill,
      just an outline -- so state and type never read as the same kind of fact. */
.so-tag-state {
  background: transparent; color: var(--so-muted); border-color: #333c4c; font-weight: 700;
}
.so-tag-state[data-cert-state="awaiting-qs"] { color:#f0b866; border-color:#50391a; }
.so-tag-state[data-cert-state="issued"] { color:#5fd9b4; border-color:#1d4a3d; }

/* 5 · The name leads. It was 16.5px/650 against metadata at 13.5px/400 -- a
      half-step, which is why the row read as one block of text. */
#certs-body .so-rowtext strong { font-size: 17px; font-weight: 700; letter-spacing: -.01em; }
#certs-body .so-rowtext small { font-size: 13px; color: var(--so-muted); }


/* ── THE SETUP CHECKLIST ───────────────────────────────────────────────────
   A done step stays VISIBLE and ticked rather than vanishing: seeing what you
   have already finished is most of why a checklist is reassuring, and a list
   that shrinks as you work gives no sense of how much is left. It disappears
   only when everything required is done. */
.setup-head { padding: 14px 16px 10px; display: grid; gap: 3px; border-bottom: 1px solid #1f2633; }
.setup-head strong { font-size: 15px; color: var(--so-text); }
.setup-head small { font-size: 13px; color: var(--so-muted); }
.setup-mark {
  display: grid; place-items: center; width: 26px; height: 26px; border-radius: 999px;
  font-size: 13px; font-weight: 800; color: var(--so-muted);
  background: #1b2130; border: 1px solid #333c4d;
}
.setup-step.setup-done .setup-mark { color: #5fd9b4; background: #0f2620; border-color: #1d4a3d; }
.setup-step.setup-done .so-rowtext strong { color: var(--so-muted); font-weight: 600; }
.setup-step.setup-done .so-rowtext small { display: none; }
.setup-opt { font-size: 11px; font-weight: 700; color: var(--so-muted); text-transform: uppercase; letter-spacing: .04em; }


/* Her face on her own answers. The header had one; the replies did not, so a
   reply read as system output rather than as her. */
.mc-turn.mc-ai { display: grid; grid-template-columns: 28px 1fr; gap: 8px 10px; align-items: start; }
.mc-turn.mc-ai .mc-face { grid-row: 1 / span 2; width: 28px; height: 28px; border-radius: 999px;
  display: grid; place-items: center; overflow: hidden;
  background: var(--so-accent); color: var(--so-accent-ink, #04221a); font-weight: 800; font-size: 14px; }
.mc-turn.mc-ai .mc-face svg, .mc-turn.mc-ai .mc-face img { width: 100%; height: 100%; display: block; }
/* The wait should look like somebody thinking, not a stalled screen. */
.mc-turn.mc-pending .mc-text { opacity: .75; }
.mc-turn.mc-pending .mc-face { animation: mc-think 1.4s ease-in-out infinite; }
@keyframes mc-think { 0%,100% { opacity: 1; transform: scale(1); } 50% { opacity: .55; transform: scale(.92); } }
@media (prefers-reduced-motion: reduce) { .mc-turn.mc-pending .mc-face { animation: none; } }


/* The outstanding count on the Sections control. Amber, because it is a thing
   to do rather than a thing that is wrong -- red is reserved for the checks
   that actually fail. "nothing missing" stays muted: finishing a page should
   feel quiet, not congratulated. */
.bw-sections-missing { color: #f0b866; font-weight: 800; }


/* ── THE BOARD PAGE'S HIERARCHY WAS UPSIDE DOWN ────────────────────────────
   Jamie, 12 Oct 2026: "add distribution board is too big, then the added dbs
   sit at the top as tabs, just seems a bit wrong way round. could the add db
   be a smaller button maybe?"
   He is right about the weight. On that screen the work is the CIRCUITS on the
   board in front of you; adding another board is an occasional act, and it was
   drawn as a full-width row with an 87px photograph in it -- heavier than
   "Add circuit", which is the thing being done forty times a day.
   Shrunk to a quiet secondary control. Nothing is removed and nothing moves:
   the photograph stays, at a size that labels the button instead of being the
   button. */
.bw-addboard {
  display: inline-flex; align-items: center; gap: 10px;
  width: auto; max-width: 100%; padding: 10px 14px;
  background: transparent; border: 1px solid var(--so-line-strong, #3d4860);
  border-radius: 999px; color: var(--so-muted);
}
.bw-addboard .bw-addboard-img { width: 34px; height: auto; opacity: .75; }
.bw-addboard .bw-addboard-t b { font-size: 14px; font-weight: 650; }
.bw-addboard .bw-plus { opacity: .8; }

/* ── THE TABLE IS NOT A TOOL ───────────────────────────────────────────────
   "the table view option only appears once rotated once and needs to be
   permanent and prominant."
   Table and Tools sat as equal twins. They are not equals: the table is how a
   board full of circuits is actually read and filled, and Tools is a menu of
   occasional actions. The table now carries the accent and leads the pair. */
.bw-tableview {
  background: var(--so-panel-2, #232936);
  border-color: var(--so-accent);
  color: var(--so-text);
  font-weight: 700;
}
.bw-tableview[disabled] { opacity: .45; border-color: var(--so-line); font-weight: 600; }
.fc-ckt-pick { display: grid; gap: 0; }
.fc-ckt-pick .so-row + .so-row { border-top: 1px solid #1f2633; }


/* A scan suggestion. Outlined and amber-inked, never a filled accent button:
   it is a question the engineer answers, not an action the app has taken. The
   small print carries why, because a suggestion you cannot judge is just
   noise. */
.product-scan-hint {
  display: block; width: 100%; text-align: left; margin: 8px 0 2px;
  padding: 10px 12px; border-radius: var(--so-r-s, 10px);
  background: #2a1f0e; border: 1px solid #50391a; color: #f0b866;
  font-size: 14px; font-weight: 700;
}
.product-scan-hint small { display: block; margin-top: 2px; font-weight: 600; color: var(--so-muted); }


/* ── THE BOARD FILLING IN ──────────────────────────────────────────────────
   Jamie, 12 Oct 2026: "tradecert, the biggest in the game have a great
   animation of the board template getting filled in. we need that."
   The proposals arrive together, so each row lands in turn rather than the
   whole list appearing at once -- the eye follows a board being read way by
   way, which is what is actually happening and what makes it legible instead
   of a wall. Fast on purpose: 55ms apart, so twelve circuits are done inside
   a second. An animation you wait for is a cost, not a delight.
   It is decoration over content that is already there -- if the animation
   never runs, every row is still in the DOM and readable, which is why the
   end state is the default and the animation only moves it. */
@media (prefers-reduced-motion: no-preference) {
  .product-scan-row { animation: scan-land .34s cubic-bezier(.2,.7,.3,1) both; }
  .product-scan-row:nth-child(1)  { animation-delay: .00s }
  .product-scan-row:nth-child(2)  { animation-delay: .055s }
  .product-scan-row:nth-child(3)  { animation-delay: .11s }
  .product-scan-row:nth-child(4)  { animation-delay: .165s }
  .product-scan-row:nth-child(5)  { animation-delay: .22s }
  .product-scan-row:nth-child(6)  { animation-delay: .275s }
  .product-scan-row:nth-child(7)  { animation-delay: .33s }
  .product-scan-row:nth-child(8)  { animation-delay: .385s }
  .product-scan-row:nth-child(9)  { animation-delay: .44s }
  .product-scan-row:nth-child(10) { animation-delay: .495s }
  .product-scan-row:nth-child(n+11) { animation-delay: .55s }
}
@keyframes scan-land {
  from { opacity: 0; transform: translateY(10px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
/* The way number lights up as its row lands, so it reads as a board being
   filled in rather than a list fading up. */
@media (prefers-reduced-motion: no-preference) {
  .product-scan-row h3 { animation: scan-mark .5s ease-out both; animation-delay: inherit; }
}
@keyframes scan-mark {
  0%   { color: var(--so-accent); }
  100% { color: inherit; }
}


/* ── THE BOARD PHOTOGRAPH IS A LABEL, NOT A FEATURE ────────────────────────
   Jamie, 12 Oct 2026: "picture of consumer unit is too big."
   166px portrait was taking the top third of the board card to say "this is a
   consumer unit", which the heading beside it already says -- at the cost of
   the board name, its location and the two controls, which are the things
   actually used. The directed figure is changed in the TEST as well, because
   that assertion exists so a width cannot drift silently and changing only the
   stylesheet would have been exactly that drift. */
.bw-board-img { width: 104px; max-width: 104px; height: auto; }
@media (min-width: 760px) { .bw-board-img { width: 120px; max-width: 120px; } }

.bw-switch-add {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 8px 14px; border-radius: 10px;
  background: transparent; border: 1px dashed var(--so-line-strong, #3d4860);
  color: var(--so-muted); font-weight: 700; font-size: 14px;
}
.bw-switch-add:hover { color: var(--so-text); border-color: var(--so-accent); }

/* ── THE BOARD NAMES SCROLL; + DB DOES NOT ─────────────────────────────────
   Adding the + DB chip made the row wrap to a second line on a 390 screen,
   which pushed the first board card from the upper part of the screen down to
   346px -- caught by board-redesign, which requires the first board to be up
   where it can be seen. Wrapping was always going to do that; it just needed a
   fourth chip to show it, and a firm with six boards would have hit it anyway.
   A row of tabs scrolls sideways. Height is now fixed whatever the board count,
   and the chips keep their size rather than being shrunk to fit.

   THEN SCROLLING HID THE ADD CONTROL. One board named "TEST DB1 Main board -
   TEST Hallway cupboard" is wide enough on a 390 screen to fill the row by
   itself and carry + DB off the right-hand edge, and + DB is the only way to
   add a board now that the full-width row is gone. So the scrolling belongs to
   the NAMES, in .bw-switch-scroll, and + DB sits outside it, pinned, always on
   screen. The old rules stayed on .bw-switch-row through two commits and
   silently beat the ones in board-workspace.css, because this file loads after
   it -- which is why the chip looked unfixed twice. */
.bw-switch-row {
  display: flex; flex-wrap: nowrap; gap: 8px; align-items: center;
}
.bw-switch-scroll {
  display: flex; flex-wrap: nowrap; gap: 8px;
  flex: 1 1 auto; min-width: 0;
  overflow-x: auto; overflow-y: hidden;
  -webkit-overflow-scrolling: touch; scrollbar-width: none;
  padding-bottom: 2px;
}
.bw-switch-scroll::-webkit-scrollbar { display: none; }
.bw-switch-scroll > * { flex: 0 0 auto; }
.bw-switch-row > .bw-switch-add { flex: 0 0 auto; }
