/* cheapfilm.io design system — tokens, base, and every component template
   markup composes. One external stylesheet, no inline styles (Constitution
   I), no build toolchain. Token values are vendored from the Claude Design
   project "cheapfilm.io Design System" (tokens/colors.css et al.): paper
   neutrals, darkroom ink, the "chemistry teal" accent, and film-box semantic
   signals. Values marked "derived" are nudged from a design primitive only
   as far as needed to clear the WCAG floors asserted in
   tests/unit/test_token_contrast.py. Font fallback metrics derived from the
   vendored WOFF2 binaries themselves (research OI-2), not guessed. */

/* ---------------------------------------------------------------------
   Fonts: metric-compatible fallback faces first, so swapping in the real
   face causes minimal layout shift (research OI-2). size-adjust/ascent-
   override/descent-override were computed from each webfont's own hhea/OS2
   metrics against published Arial/Georgia/Courier New metrics.
   --------------------------------------------------------------------- */
@font-face {
  font-family: "Archivo Fallback";
  src: local("Arial");
  size-adjust: 95.77%;
  ascent-override: 91.68%;
  descent-override: 21.93%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Newsreader Fallback";
  src: local("Georgia");
  size-adjust: 93.6%;
  ascent-override: 78.53%;
  descent-override: 28.31%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Plex Mono Fallback";
  src: local("Courier New");
  size-adjust: 122.18%;
  ascent-override: 83.89%;
  descent-override: 22.51%;
  line-gap-override: 0%;
}

@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/archivo-400.woff2") format("woff2");
}
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/archivo-600.woff2") format("woff2");
}
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/newsreader-400.woff2") format("woff2");
}
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/newsreader-600.woff2") format("woff2");
}
@font-face {
  font-family: "Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/plex-mono-400.woff2") format("woff2");
}

/* ---------------------------------------------------------------------
   Tokens. Every fg/bg pairing the site composites for text or for a UI
   boundary is enumerated in tests/unit/test_token_contrast.py;
   --border-hairline and --selection are decorative and deliberately
   unpinned.
   --------------------------------------------------------------------- */
:root {
  /* surfaces: paper white → darkroom ink */
  --surface-page: #f3f3f1; /* paper-100 */
  --surface-card: #fbfbfa; /* paper-50 */
  --surface-sunken: #eaeae8; /* paper-200: sunken panels, zebra rows */
  --surface-inverse: #171613; /* ink-900: darkroom bands (footer, strip) */

  --text-primary: #171613; /* ink-900 */
  --text-secondary: #4b4a45; /* neutral-700 (neutral-600 misses 4.5:1 on sunken) */
  --text-muted: #68675f; /* derived from neutral-600 to clear 4.5:1 on sunken */
  --text-on-inverse: #fbfbfa; /* paper-50 */
  --text-secondary-on-inverse: #dfdfdc; /* paper-300: footer body text on ink */
  /* De-emphasized text on the dark bands needs its own value: neutral-500
     clears 4.5:1 against ink-900 and the strip's translucent card alike. */
  --text-muted-on-inverse: #98978f; /* neutral-500 */

  /* Load-bearing boundaries (inputs, buttons, toggle) hold the 3:1 floor;
     decorative rules (table rows, card edges) use the design hairline. */
  --border: #898880; /* derived from neutral-500 to clear 3:1 on card */
  --border-strong: #6f6e67; /* neutral-600 */
  --border-hairline: #ddddd9; /* line-300: decorative hairlines only */

  /* "chemistry teal" accent */
  --accent: #0b6362; /* teal-600: fills, wordmark, link hover */
  --accent-strong: #084d4c; /* teal-700: link text */
  --accent-muted: #5fa9a7; /* teal-300: links and eyebrows on dark bands */
  --text-on-accent: #ffffff; /* button labels on accent fills */
  --focus-ring: #0e7c7b; /* teal-500 */
  --selection: #c0dedd; /* teal-100 */

  /* semantic signals — soft badge pairs from the design ramp */
  --best-bg: #e5efe6; /* green-50 */
  --best-fg: #234631; /* green-700 */
  --stale-bg: #f3ead2; /* ochre-50 */
  --stale-fg: #815f1b; /* derived from ochre-600 to clear 4.5:1 on its tint */
  --low-bg: #e2efee; /* teal-50 */
  --low-fg: #084d4c; /* teal-700 */
  --out-bg: #dfdfdc; /* paper-300: out-of-stock is neutral, not alarming */
  --out-fg: #4b4a45; /* neutral-700 */

  /* typography: editorial serif voice, industrial grotesque UI,
     measurement mono for numbers */
  --font-display: "Newsreader", "Newsreader Fallback", Georgia, serif;
  --font-body: "Archivo", "Archivo Fallback", Arial, sans-serif;
  --font-mono: "Plex Mono", "Plex Mono Fallback", "Courier New", monospace;
  --text-2xs: 0.6875rem; /* 11px: eyebrows, table headers */
  --text-xs: 0.75rem; /* 12px */
  --text-sm: 0.8125rem; /* 13px */
  --text-base: 0.9375rem; /* 15px: body */
  --text-lg: 1.25rem; /* 20px */
  --text-xl: 1.5625rem; /* 25px */
  --text-2xl: 1.9375rem; /* 31px */
  --text-3xl: 2.4375rem; /* 39px */
  --leading-tight: 1.15;
  --leading-normal: 1.55;
  --tracking-tighter: -0.02em;
  --tracking-tight: -0.01em;
  --tracking-wide: 0.02em;
  --tracking-wider: 0.06em;

  /* spacing (4px scale) */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;

  /* radii — small & precise, like printed cards */
  --radius-sm: 3px;
  --radius-md: 5px;
  --radius-lg: 8px;

  --content-max: 68.75rem; /* 1100px, --width-content */
}

/* ---------------------------------------------------------------------
   Reset + base
   --------------------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--surface-page);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  margin: 0 0 var(--space-4);
  color: var(--text-primary);
  text-wrap: balance;
}

/* Stepped by media query rather than a vw clamp: viewport-relative type
   partially cancels browser zoom (WCAG 2.1 SC 1.4.4). */
h1 {
  font-size: var(--text-2xl);
  letter-spacing: var(--tracking-tighter);
}
@media (min-width: 48rem) {
  h1 {
    font-size: var(--text-3xl);
  }
}
h2 {
  font-size: var(--text-xl);
}
h3 {
  font-size: var(--text-lg);
}

p {
  margin: 0 0 var(--space-4);
}

a {
  color: var(--accent-strong);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  text-decoration-color: color-mix(in srgb, var(--accent-strong) 40%, transparent);
}
a:hover {
  color: var(--accent);
  text-decoration-color: currentColor;
}

img,
svg {
  max-width: 100%;
}

/* Numbers are the product: timestamps render in the measurement mono
   (templates mark every stamp up as <time>). No font-size here — an em
   shrink compounds differently depending on whether .freshness sits on the
   <time> itself or on a wrapper, splitting one stamp into mixed sizes. */
time {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

table {
  font-variant-numeric: tabular-nums;
}

::selection {
  background: var(--selection);
  color: var(--text-primary);
}

:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------------
   Skip link (contract B4)
   --------------------------------------------------------------------- */
.skip-link {
  position: absolute;
  top: -100%;
  left: var(--space-4);
  z-index: 100;
  padding: var(--space-2) var(--space-4);
  background: var(--surface-inverse);
  color: var(--text-on-inverse);
  border-radius: var(--radius-sm);
  text-decoration: none;
  transition: top 0.15s ease;
}
.skip-link:focus {
  top: var(--space-4);
}

/* ---------------------------------------------------------------------
   Header / nav / footer landmarks. Header sits on the page paper with a
   hairline rule; footer is a darkroom band capped by the sprocket motif.
   --------------------------------------------------------------------- */
.site-header {
  border-bottom: 1px solid var(--border-hairline);
  background: var(--surface-page);
}

.site-header__bar {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: var(--space-4) var(--space-5);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3) var(--space-6);
}

/* Wordmark is set in the grotesque, not the serif (design brand-wordmark) */
.site-header__wordmark {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--text-lg);
  font-weight: 600;
  letter-spacing: var(--tracking-tight);
  line-height: 1;
}
.site-header__wordmark a {
  color: var(--text-primary);
  text-decoration: none;
}

.site-header__nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  font-size: var(--text-sm);
}
.site-header__nav a {
  color: var(--text-secondary);
  text-decoration: none;
  padding: var(--space-1) 0;
}
.site-header__nav a:hover {
  color: var(--text-primary);
}

main {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: var(--space-6) var(--space-5);
}

/* 35mm sprocket-hole strip: holes punch through to the page paper.
   Decorative, empty pseudo-content, never exposed to assistive tech. */
.site-footer::before {
  content: "";
  display: block;
  height: 22px;
  background: var(--surface-inverse);
  background-image: radial-gradient(
    4px 5.5px at 14px 50%,
    var(--surface-page) 98%,
    transparent 100%
  );
  background-size: 28px 22px;
  background-repeat: repeat-x;
}

.site-footer {
  background: var(--surface-inverse);
  color: var(--text-secondary-on-inverse);
  font-size: var(--text-xs);
}
.site-footer__inner {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: var(--space-6) var(--space-5) var(--space-7);
}
.site-footer p {
  margin: 0 0 var(--space-3);
  max-width: 72ch;
}
.site-footer p:last-child {
  margin-bottom: 0;
}
.site-footer a {
  color: var(--accent-muted);
  text-decoration-color: color-mix(in srgb, var(--accent-muted) 40%, transparent);
}
.site-footer a:hover {
  color: var(--text-on-inverse);
  text-decoration-color: currentColor;
}

/* ---------------------------------------------------------------------
   Scroll region + tables (contract C1/C2 -- the load-bearing one). NEVER
   set display:block|flex|grid|contents on a table element, and NEVER hide
   <thead>: enforced by tests/unit/test_table_semantics_guard.py.
   Printed price-list feel: hairline rules, tiny tracked caps headers.
   --------------------------------------------------------------------- */
.scroll-region {
  overflow-x: auto;
  /* Keyboard-focusable interactive component (contract C1: role="group" +
     tabindex="0"), so its boundary holds the 3:1 floor — never the
     decorative hairline. */
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-card);
}
.scroll-region:focus-visible {
  outline-offset: -3px;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}

caption {
  text-align: left;
  padding: var(--space-3) var(--space-4);
  font-weight: 600;
  color: var(--text-primary);
}

th,
td {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  border-bottom: 1px solid var(--border-hairline);
  white-space: nowrap;
  vertical-align: top;
}

thead th {
  font-family: var(--font-body);
  font-size: var(--text-2xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--text-secondary);
  background: var(--surface-sunken);
  border-bottom: 1px solid var(--border-hairline);
}

tbody tr:last-child td {
  border-bottom: none;
}

tbody tr:nth-child(even) {
  background: var(--surface-sunken);
}

/* Once filter.js runs it stripes the *visible* rows itself (see `restripe`):
   :nth-child is structural and keeps counting rows the filter has hidden, so
   the alternation breaks as soon as a query is active. The rule above stays the
   no-JS default; these two take over only when the script is present. */
tbody.is-js-striped tr:nth-child(even) {
  background: transparent;
}

tbody.is-js-striped tr.is-alt {
  background: var(--surface-sunken);
}

td.is-narrow-hide,
th.is-narrow-hide {
  /* narrow-width column hiding, contract C4 -- header+cell always paired */
}

/* ---------------------------------------------------------------------
   Icons
   --------------------------------------------------------------------- */
.icon-sprite {
  display: none;
}

.icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: none;
  stroke: currentColor;
}

/* ---------------------------------------------------------------------
   Badges (contract B7, FR-008): text is always the primary carrier; color
   and icon reinforce, never substitute. Printed-label look: small radius,
   tracked caps-adjacent type — never a pill.
   --------------------------------------------------------------------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 3px var(--space-2);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  line-height: 1.4;
  white-space: nowrap;
}
.badge--best {
  background: var(--best-bg);
  color: var(--best-fg);
}
.badge--stale {
  background: var(--stale-bg);
  color: var(--stale-fg);
}
.badge--low {
  background: var(--low-bg);
  color: var(--low-fg);
}
.badge--out {
  background: var(--out-bg);
  color: var(--out-fg);
}
.badge--ok {
  background: var(--best-bg);
  color: var(--best-fg);
}

/* ---------------------------------------------------------------------
   Price / freshness / availability — measurement mono
   --------------------------------------------------------------------- */
.price {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--text-primary);
}
.price--stale {
  color: var(--text-muted);
  font-weight: 400;
  text-decoration: line-through;
  text-decoration-color: var(--border-strong);
}
.price--none {
  font-family: var(--font-body);
  font-style: italic;
  font-weight: 400;
  color: var(--text-muted);
}

.freshness {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  font-size: var(--text-xs);
}
.freshness--stale {
  color: var(--stale-fg);
}

.empty-state {
  color: var(--text-muted);
  font-style: italic;
}

/* ---------------------------------------------------------------------
   Buttons / links / toggle pair
   --------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--surface-card);
  color: var(--text-primary);
  font: inherit;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}
.btn:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--text-on-accent);
}
.btn--primary:hover {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  color: var(--text-on-accent);
}

/* A film's spec sheet on its detail page. Also the reason the catalog may hide
   its Type and ISO columns below 40rem (contract C4): the data has to stay
   reachable somewhere, and this is where. */
.film-spec {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-6);
  margin: var(--space-4) 0;
  padding: var(--space-3) var(--space-4);
  background: var(--surface-sunken);
  border-radius: var(--radius-lg);
}

.film-spec dt {
  font-size: var(--text-2xs);
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  font-weight: 600;
}

.film-spec dd {
  margin: 0;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* The per-roll / per-frame ordering toggle: two published pages styled as a
   segmented control; the active segment is inked like a darkroom stamp. */
.toggle-pair {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--surface-card);
  margin: 0 0 var(--space-4);
}
.toggle-pair__link {
  padding: var(--space-2) var(--space-4);
  text-decoration: none;
  color: var(--text-secondary);
  font-weight: 600;
  font-size: var(--text-sm);
}
.toggle-pair__link + .toggle-pair__link {
  border-left: 1px solid var(--border);
}
.toggle-pair__link.is-active {
  background: var(--surface-inverse);
  color: var(--text-on-inverse);
}

/* ---------------------------------------------------------------------
   Callout
   --------------------------------------------------------------------- */
.callout {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  border: 1px solid var(--low-fg);
  background: var(--low-bg);
  color: var(--text-primary);
  margin: 0 0 var(--space-5);
}
.callout .icon {
  flex-shrink: 0;
  margin-top: 0.2em;
  color: var(--low-fg);
}
.callout p {
  margin: 0;
}
.callout--warning {
  border-color: var(--stale-fg);
  background: var(--stale-bg);
}
.callout--warning .icon {
  color: var(--stale-fg);
}

/* ---------------------------------------------------------------------
   Lowest-right-now strip (signature element): a darkroom band with the
   35mm sprocket-hole edge, pure CSS, purely decorative (aria-hidden via
   empty pseudo-content, never exposed to assistive tech).
   --------------------------------------------------------------------- */
.lowest-now {
  margin: 0 0 var(--space-6);
  border-radius: var(--radius-lg);
  background: var(--surface-inverse);
  color: var(--text-on-inverse);
  overflow: hidden;
}
/* Same hole geometry as the footer strip; only the band height differs
   (the strip's edges are slimmer than the footer's full-height run). */
.lowest-now::before,
.lowest-now::after {
  content: "";
  display: block;
  height: 16px;
  background-image: radial-gradient(
    4px 5.5px at 14px 50%,
    var(--surface-page) 98%,
    transparent 100%
  );
  background-size: 28px 16px;
  background-repeat: repeat-x;
  opacity: 0.9;
}
.lowest-now__body {
  padding: var(--space-5);
}
/* Film edge-code eyebrow: tracked mono caps in teal, like the markings
   printed along a film rebate. */
.lowest-now h2 {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 400;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent-muted);
  margin-bottom: var(--space-3);
}
.lowest-now__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: var(--space-3);
}
.lowest-now__item {
  padding: var(--space-3) var(--space-4);
  background: rgba(251, 251, 250, 0.06);
  border: 1px solid rgba(251, 251, 250, 0.22);
  border-radius: var(--radius-md);
}
.lowest-now__item .freshness {
  color: var(--text-muted-on-inverse);
}

.lowest-now__item a {
  color: var(--text-on-inverse);
  font-weight: 600;
  text-decoration: none;
}
.lowest-now__item a:hover {
  text-decoration: underline;
}
.lowest-now .price {
  color: var(--text-on-inverse);
}

/* ---------------------------------------------------------------------
   Forms: search field, select, text field, feedback form
   --------------------------------------------------------------------- */
.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin: 0 0 var(--space-4);
  max-width: 32rem;
}
.field label {
  font-weight: 600;
  font-size: var(--text-sm);
}
.field__hint {
  font-size: var(--text-xs);
  color: var(--text-muted);
}
input,
select,
textarea {
  font: inherit;
  color: var(--text-primary);
  background: var(--surface-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
}
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  border-radius: var(--radius-md);
}
textarea {
  min-height: 8rem;
  resize: vertical;
}

.search-field {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-4);
  max-width: 24rem;
}
.search-field input {
  flex: 1;
}

.filter-status {
  margin: 0 0 var(--space-3);
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.filter-empty {
  display: none;
  padding: var(--space-4);
  color: var(--text-muted);
}
.filter-empty.is-visible {
  display: block;
}

/* ---------------------------------------------------------------------
   Cards (browse index)
   --------------------------------------------------------------------- */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: var(--space-4);
  list-style: none;
  margin: 0;
  padding: 0;
}
.card {
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-card);
  padding: var(--space-4);
  transition: border-color 0.18s ease;
}
.card:hover {
  border-color: var(--border-strong);
}
.card h3 {
  margin-bottom: var(--space-2);
}
.card a {
  text-decoration: none;
  font-weight: 600;
}

/* ---------------------------------------------------------------------
   Trend chart (build-time inline SVG, contract D4)
   --------------------------------------------------------------------- */
.trend-chart {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 0 0 var(--space-5);
}
.trend-chart__line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
}
.trend-chart__point {
  fill: var(--accent);
}
.trend-chart__axis {
  stroke: var(--border);
  stroke-width: 1;
}

.stat-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: var(--space-4);
  margin: 0 0 var(--space-5);
}
.stat-card {
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--surface-card);
  border: 1px solid var(--border-hairline);
}
.stat-card--best {
  background: var(--best-bg);
  color: var(--best-fg);
  border-color: var(--best-fg);
}
.stat-card__label {
  display: block;
  font-size: var(--text-2xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: var(--space-2);
}
.stat-card--best .stat-card__label {
  color: inherit;
}
.stat-card__value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-lg);
  font-weight: 600;
}

/* ---------------------------------------------------------------------
   Reduced motion (FR-015, contract B8)
   --------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition: none !important;
    animation: none !important;
    scroll-behavior: auto !important;
  }
}

/* ---------------------------------------------------------------------
   Narrow-width column hiding (FR-014c, contract C4). Header and cell
   share the class so they hide together and the table stays well-formed.
   --------------------------------------------------------------------- */
@media (max-width: 40rem) {
  .site-header__bar {
    flex-direction: column;
    align-items: flex-start;
  }
  th.is-narrow-hide,
  td.is-narrow-hide {
    display: none;
  }
  .lowest-now__list {
    grid-template-columns: 1fr;
  }
}
