/* FORGE components -- the shared token layer (B5, 2026-09-24).

   THE ONLY WAY TO SKIN A COMPONENT IS A CUSTOM PROPERTY. Every `--fc-*` token
   below reads the design-system contract first (design_systems.REQUIRED_VARS:
   --color-accent, --radius-md, --font-body ...) and falls back to a neutral
   that passes WCAG AA on white, so a component dropped on a bare page is
   still legible, and a page that links any tokens.css re-skins all of them.
   No component CSS names a colour, a font or a radius directly.

   CONTRAST THAT HOLDS UNDER ANY SKIN (W3d, 2026-09-24). The first cut trusted
   the skin's pairs -- --color-accent-contrast over --color-accent, the accent
   as text over the surface -- and the live Heindl build showed what that
   trust costs: an orange accent with a white contrast token is 3.8:1, so
   every button, badge and pressed chip on the parts page failed AA. Two
   tokens are therefore DERIVED here rather than read, where the browser can
   do the arithmetic (relative colour syntax, Chromium 119+ / Safari 16.4+ /
   Firefox 128+), with the skin's own token as the fallback everywhere else:
     --fc-accent-contrast   black or white over the accent, by CIE lightness:
                            L* 49.44 is the lightness where black and white
                            both clear 4.5:1, so one of the two ALWAYS reads.
     --fc-accent-text       the accent when it is TEXT on a surface: pulled
                            45% toward the text colour in lch, so it keeps
                            its hue but sits on the text's side of the
                            surface (an orange on white becomes a deep rust,
                            a gold on near-black a pale gold).
   What CSS cannot check -- a muted token typed for the canvas and read on a
   card, a text token over an alt surface -- _shared/contrast.js measures on
   mount and repairs, leaving its trail in data-fc-contrast-* attributes. */
[data-forge-component], .fc-composition {
  --fc-bg: var(--color-surface, #ffffff);
  --fc-bg-alt: var(--color-surface-alt, #f3f4f6);
  --fc-text: var(--color-text, #111827);
  --fc-muted: var(--color-text-muted, #4b5563);     /* 7.5:1 on white */
  --fc-accent: var(--color-accent, #1d4ed8);         /* 6.7:1 on white */
  --fc-accent-contrast: var(--color-accent-contrast, #ffffff);
  --fc-accent-2: var(--color-accent-2, #0f172a);
  --fc-accent-2-contrast: var(--color-accent-contrast, #ffffff);
  --fc-accent-text: var(--fc-accent);                /* derived below where the browser can */
  /* THE FILL (W35b, 2026-09-25): the accent as a FIELD -- buttons, badges, a
     pressed chip, the current-page rule, a price-list section bar -- is the
     brand's TRUE colour, not the text-safe tint. O'Betty's wordmark red has to
     be lightened to read as TEXT on its charcoal (it came out pink), but as a
     field under white text it is already 5.9:1. art_director writes
     --color-accent-fill only when it tinted the text accent; every other skin
     falls through to the accent itself, so an old tokens.css (Heindl's
     yellow under black) renders exactly as before. */
  --fc-accent-fill: var(--color-accent-fill, var(--fc-accent));
  --fc-accent-fill-contrast: var(--color-accent-fill-contrast, var(--fc-accent-contrast));
  --fc-border: var(--color-border, #cbd5e1);
  --fc-font: var(--font-body, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  --fc-font-display: var(--font-display, inherit);
  /* the mono face is the COMPONENTS' (W24, 2026-09-24): part numbers, SKUs
     and prices in tables are what a monospace face aligns, and the design
     skill allows it nowhere else. tokens.css declares --font-mono inside
     [data-forge-component] only (art_director._tokens_css), so a producer
     rule that reaches for var(--font-mono) on a label or a button resolves
     to nothing and inherits the body face; here it resolves. */
  --fc-font-mono: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  --fc-size-sm: var(--size-sm, 0.875rem);
  --fc-size: var(--size-base, 1rem);
  --fc-size-lg: var(--size-lg, 1.25rem);
  --fc-space-xs: var(--space-xs, 0.5rem);
  --fc-space-sm: var(--space-sm, 0.75rem);
  --fc-space-md: var(--space-md, 1.25rem);
  --fc-space-lg: var(--space-lg, 2rem);
  --fc-radius: var(--radius-md, 8px);
  --fc-radius-sm: var(--radius-sm, 4px);
  --fc-radius-pill: var(--radius-pill, 999px);
  --fc-border-w: var(--border-w, 1px);
  --fc-shadow: var(--shadow-card, 0 1px 2px rgba(0, 0, 0, 0.08));
  --fc-tap: 44px;                 /* the minimum tap target, everywhere */
  --fc-focus: var(--color-accent-2, var(--fc-accent));
  font-family: var(--fc-font);
  font-size: var(--fc-size);
  color: var(--fc-text);
  line-height: var(--leading-body, 1.5);
  box-sizing: border-box;
}
/* the derivations: only where the browser understands them; a browser that
   does not keeps the skin's own tokens (and the guard still measures) */
@supports (color: lch(from red calc((49.44 - l) * infinity) 0 0)) {
  [data-forge-component], .fc-composition {
    --fc-accent-contrast: lch(from var(--fc-accent) calc((49.44 - l) * infinity) 0 0);
    --fc-accent-2-contrast: lch(from var(--fc-accent-2) calc((49.44 - l) * infinity) 0 0);
    --fc-accent-fill-contrast: lch(from var(--fc-accent-fill) calc((49.44 - l) * infinity) 0 0);
  }
}
@supports (color: color-mix(in lch, red 55%, blue)) {
  [data-forge-component], .fc-composition {
    --fc-accent-text: color-mix(in lch, var(--fc-accent) 55%, var(--fc-text));
  }
}
[data-forge-component] *, [data-forge-component] *::before, [data-forge-component] *::after {
  box-sizing: inherit;
}
[data-forge-component] :focus-visible {
  outline: 3px solid var(--fc-focus);
  outline-offset: 2px;
}
[data-forge-component] img { max-width: 100%; height: auto; display: block; }

/* the visually-hidden idiom: read by assistive tech, drawn nowhere */
.fc-sr-only {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* buttons: one shape, two weights; never smaller than the tap target */
.fc-btn {
  min-height: var(--fc-tap); min-width: var(--fc-tap);
  padding: 0 var(--fc-space-md);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--fc-space-xs);
  font: inherit; font-weight: 600; cursor: pointer;
  border: var(--fc-border-w) solid var(--fc-accent-fill);
  border-radius: var(--fc-radius);
  background: var(--fc-accent-fill); color: var(--fc-accent-fill-contrast);
  text-decoration: none;
}
.fc-btn--ghost { background: transparent; color: var(--fc-accent-text); }
.fc-btn:disabled, .fc-btn[aria-disabled="true"] { opacity: 0.55; cursor: not-allowed; }
.fc-btn--icon { padding: 0; width: var(--fc-tap); }

/* chips: a pressed chip is filled, an idle chip is outlined. A chip that is
   a LABEL (the fitment tag on a card) is 2rem tall; a chip that is a TARGET
   (a filter button, a category link) is the shared 44px minimum, and it is
   said here with a selector that OUTRANKS the label rule whatever order the
   two arrive in. WHY (W24, 2026-09-24): the mounts each ship base.css
   inside their own file -- forge-components.css, forge-nav.css,
   forge-garage.css -- and a page links them in mount order, so a later
   copy of `.fc-chip { min-height: 2rem }` (0,1,0) overrode an earlier
   `.fc-chip--button { min-height: 44px }` (0,1,0) on the live Heindl build:
   every filter chip measured 32px on a phone, and it was the critic's last
   fault in every round. `.fc-chip:is(a, button, [role="button"])` is
   (0,2,0): the cascade order between the copies no longer decides it. */
.fc-chip {
  display: inline-flex; align-items: center; gap: 0.35em;
  min-height: 2rem; padding: 0.25em 0.75em;
  border: var(--fc-border-w) solid var(--fc-border);
  border-radius: var(--fc-radius-pill);
  background: var(--fc-bg-alt); color: var(--fc-text);
  font-size: var(--fc-size-sm); line-height: 1.2;
}
.fc-chip:is(a, button, [role="button"]) {
  min-height: var(--fc-tap); min-width: var(--fc-tap);
  padding: 0 var(--fc-space-sm);
  justify-content: center; cursor: pointer;
}

/* form controls share the tap height and the border token */
.fc-field { display: flex; flex-direction: column; gap: 0.35rem; min-width: 0; }
.fc-field > label, .fc-label { font-weight: 600; font-size: var(--fc-size-sm); }
.fc-input, .fc-select {
  min-height: var(--fc-tap); padding: 0 var(--fc-space-sm);
  font: inherit; color: var(--fc-text); background: var(--fc-bg);
  border: var(--fc-border-w) solid var(--fc-border); border-radius: var(--fc-radius);
  width: 100%;
}
.fc-muted { color: var(--fc-muted); }
.fc-composition { display: grid; gap: var(--fc-space-lg); }

/* site-nav -- the navigation, built by code (W22, 2026-09-24).

   WHY THE COMPONENT SETS ITS OWN COLOURS ON EVERY LINK. The producer's page
   CSS styles `header a` and `nav a` freely, and on one head-to-head build
   that came out grey on grey. A component that mounts INTO that page has to
   out-specify those rules, so every text-bearing element here is named by
   `[data-forge-component="site-nav"] .fc-nav__…` (0,2,0 beats `header nav a`)
   and takes its colour from the token layer, never from inheritance. The
   accent is a FILL (the action button, the current-page rule), never text:
   base.css derives the on-accent colour, and the contrast guard measures
   the rest on mount.

   THREE STATES, ONE MARKUP. Without JavaScript the bar shows the brand and
   the primary row, and the drawer is a plain list of the other pages under
   it -- every page reachable, nothing to open. JavaScript marks the root
   data-fc-nav-js, hides the drawer until the toggle opens it, and reveals
   the toggle. Above 900px the toggle reads "More" and the drawer is a panel
   under the bar (the primary group inside it is hidden: it is already in
   the bar). At 900px and below the bar's primary row is hidden, the toggle
   reads "Menu", and the drawer is a full-screen dialog carrying primary and
   secondary together. Every target is the shared 44px minimum. */
[data-forge-component="site-nav"] {
  position: relative;
  background: var(--fc-bg);
  color: var(--fc-text);
  border-bottom: var(--fc-border-w) solid var(--fc-border);
}
[data-forge-component="site-nav"] .fc-nav__bar {
  display: flex; align-items: center; gap: var(--fc-space-sm);
  max-width: var(--container-max, 1120px); margin: 0 auto;
  min-height: calc(var(--fc-tap) + var(--fc-space-sm) * 2);
  padding: var(--fc-space-xs) var(--fc-space-md);
}
[data-forge-component="site-nav"] .fc-nav__brand {
  display: inline-flex; align-items: center; gap: var(--fc-space-xs);
  min-height: var(--fc-tap); margin-right: auto;
  color: var(--fc-text); text-decoration: none;
  font-family: var(--fc-font-display); font-weight: 700; font-size: var(--fc-size-lg);
  white-space: nowrap;
}
[data-forge-component="site-nav"] .fc-nav__logo { height: 2.5rem; width: auto; max-width: 9rem; object-fit: contain; }
[data-forge-component="site-nav"] .fc-nav__name { color: var(--fc-text); }

/* the primary row. W22 wrote "one line, never wraps -- the plan capped it
   at a handful of short labels so it does not have to", and the W25 shot
   at 1280px (hub/w25_shots/after_1280.jpg) showed five labels plus the
   brand, the action and "More" overflowing the row: `min-width: 0` let the
   nav shrink, `nowrap` kept the list at its full width, and "Sidecar Parts"
   ran under the yellow "Quote list" button -- clipped, unreachable. MEASURE
   AND FOLD (W27, 2026-09-24): the script measures the row and moves the
   items that do not fit into the drawer, at any width (component.js
   `fit`); the CSS-only fallback is that the list WRAPS, so with no script
   the row takes a second line and never overlaps the button. Under the
   script the list is nowrap again (it folds instead of wrapping) and a
   folded item is hidden here and shown in the drawer. */
[data-forge-component="site-nav"] .fc-nav__primary { min-width: 0; }
[data-forge-component="site-nav"] .fc-nav__list,
[data-forge-component="site-nav"] .fc-nav__drawer-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--fc-space-2xs, 0.25rem);
}
[data-forge-component="site-nav"][data-fc-nav-js] .fc-nav__list { flex-wrap: nowrap; }
[data-forge-component="site-nav"] .fc-nav__list > li[data-fc-folded] { display: none; }
/* on a desktop the action and the toggle keep their width whatever the row
   does: an overflow used to squeeze the action button (its explicit
   min-width let it shrink) and the row's tail was painted under it. On a
   phone (the row hidden) they shrink as before, so the bar never overflows
   390px. */
@media (min-width: 901px) {
  [data-forge-component="site-nav"] .fc-nav__action,
  [data-forge-component="site-nav"] .fc-nav__toggle { flex-shrink: 0; }
}
[data-forge-component="site-nav"] .fc-nav__link {
  display: inline-flex; align-items: center;
  min-height: var(--fc-tap); padding: 0 var(--fc-space-sm);
  color: var(--fc-text); text-decoration: none; font-weight: 600; font-size: var(--fc-size);
  border-radius: var(--fc-radius-sm);
  border-bottom: 3px solid transparent;
  white-space: nowrap;
}
[data-forge-component="site-nav"] .fc-nav__link:hover { background: var(--fc-bg-alt); color: var(--fc-text); }
[data-forge-component="site-nav"] .fc-nav__link[aria-current="page"] {
  border-bottom: 3px solid var(--fc-accent-fill);
  border-radius: var(--fc-radius-sm) var(--fc-radius-sm) 0 0;
}

/* the one action: a real button, with the list's count as a badge */
[data-forge-component="site-nav"] .fc-nav__action { white-space: nowrap; }
[data-forge-component="site-nav"] .fc-nav__badge {
  display: inline-grid; place-items: center; min-width: 1.6em; height: 1.6em; padding: 0 0.35em;
  border-radius: var(--fc-radius-pill);
  background: var(--fc-text); color: var(--fc-bg);
  font-size: var(--fc-size-sm); font-weight: 700; line-height: 1;
}
[data-forge-component="site-nav"] .fc-nav__badge[hidden] { display: none; }
[data-forge-component="site-nav"] .fc-nav__toggle { white-space: nowrap; }
[data-forge-component="site-nav"] .fc-nav__toggle[hidden] { display: none; }

/* the drawer: a plain list under the bar until script hides it */
[data-forge-component="site-nav"] .fc-nav__drawer-panel {
  max-width: var(--container-max, 1120px); margin: 0 auto;
  padding: var(--fc-space-xs) var(--fc-space-md);
}
[data-forge-component="site-nav"] .fc-nav__drawer-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--fc-space-sm);
  margin-bottom: var(--fc-space-xs);
}
[data-forge-component="site-nav"] .fc-nav__drawer-head[hidden] { display: none; }
[data-forge-component="site-nav"] .fc-nav__drawer-title {
  margin: 0; color: var(--fc-muted); font-size: var(--fc-size-sm); font-weight: 600;
  letter-spacing: var(--tracking-caps, 0.08em); text-transform: uppercase;
}
[data-forge-component="site-nav"] .fc-nav__drawer-list { flex-wrap: wrap; }
[data-forge-component="site-nav"][data-fc-nav-js] .fc-nav__drawer:not([data-fc-open]) { display: none; }

@media (min-width: 901px) {
  /* the bar already carries the primary row; the drawer holds only the
     rest -- plus (W27) whatever the script FOLDED out of the row, which is
     marked data-fc-folded in both lists and shown here. A nav with no
     secondary pages keeps its drawer and its toggle only while something
     is folded (data-fc-nav-folded on the root). */
  [data-forge-component="site-nav"] .fc-nav__drawer-list--primary > li:not([data-fc-folded]) { display: none; }
  [data-forge-component="site-nav"].fc-nav--no-secondary:not([data-fc-nav-folded]) .fc-nav__drawer { display: none; }
  [data-forge-component="site-nav"].fc-nav--no-secondary:not([data-fc-nav-folded]) .fc-nav__toggle { display: none; }
  [data-forge-component="site-nav"] .fc-nav__drawer { border-top: var(--fc-border-w) solid var(--fc-border); }
  [data-forge-component="site-nav"][data-fc-nav-folded] .fc-nav__drawer-list--primary + .fc-nav__drawer-list--secondary {
    border-top: var(--fc-border-w) solid var(--fc-border);
    margin-top: var(--fc-space-xs); padding-top: var(--fc-space-xs);
  }
}

@media (max-width: 900px) {
  [data-forge-component="site-nav"] .fc-nav__primary { display: none; }
  [data-forge-component="site-nav"] .fc-nav__bar { padding-left: var(--fc-space-sm); padding-right: var(--fc-space-sm); }
  [data-forge-component="site-nav"] .fc-nav__brand { font-size: var(--fc-size); }
  [data-forge-component="site-nav"] .fc-nav__name { overflow: hidden; text-overflow: ellipsis; max-width: 40vw; }
  /* open, the drawer is a full-screen dialog: its own surface, its own
     scroll, one column of 44px rows */
  [data-forge-component="site-nav"][data-fc-nav-js] .fc-nav__drawer[data-fc-open] {
    position: fixed; inset: 0; z-index: 1000;
    background: var(--fc-bg); color: var(--fc-text);
    overflow-y: auto;
  }
  [data-forge-component="site-nav"] .fc-nav__drawer-list { flex-direction: column; align-items: stretch; }
  [data-forge-component="site-nav"] .fc-nav__drawer-list--secondary {
    border-top: var(--fc-border-w) solid var(--fc-border);
    margin-top: var(--fc-space-xs); padding-top: var(--fc-space-xs);
  }
  [data-forge-component="site-nav"] .fc-nav__drawer-list .fc-nav__link {
    width: 100%; min-height: 48px; font-size: var(--fc-size-lg); white-space: normal;
    border-bottom: 0; border-left: 4px solid transparent;
  }
  [data-forge-component="site-nav"] .fc-nav__drawer-list .fc-nav__link[aria-current="page"] {
    border-left: 4px solid var(--fc-accent-fill); background: var(--fc-bg-alt);
  }
}

@media (prefers-reduced-motion: no-preference) {
  [data-forge-component="site-nav"] .fc-nav__link { transition: background-color 120ms ease; }
}

/* THE PRODUCER'S OWN LINK LISTS (W24, 2026-09-24). The nav is on every
   page, so its stylesheet is the one place a rule reaches every page's
   FOOTER: the producer writes the footer freehand, and on the live Heindl
   build its ten page links were 23px line boxes stacked under each other --
   the a11y probe's remaining small targets once the components were right.
   On a phone (the 900px line the nav itself turns at) a link that stands
   ALONE in a footer or nav -- the child of the footer, of a nav, or of a
   list item -- becomes a 44px row. A link inside running text (a
   paragraph, a small print line) is left alone: the guideline exempts an
   inline link, and so does the probe. The rule names no colour, no face:
   the producer's look stands, only the line box grows. */
@media (max-width: 900px) {
  footer > a, nav > a,
  :is(footer, nav) :is(ul, ol, li, nav) > a:not([data-forge-component] a) {
    display: flex; align-items: center;
    min-height: var(--fc-tap, 44px); min-width: var(--fc-tap, 44px);
  }
  /* a link in a footer DIV keeps its own display (the div may hold prose):
     a block link grows to the floor, an inline one is left to the probe's
     inline exemption */
  :is(footer, nav) div > a:not([data-forge-component] a) {
    min-height: var(--fc-tap, 44px); min-width: var(--fc-tap, 44px); box-sizing: border-box;
  }
}
@media print {
  [data-forge-component="site-nav"] .fc-nav__toggle,
  [data-forge-component="site-nav"] .fc-nav__action,
  [data-forge-component="site-nav"] .fc-nav__drawer { display: none !important; }
}
