/* ═════════════════════════════════════════════════════════════════════════════
   js/forgeos-brand.css — the ForgeOS look, for the STAFF-FACING CRM.

   Phoenix, 2026-09-13: "bring Spartan CRM into this … calling it ForgeOS CRM
   with ForgeOS branding and colours. Complete colour change across all buttons,
   everything in ForgeOS branding."

   ─── WHAT THIS FILE IS ────────────────────────────────────────────────────
   The TOKEN layer. `js/spartan-tokens.css`, `js/portal-theme.css` and
   `js/polish.css` each declare a `:root` block; this one is loaded after all
   three and RE-DECLARES the same custom properties with ForgeOS values. So
   every one of the ~350 `var(--red)` / `var(--spartan-red)` / `var(--sNNN)`
   reads across nineteen stylesheets moves at once, without any of them being
   edited. Then it restyles the handful of SHARED component classes — `.btn-r`
   (386 call sites), `.nav-item`, `.pill`, `.inp`, `.bdg`, the login card — so
   the primitives every page is built from are on-brand in one place rather
   than at 386 of them.

   What it CANNOT reach is a hex written into an inline `style="…"` inside a
   module's template literal. Those are rewritten in the modules themselves.
   This file's job is to make sure that work is a FINISH, not a prerequisite:
   miss one and it still reads brand-correct, because the token fallbacks are
   chosen to be legible either way (see THE ACCENT HAS THREE FORMS below).

   ─── WHERE IT LOADS ───────────────────────────────────────────────────────
   ABOVE `js/responsive.css`, which must stay LAST in <head> (CLAUDE.md, the
   A-668 chain: responsive.css works by overriding inline styles and needs last
   position, and `test/a668-responsive-chain.mjs` fails the deploy if it moves).
   Below everything else, because overriding is the whole point.
   `js/*.css` is served immutable/max-age=1yr and is NOT manifest-hashed, so
   the `?v=` on its <link> is the only thing that reaches a device that already
   has the file — bump it on every edit.

   ─── THE ACCENT HAS THREE FORMS, AND THAT IS THE WHOLE RULE ───────────────
   The CRM is a LIGHT application: white cards on a pale body. BRAND.md §2 is
   explicit about what the brand does there — "On light backgrounds use the
   light lockup — ink mark, ink wordmark, #a16207 slashes. NEVER yellow on
   white." So the accent is not one colour:

     PLATE       a filled accent block: a primary button, an active nav pill,
                 a progress bar. #facc15 — and whatever sits ON it is BLACK.
     ACCENT INK  the accent used as TEXT, an icon, or a border that carries
                 meaning, on a white or pale ground. #a16207 — 4.96:1 on white.
     SOFT        the pale accent wash that replaces --pale-red #FBF3F4.
                 #fefce8, carrying #854d0e.

   Getting this wrong is the ONE failure a search-and-replace of the old
   #C41230 guarantees, and it is silent: white on #c41230 is 6.6:1 and was
   correct everywhere, white on #facc15 is 1.7:1 and is a button whose label
   has vanished. Nothing throws; the text is merely gone. That is the same
   shape as every silent defect CLAUDE.md records.

   Which is why `--red` below is mapped to the INK form, not to the yellow:
   #a16207 is the only value that is legible BOTH as text on white AND as a
   plate under white text, so a `var(--red)` this rebrand has not reached yet
   degrades to something readable in either role rather than to an invisible
   one. The bright #facc15 plate is applied deliberately, per component,
   together with its black foreground — never by a token nobody can see the
   other half of.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── TOKENS ─────────────────────────────────────────────────────────────────
   Re-declared, not renamed. Every consumer keeps reading the name it always
   read; only the value moves. New `--fos-*` names are added alongside for the
   three accent roles, because `--red` cannot express "and the text on it is
   black" and a renderer must be able to ask for the ROLE. */
:root {
  /* Brand — spartan-tokens.css */
  --red:          #a16207;   /* accent INK: legible as text on white AND under white */
  --red-dark:     #854d0e;
  --pale-red:     #fefce8;
  --ink:          #0a0a0a;
  --anthracite:   #262626;

  /* Brand — portal-theme.css */
  --spartan-red:       #a16207;
  --spartan-red-hover: #854d0e;
  --spartan-red-soft:  #fefce8;
  --portal-sidebar:    #0a0a0a;
  --portal-bg:         #fafafa;
  --portal-overlay:    rgba(10,10,10,.45);

  /* Brand — polish.css */
  --red-light:       #ca8a04;
  --shadow-red-glow: 0 0 0 4px rgba(250,204,21,.22), 0 8px 24px -8px rgba(161,98,7,.30);
  --pill-inner-red:  inset 0 0 0 1px rgba(161,98,7,.22);

  /* The three accent roles, named. A renderer asks for one of these. */
  --fos-yellow:       #facc15;
  --fos-yellow-hi:    #fde047;
  --fos-plate:        #facc15;
  --fos-plate-hover:  #eab308;
  --fos-on-plate:     #000000;
  --fos-ink:          #a16207;
  --fos-ink-hover:    #854d0e;
  --fos-soft:         #fefce8;
  --fos-soft-ink:     #854d0e;
  --fos-soft-border:  #fef08a;
  --fos-near-black:   #0a0a0a;
  --fos-fg:           #fafafa;

  /* Neutrals. BRAND.md §3 is greyscale; the CRM's two scales were WARM STONE,
     which reads brown beside yellow. Both scales (spartan-tokens' --sNNN and
     portal-theme's --stone-NNN) move onto the brand's own neutral ramp, step
     for step, so nothing shifts in weight — only in temperature. */
  --s50:  #fafafa;  --stone-50:  #fafafa;
  --s100: #f5f5f5;  --stone-100: #f5f5f5;
  --s200: #e5e5e5;  --stone-200: #e5e5e5;
  --s300: #d4d4d4;  --stone-300: #d4d4d4;
  --s400: #a3a3a3;  --stone-400: #a3a3a3;
  --s500: #737373;  --stone-500: #737373;
  --s600: #525252;  --stone-600: #525252;
  --s700: #404040;  --stone-700: #404040;
  --s800: #262626;  --stone-800: #262626;
  --s900: #171717;  --stone-900: #171717;

  /* Status. BRAND.md §3's own hues, kept paired with the pale grounds the CRM
     already uses for them — §9: colour never carries state alone, so these
     stay distinguishable from the accent by more than temperature. */
  --err-fg: #b91c1c;  --err-bg: #fee2e2;

  /* Type — BRAND.md §4. Inter carries the interface; Archivo Black is DISPLAY
     and is reserved for the lockup and the page heroes below. JetBrains Mono
     is the label/data face.

     ⚠ `system-ui` IS DELIBERATELY ABSENT FROM THESE STACKS, and that is a
     MEASURED decision, not a style preference. CLAUDE.md's own scenario — a
     surveyor's tablet with no network — means the Google Fonts <link> does not
     resolve and every stack falls through to its LAST resolvable entry. On a
     box with no Inter installed, `system-ui` resolves to a face **19% wider**
     than the generic `sans-serif` the old `'DM Sans', sans-serif` stack landed
     on (measured: 'Installation Schedule' at 20px/800 — sans-serif 200px,
     system-ui 238px). Putting `system-ui` first therefore made an OFFLINE
     device silently re-flow every heading and every <select> ~19% wider, which
     is how this change first arrived at 2 horizontal overflows in the A-668
     sweep — the sweep's Chromium loads the font CSS but not the font FILES,
     so it is exactly that offline device. Ending at `sans-serif` instead
     restores the old metric byte for byte when nothing loads, while
     -apple-system / Segoe UI / Roboto still give a Mac, a Windows box and an
     Android phone their own native face. I reported the cause as "Inter is
     wider than DM Sans" before measuring; it is not — the two faces were never
     loaded in that comparison at all.

     --ff-narrow deliberately STAYS Archivo Narrow: it is not a brand hue, it
     is a METRIC. The dense numeric tables (`.narrow`, `font-narrow`) are laid
     out to its advance widths, and the brand's mono face is far wider per
     character — swapping it would not re-brand those tables, it would reflow
     them off the edge of a phone. */
  --ff-sans:    'Inter', -apple-system, 'Segoe UI', Roboto, 'DM Sans', sans-serif;
  --ff-display: 'Archivo Black', 'Inter', -apple-system, 'Segoe UI', Roboto, sans-serif;
  --ff-mono:    'JetBrains Mono', 'SF Mono', ui-monospace, monospace;

  /* §5: "Corner radius 6px maximum. We're rectilinear." The CRM's ramp ran to
     16px; every step is capped rather than flattened to one value, so the
     existing hierarchy (a chip is tighter than a modal) survives. */
  --r-xs: 3px;  --r-sm: 4px;  --r-md: 5px;  --r-lg: 6px;
  --r-xl: 6px;  --r-2xl: 6px;
  --portal-radius-sm: 4px; --portal-radius-md: 6px;
  --portal-radius-lg: 6px; --portal-radius-xl: 6px;

  /* Focus ring — the brand's own, and it must be visible on both grounds. */
  --ring-focus: 0 0 0 2px #fff, 0 0 0 4px #a16207;
}

/* ── TYPE ───────────────────────────────────────────────────────────────────
   styles.css sets the body to DM Sans and h1-h4 to Syne. Both move onto the
   brand's stacks. Headings take Inter 800 rather than Archivo Black: §4 calls
   Archivo Black the DISPLAY face, and display is the lockup and the hero — a
   table's h4 set in it is a third heavier and a third wider than the layout
   was measured for, which on a 344px cover screen is an overflow, not a
   rebrand. The genuine display sizes below do take it. */
body { font-family: var(--ff-sans); }
h1, h2, h3, h4 { font-family: var(--ff-sans); letter-spacing: -.02em; }
.display, .display-xl, .h-hero, .h-page { font-family: var(--ff-display); font-weight: 900; }

/* The mono-cap label — §4, "the brand signature". The CRM already had an
   all-caps wide-tracked eyebrow in every page header; it becomes the real one. */
.eyebrow, .fos-label {
  font-family: var(--ff-mono);
  font-size: 10px; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--s500); line-height: 1.2;
}
.fos-label::before { content: '// '; color: var(--fos-ink); }

/* A hairline accent rule — §5, "2px tall". */
.fos-rule { height: 2px; background: var(--fos-yellow); border: 0; }

/* ── THE PRIMARY BUTTON ─────────────────────────────────────────────────────
   `.btn-r` is THE call to action in this app — 386 call sites across 97
   modules. It was a red plate under white text. It becomes a yellow plate
   under BLACK text, and the black is not a preference: #fff on #facc15 is
   1.7:1 and the label is gone.
   §6 also gives the primary CTA a mono-cap label, and that part is deliberately
   NOT applied here — MEASURED, not assumed. `.btn-r` has 386 call sites, many
   of them in dense toolbars beside three or four siblings, and mono + uppercase
   + 0.22em of tracking is roughly a third more width on every one. The A-668
   sweep is the arbiter: it runs 480 page x viewport combinations and `main` is
   0 failing, so any width this layer adds has to be paid for somewhere. The
   mono-cap belongs on the chrome lockup and the .eyebrow (which is already
   all-caps in this app, so it costs nothing), not on every button in a CRM. */
.btn-r {
  background: var(--fos-plate);
  color: var(--fos-on-plate);
  border-radius: var(--r-sm);
  font-weight: 700;
}
.btn-r:hover { background: var(--fos-plate-hover); color: var(--fos-on-plate); }
.btn-r:disabled, .btn-r[disabled] { background: var(--s300); color: var(--s600); }

/* DESTRUCTIVE. Until this rebrand, `.btn-r` was #c41230 — so every irreversible
   button in the app read as a warning by ACCIDENT, because the brand happened to
   be red. The brand is now yellow and that signal is gone: "Delete selected" and
   "Cancel job & refund" would wear exactly the plate that Save and Send wear.
   This is the one place the brand must NOT win. #fff on #b91c1c is 7.0:1 — the
   same pairing, and the same legibility, the old brand had. 24-commission.ts was
   already hand-overriding to #b91c1c for its delete; this gives that a name. */
.btn-r.danger { background: #b91c1c; color: #fff; }
.btn-r.danger:hover { background: #991b1b; color: #fff; }
.btn-r.danger svg, .btn-r.danger i, .btn-r.danger .icon { color: #fff; }
/* An icon inside the plate is a foreground too. */
.btn-r svg, .btn-r i, .btn-r .icon { color: var(--fos-on-plate); stroke: currentColor; }

.btn-w  { border-radius: var(--r-sm); border-color: var(--s200); color: var(--s700); }
.btn-w:hover { background: var(--s50); border-color: var(--s300); }
.btn-g  { border-radius: var(--r-sm); color: var(--s500); }
.btn-g:hover { background: var(--s100); color: var(--s800); }

/* ── FIELDS ─────────────────────────────────────────────────────────────── */
.inp, .sel { border-radius: var(--r-sm); border-color: var(--s200); }
.inp:focus, .sel:focus {
  border-color: var(--fos-ink);
  box-shadow: 0 0 0 2px rgba(250,204,21,.28);
}

/* ── NAV ────────────────────────────────────────────────────────────────────
   The sidebar is already near-black — #1a1a1a, a shade off the brand's own
   #0a0a0a — so it only needs the ground corrected and the active pill moved
   off red. On the DARK rail the plate is the bright yellow and the label is
   ink: that pairing is 12.6:1 and is the brand's own CTA. */
.nav-item { border-radius: var(--r-sm); color: var(--s400); }
.nav-item:hover { color: var(--fos-fg); background: rgba(250,204,21,.10); }
.nav-item.on {
  background: var(--fos-plate); color: var(--fos-on-plate); font-weight: 700;
}
.nav-item.on svg, .nav-item.on i { color: var(--fos-on-plate); }

/* ── PILLS, TAGS, BADGES ────────────────────────────────────────────────── */
.pill { border-radius: 4px; border-color: var(--s200); color: var(--s600); }
.pill.on { background: var(--fos-near-black); color: var(--fos-fg); border-color: var(--fos-near-black); }
.tag  { border-radius: 3px; background: var(--s100); color: var(--s600); }
.bdg  { border-radius: 4px; }
.conv-sentiment { border-radius: 4px; }

/* ── SURFACES ───────────────────────────────────────────────────────────────
   §5: 1px hairline borders, no drop shadows, no gradients. The CRM leans on
   shadow for hierarchy (js/polish.css is an entire pass of it), so the rule is
   applied where it reads as brand — the card edge — and the lift shadows are
   left to polish.css, which is a MOTION affordance rather than a decoration.
   Removing those outright is a separate change with its own before/after. */
.card, .kanban-col, .deal-card { border-radius: var(--r-lg); border-color: var(--s200); }
.modal { border-radius: var(--r-lg); }
.modal-footer { border-radius: 0 0 var(--r-lg) var(--r-lg); background: var(--s50); }
.modal-header, .modal-footer { border-color: var(--s200); }
/* .th keeps the sans on purpose — a table header is a METRIC, the same reason
   --ff-narrow stays Archivo Narrow above. JetBrains Mono is far wider per
   character and every column in this app is sized to its header. */
.th { background: var(--s50); color: var(--s500); border-color: var(--s200); }
.td { color: var(--s800); border-color: var(--s100); }
tr:hover .td { background: var(--s50); }

/* The modal's only exit — A-352 made it pulse so it cannot be missed. It stays
   a pulse; the hue comes off red because red here meant BRAND, not danger. */
@keyframes scModalXPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(250,204,21,.60); }
  50%      { box-shadow: 0 0 0 6px rgba(250,204,21,0); }
}
.sc-modal-x-pulse { color: var(--fos-ink) !important; }

/* ── THE LOGIN SCREEN ───────────────────────────────────────────────────────
   Already a near-black field with a brand-red glow. The ground goes to the
   brand's #0a0a0a exactly and the glow to yellow — §5 forbids a gradient as
   DECORATION, and this is one; it is kept because it is the login page's only
   depth cue and removing it flattens the card onto the field. Toned down to
   .10 so it reads as a lit surface rather than a coloured one. */
.login-bg {
  background: radial-gradient(circle at 50% 28%, rgba(250,204,21,.10) 0%, transparent 62%), #0a0a0a;
}
.login-card { border-radius: var(--r-lg); }

/* ── SCROLLBARS ─────────────────────────────────────────────────────────── */
::-webkit-scrollbar-track { background: var(--s100); }
::-webkit-scrollbar-thumb { background: var(--s300); border-radius: 2px; }

/* ── THE V2 SALES-APP SHELL ─────────────────────────────────────────────────
   The phone app is Tailwind, so its accent lives in arbitrary-value classes
   (`bg-[#C41230]`) rewritten in its own sources. What it shares with the
   desktop is this stylesheet's tokens plus the two utility families below,
   which the precompiled Tailwind sheet cannot express. */
[data-ui-v2] body, [data-ui-v2] { --tw-ring-color: rgba(250,204,21,.4); }

/* ── THE DESKTOP IS THE CONTROL ─────────────────────────────────────────────
   A-668 rule 8 in reverse: this layer changes COLOUR and TYPE FAMILY and must
   change no BOX. Nothing here sets width, height, padding, margin, display,
   position or flex — the only metric properties it touches are border-radius
   and letter-spacing, both of which the responsive sweep re-measures. If a
   rule below ever needs a dimension, it belongs in the renderer that owns the
   surface, not here. */

/* ═══════════════════════════════════════════════════════════════════════════
   A-807 · ONE WAY TO SAY "THIS ONE IS SELECTED"
   ═══════════════════════════════════════════════════════════════════════════
   Phoenix, 2026-09-14: "making them more user friendly more obvious and more
   forge brand … Just cleaner and easier to read."

   Measured on the live app, the CRM had SIX different ways of showing that a
   control is the active one:

     sidebar nav        .nav-item.on              yellow plate + black   ← right
     dashboard periods  .ins2-period-tab.on       near-black + white + shadow
     jobs segmented     .jl-seg button.jl-on      near-black + white
     jobs view switch   .jl-view-sw button.jl-on  white + shadow          ← faint
     leads tab strip    .ll-seg button.ll-on      white on grey + shadow  ← faint
     invoicing filter   (inline)                  soft wash + ink at 10px ← faint

   Two problems, not one. Three of those are so quiet that at a glance you
   cannot tell which filter you are looking through — which is the whole of
   "more obvious". And the app disagreed with ITSELF about what selection looks
   like, so a rep had to learn the answer once per screen.

   The sidebar was already right, so the sidebar's answer wins: SELECTION IS THE
   PLATE. #facc15 carrying #000 — 12.6:1, and the one form of the accent that
   cannot be mistaken for a resting control.

   THE ONE EXCEPTION, and it is a matter of SCALE rather than a second opinion:
   a selected STAT CARD (.ll-tcard.ll-on) stays dark. It is a 150px block, and a
   plate that size stops being an accent and becomes the page — it would also
   collide with the soft-wash STALE card sitting beside it, which uses yellow to
   mean "look here". Small controls take the plate; a card-sized surface does
   not.

   Nothing here sets a box (see the header above): background, colour, border
   and font-size only. Font-size is the exception to the no-metrics rule and is
   deliberate — a 10px filter label is below what this app asks anyone to read,
   and the sweep re-measures it. */

.ins2-period-tab.on,
.wd2-page .wd2-period-tab.on,
.jl-seg button.jl-on,
.jl-view-sw button.jl-on,
.jl-chip.jl-on,
.jl-tbl-foot-r button.jl-on,
.ll-seg button.ll-on,
.ll-chip.ll-on,
.ll-pager-btn.ll-on,
.ll-tbl-foot-r button.ll-on {
  background: var(--fos-plate);
  color: var(--fos-on-plate);
  border-color: var(--fos-ink);
  font-weight: 700;
  /* No shadow. The brand is hairlines and flat blocks; a drop shadow under a
     selected tab was the old system's way of saying "raised", and the plate
     says it louder without the blur. */
  box-shadow: none;
}

/* An icon or count riding inside a selected control is a foreground too — the
   class of bug that leaves a white glyph invisible on a yellow plate. */
.ins2-period-tab.on svg,
.wd2-page .wd2-period-tab.on svg,
.jl-chip.jl-on svg,
.jl-seg button.jl-on svg,
.jl-view-sw button.jl-on svg,
.ll-seg button.ll-on svg,
.ll-chip.ll-on svg {
  color: var(--fos-on-plate);
  stroke: currentColor;
}

/* The count badge inside a selected tab. It was white-on-translucent-white in
   one file and black-on-dark in another; on the plate it reads as ink on a
   translucent black chip. */
.jl-seg button.jl-on .jl-seg-count,
.jl-chip.jl-on .jl-chip-c,
.ll-seg button.ll-on .ll-seg-count,
.ll-chip.ll-on .ll-chip-c {
  background: rgba(0, 0, 0, .14);
  color: var(--fos-on-plate);
}

/* ── ONE PRIMARY BUTTON ─────────────────────────────────────────────────────
   The dashboard shipped two primary buttons side by side wearing different
   clothes: "+ New Deal" as the plate, "+ Add Widget" as the INK form with white
   text and a yellow glow. Same weight of action, two answers, eighteen inches
   apart. The plate is the primary; the ink form is for text and rules. */
.ins2-page .ins2-btn-r {
  background: var(--fos-plate);
  color: var(--fos-on-plate);
  border-color: var(--fos-ink);
}

/* And the primary plate is FLAT. Both of those buttons carried a yellow glow —
   the old red one, recoloured by the rebrand — which BRAND.md does not have:
   the system is hairline borders and flat blocks, no shadows and no gradients.
   Dropped on the resting state only. `:active` keeps its press feedback and
   `:focus-visible` keeps its focus RING, which is an accessibility affordance
   drawn with box-shadow and must never be cancelled; both out-specify this. */
.btn-r, .ins2-page .ins2-btn-r { box-shadow: none; }
.ins2-page .ins2-btn-r:hover { background: var(--fos-plate-hover); color: var(--fos-on-plate); }
.ins2-page .ins2-btn-r svg { color: var(--fos-on-plate); stroke: currentColor; }

/* ── NOTHING A REP HAS TO READ IS UNDER 11px ────────────────────────────────
   The count badges and filter labels sat at 10px. On a 1440px monitor that is
   small; on the phone and the Fold this app also runs on, it is a squint. 11px
   is the floor, and these are the only places that were under it. */
.jl-seg-count,
.ll-seg-count,
.ll-chip-c { font-size: 11px; }
