/* Steels leaf (A2, 2026-09-08): the steel-first index and its per-brand
   drill-down on the detail page. Tokens only, verbatim from app.css/cards.css
   -- no new colour literals. Structural card/grid/filter classes (.fc, .fc-grid,
   .fc-cat, .fc-filter) come from the frozen cards.css and are reused, not
   redefined, here; this file only adds the steel-card content and the
   detail-page brand block that cards.css and charts.css do not already cover.
   Neither cards.css nor charts.css nor app.js is mine to edit on this leaf. */

/* ---- Steel-first index card (one per STEEL, not per steel-and-brand) ---- */
.sc {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  padding: var(--card-pad, var(--sp-4));
  background: var(--paper-white);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  text-decoration: none;
  color: inherit;
}

@media (prefers-reduced-motion: no-preference) {
  .sc {
    transition: transform var(--dur-ui, 140ms) var(--ease-out), box-shadow var(--dur-ui, 140ms) var(--ease-out);
  }
  .sc:hover {
    transform: translateY(var(--lift-hover, -2px));
    box-shadow: var(--shadow-btn-hover, var(--shadow-card));
  }
}

.sc:focus-visible {
  outline: 2px solid var(--lore-navy);
  outline-offset: 2px;
}

.sc__name {
  margin: 0;
  font-size: var(--fs-h4, 1.05rem);
  font-weight: var(--fw-bold, 700);
  line-height: var(--lh-snug, 1.2);
  color: var(--ink-strong);
}

.sc__row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-2);
  margin-top: var(--sp-1);
}

.sc__angle {
  font-size: 30px;
  font-weight: var(--fw-bold, 700);
  line-height: 1;
  color: var(--lore-navy);
  font-variant-numeric: tabular-nums;
}

.sc__angle small {
  font-size: var(--fs-meta);
  font-weight: var(--fw-regular, 400);
  color: var(--ink-muted);
  margin-left: 2px;
}

/* Absent is not zero (input.md 2.3): "no pass yet" and "angle not recorded"
   are both text states, never a borrowed or zeroed angle. */
.sc__angle--nopass {
  font-size: var(--fs-h4, 1.05rem);
  color: var(--ink-muted);
}

.sc__angle--noangle {
  font-size: var(--fs-small, 0.875rem);
  font-weight: var(--fw-bold, 700);
  color: var(--success);
}

.sc__result { text-align: right; }

.sc__chip {
  display: inline-flex;
  align-items: center;
  font-size: var(--fs-small, 0.875rem);
  font-weight: var(--fw-bold, 700);
  letter-spacing: var(--ls-pill, 0.04em);
  text-transform: uppercase;
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  border: 1px solid;
}

.sc__chip--pass {
  background: var(--surface-success);
  border-color: var(--border-success);
  color: var(--success);
}

.sc__chip--fail {
  background: var(--surface-danger);
  border-color: var(--border-danger);
  color: var(--danger);
}

.sc__n {
  margin-top: 2px;
  font-size: var(--fs-meta);
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}

.sc__brands {
  margin-top: var(--sp-1);
  font-size: var(--fs-micro);
  color: var(--ink-muted);
}

/* ---- Detail page: "By brand" drill-down ---------------------------------
   Reuses the .brand-row grid + confidence-halo dot vocabulary shipped for
   Compare (charts.css, leaf B2) rather than a second visual language for the
   same idea. No positioned axis/track here -- this block states each
   brand's result and confidence, it does not plot one, so the track column
   .brand-row leaves for desktop simply stays empty.

   That reuse still needs a containing block, though (R1, Sivali 2026-09-09:
   "what is those stray circle"). .brand-row__dot is `position: absolute`
   (charts.css) and relies on Compare's .brand-row__track (`position:
   relative`) to anchor it; here the dot sits inside .brand-row__value
   instead, which had no positioned ancestor, so it escaped to the viewport
   and floated over the "Limited data" note. .brand-row__value gets its own
   `position: relative` so it always has a containing block, without
   touching charts.css's shared .brand-row__dot rule that Compare depends on.

   That alone is not enough to look right: .brand-row__dot has `top: 50%`
   but no `left`, because on Compare `left` comes from an inline `--pos`
   style the JS never sets here. With only a containing block, the browser
   falls back to the dot's static position -- flush against the value box's
   own left edge, on top of the angle/fail text that used to start there.
   .brand-row__dot is pulled out of absolute positioning on this page only
   (`position: relative`, sized as a normal inline-block) so it sits in the
   text flow instead of plotted on a track it does not have; it keeps its
   OWN `position: relative` so the confidence-halo ::before ring (below)
   still centers on the dot itself rather than on the whole row. */
.brand-block { padding: var(--s-2) var(--s-5); }
.brand-block .brand-row + .brand-row { border-top: 1px solid var(--navy-05); }
.brand-block .brand-row__value { position: relative; }
.brand-block .brand-row__dot {
  position: relative;
  top: auto;
  left: auto;
  transform: none;
  display: inline-block;
  align-self: center;
  flex: none;
}

.brand-row__angle--pending {
  color: var(--success);
  font-weight: 700;
}

/* ---- Type filter (Sivali re-ask, 2026-09-08) -----------------------------
   Her actual ask was never the taxonomy vocabulary (that reverted to
   chemistry, read from `steel_categories`, never hardcoded here); it was
   "categorization... visually" -- a pressable type control, and groups that
   read apart from each other at a glance, filter on or off.

   Structural grammar (label + chip set, grouped in one card) is borrowed
   from A3's /records filter panel so the two pages read as one product; the
   class NAMES are this leaf's own (`.filters`, `.filter-group`,
   `.filter-chip*`), because steels.css also loads on steel_detail.html,
   which already uses the frozen layout.css `.chip` for tag pills in the
   tests table -- reusing that exact name here would have silently restyled
   those tags too. records.css itself is not loaded on this page or edited
   by this leaf.

   Colour follows chip POSITION among `steel_categories`, never the category
   NAME string: the taxonomy was renamed once already today, and a
   position-based system re-colours correctly through a future rename with
   no matching change needed here. Four hues, all existing app.css tokens
   (no new colour literals); Unclassified and "All" stay neutral on purpose
   -- neither is a chosen type. */
.filters {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  margin: var(--sp-3) 0 var(--sp-5);
}

.filter-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
}

.filter-group + .filter-group {
  padding-top: var(--sp-4);
  border-top: 1px solid var(--border-parch);
}

.filter-group__label {
  flex: none;
  min-width: 88px;
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.filter-chip-set {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--border-card);
  border-left-width: 3px;
  background: var(--paper-white);
  color: var(--ink-body);
  border-radius: var(--radius-pill);
  font-size: var(--fs-micro);
  font-weight: 700;
  padding: 6px 12px;
  text-decoration: none;
}

@media (prefers-reduced-motion: no-preference) {
  .filter-chip { transition: background var(--dur-ui, 140ms) var(--ease-out), color var(--dur-ui, 140ms) var(--ease-out); }
}

.filter-chip:focus-visible {
  outline: 2px solid var(--lore-navy);
  outline-offset: 2px;
}

.filter-chip__count {
  font-weight: 700;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}

.filter-chip.is-on {
  border-color: var(--lore-navy);
  background: var(--lore-navy);
  color: var(--parchment);
}

.filter-chip.is-on .filter-chip__count { color: var(--parchment); opacity: .8; }

.filter-chip__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
}

/* Four type hues, cycled by chip position -- see comment above. Left border
   carries the identity even when the chip is off (unpressed); the dot
   repeats it beside the label so the identity survives on the `.is-on`
   navy fill too, where the border colour is overridden. */
.filter-chip--t0 { border-left-color: var(--signal-blue); }
.filter-chip--t0 .filter-chip__dot { background: var(--signal-blue); }
.filter-chip--t1 { border-left-color: var(--lore-olive); }
.filter-chip--t1 .filter-chip__dot { background: var(--lore-olive); }
.filter-chip--t2 { border-left-color: var(--field-teal); }
.filter-chip--t2 .filter-chip__dot { background: var(--field-teal); }
.filter-chip--t3 { border-left-color: var(--lore-navy); }
.filter-chip--t3 .filter-chip__dot { background: var(--lore-navy); }
.filter-chip--neutral { border-left-color: var(--mist-gray); }
.filter-chip--neutral .filter-chip__dot { background: var(--mist-gray); }
.filter-chip--neutral.is-on { border-left-color: var(--lore-navy); }

.filters-clear-all {
  align-self: flex-start;
  font-size: var(--fs-meta);
  font-weight: 700;
  color: var(--ink-muted);
  text-decoration: underline;
}

.filters-clear-all:hover { color: var(--ink-strong); }

@media (max-width: 680px) {
  .filter-group { flex-direction: column; align-items: stretch; gap: var(--sp-2); }
  .filter-group__label { min-width: 0; }
}

/* Category heading swatch: the SAME colour-by-position system as the type
   chips above, so the grouped sections on the page and the pressable
   control read as one system -- groups distinct at a glance whether or not
   a filter is active, which is the actual ask.

   The frozen cards.css puts the section's top margin on `.fc-cat` itself,
   with a reduced `:first-of-type` margin for the section right under the
   page head. Wrapping the h2 in `.cat-head` moves it out of the h2 sibling
   chain that `:first-of-type` counts (its siblings are now `.filters` and
   `.fc-grid` divs, not other h2s), so the margin is re-homed on `.cat-head`
   here, zeroed on the nested h2, and "first" is a Jinja-tracked class
   (`.cat-head--first`) rather than a CSS structural guess -- also keeps the
   dot's flex `align-items: center` measuring against the h2's actual line
   box instead of a tall margin box. */
.cat-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: var(--sp-6);
}

.cat-head--first {
  margin-top: var(--sp-4);
}

.cat-head .fc-cat {
  margin-top: 0;
}

.cat-dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex: none;
}

.cat-dot--0 { background: var(--signal-blue); }
.cat-dot--1 { background: var(--lore-olive); }
.cat-dot--2 { background: var(--field-teal); }
.cat-dot--3 { background: var(--lore-navy); }
.cat-dot--neutral { background: var(--mist-gray); }

/* ============================================================================
   R4: side-sheet peek (?peek=<id>). Sivali, 2026-09-09: "pressing the card in
   the steel tab i was thinking of a center peek instead of like another
   window" -- accepted as a right-side sheet over a centred modal, because a
   steel's content (headline angle, per-brand rows, confidence note) is a tall
   vertical list, and a tall centred box scrolls INSIDE an already-scrolling
   page. Server-rendered in _partials/steel_peek.html; this block is purely
   the sheet mechanics -- position, motion, phone treatment -- plus the two
   narrow-width overrides the reused .brand-row / .headline-block vocabulary
   needs at 480px that it does not need at its usual full-page width.

   Base state (no JS, or before sheet.js runs) is OPEN: .peek-sheet has no
   transform of its own, so a script-blocked visitor who navigated straight
   to /steels?peek=<id> gets a fully visible, readable panel with no motion
   at all (web-delivery.md 1.5). `.is-entering` / `.is-leaving` are the ONLY
   states with a transform, and sheet.js is the only thing that ever applies
   them, to animate a JS-driven open/close of a sheet that is already in the
   DOM either way -- never a JS-dependent "closed by default" state, which
   would be exactly the kind of control that only works with a script. */

.peek-backdrop {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: rgba(17, 34, 80, 0.32);
  opacity: 0;
  pointer-events: none;
}
.peek-backdrop.is-open { opacity: 1; pointer-events: auto; }
@media (prefers-reduced-motion: no-preference) {
  .peek-backdrop { transition: opacity 220ms ease-in-out; }
}

.peek-sheet {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 91;
  width: min(480px, 100vw);
  max-width: 100vw;
  background: var(--parchment-card);
  border-left: 1px solid var(--border-card);
  box-shadow: var(--shadow-float);
  padding: var(--s-5) var(--s-5) var(--s-7);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.peek-sheet:focus-visible { outline: none; }  /* the panel itself is a focus TARGET, not a control -- no visible ring on a container that carries no click affordance of its own */
.peek-sheet.is-entering,
.peek-sheet.is-leaving {
  transform: translateX(100%);
}
@media (prefers-reduced-motion: no-preference) {
  .peek-sheet { transition: transform 260ms ease-in-out; }
}

.peek-sheet__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
}
.peek-sheet__eyebrow {
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.peek-sheet__title {
  margin: 2px 0 0;
  font-size: var(--t-lg);
  color: var(--ink-strong);
}
.peek-sheet__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  margin: calc(-1 * var(--s-2)) calc(-1 * var(--s-2)) 0 0;
  flex: none;
  border-radius: 50%;
  color: var(--ink-muted);
  font-size: 22px;
  line-height: 1;
  text-decoration: none;
}
@media (prefers-reduced-motion: no-preference) {
  .peek-sheet__close { transition: background var(--dur-ui, 120ms) var(--ease-out), color var(--dur-ui, 120ms) var(--ease-out); }
}
.peek-sheet__close:hover { color: var(--lore-navy); background: var(--navy-05); }
.peek-sheet__close:focus-visible { outline: 2px solid var(--lore-navy); outline-offset: 2px; }

/* The headline angle uses --t-mega elsewhere (the full detail page, at full
   page width); here the sheet is a fixed 480px column, so it is capped to a
   size that still reads as a hero number without forcing the caption below
   it to wrap into a much taller card than the rest of the sheet. */
.peek-sheet__headline { padding: var(--s-4); margin-bottom: var(--s-4); }
.peek-sheet__headline .headline-block__angle { font-size: clamp(2.5rem, 9vw, 3.5rem); }
.peek-sheet__headline .headline-block__caption { max-width: none; }

.peek-sheet__section-label {
  margin: var(--s-5) 0 var(--s-2);
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* .brand-row's desktop rule (charts.css, >=680px) lays out a 230px name
   column + a 132px value column + a track, sized for the full-width Compare
   and steel-detail pages. The sheet is a fixed 480px column regardless of
   viewport, so it always needs the same compact two-column grid the base
   (mobile) rule already declares -- restated here at higher specificity
   (two classes beat one, inside or outside a media query) rather than fought
   with !important. */
.peek-sheet__brands { padding: var(--s-2) var(--s-4); }
.peek-sheet__brands .brand-row { grid-template-columns: 1fr auto; gap: 2px var(--s-3); }
.peek-sheet__brands .brand-row__value { justify-self: end; }

.peek-sheet__full-link {
  /* The brief calls this the point of the sheet ("a fast path TO the full
     page"). It measured 24px tall on a phone (code review, 2026-09-09).
     Floor it at --tap; inline-flex keeps the underline on the text. */
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  margin-top: var(--s-5);
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--lore-navy);
  text-decoration: underline;
}

/* Phone: a bottom sheet, not a 480px column on a 390px screen (Sivali's own
   framing of the non-negotiable: a side panel that wide on a phone is a
   broken full-screen page, not a panel). Same 680px split the rest of this
   file and its siblings already use (steels.css .filter-group,
   app.css .shell, cards.css, charts.css, layout.css). */
@media (max-width: 679px) {
  .peek-sheet {
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    max-width: 100%;
    max-height: 85vh;
    border-left: 0;
    border-top: 1px solid var(--border-card);
    border-radius: 16px 16px 0 0;
    padding-bottom: calc(var(--s-6) + env(safe-area-inset-bottom));
  }
  .peek-sheet.is-entering,
  .peek-sheet.is-leaving {
    transform: translateY(100%);
  }
}

/* Body scroll lock while the peek is open. Set by sheet.js on wire, removed on
   unwire. Without it the page scrolled under the sheet (measured 0 -> 600px);
   `overscroll-behavior: contain` on the sheet never governed the backdrop. */
html.peek-open,
html.peek-open body {
  overflow: hidden;
}
