/* ============================================================================
   LORE LAB Steel Test Database - design system
   Author: Ezra (Design Engineer). Brand: LORE LAB.
   Aesthetic: editorial field-almanac. Parchment ground, Lore Navy ink,
   measured baseline, Baron Neue display against Inter precision.
   No chart library: dot plots and scatters are hand-built CSS/SVG so every
   honesty rule in the viz-spec (position-only encoding, three-way pass/fail,
   binned confidence, 44px tap halos) is under direct control.
   ========================================================================= */

/* ---- Self-hosted fonts ------------------------------------------------- */
/* Baron Neue is reserved for the wordmark only (brand rule). Glacial is the
   English display face everywhere else: headings, stat numbers, chart
   labels, popovers, card heads. */
@font-face {
  font-family: "Baron Neue";
  src: url("/static/fonts/BaronNeue-Bold.otf") format("opentype");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Glacial";
  src: url("/static/fonts/Glacial-Regular.otf") format("opentype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Glacial";
  src: url("/static/fonts/Glacial-Bold.otf") format("opentype");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("/static/fonts/Inter-Variable.ttf") format("truetype");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
/* ZH-TW fast-follow face. Declared now, not yet applied. */
@font-face {
  font-family: "jf-openhuninn";
  src: url("/static/fonts/jf-openhuninn-2.1.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* ---- Design tokens ------------------------------------------------------
   Canonical names and values lifted from the claude.ai Lore Lab Design
   System (project 1bd5f23f), per records/design-directions/gen_directions.py
   DS block and PLAN.md C2. Values not present in that source (surface/border
   for warning, chip/float shadows, spacing/card geometry, motion) are
   reasoned extensions of the same system and are called out below.
   The app's original token names (--lore-navy, --ink-soft, --t-*, --s-*, …)
   are kept as ALIASES onto the new names so charts.css and every untouched
   template keep rendering unchanged. */
:root {
  /* ---- Brand palette (canonical DS names) ---- */
  --lore-navy:   #112250;  /* == --ink-strong; primary text, headings, headline dot */
  --lore-olive:  #5e6b49;  /* active / selected, primary interactive accent */
  --field-teal:  #6b8b81;  /* labels, overlines, hairlines - NOT body text (3.03:1) */
  --signal-blue: #193276;  /* PASS encoding (reached 600 cuts) */
  --deep-teal:   #46707e;  /* secondary teal accent, clears 4.5:1 where teal text is needed */
  --parchment:   #f0e7d5;  /* page ground */
  --mist-gray:   #cecdb9;  /* context: gridlines, axis, non-focus, dividers */
  --paper-white: #fff;     /* lifted surface, DS card ground */
  --paper-faint: #faf5ea;  /* sunk panel / table-header band */
  --chip-tan:    #e5dcc6;  /* static chip / tag fill */
  --border-card: #d8cdb4;  /* 1px hairline on cards */
  --border-parch:#e0d5bd;  /* 1px hairline on parchment surfaces (masthead, footer) */
  --rule-mist:   #ece1c9;  /* lightest divider */
  --ink-strong:  #112250;  /* headings, high-emphasis text (== --lore-navy) */
  --ink-body:    #4a5540;  /* default body ink, 6.42:1 on parchment */
  /* --ink-muted: the brand's literal #8a7f63 measures 3.23:1 on parchment,
     below the 4.5:1 floor. Darkened here to #6e664f (4.65:1); flagged below. */
  --ink-muted:   #6e664f;
  --danger:  #8a4632;
  --success: #4e7a63;
  --warning: #b0862f;
  --surface-success: #e2ece6; --border-success: #b9d2c5;
  --surface-danger:  #f4e3dc; --border-danger:  #d3a89f;
  /* --surface-warning / --border-warning: not in the DS source block (no
     warning-role component exists there yet). Tinted from --warning at the
     same ratios the DS used for danger/success; flagged below. */
  --surface-warning: #f6ebd4; --border-warning: #ddc48a;

  /* ---- Type: faces ---- */
  --font-display: "Glacial", "Inter", system-ui, sans-serif;  /* headings, stat numbers, chart/popover/card labels */
  --font-wordmark: "Baron Neue", "Inter", system-ui, sans-serif;  /* the LORE LAB wordmark ONLY */
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, monospace;

  /* ---- Type: DS scale (exact) ---- */
  --fs-h1: 36px; --fs-h2: 30px; --fs-h3: 23px; --fs-h4: 19px;
  --fs-lead: 18.5px; --fs-body: 16px; --fs-small: 14.5px;
  --fs-caption: 13.5px; --fs-meta: 12.5px; --fs-label: 12px; --fs-micro: 11.5px;
  /* --fs-hero / --fs-body-lg / --fs-nano: no DS value exists for these three
     roles. Interpolated to fit the given ladder; flagged below. */
  --fs-hero: clamp(24px, min(4.4vw, 5vh), 46px);
  --fs-body-lg: 16.5px;
  --fs-nano: 10px;
  --fw-regular: 400; --fw-bold: 700;
  /* --fw-black: only Regular (400) and Bold (700) Glacial files are shipped;
     no black weight exists to synthesize. Aliased to 700. Flagged below. */
  --fw-black: 900;
  --lh-tight: 1.08; --lh-snug: 1.3; --lh-body: 1.7; --lh-relaxed: 1.8;
  --ls-nav: .08em; --ls-pill: .1em; --ls-label: .18em; --ls-eyebrow: .34em; --ls-wordmark: .06em;

  /* Legacy modular scale, kept for every existing --t-* call site. */
  --t-xs:   0.75rem;   /* 12px meta / n labels */
  --t-sm:   0.875rem;  /* 14px captions, table cells */
  --t-base: 1rem;      /* 16px body */
  --t-md:   1.125rem;  /* 18px lead */
  --t-lg:   1.5rem;    /* 24px section heads */
  --t-xl:   2.0rem;    /* 32px page title (mobile) */
  --t-2xl:  2.75rem;   /* 44px page title (desktop) */
  --t-mega: clamp(3.5rem, 12vw, 6rem);  /* the big feasible angle */

  /* ---- Spacing (DS --sp-1..9, 8px base; identical to the legacy --s-* scale) ---- */
  --sp-1: 0.25rem; --sp-2: 0.5rem; --sp-3: 0.75rem; --sp-4: 1rem; --sp-5: 1.5rem;
  --sp-6: 2rem; --sp-7: 3rem; --sp-8: 4rem; --sp-9: 6rem;
  --s-1: var(--sp-1); --s-2: var(--sp-2); --s-3: var(--sp-3); --s-4: var(--sp-4);
  --s-5: var(--sp-5); --s-6: var(--sp-6); --s-7: var(--sp-7); --s-8: var(--sp-8); --s-9: var(--sp-9);

  /* ---- Geometry (DS exact) ---- */
  --radius-card: 8px;
  --radius-sm:   6px;
  --radius-pill: 999px;
  --border-hairline: 1px solid var(--rule-mist);
  --shadow-btn:       0 2px 5px rgba(17, 34, 80, .2);
  --shadow-btn-hover: 0 4px 10px rgba(17, 34, 80, .24);
  --shadow-card:      0 1px 3px rgba(17, 34, 80, .12);
  /* --shadow-chip / --shadow-float: no DS value given. --shadow-chip is a
     lighter version of --shadow-btn for small pill tags; --shadow-float
     reuses the app's own pre-existing popover shadow (--shadow-pop) exactly,
     just renamed onto the canonical token. Flagged below. */
  --shadow-chip:  0 1px 2px rgba(17, 34, 80, 0.16);
  --shadow-float: 0 6px 14px rgba(17, 34, 80, 0.22);
  --ease-out: cubic-bezier(.2, .7, .3, 1);
  --dur-ui: 120ms;
  --dur-entrance: 220ms;
  --lift-hover: -1px;
  /* Legacy geometry aliases so untouched card/table/button rules pick up the
     new values without every call site being rewritten. */
  --radius: var(--radius-sm);
  --radius-lg: var(--radius-card);
  --border: 1px solid var(--border-card);
  --border-strong: 1.5px solid var(--lore-navy);
  --shadow-pop: var(--shadow-float);

  /* ---- Layout ---- */
  --container-max: 1180px;
  --maxw: var(--container-max);   /* legacy alias */
  --maxw-text: 64ch;   /* readable measure for methodology prose */
  --tap: 44px;         /* minimum touch target (accessibility) */
  /* --card-pad / --grid-gap / --card-min: no DS value given; set to the
     spacing/sizing the rest of this system already uses for card interiors
     and grid gutters. Flagged below. */
  --card-pad: 22px;   /* 24px */
  --grid-gap: 22px;   /* 24px */
  --card-min: 290px;

  /* ---- Navy-surface grid texture (the only texture allowed off-parchment) ---- */
  --grid-line: rgba(240, 231, 213, 0.07);
  --grid-size: 48px;

  /* Legacy tint aliases (unchanged app-only tokens, still referenced widely). */
  --parchment-deep:  #e7dcc5;  /* sunk panels, table header bands */
  --parchment-card:  #f6efe1;  /* lifted cards over the parchment ground */
  --navy-12:  rgba(17, 34, 80, 0.12);
  --navy-08:  rgba(17, 34, 80, 0.08);
  --navy-05:  rgba(17, 34, 80, 0.05);
  --olive-06: rgba(94, 107, 73, 0.07);  /* row hover lift */
  --olive-14: rgba(94, 107, 73, 0.16);  /* selected row wash */
  --ink-soft: var(--ink-body);   /* legacy alias, was #41506f */
  --ink-faint: var(--ink-muted); /* legacy alias, was #6c7791 (3.65:1, failed AA) */

  /* Chart geometry (shared so all three views read one scale) */
  --dot-sm: 8px;       /* n = 1 */
  --dot-md: 12px;      /* n = 2..4 */
  --dot-lg: 16px;      /* n >= 5 */
}

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

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

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--t-base);
  line-height: var(--lh-body);
  color: var(--ink-strong);
  /* Flat parchment ground. The dot grain the old system carried is gone;
     the only texture this system allows is the 48px grid line, and it is
     scoped to navy surfaces only (see .footer, .metric-explain). */
  background-color: var(--parchment);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: 0.01em;
  color: var(--ink-strong);
  margin: 0 0 var(--s-4);
}

p { margin: 0 0 var(--s-4); }
a { color: var(--lore-olive); text-decoration: none; }
a:hover { text-decoration: underline; }
strong { font-weight: 600; }

/* Visually hidden but available to screen readers */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Skip-to-content link (web-interaction.md accessibility floor / C7): visually
   hidden the same way as .sr-only, but restored to a visible, tappable pill
   the moment it is focused, so a keyboard user never tabs the full masthead
   nav to reach content. */
.skip-link {
  position: absolute;
  top: -1px; left: -1px; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.skip-link:focus {
  position: fixed;
  top: var(--s-3); left: var(--s-3);
  width: auto; height: auto;
  padding: var(--s-2) var(--s-4);
  margin: 0;
  clip: auto;
  overflow: visible;
  white-space: normal;
  z-index: 100;
  background: var(--lore-navy);
  color: var(--parchment);
  border-radius: var(--radius-pill);
  font-size: var(--t-sm);
  font-weight: 600;
  box-shadow: var(--shadow-btn);
}

/* Focus visibility (keyboard accessibility) */
:focus-visible {
  outline: 2.5px solid var(--lore-olive);
  outline-offset: 2px;
  border-radius: 2px;
}
/* The olive ring is 4.65:1 on parchment and 5.71:1 on a white card, but only
   2.69:1 on --lore-navy, under the 3:1 floor for a non-text indicator. That is
   not a footer problem: `.btn--primary` is navy, so the primary button on every
   page had a focus ring a keyboard user could barely see, and the admin's
   selected segmented options and checked chips are navy too. Measured
   2026-09-09 while reviewing the footer rebuild, which had fixed it only for
   itself.

   Rather than chase every navy surface, pair the olive ring with a parchment
   halo just outside it. On a light ground the olive ring carries the
   indication and the halo is invisible; on navy the halo carries it at 12.49:1
   and the olive recedes. One of the two always clears the floor, including on
   navy surfaces nobody has built yet.

   CORRECTION 2026-09-09, caught by code review: an earlier version of this
   comment claimed `.btn` sets box-shadow on :hover and :active only. That was
   false -- `.btn` sets `box-shadow: var(--shadow-btn)` at REST (below), at the
   same (0,1,0) specificity and declared later, so it won and this halo never
   applied to a button at all. The rule existed for the navy primary button and
   was inert on exactly that element. Measured live: a keyboard-focused
   `.btn--primary` computed the button shadow, not the halo. The button cases
   are handled after the .btn block, where they can win. */
:focus-visible {
  box-shadow: 0 0 0 5.5px rgba(240, 231, 213, 0.92);
}
/* A card already carries its own elevation and sits on a light ground, where
   the olive ring is sufficient; leave its shadow alone. */
.steel-card:focus-visible,
.sc:focus-visible,
.fc:focus-visible {
  box-shadow: var(--shadow-btn);
}

/* ---- Shell ------------------------------------------------------------- */
.shell { max-width: var(--maxw); margin: 0 auto; padding: 0 var(--s-4); }
@media (min-width: 680px)  { .shell { padding: 0 var(--s-5); } }
@media (min-width: 960px) { .shell { padding: 0 var(--s-6); } }

/* ---- Masthead ---------------------------------------------------------- */
.masthead {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(240, 231, 213, 0.86);
  backdrop-filter: saturate(140%) blur(8px);
  border-bottom: var(--border);
}
.masthead__inner {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: var(--s-3) var(--s-4);
  display: flex;
  align-items: center;
  gap: var(--s-4);
}
@media (min-width: 960px) { .masthead__inner { padding: var(--s-3) var(--s-6); } }

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  color: var(--lore-navy);
  min-height: var(--tap);
}
.brand:hover { text-decoration: none; }
.brand__mark { width: 34px; height: 34px; border-radius: 50%; display: block; }
.brand__words { display: flex; flex-direction: column; line-height: 1; }
.brand__lab {
  /* The wordmark: the ONE place Baron Neue is allowed to appear. */
  font-family: var(--font-wordmark);
  font-size: 1.2rem;
  letter-spacing: var(--ls-wordmark);
}
.brand__sub {
  font-family: var(--font-body);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-top: 3px;
}
@media (min-width: 680px) {
  .brand__lab { font-size: 1.3rem; }
  .brand__sub { font-size: 0.72rem; }
}

.mastnav { margin-left: auto; display: flex; align-items: center; gap: var(--s-1); }
.mastnav a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: 0 var(--s-3);
  color: var(--ink-soft);
  font-size: var(--t-sm);
  font-weight: 600;
  letter-spacing: 0.02em;
  border-radius: var(--radius);
}
.mastnav a:hover { color: var(--lore-navy); background: var(--olive-06); text-decoration: none; }
.mastnav a.is-active { color: var(--lore-olive); }
.mastnav a.is-active::after {
  content: "";
  display: block;
  position: relative;
}

/* Below 640px the brand + full nav cannot share one row without clipping the
   last link (measured: nav alone is ~284px). Stack the nav onto its own row
   under the brand, full width and evenly spread, so every link stays visible
   and tappable. No JS: progressive enhancement, links never hidden. */
@media (max-width: 679px) {
  .brand__sub { display: none; }
  .masthead__inner {
    flex-wrap: wrap;
    row-gap: var(--s-2);
    column-gap: var(--s-3);
  }
  .mastnav {
    margin-left: 0;
    width: 100%;
    flex-wrap: wrap;
    justify-content: flex-start;
    gap: 2px;
    border-top: var(--border);
    padding-top: var(--s-2);
  }
  .mastnav a { padding: 0 var(--s-2); }
}

/* Narrowest phones: shrink nav type a touch so the 4 nav links still fit on
   two comfortable rows without spilling (admin now lives in the footer). */
@media (max-width: 380px) {
  .mastnav a { padding: 0 6px; font-size: var(--t-xs); }
}

/* ---- Flash messages ---------------------------------------------------- */
.flash {
  margin: var(--s-4) 0 0;
  padding: var(--s-3) var(--s-4);
  background: var(--parchment-card);
  border: var(--border);
  border-left: 3px solid var(--field-teal);
  border-radius: var(--radius);
  font-size: var(--t-sm);
}

/* ---- Footer (C7: navy surface) ------------------------------------------ */
.footer {
  margin-top: var(--s-9);
  background: var(--lore-navy);
  color: rgba(240, 231, 213, 0.72);
  /* The only texture this system allows off-parchment: a 48px grid line,
     scoped to navy surfaces. */
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: var(--grid-size) var(--grid-size);
}
.footer__inner {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: var(--s-6) var(--s-4);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4) var(--s-6);
  align-items: center;
  justify-content: space-between;
  font-size: var(--t-xs);
}
.footer__nav { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-5); list-style: none; margin: 0; padding: 0; }
.footer__inner a { color: var(--parchment); }
.footer__inner a:hover { text-decoration: underline; }
/* Discreet admin door (web-interaction.md 6.1): a small key glyph, not a
   text link, low-contrast against the navy footer, still 44px tappable and
   named for a screen reader. */
.footer__inner a.footer__admin {
  /* Specificity note: plain `.footer__admin` (0,1,0) loses to the
     `.footer__inner a` rule above (0,1,1), which repaints it fully opaque.
     Qualifying the selector this way is what actually keeps it discreet;
     measured via getComputedStyle before this fix, it read opaque. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  color: rgba(240, 231, 213, 0.4);
  border-radius: 50%;
  transition: color var(--dur-ui) var(--ease-out), background var(--dur-ui) var(--ease-out);
}
.footer__inner a.footer__admin:hover, .footer__inner a.footer__admin:focus-visible { color: var(--parchment); background: rgba(240, 231, 213, 0.1); text-decoration: none; }
.footer__admin svg { width: 16px; height: 16px; display: block; }

/* ============================================================================
   SHARED CHART PRIMITIVES (viz-spec section 4 consistency contract)
   These classes are identical across Index, Detail, and Compare so the app
   teaches one scale and never breaks it.
   ========================================================================= */

/* The persistent direction legend that sits above every scale (0.3). */
.legend-strip {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  background: var(--parchment-deep);
  border: var(--border);
  border-radius: var(--radius);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
  line-height: 1.4;
}
.legend-strip__arrow { color: var(--lore-olive); font-weight: 700; white-space: nowrap; }

/* Pass/fail/heat key (three-way, never color alone) */
.passkey { display: flex; flex-wrap: wrap; gap: var(--s-4); font-size: var(--t-xs); color: var(--ink-soft); }
.passkey__item { display: inline-flex; align-items: center; gap: var(--s-2); }

/* Dot glyphs as inline markers (used in keys, tooltips, table ticks). */
.glyph { display: inline-block; width: 14px; height: 14px; flex: none; }
.glyph--pass { border-radius: 50%; background: var(--signal-blue); }
.glyph--fail { border-radius: 50%; background: transparent; border: 1.5px solid var(--lore-navy); }
.glyph--heat {
  border-radius: 50%; background: transparent;
  border: 1.5px dashed var(--mist-gray);
  position: relative;
}
.glyph--head { border-radius: 50%; background: var(--lore-navy); }

/* Confidence chip ("n = 11") - literal count, always paired with size (0.6). */
.nchip {
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--ink-soft);
  white-space: nowrap;
  font-feature-settings: "tnum" 1;
}
.nchip--solo {
  color: var(--lore-navy);
  background: var(--mist-gray);
  padding: 1px 6px;
  border-radius: 999px;
}

/* "No passing result yet" chip (state: tested, no pass - 0.7) */
.tag-nopass {
  display: inline-block;
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--lore-navy);
  background: var(--mist-gray);
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  line-height: 1.3;
}
/* "Not yet tested" - greyed italic, never shares the no-pass encoding (0.7) */
.tag-untested {
  font-style: italic;
  color: var(--ink-faint);
}

/* ---- Reusable card ----------------------------------------------------- */
.card {
  background: var(--parchment-card);
  border: var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}

/* Index category section headers (Task 3: grouping by Stainless / High Alloy / Low Alloy / Other) */
.index-category-hdr {
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  /* Field Teal at 12px text measures 3.03:1 on parchment, below the 4.5:1
     text floor (redesign audit, item B). --ink-muted (darkened, 4.65:1)
     carries this label instead; teal stays for hairlines/borders only. */
  color: var(--ink-muted);
  padding: var(--s-4) var(--s-1) var(--s-2);
  border-bottom: 1px solid var(--navy-08);
  margin-top: var(--s-4);
}
.index-category-hdr:first-child { margin-top: 0; }
.index-category-hdr--muted { color: var(--ink-faint); border-bottom-style: dashed; }

/* ---- Buttons, chips, tags: pill radius, lift on hover, press on active --
   Hover never shifts fill colour on these; it lifts 1px and deepens the
   shadow (--shadow-btn-hover). Colour is reserved for the pressed/active
   state and for :focus-visible. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: var(--tap);
  padding: 0 var(--s-5);
  font-family: var(--font-body);
  font-size: var(--t-sm);
  font-weight: 600;
  letter-spacing: 0.02em;
  border-radius: var(--radius-pill);
  border: 1.5px solid transparent;
  box-shadow: var(--shadow-btn);
  cursor: pointer;
  transition: transform var(--dur-ui) var(--ease-out), box-shadow var(--dur-ui) var(--ease-out), border-color var(--dur-ui) var(--ease-out);
}
.btn:hover { transform: translateY(var(--lift-hover)); box-shadow: var(--shadow-btn-hover); text-decoration: none; }
.btn:active { transform: translateY(1px); box-shadow: var(--shadow-btn); }
.btn--primary { background: var(--lore-navy); color: var(--parchment); }
.btn--olive { background: var(--lore-olive); color: var(--parchment); }
.btn--ghost { background: transparent; color: var(--lore-navy); border-color: var(--border-card); box-shadow: none; }
.btn--ghost:hover { border-color: var(--lore-navy); box-shadow: none; }
/* Focus halo for buttons, placed after the .btn cascade so it beats
   `.btn`'s rest shadow and `.btn--ghost { box-shadow: none }`, both (0,1,0)
   and both declared above. The button keeps its own elevation AND gains the
   halo, rather than trading one for the other. */
.btn:focus-visible {
  box-shadow: var(--shadow-btn), 0 0 0 5.5px rgba(240, 231, 213, 0.92);
}
.btn--ghost:focus-visible {
  box-shadow: 0 0 0 5.5px rgba(17, 34, 80, 0.55);
}

.btn--sm { min-height: 36px; padding: 0 var(--s-3); font-size: var(--t-xs); }
.btn[disabled] { opacity: 0.5; cursor: not-allowed; transform: none; }
