/* =========================================================
   1. Design tokens
   Fonts (Anton / Instrument Serif / Archivo / IBM Plex Mono / Caveat) are
   loaded via <link> in base.html, not @import, to avoid an extra
   render-blocking round trip.

   Palette: DialShare now reads as a bright, pastel, printed poster —
   cream paper stock, a warm plum-black ink, poster-weight uppercase
   display type set enormous, and pastel band fills (blush / sky / butter /
   mint / lilac) with one hot vermillion lead accent. Pages are stacks of
   full-bleed horizontal bands — paper, then a block of solid colour, then
   ink — with hairline rules, rotated stamps and handwritten asides.
   Nothing floats, nothing has a shadow, corners are square by default.

   Old semantic names (--paper, --ink, --cobalt, --steel, etc.) are kept
   and re-pointed at the new palette below so the rest of this file — and
   every template that doesn't reference the theme by name — keeps
   working without a global rename.
   ========================================================= */
:root {
  /* Paper */
  --paper: #fdf6e8;
  --paper-deep: #f7ead6;
  --paper-white: #fffdf6;

  /* Ink — plum-black, warm. #3a1620 on paper measures 13.8:1 */
  --ink: #3a1620;
  --ink-soft: #6b3a44;
  --ink-muted: #9a7a80;
  --plum-700: #4a1b24;

  /* Vermillion — the lead accent. -500 for fills and display type,
     -600 (4.6:1 on paper) for small text. */
  --vermillion-100: #fddcd2;
  --vermillion-500: #f0522e;
  --vermillion-600: #cc3a1c;

  /* Pastel band scales — 200 is the band/block fill, 100 the chip tint,
     500 stamps/avatars/small blocks, 600 the readable text tone on that
     family's tint. */
  --blush-100: #fde8ef;
  --blush-200: #f9c9d8;
  --blush-500: #f2809f;
  --blush-600: #a83e5c;

  --sky-100: #e4f2fb;
  --sky-200: #c5e4f5;
  --sky-500: #74bfe3;
  --sky-600: #1d6a8c;

  --butter-100: #fef6d8;
  --butter-200: #fdeaa8;
  --butter-500: #f9cf4f;
  --butter-600: #8a6410;

  --mint-100: #eaf6de;
  --mint-200: #d6eec2;
  --mint-500: #a9d97e;
  --mint-600: #4a7a2c;

  --lilac-100: #efe9f9;
  --lilac-200: #ddd3f0;
  --lilac-500: #b09ada;
  --lilac-600: #5f4a8c;

  /* Legacy palette names re-pointed onto the pastel scales, and onto
     the old sage/crema/espresso roles, so every template class that
     hasn't been touched yet keeps rendering coherently. */
  --marigold-100: var(--butter-100);
  --marigold-200: var(--butter-200);
  --marigold-500: var(--butter-500);
  --marigold-600: var(--butter-600);
  --teal-100: var(--sky-100);
  --teal-200: var(--sky-200);
  --teal-500: var(--sky-500);
  --teal-600: var(--sky-600);
  --magenta-100: var(--blush-100);
  --magenta-200: var(--blush-200);
  --magenta-500: var(--blush-500);
  --magenta-600: var(--blush-600);
  --lime-100: var(--mint-100);
  --lime-200: var(--mint-200);
  --lime-500: var(--mint-500);
  --lime-600: var(--mint-600);

  --sage-100: var(--mint-200);
  --sage-500: var(--mint-500);
  --sage-600: var(--mint-600);
  --crema-300: var(--butter-200);
  --crema-500: var(--butter-500);
  --crema-600: var(--butter-600);
  --crema-700: var(--butter-600);
  --espresso-900: var(--ink-soft);
  --espresso-950: var(--ink);
  --cream-50: var(--paper-white);
  --cream-100: var(--paper-deep);
  --stone-100: var(--paper-deep);
  --stone-150: var(--ink-muted);
  --stone-300: var(--ink-muted);
  --canvas-bg: var(--paper);
  --footer-bg: var(--ink);
  --panel-dark: var(--ink);
  --track-dark: rgba(253, 246, 232, 0.35);
  --rust-600: var(--vermillion-600);
  --rust-100: var(--vermillion-100);

  /* Semantic aliases — everything below in this file (and every
     template) reads through these, not the raw palette above. */
  --paper-alias: var(--paper);
  --surface: var(--paper-white);
  --muted: var(--ink-muted);

  --steel: var(--rule);
  --steel-strong: var(--ink);
  --border-subtle: rgba(58, 22, 32, 0.1);
  --border-strong: rgba(58, 22, 32, 0.16);

  --cobalt: var(--vermillion-500);
  --cobalt-bright: var(--vermillion-600);
  --cobalt-dim: var(--vermillion-100);

  --signal-green: var(--mint-600);
  --signal-green-dim: var(--mint-100);
  --signal-amber: var(--butter-600);
  --signal-amber-dim: var(--butter-100);
  --signal-red: var(--vermillion-600);
  --signal-red-dim: var(--vermillion-100);

  /* Semantic */
  --text-heading: var(--ink);
  --text-body: var(--ink-soft);
  --text-quiet: var(--ink-muted);
  --accent: var(--vermillion-500);
  --accent-text: var(--vermillion-600);
  --accent-hover: var(--vermillion-600);

  --rule: rgba(58, 22, 32, 0.16);
  --rule-strong: var(--ink);

  --state-good: var(--mint-600);
  --state-good-fill: var(--mint-100);
  --state-warn: var(--butter-600);
  --state-warn-fill: var(--butter-100);
  --state-bad: var(--vermillion-600);
  --state-bad-fill: var(--vermillion-100);

  /* Five faces, each with one job: Anton for all display (uppercase
     only), Instrument Serif italic for pull quotes/lead-ins, Archivo for
     body and UI, IBM Plex Mono for every label/spec/eyebrow, Caveat for
     rare handwritten asides. */
  --font-display: 'Anton', 'Archivo', Impact, sans-serif;
  --font-editorial: 'Instrument Serif', Georgia, serif;
  --font-wordmark: var(--font-display);
  --font-body: 'Archivo', 'Helvetica Neue', sans-serif;
  --font-mono: 'IBM Plex Mono', 'SFMono-Regular', Consolas, monospace;
  --font-annotate: 'Caveat', 'Segoe Script', cursive;

  /* Display scale — Anton, uppercase, -0.02em, line-height under 1 */
  --type-mega-size: clamp(3.6rem, 9vw, 8.5rem);
  --type-mega-line: 0.84;
  --type-hero-size: clamp(3rem, 6.5vw, 5.75rem);
  --type-hero-line: 0.88;
  --type-section-size: clamp(2rem, 3.4vw, 3.25rem);
  --type-section-line: 0.92;
  --type-display-tracking: -0.02em;
  --type-quote-size: clamp(1.5rem, 2.4vw, 2.25rem);
  --type-eyebrow-size: 0.7rem;
  --type-eyebrow-tracking: 0.16em;
  --type-mono-label-size: 0.66rem;
  --type-mono-label-tracking: 0.1em;
  --type-mono-value-size: 0.95rem;
  --type-stat-size: clamp(1.75rem, 2.6vw, 2.5rem);
  --type-lead-size: 1.15rem;
  --type-body-size: 1rem;
  --type-small-size: 0.86rem;
  --type-annotate-size: 1.35rem;

  --shadow-sm: none;
  --shadow-md: none;

  /* Crisp edges. Corners are square by default; rounding is reserved
     for chips, sticker badges and the two circular marks. No shadows
     anywhere — depth comes from full-bleed colour blocks and rules. */
  --radius-none: 0px;
  --radius-xs: 2px;
  --radius-sm: 2px;
  --radius-md: 2px;
  --radius-lg: 0px;
  --radius-xl: 0px;
  --radius-pill: 999px;
  --radius-circle: 50%;

  --rule-hair: 1px;
  --rule-bold: 1.5px;
  --rule-heavy: 3px;
  --stripe-size: 14px;

  /* Snappy and flat. Nothing bounces or floats: colour swaps, underline
     growth, a 2% press. Nothing exceeds 240ms. */
  --ease-out-quart: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ease-out-quint: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ease-out-expo: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ease-standard: cubic-bezier(0.4, 0, 0.1, 1);
  --ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ease-snap: cubic-bezier(0.4, 0, 0.1, 1);
  --duration-instant: 90ms;
  --duration-fast: 140ms;
  --duration-base: 240ms;
  --press-scale: 0.98;
  --sticker-tilt: -8deg;

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 72px;
  --space-9: 112px;

  /* Full-bleed band padding and the centred content column */
  --band-pad-y: 96px;
  --band-pad-y-tight: 56px;
  --band-pad-x: 56px;
  --band-pad-y-mobile: 56px;
  --band-pad-x-mobile: 24px;
  --content-max: 1240px;
  --grid-gap: 32px;

  /* Bootstrap variable overrides — re-skins every existing page for free */
  --bs-primary: var(--vermillion-500);
  --bs-primary-rgb: 240, 82, 46;
  --bs-link-color: var(--accent-text);
  --bs-link-hover-color: var(--ink);
  --bs-link-color-rgb: 204, 58, 28;
  --bs-body-bg: var(--paper);
  --bs-body-color: var(--text-body);
  --bs-body-font-family: var(--font-body);
  --bs-border-radius: var(--radius-xs);
  --bs-border-radius-lg: var(--radius-xs);
  --bs-border-color: var(--rule-strong);
  --bs-secondary-color: var(--muted);
  --bs-tertiary-bg: var(--paper-deep);
  --bs-card-bg: var(--paper-white);
  --bs-card-border-color: var(--rule);
}

/* =========================================================
   2. Base / reset
   ========================================================= */
*, *::before, *::after { box-sizing: border-box; }

body {
  min-height: 100vh;
  margin: 0;
  background: var(--paper);
  color: var(--text-body);
  font-family: var(--font-body);
  font-size: var(--type-body-size);
  font-feature-settings: "liga" 1;
  -webkit-font-smoothing: antialiased;
}

p { text-wrap: pretty; }

h1, h2, h3, h4, h5, .display-4, .display-5 {
  font-family: var(--font-body);
  color: var(--ink);
  font-weight: 800;
  letter-spacing: -0.01em;
}

/* Giant uppercase headline treatment — Anton, always uppercase, always
   tight — used for hero titles and section titles, not every heading. */
.display-heading,
.mega,
.hero-type {
  font-family: var(--font-display);
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: var(--type-display-tracking);
  line-height: var(--type-hero-line);
  color: inherit;
  margin: 0;
}

.mega { font-size: var(--type-mega-size); line-height: var(--type-mega-line); }

/* Wordmark: no logo file exists — the mark is set type, Anton,
   uppercase, one word, never letterspaced wide. */
.wordmark {
  font-family: var(--font-display);
  font-style: normal;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.01em;
}

a { color: var(--accent-text); text-decoration: none; }
a:hover { color: var(--ink); }

.btn {
  font-family: var(--font-body);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  border-radius: var(--radius-xs);
  border-width: var(--rule-bold);
  transition: background-color var(--duration-fast) var(--ease-out), border-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out), opacity var(--duration-fast) var(--ease-out);
}

.btn:active {
  transform: scale(var(--press-scale));
  transition-duration: var(--duration-instant);
}

.form-control,
.form-select {
  border-radius: var(--radius-xs);
  border: var(--rule-bold) solid var(--ink);
  background: var(--paper-white);
  color: var(--ink);
  transition: border-color 0.2s var(--ease-out-quart), box-shadow 0.2s var(--ease-out-quart);
}

.form-control:focus,
.form-select:focus {
  border-color: var(--vermillion-500);
  box-shadow: none;
  background: var(--paper-white);
}

.btn-primary {
  --bs-btn-color: var(--paper-white);
  --bs-btn-bg: var(--vermillion-500);
  --bs-btn-border-color: var(--vermillion-500);
  --bs-btn-hover-color: var(--paper-white);
  --bs-btn-hover-bg: var(--ink);
  --bs-btn-hover-border-color: var(--ink);
  --bs-btn-active-bg: var(--ink);
  --bs-btn-active-border-color: var(--ink);
}

/* Gold — the app's existing primary-CTA class. In the poster system the
   lead accent for the primary CTA is vermillion, not gold, so this now
   renders as the "solid" vermillion button (kept under its old name so
   every template using it needs no edits). */
.btn-gold {
  --bs-btn-color: var(--paper-white);
  --bs-btn-bg: var(--vermillion-500);
  --bs-btn-border-color: var(--vermillion-500);
  --bs-btn-hover-color: var(--paper-white);
  --bs-btn-hover-bg: var(--ink);
  --bs-btn-hover-border-color: var(--ink);
  --bs-btn-active-color: var(--paper-white);
  --bs-btn-active-bg: var(--ink);
  --bs-btn-active-border-color: var(--ink);
}

/* Ink — solid plum-black button, used where vermillion is already the
   band's own accent (e.g. on a vermillion or blush band) and a CTA needs
   to read as ink instead. */
.btn-ink {
  --bs-btn-color: var(--paper-white);
  --bs-btn-bg: var(--ink);
  --bs-btn-border-color: var(--ink);
  --bs-btn-hover-color: var(--paper-white);
  --bs-btn-hover-bg: var(--vermillion-500);
  --bs-btn-hover-border-color: var(--vermillion-500);
  --bs-btn-active-bg: var(--vermillion-500);
  --bs-btn-active-border-color: var(--vermillion-500);
}

/* Marigold/butter — the secondary highlight block (used sparingly:
   subscribe/tab-active accents), new alongside the vermillion primary. */
.btn-marigold {
  --bs-btn-color: var(--ink);
  --bs-btn-bg: var(--butter-500);
  --bs-btn-border-color: var(--butter-500);
  --bs-btn-hover-color: var(--butter-500);
  --bs-btn-hover-bg: var(--ink);
  --bs-btn-hover-border-color: var(--ink);
}

/* Ghost — text-only button, used for "Log in" style nav actions. */
.btn-ghost {
  --bs-btn-color: var(--ink);
  --bs-btn-border-color: transparent;
  --bs-btn-bg: transparent;
  --bs-btn-hover-color: var(--paper-white);
  --bs-btn-hover-bg: var(--ink);
  --bs-btn-hover-border-color: transparent;
}

.btn-outline-dark,
.btn-outline-secondary {
  --bs-btn-color: var(--ink);
  --bs-btn-border-color: var(--ink);
  --bs-btn-hover-color: var(--paper-white);
  --bs-btn-hover-bg: var(--ink);
  --bs-btn-hover-border-color: var(--ink);
}

/* Same "outline" look on the dark canvas (footer, dark panels). */
.btn-outline-light {
  --bs-btn-color: var(--paper-white);
  --bs-btn-border-color: var(--paper-white);
  --bs-btn-hover-color: var(--ink);
  --bs-btn-hover-bg: var(--paper-white);
  --bs-btn-hover-border-color: var(--paper-white);
}

.btn-sm {
  --bs-btn-padding-y: 8px;
  --bs-btn-padding-x: 16px;
  --bs-btn-font-size: 0.7rem;
}

.btn-lg {
  --bs-btn-padding-y: var(--space-4);
  --bs-btn-padding-x: var(--space-6);
  --bs-btn-font-size: 0.86rem;
}

.alert {
  border: none;
  border-left: 4px solid currentColor;
  border-radius: 0;
  font-weight: 600;
  color: var(--ink);
}

/* =========================================================
   3. Band layout
   base.html no longer wraps pages in a dark "canvas" with floating
   rounded cards — each page is a stack of full-bleed, edge-to-edge
   colour bands. .floating-stack is kept as the plain wrapper name (no
   gap, no padding) so base.html needs no structural changes beyond
   dropping .canvas; each page template supplies its own
   <section class="card-section ..."> bands.
   ========================================================= */
.floating-stack {
  display: block;
}

.card-section {
  padding:
    var(--band-pad-y)
    max(var(--band-pad-x), calc((100% - var(--content-max)) / 2));
  background: var(--paper);
  color: var(--text-body);
}

/* Compact banner variant — a single content row (icon + name), not a
   full section, so it doesn't want the standard section's tall
   top/bottom padding. */
.card-section--compact {
  padding:
    var(--space-5)
    max(var(--band-pad-x), calc((100% - var(--content-max)) / 2));
}

@media (max-width: 767.98px) {
  .card-section {
    padding: var(--band-pad-y-mobile) var(--band-pad-x-mobile);
  }

  .card-section--compact {
    padding: var(--space-4) var(--band-pad-x-mobile);
  }
}

/* Tone modifiers. Old names are kept as hooks (used across every
   template) but now read through to the pastel/ink poster palette:
   crema -> butter, sage -> mint, cream -> paper-deep,
   surface -> paper-white, dark -> ink. New modifiers add the rest of
   the pastel family plus vermillion/plum for punctuation bands — never
   two saturated bands adjacent, and vermillion/plum sparingly. */
.card-section--crema { background: var(--butter-200); color: var(--ink); }
.card-section--sage { background: var(--mint-200); color: var(--ink); }
.card-section--cream { background: var(--paper-deep); color: var(--text-body); }
.card-section--surface { background: var(--paper-white); color: var(--text-body); }
.card-section--blush { background: var(--blush-200); color: var(--ink); }
.card-section--sky { background: var(--sky-200); color: var(--ink); }
.card-section--butter { background: var(--butter-200); color: var(--ink); }
.card-section--mint { background: var(--mint-200); color: var(--ink); }
.card-section--lilac { background: var(--lilac-200); color: var(--ink); }
.card-section--vermillion { background: var(--vermillion-500); color: var(--paper-white); }
.card-section--plum { background: var(--plum-700); color: var(--paper); }
.card-section--dark {
  background: var(--ink);
  color: var(--paper);
}

.card-section--dark h1,
.card-section--dark h2,
.card-section--dark h3,
.card-section--dark .display-heading,
.card-section--plum h1,
.card-section--plum h2,
.card-section--plum h3,
.card-section--plum .display-heading,
.card-section--vermillion h1,
.card-section--vermillion h2,
.card-section--vermillion h3,
.card-section--vermillion .display-heading {
  color: inherit;
}

/* The striped divider, straight off the reference stock — used sparingly,
   between two saturated bands that would otherwise sit flush against
   each other. */
.stripe {
  height: var(--stripe-size);
  background: repeating-linear-gradient(90deg, var(--vermillion-500) 0 14px, var(--blush-200) 14px 28px);
}

.canvas-fineprint {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-3) var(--band-pad-x) var(--space-5);
  background: var(--ink);
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: rgba(253, 246, 232, 0.6);
}

@media (max-width: 767.98px) {
  .canvas-fineprint {
    padding: var(--space-3) var(--band-pad-x-mobile) var(--space-5);
  }
}

/* =========================================================
   4. Masthead — centred-wordmark, two-row site header. Row 1 is a
   1fr/auto/1fr grid: utility links left, the Anton wordmark dead
   centre, auth actions right. Row 2 is the full nav, centred, under a
   hairline, active link carrying a 3px vermillion underline. Sits
   merged into the page's first band, not a separate sticky bar.
   ========================================================= */
/* Standalone wordmark/user-link styles for chrome that isn't the public
   masthead (the admin topbar's single-row layout). */
.ds-nav-row__brand {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: 1.7rem;
  color: var(--ink);
}

.ds-nav-row__user {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink);
}

.ds-masthead {
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-6);
  border-bottom: var(--rule-bold) solid currentColor;
}

.ds-masthead__utility {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-4);
  padding-bottom: var(--space-4);
}

.ds-masthead__utility-links {
  display: flex;
  gap: var(--space-4);
  align-items: center;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
}

.ds-masthead__utility-links a { color: inherit; }
.ds-masthead__utility-links a:hover { color: var(--vermillion-500); }
.ds-masthead__utility-links span { opacity: 0.4; }

.ds-masthead__brand {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  letter-spacing: 0.02em;
  line-height: 1;
  color: var(--ink);
  white-space: nowrap;
}

.card-section--dark .ds-masthead__brand,
.card-section--plum .ds-masthead__brand,
.card-section--vermillion .ds-masthead__brand {
  color: inherit;
}

.ds-masthead__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
  align-items: center;
}

.ds-masthead__actions .btn {
  --bs-btn-padding-y: 0.4rem;
  --bs-btn-padding-x: 1rem;
  --bs-btn-font-size: 0.72rem;
}

.ds-masthead__user {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink);
}

.ds-masthead__nav {
  display: flex;
  justify-content: center;
  gap: var(--space-7);
  flex-wrap: wrap;
  padding: var(--space-3) 0 0;
  border-top: var(--rule-hair) solid var(--rule);
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink-soft);
}

.ds-masthead__nav a {
  color: inherit;
  cursor: pointer;
  /* deliberate 3px underline-offset nudge, not a spacing-scale value */
  padding-bottom: 3px;
  border-bottom: var(--rule-heavy) solid transparent;
  transition: color var(--duration-fast) var(--ease-out), border-color var(--duration-fast) var(--ease-out);
}

.ds-masthead__nav a:hover,
.ds-masthead__nav a.active {
  color: var(--vermillion-500);
  border-bottom-color: var(--vermillion-500);
}

@media (max-width: 640px) {
  .ds-masthead__utility { grid-template-columns: auto 1fr; }
  .ds-masthead__utility-links { display: none; }
  .ds-masthead__brand { text-align: left; }
}

/* =========================================================
   4c. Cafe map hero — full-bleed poster map (a plotted paper grid, not
   a photographic map service: no key, no external tiles, on-brand)
   with a floating cream search panel over it. Sits between the
   masthead band and the cafe grid, outside the padded card-section
   column so the map itself reaches both edges.
   ========================================================= */
.cafe-map-hero {
  position: relative;
  height: min(72vh, 620px);
  min-height: 420px;
  background: var(--sky-200);
  border-top: var(--rule-bold) solid var(--ink);
  border-bottom: var(--rule-bold) solid var(--ink);
  overflow: hidden;
}

.cafe-map__grid-lines {
  position: absolute;
  inset: 0;
  opacity: 0.5;
  background-image:
    repeating-linear-gradient(0deg, rgba(58, 22, 32, .16) 0 1px, transparent 1px 12.5%),
    repeating-linear-gradient(90deg, rgba(58, 22, 32, .16) 0 1px, transparent 1px 12.5%);
}

.cafe-map__pins {
  position: absolute;
  inset: 0;
}

.cafe-map__pin {
  position: absolute;
  width: 14px;
  height: 14px;
  transform: translate(-50%, -50%);
  border-radius: var(--radius-circle);
  background: var(--vermillion-500);
  border: 1.5px solid var(--ink);
  transition: transform var(--duration-fast) var(--ease-out), background var(--duration-fast) var(--ease-out);
}

.cafe-map__pin:hover {
  background: var(--ink);
  transform: translate(-50%, -50%) scale(1.3);
}

.cafe-map__caption {
  position: absolute;
  left: 16px;
  bottom: 14px;
  font-family: var(--font-mono);
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  background: var(--paper);
  padding: 4px 8px;
}

.cafe-map__overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  pointer-events: none;
}

.cafe-map__panel {
  width: min(560px, 92%);
  margin: 0 auto 0 max(var(--band-pad-x), calc((100% - var(--content-max)) / 2));
  background: var(--paper);
  border: var(--rule-bold) solid var(--ink);
  padding: clamp(24px, 3vw, 38px);
  pointer-events: auto;
}

.cafe-map__panel .field__input {
  width: 100%;
  box-sizing: border-box;
}

.cafe-map__locate {
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--vermillion-600);
}

.cafe-map__locate:hover { color: var(--vermillion-500); }

.cafe-map__status {
  margin: var(--space-4) 0 0;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-quiet);
  min-height: 1.2em;
}

@media (max-width: 767.98px) {
  .cafe-map-hero { height: min(80vh, 540px); }
  .cafe-map__panel { margin-left: var(--band-pad-x-mobile); margin-right: var(--band-pad-x-mobile); width: auto; }
}

/* Retained for admin's non-hero top bar (see section 14). */
.indicator-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: var(--radius-xs);
  background: var(--vermillion-500);
  flex-shrink: 0;
}

/* =========================================================
   5. Layout primitives
   ========================================================= */
.text-ink-soft { color: var(--ink-soft); }
.text-ink { color: var(--ink); }
.font-mono { font-family: var(--font-mono); }

/* Small mono uppercase link — "All N →" style counters beside an eyebrow. */
.link-mono {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  background: transparent;
  border: none;
  padding: 0;
}

.link-mono:hover { color: var(--vermillion-500); }
.link-mono--danger { color: var(--vermillion-600); }
.link-mono--danger:hover { color: var(--vermillion-500); }

.blog-post-body { font-size: 1.05rem; }
.blog-post-body p { margin: 0 0 18px; }

.eyebrow {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--type-eyebrow-size);
  font-weight: 600;
  letter-spacing: var(--type-eyebrow-tracking);
  text-transform: uppercase;
  color: var(--accent-text);
  opacity: 1;
  margin-bottom: var(--space-3);
}

.card-section--dark .eyebrow,
.card-section--plum .eyebrow,
.card-section--vermillion .eyebrow {
  color: var(--butter-500);
}

.section-title {
  font-family: var(--font-display);
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: var(--type-display-tracking);
  line-height: var(--type-section-line);
  font-size: var(--type-section-size);
  margin-bottom: var(--space-4);
  text-wrap: balance;
}

.hero { padding: 8px 0 0; }

/* =========================================================
   5b. Calibration scale — the signature measurement readout: a 2px ink
   rule with tick marks, the value marked by a rotated vermillion pip,
   bounds in mono at each end. Falls back to plain mono text when a
   parameter has no range.
   ========================================================= */
.calibration-scale {
  margin-bottom: var(--space-5);
}

.calibration-scale__label {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--type-mono-label-size);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--type-mono-label-tracking);
  color: var(--text-quiet);
  margin-bottom: 6px;
}

.calibration-scale__label .calibration-scale__value-text {
  font-size: 1rem;
  font-weight: 600;
  color: var(--ink);
}

.card-section--dark .calibration-scale__label,
.card-section--plum .calibration-scale__label {
  color: rgba(253, 246, 232, 0.72);
}

.card-section--dark .calibration-scale__label .calibration-scale__value-text,
.card-section--plum .calibration-scale__label .calibration-scale__value-text {
  color: var(--paper);
}

.calibration-scale__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.calibration-scale__bound {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-quiet);
  white-space: nowrap;
}

.card-section--dark .calibration-scale__bound,
.card-section--plum .calibration-scale__bound {
  color: rgba(253, 246, 232, 0.72);
}

.calibration-scale__track {
  position: relative;
  flex: 1 1 auto;
  height: 20px;
  background-image: linear-gradient(to right, var(--ink) 1px, transparent 1px);
  background-size: 10% 60%;
  background-repeat: repeat-x;
  background-position: left bottom;
  border-bottom: 2px solid var(--ink);
}

.card-section--dark .calibration-scale__track,
.card-section--plum .calibration-scale__track {
  background-image: linear-gradient(to right, var(--paper) 1px, transparent 1px);
  border-bottom-color: var(--paper);
}

.calibration-scale__pip {
  position: absolute;
  bottom: -1px;
  width: 12px;
  height: 12px;
  background: var(--vermillion-500);
  border: 2px solid var(--paper-white);
  transform: translate(-50%, 50%) rotate(45deg);
}

.card-section--dark .calibration-scale__pip,
.card-section--plum .calibration-scale__pip {
  border-color: var(--ink);
}

.calibration-scale__value-text {
  font-family: var(--font-mono);
  font-size: 0.95rem;
  color: var(--ink);
}

.card-section--dark .calibration-scale__value-text,
.card-section--plum .calibration-scale__value-text {
  color: var(--paper);
}

.calibration-scale--mini .calibration-scale__label { display: none; }

.calibration-scale--mini .calibration-scale__track {
  height: 12px;
  background-image: none;
  border-bottom-width: 1px;
}

.calibration-scale--mini .calibration-scale__bound { font-size: 0.64rem; }

.calibration-scale--mini .calibration-scale__pip {
  width: 9px;
  height: 9px;
}

/* Compact: like mini, but keeps a visible label — used when several
   different stats sit side by side in a small grid (e.g. recipe cards)
   and hiding the label would make them indistinguishable. */
.calibration-scale--compact { margin-bottom: 0; }

.calibration-scale--compact .calibration-scale__label {
  font-size: 0.6rem;
  margin-bottom: var(--space-1);
}

.calibration-scale--compact .calibration-scale__track {
  height: 12px;
  background-image: none;
  border-bottom-width: 1px;
}

.calibration-scale--compact .calibration-scale__bound { font-size: 0.66rem; }

.calibration-scale--compact .calibration-scale__pip {
  width: 9px;
  height: 9px;
}

.calibration-scale--compact .calibration-scale__value-text { font-size: 0.85rem; }

.recipe-card__stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3) var(--space-4);
  margin: var(--space-3) 0;
}

/* Homepage hero power-on: ticks fade up, pip materialises at its resting
   reading — like an instrument switching on, not a needle physically
   sweeping. Runs once on load, never retriggered by scroll. */
.calibration-scale--illustrative .calibration-scale__track {
  opacity: 0;
  animation: scale-fade-in 0.5s var(--ease-out-quart) forwards;
}

.calibration-scale--illustrative .calibration-scale__pip {
  animation: pip-appear 0.5s var(--ease-out-expo) 0.35s backwards;
}

@keyframes scale-fade-in {
  to { opacity: 1; }
}

@keyframes pip-appear {
  from {
    opacity: 0;
    transform: translate(-50%, 50%) rotate(45deg) scale(0.2);
  }
  to {
    opacity: 1;
    transform: translate(-50%, 50%) rotate(45deg) scale(1);
  }
}

.stats-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 0;
  padding: var(--space-6) 0 var(--space-3);
}

.stats-strip .stat-tile {
  padding: 0 var(--space-6);
  border-left: var(--rule-hair) solid var(--border-strong);
  text-align: left;
}

.stats-strip .stat-tile:first-child {
  padding-left: 0;
  border-left: none;
}

/* Roaster credit strip: logos desaturate to the ink/steel palette so
   mismatched source photography still reads as one deliberate row;
   hover restores colour as a quiet reward for attention. */
.roaster-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-6);
  margin-bottom: var(--space-6);
  padding-top: var(--space-5);
  border-top: var(--rule-hair) solid var(--rule);
}

.roaster-strip--flush {
  border-top: none;
  padding-top: 0;
  margin-bottom: 0;
}

.roaster-strip__eyebrow { flex: 0 0 100%; }

.roaster-strip__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--ink-soft);
  font-weight: 600;
  font-size: 0.88rem;
}

.roaster-strip__logo {
  width: 30px;
  height: 30px;
  object-fit: cover;
  border-radius: var(--radius-xs);
  border: var(--rule-hair) solid var(--rule-strong);
  filter: grayscale(1) contrast(1.05);
  transition: filter var(--duration-base) var(--ease-out-quart);
}

.roaster-strip__item:hover .roaster-strip__logo {
  filter: grayscale(0);
}

.stat-tile { text-align: center; }

.stat-tile__value {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: var(--type-stat-size);
  line-height: 0.9;
  letter-spacing: var(--type-display-tracking);
  color: var(--ink);
  display: block;
}

/* Profile page's "your saved/authored" tiles read as an accent readout
   rather than the homepage's neutral site-wide totals. */
.stat-tile__value--accent { color: var(--vermillion-500); }

.stat-tile__label {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-quiet);
  margin-top: 8px;
}

/* =========================================================
   6. Cards
   Flat: a pastel colour block plus type below a hairline — no border,
   no fill, no shadow. Colour blocks stand in for imagery (the app ships
   none) — the title is ghosted into the block at 18% ink.
   ========================================================= */
.card-product {
  border: none;
  border-radius: 0;
  overflow: hidden;
  background: transparent;
  box-shadow: none;
  transition: none;
  height: 100%;
  display: flex;
  flex-direction: column;
}

.card-product:hover { box-shadow: none; transform: none; }

.card-product__image-wrap {
  position: relative;
  aspect-ratio: 4 / 3;
  background: var(--blush-200);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* Cycle the block colour across a grid — blush / sky / butter / mint /
   lilac — never one flat colour across a whole grid of cards. */
.card-product:nth-of-type(5n+1) .card-product__image-wrap { background: var(--blush-200); }
.card-product:nth-of-type(5n+2) .card-product__image-wrap { background: var(--sky-200); }
.card-product:nth-of-type(5n+3) .card-product__image-wrap { background: var(--butter-200); }
.card-product:nth-of-type(5n+4) .card-product__image-wrap { background: var(--mint-200); }
.card-product:nth-of-type(5n+5) .card-product__image-wrap { background: var(--lilac-200); }

.card-product__image-wrap img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--duration-base) var(--ease-out-quart);
}

.card-product:hover .card-product__image-wrap img { transform: scale(1.04); }

/* Ghosted display-type placeholder — replaces the old emoji icon. The
   card's title (set via the template) fills the block at 18% ink. */
.card-product__placeholder {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: clamp(1.6rem, 5vw, 3rem);
  line-height: 0.85;
  letter-spacing: var(--type-display-tracking);
  color: rgba(58, 22, 32, 0.18);
  padding: 0 var(--space-4);
  text-align: center;
  text-wrap: balance;
}

.card-product__body { padding: var(--space-3) 0 0; }

.card-product__title {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 400;
  font-size: 1.5rem;
  line-height: 0.95;
  letter-spacing: var(--type-display-tracking);
  color: var(--ink);
  margin-bottom: 6px;
  transition: color var(--duration-fast) var(--ease-out);
}

.card-product:hover .card-product__title { color: var(--vermillion-500); }

.card-product__meta {
  font-family: var(--font-mono);
  color: var(--text-quiet);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin-bottom: var(--space-3);
  padding-bottom: var(--space-3);
  border-bottom: var(--rule-hair) solid var(--rule);
}

/* Feature variant — the homepage's "Popular Roasts" spotlight: a taller
   card with the title set directly over the block behind an ink scrim,
   instead of the plain card body below it. */
.card-product--feature .card-product__image-wrap {
  position: relative;
  aspect-ratio: 3 / 4;
}

.card-product--feature .card-product__scrim {
  position: absolute;
  inset: auto 0 0 0;
  padding: var(--space-7) var(--space-4) var(--space-4);
  background: linear-gradient(to top, rgba(58, 22, 32, 0.92) 0%, rgba(58, 22, 32, 0.6) 45%, transparent 100%);
}

.card-product--feature .card-product__title {
  font-size: 1.6rem;
  line-height: 0.92;
  color: var(--paper-white);
  margin-bottom: 4px;
}

.card-product--feature:hover .card-product__title { color: var(--paper-white); }

.card-product--feature .card-product__meta {
  color: var(--butter-500);
  font-size: 0.66rem;
  border-bottom: none;
  padding-bottom: 0;
  margin-bottom: 0;
}

@media (max-width: 767.98px) {
  .card-product--feature .card-product__image-wrap { aspect-ratio: 4 / 3; }
}

/* Roaster logo cards use a contained, non-cropped image on a flat field */
.card-product__image-wrap--logo { background: var(--paper-deep); }

.card-product__image-wrap--logo img {
  object-fit: contain;
  padding: var(--space-4);
}

/* A recipe as a printed spec sheet: white block, ink border, method
   stamp, Anton title, then the measurements stacked as calibration
   scales. Hover lifts it 3px — the whole effect. */
.recipe-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--paper-white);
  border: var(--rule-bold) solid var(--ink);
  padding: var(--space-5);
  transition: transform var(--duration-fast) var(--ease-out);
}

.recipe-card:hover { transform: translateY(-3px); }

.recipe-card__title {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 400;
  font-size: 1.5rem;
  line-height: 0.95;
  letter-spacing: var(--type-display-tracking);
  color: var(--ink);
  margin: var(--space-3) 0 6px;
}

.recipe-card__meta {
  font-family: var(--font-mono);
  font-size: 0.64rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-quiet);
  margin: 0 0 var(--space-4);
}

.recipe-card__stats {
  padding-top: var(--space-3);
  border-top: var(--rule-hair) solid var(--rule);
}

.recipe-card__author {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: var(--rule-hair) solid var(--rule);
}

/* =========================================================
   7. Chips & badges
   ========================================================= */
.chip {
  display: inline-block;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  border: none;
  background: var(--sky-100);
  color: var(--sky-600);
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin: 0 6px 6px 0;
}

.chip-note {
  background: transparent;
  color: var(--ink-soft);
  border: var(--rule-hair) solid var(--rule-strong);
}

.badge-process {
  display: inline-block;
  padding: var(--space-1) var(--space-2);
  border-radius: 0;
  font-family: var(--font-mono);
  font-size: 0.64rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  background: var(--ink);
  color: var(--paper-white);
  border: none;
}

/* Author dot — one of the two circular exceptions in an otherwise
   squared system. */
.avatar-initial {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-circle);
  background: var(--vermillion-500);
  color: var(--paper-white);
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: 0.75rem;
  flex-shrink: 0;
}

/* Entity avatar: a roaster/cafe logo, or an initial-letter placeholder
   when no logo_url is set. Squared — the only circles in the system are
   sticker badges and author dots. */
.entity-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  border-radius: var(--radius-xs);
  background: var(--ink);
  overflow: hidden;
}

.entity-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.entity-avatar__initial {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 400;
  color: var(--paper-white);
}

.entity-avatar--crema {
  background: var(--butter-500);
}

.entity-avatar--crema .entity-avatar__initial { color: var(--ink); }

.entity-avatar--96 { width: 96px; height: 96px; }
.entity-avatar--96 .entity-avatar__initial { font-size: 2.4rem; }

.entity-avatar--56 { width: 56px; height: 56px; }
.entity-avatar--56 .entity-avatar__initial { font-size: 1.3rem; }

.entity-avatar--44 { width: 44px; height: 44px; color: var(--paper-white); }
.entity-avatar--44 .icon { width: 22px; height: 22px; }

/* Entity row link — avatar, name + meta, arrow. Used for cafe and
   roaster lists inside a band (roast/roaster "where to buy", account
   "following"). Hairline-divided rows, no card chrome. */
.entity-row-link {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) 0;
  border-bottom: var(--rule-hair) solid var(--rule);
  color: var(--ink);
  text-decoration: none;
}

.entity-row-link:last-child { border-bottom: none; }
.entity-row-link:hover .entity-row-link__name { color: var(--vermillion-500); }
.entity-row-link:hover .entity-row-link__arrow { color: var(--vermillion-600); }

.entity-row-link__name {
  display: block;
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: 1.35rem;
  line-height: 1;
  letter-spacing: var(--type-display-tracking);
  color: var(--ink);
  transition: color var(--duration-fast) var(--ease-out);
}

.entity-row-link__meta {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.64rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-top: 6px;
}

.entity-row-link__arrow {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--vermillion-600);
  flex-shrink: 0;
  transition: color var(--duration-fast) var(--ease-out);
}

/* =========================================================
   7b. Indicator lights — flavour characteristics as filled/hollow
   squares. Filled squares take the poster accent; hollow ones are a
   hairline ink outline. Supplements the tasting-note chips, never
   replaces them.
   ========================================================= */
.indicator-lights {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin: var(--space-2) 0 var(--space-4);
}

.indicator-light {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-quiet);
}

.indicator-light__dot {
  width: 8px;
  height: 8px;
  border-radius: 0;
  border: 1.5px solid var(--text-quiet);
  background: transparent;
  flex-shrink: 0;
}

.indicator-light--active { color: var(--ink); }

.indicator-light--active .indicator-light__dot {
  background: var(--vermillion-500);
  border-color: var(--vermillion-500);
}

.indicator-lights--compact {
  gap: 4px;
  margin: var(--space-1) 0 0;
}

.indicator-lights--compact .indicator-light { gap: 0; }

/* =========================================================
   8. Brew guide panel — an ink block on the bright page, the one
   inverted panel in the system. Method tabs are mono chips along the top.
   ========================================================= */
.brew-guide-panel {
  background: var(--ink);
  color: var(--paper);
  border: none;
  border-radius: 0;
  padding: var(--space-6);
}

.brew-guide-panel dt {
  font-family: var(--font-mono);
  font-weight: 600;
  color: rgba(253, 246, 232, 0.7);
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  margin-bottom: 5px;
}

.brew-guide-panel dd {
  font-family: var(--font-body);
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--paper);
  margin-bottom: var(--space-4);
}

/* =========================================================
   9. Filter sidebar
   Facet filters as a flat column of custom checkboxes under hairline
   group rules. No card — it sits directly on the band.
   ========================================================= */
/* Underlined search field — a rule, not a box — for public-site hero
   search fields (Roasters, Cafes, Blog, Search). */
.search-input-pill {
  border: none;
  border-bottom: 2px solid var(--ink);
  border-radius: 0;
  background: transparent;
  padding: 8px 0 8px 26px;
  font-family: var(--font-mono);
  font-size: 0.9rem;
}

.search-input-pill:focus {
  border-color: var(--vermillion-500);
  box-shadow: none;
  background: transparent;
}

.filter-sidebar {
  background: transparent;
  border: none;
  border-radius: 0;
  padding: 0;
  margin-bottom: var(--space-5);
}

.filter-sidebar__header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding-bottom: var(--space-3);
  border-bottom: var(--rule-bold) solid var(--ink);
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink);
}

.filter-sidebar__header .clear {
  font-family: var(--font-mono);
  font-size: 0.64rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-text);
}

.is-navigating {
  opacity: 0.5;
  pointer-events: none;
  transition: opacity var(--duration-fast) var(--ease-out-quart);
}

.filter-sidebar .facet-group legend,
.filter-sidebar .facet-group > .form-label {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-quiet);
}

.filter-sidebar .facet-group {
  border: none;
  padding: var(--space-4) 0;
  margin: 0;
  border-bottom: var(--rule-hair) solid var(--rule);
}

.filter-sidebar .form-check {
  margin-bottom: 0.4rem;
}

.filter-sidebar .form-check-input {
  width: 15px;
  height: 15px;
  border: var(--rule-bold) solid var(--ink);
  border-radius: 0;
}

.filter-sidebar .form-check-input:checked {
  background-color: var(--vermillion-500);
  border-color: var(--vermillion-500);
}

.filter-sidebar .form-check-input:focus {
  box-shadow: none;
  border-color: var(--vermillion-500);
}

/* =========================================================
   10. Footer card — the ink band at the bottom of every public page:
   oversized wordmark, newsletter row, link columns, fine print. Full
   bleed, squared, no card.
   ========================================================= */
.footer-card {
  display: flex;
  justify-content: space-between;
  gap: var(--space-8);
  flex-wrap: wrap;
  padding-bottom: var(--space-6);
  border-bottom: var(--rule-hair) solid rgba(253, 246, 232, 0.2);
}

.footer-card .eyebrow { color: var(--butter-500); }

.footer-newsletter {
  flex: 1 1 380px;
  max-width: 520px;
}

.footer-newsletter h2 {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 400;
  font-size: clamp(1.75rem, 3vw, 2.75rem);
  line-height: 0.9;
  letter-spacing: var(--type-display-tracking);
  color: var(--paper);
  margin: 0 0 var(--space-4);
}

.footer-newsletter__form {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-top: var(--space-4);
}

.footer-newsletter__form .form-control {
  flex: 1 1 240px;
  padding: var(--space-3) 0;
  border: none;
  border-bottom: var(--rule-bold) solid var(--paper);
  border-radius: 0;
  background: transparent;
  color: var(--paper);
  font-family: var(--font-mono);
  font-size: 0.85rem;
}

.footer-newsletter__form .form-control:focus {
  border-color: var(--butter-500);
  background: transparent;
}

.footer-newsletter__form .form-control::placeholder { color: rgba(253, 246, 232, 0.5); }

.footer-newsletter__success {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--butter-500);
  margin: 0;
}

.footer-links {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  flex: 0 0 auto;
}

.footer-links__heading {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 0.64rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-muted);
  margin-bottom: var(--space-1);
}

.footer-links a {
  color: var(--paper);
  font-weight: 600;
}

.footer-links a:hover { color: var(--butter-500); }

.footer-wordmark {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--space-5);
  flex-wrap: wrap;
  padding-top: var(--space-5);
}

.footer-wordmark__brand {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: clamp(2.5rem, 9vw, 7rem);
  line-height: 0.8;
  letter-spacing: -0.02em;
  color: var(--paper);
}

.footer-wordmark__fine {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* =========================================================
   11. Feature links & CTA banner
   Numbered capability rows: index in mono, label in Anton, arrow that
   slides. The homepage's "Why DialShare" split keeps its intro column +
   2x2 grid; each cell now reads as a numbered row.
   ========================================================= */
.feature-split {
  display: flex;
  justify-content: space-between;
  gap: var(--space-7);
  flex-wrap: wrap;
}

.feature-split__intro { flex: 1 1 320px; }
.feature-split__intro p { max-width: 38ch; }

.feature-split__grid {
  flex: 2 1 480px;
  display: grid;
  grid-template-columns: 1fr;
}

.feature-link {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: start;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-4);
  border-top: var(--rule-hair) solid var(--border-subtle);
  color: inherit;
  transition: background-color var(--duration-fast) var(--ease-standard);
}

.feature-link:hover { background: rgba(58, 22, 32, 0.04); }

.feature-link__index {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--text-quiet);
  /* deliberate 6px baseline nudge to align against the larger label, not a spacing-scale value */
  padding-top: 6px;
  transition: color var(--duration-fast) var(--ease-out);
}

.feature-link:hover .feature-link__index { color: var(--vermillion-500); }

.feature-link__row {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-3);
}

.feature-link__label {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 400;
  font-size: 1.2rem;
  line-height: 0.95;
  letter-spacing: var(--type-display-tracking);
  color: var(--ink);
  transition: color var(--duration-fast) var(--ease-out);
}

.feature-link:hover .feature-link__label { color: var(--vermillion-500); }

.feature-link__arrow {
  font-size: 1.2rem;
  color: var(--ink);
  transition: transform var(--duration-fast) var(--ease-out-quart), color var(--duration-fast) var(--ease-out);
}

.feature-link:hover .feature-link__arrow {
  transform: translate(3px, -3px);
  color: var(--vermillion-500);
}

/* Rotated corner stamp — a homepage-specific placement for the "one
   sticker per band" claim. Hidden below the tablet breakpoint so it
   never overlaps a heading on a narrow band. */
.sticker-badge--corner {
  position: absolute;
  top: 40px;
  right: var(--band-pad-x);
}

@media (max-width: 991.98px) {
  .sticker-badge--corner { display: none; }
}

/* =========================================================
   12. Spec panel & roaster mini-card
   Spec grid: mono micro-labels over values, separated by hairlines. No
   card, no fill by default — it sits directly on the band. The base
   class carries only typography + a top hairline so it composes safely
   with however a template lays its dt/dd out: Bootstrap's own
   `.row`/`.col-*` grid (admin's `<dl class="row spec-panel">`), or a
   plain, non-dl wrapper with no dt/dd at all (--plain, used for review
   cards, trip rows, cafe/roaster info blocks). `--grid` opts a bare
   `<dl class="spec-panel spec-panel--grid">` with direct dt/dd children
   into the true 4-column instrument-grid layout.
   ========================================================= */
.spec-panel {
  background: transparent;
  border: none;
  border-top: var(--rule-hair) solid var(--rule);
  border-radius: 0;
  padding: var(--space-3) 0 0;
}

.spec-panel dt {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--text-quiet);
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  margin-bottom: 6px;
}

.spec-panel dd {
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 700;
  color: var(--ink);
  margin: 0 0 var(--space-3);
  text-transform: capitalize;
}

.spec-panel--plain dt,
.spec-panel--plain dd { text-transform: none; }

.spec-panel--boxed {
  background: var(--paper-white);
  padding: var(--space-4) var(--space-5);
}

/* Opt-in true grid: direct dt/dd children only, no Bootstrap .row
   involved — the roast/recipe "measurement readout" spec grids. */
.spec-panel--grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  padding: 0;
}

.spec-panel--grid > dt,
.spec-panel--grid > dd {
  padding: var(--space-4) var(--space-4) var(--space-4) 0;
  border-bottom: var(--rule-hair) solid var(--rule);
  margin: 0;
}

/* deliberate 6px override to tighten the dt/dd pairing, not a spacing-scale value */
.spec-panel--grid > dt { grid-row: 1; padding-bottom: 6px; }

@media (max-width: 599.98px) {
  .spec-panel--grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Numbered step list (homepage CTA banner): a 3-item dt/dd sequence
   reads wrong forced into the 4-column measurement grid above, so it
   gets its own single-column layout instead. */
.cta-banner__list.spec-panel--grid {
  grid-template-columns: auto 1fr;
  column-gap: var(--space-4);
  max-width: 46ch;
}

.cta-banner__list.spec-panel--grid > dt,
.cta-banner__list.spec-panel--grid > dd {
  grid-row: auto;
  padding: var(--space-3) 0;
}

.cta-banner__list.spec-panel--grid > dt {
  color: var(--vermillion-600);
}

.roaster-mini-card {
  border: none;
  border-radius: 0;
  padding: var(--space-5);
  background: var(--paper-white);
}

.stats-strip--tight {
  margin: 0;
  padding-top: 0;
  gap: var(--space-6);
}

.profile-equipment-tile {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4);
  border-radius: 0;
  background: var(--paper-white);
  border: var(--rule-bold) solid var(--ink);
}

/* =========================================================
   13. Brew guide tabs / TabStrip
   A centred row of mono labels between two rules; the active one is a
   solid ink block with a small pointer.
   ========================================================= */
.brew-guide-tabs {
  border-top: var(--rule-bold) solid var(--ink);
  border-bottom: var(--rule-bold) solid var(--ink);
  gap: 0;
  flex-wrap: wrap;
  padding: var(--space-1) 0;
  justify-content: center;
}

.brew-guide-tabs .nav-link {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink);
  background: transparent;
  border: none;
  border-radius: 0;
  padding: var(--space-3) var(--space-4);
  transition: color var(--duration-fast) var(--ease-out-quart), background-color var(--duration-fast) var(--ease-out-quart);
}

.brew-guide-tabs .nav-link:hover {
  color: var(--vermillion-500);
}

.brew-guide-tabs .nav-link.active {
  color: var(--paper-white);
  background: var(--ink);
  border: none;
}

.brew-guide-community {
  color: var(--ink-soft);
  font-family: var(--font-mono);
}

.icon {
  display: inline-block;
  width: 16px;
  height: 16px;
  vertical-align: -3px;
  flex: 0 0 auto;
}

/* =========================================================
   14. Admin console shell
   Flat: an ink-bordered topbar strip, a flat paper rail with an ink
   active row, a flat content area — no floating cards, no rounding, no
   shadow. Status is still signalled with the red/amber/green telltale
   system below, only where the data has a genuine state.
   ========================================================= */
.admin-topbar-card {
  border-radius: 0;
  background: var(--butter-200);
  border-bottom: var(--rule-bold) solid var(--ink);
  padding: var(--space-5) var(--space-6);
}

.admin-row {
  display: flex;
  gap: 0;
  align-items: stretch;
}

.admin-shell { display: contents; }

.admin-rail {
  flex: 0 0 216px;
  border: none;
  border-right: var(--rule-bold) solid var(--ink);
  border-radius: 0;
  background: var(--paper-white);
  padding: var(--space-4) 0;
  min-height: 100vh;
}

/* The rail itself stretches full-height for a continuous background/
   border (see .admin-row's align-items: stretch below); the nav list
   inside is what actually sticks as you scroll, since a sticky element
   the same height as its container has no room to move. */
.admin-rail__nav {
  display: flex;
  flex-direction: column;
  gap: 0;
  position: sticky;
  top: 0;
}

.admin-rail__link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: 0;
  color: var(--ink);
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
  position: relative;
  transition: background-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
}

.admin-rail__link .icon {
  flex: 0 0 17px;
  width: 17px;
  height: 17px;
}

.admin-rail__link:hover { background: var(--paper-deep); }

.admin-rail__link.active {
  background: var(--ink);
  color: var(--paper-white);
}

.admin-rail__badge {
  flex: 0 0 auto;
  margin-left: auto;
  min-width: 18px;
  height: 18px;
  padding: 0 var(--space-1);
  border-radius: 0;
  background: var(--vermillion-500);
  color: var(--paper-white);
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 600;
  line-height: 18px;
  text-align: center;
}

.admin-rail__link.active .admin-rail__badge {
  background: var(--butter-500);
  color: var(--ink);
}

.admin-toggle { display: none; }

.admin-main {
  flex: 1 1 auto;
  min-width: 0;
  border-radius: 0;
  background: var(--paper-deep);
  padding: var(--space-6) var(--space-6);
}

.admin-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.admin-topbar-card .admin-topbar { gap: var(--space-4); }

.admin-breadcrumb {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}

.admin-breadcrumb a { color: var(--ink-soft); }
.admin-breadcrumb a:hover { color: var(--ink); }

.admin-breadcrumb__current { color: var(--ink); font-weight: 700; }

.admin-search {
  position: relative;
  flex: 0 1 320px;
  margin: 0;
}

.admin-search__input {
  width: 100%;
  padding: var(--space-2) var(--space-4) var(--space-2) 34px;
  border: var(--rule-bold) solid var(--ink);
  border-radius: 0;
  background: var(--paper-white);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: var(--ink);
}

.admin-search__input:focus {
  outline: none;
  border-color: var(--vermillion-500);
  background: var(--paper-white);
}

.admin-search__icon {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 15px;
  height: 15px;
  color: var(--muted);
  pointer-events: none;
}

.admin-search__results {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 20;
  background: var(--paper-white);
  border: var(--rule-bold) solid var(--ink);
  border-radius: 0;
  box-shadow: none;
  max-height: 360px;
  overflow-y: auto;
  display: none;
}

.admin-search__results.is-open { display: block; }

.admin-search__group-label {
  padding: var(--space-2) var(--space-3) 4px;
  font-family: var(--font-mono);
  font-size: 0.64rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-quiet);
}

.admin-search__result {
  display: block;
  padding: var(--space-2) var(--space-3);
  color: var(--ink);
  font-size: 0.88rem;
}

.admin-search__result:hover {
  background: var(--butter-100);
  color: var(--ink);
}

.admin-search__empty,
.admin-search__see-all {
  padding: var(--space-2) var(--space-3);
  font-size: 0.8rem;
  color: var(--muted);
}

.admin-search__see-all {
  border-top: var(--rule-hair) solid var(--rule);
  font-weight: 600;
}

.admin-search__see-all a { color: var(--accent-text); }

.admin-panel {
  border: none;
  border-radius: 0;
  background: var(--paper-white);
  padding: var(--space-5);
  margin-bottom: var(--space-5);
  box-shadow: none;
}

.admin-stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-3);
}

@media (min-width: 992px) {
  .admin-stat-grid { grid-template-columns: repeat(5, 1fr); }
}

.admin-stat-tile {
  display: block;
  border: var(--rule-hair) solid var(--border-subtle);
  border-radius: 0;
  background: var(--paper-white);
  padding: var(--space-4);
  box-shadow: none;
}

.admin-stat-tile__body { flex: 1 1 auto; min-width: 0; }

.admin-stat-tile__value {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: 1.9rem;
  line-height: 0.9;
  color: var(--ink);
  display: block;
}

.admin-stat-tile__label {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  margin-top: 6px;
}

.admin-stat-tile__link {
  display: block;
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--accent-text);
  white-space: nowrap;
}

.admin-recent-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-5);
}

.admin-recent-panel__title {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  margin-bottom: var(--space-3);
}

.admin-recent-list { list-style: none; margin: 0; padding: 0; }

.admin-recent-list li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-bottom: var(--rule-hair) solid var(--border-subtle);
}

.admin-recent-list li:last-child { border-bottom: none; padding-bottom: 0; }

.admin-recent-list a { font-weight: 600; color: var(--ink); }
.admin-recent-list a:hover { color: var(--accent-text); }

.admin-recent-list time {
  font-family: var(--font-mono);
  font-size: 0.76rem;
  color: var(--muted);
  white-space: nowrap;
}

.admin-thumb {
  width: 96px;
  height: 96px;
  object-fit: cover;
  border-radius: 0;
  border: var(--rule-hair) solid var(--rule);
}

/* =========================================================
   14b. Status & role pills — squared so they read as printed stamps
   rather than UI badges. Only for data that has a genuine state.
   ========================================================= */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: var(--space-1) var(--space-3) var(--space-1) var(--space-2);
  border-radius: var(--radius-xs);
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  white-space: nowrap;
}

.pill__dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-circle);
  flex: 0 0 auto;
  background: currentColor;
}

.pill--success { background: var(--state-good-fill); color: var(--state-good); }
.pill--pending { background: var(--state-warn-fill); color: var(--state-warn); }
.pill--danger { background: var(--state-bad-fill); color: var(--state-bad); }
.pill--accent { background: var(--vermillion-100); color: var(--accent-text); }

.pill--neutral {
  background: var(--paper-deep);
  color: var(--ink-soft);
  border: var(--rule-hair) solid var(--rule);
  /* -1px per side: compensates for this variant's added hairline border
     so the rendered box matches .pill's size exactly. */
  padding:
    calc(var(--space-1) - var(--rule-hair))
    calc(var(--space-3) - var(--rule-hair))
    calc(var(--space-1) - var(--rule-hair))
    calc(var(--space-2) - var(--rule-hair));
}

.pill--neutral .pill__dot { background: var(--text-quiet); }

/* =========================================================
   14c. Admin data tables, filter bar, pagination
   Ink header row, hairline body rows, a butter-100 hover wash.
   ========================================================= */
.admin-filter-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
  flex-wrap: wrap;
}

.admin-filter-bar__search {
  position: relative;
  flex: 0 1 320px;
  margin: 0;
}

.admin-filter-bar__search input {
  width: 100%;
  padding: var(--space-2) var(--space-3) var(--space-2) 34px;
  border-radius: 0;
}

.admin-filter-bar__search .icon {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  width: 15px;
  height: 15px;
  color: var(--muted);
  pointer-events: none;
}

.admin-table-wrap {
  border: var(--rule-bold) solid var(--ink);
  border-radius: 0;
  background: var(--paper-white);
  overflow: hidden;
  margin-bottom: var(--space-4);
  box-shadow: none;
}

.admin-table-wrap .table { margin-bottom: 0; }

.admin-table-wrap thead th {
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font-mono);
  font-size: 0.64rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  border-bottom: none;
  padding: var(--space-3) var(--space-4);
}

.admin-table-wrap tbody td { padding: 12px var(--space-4); }

.admin-table-wrap tbody tr {
  transition: background-color var(--duration-instant) var(--ease-out-quart);
}

.admin-table-wrap tbody tr:not(:first-child) { border-top: var(--rule-hair) solid var(--rule); }

.admin-table-wrap tbody tr:hover { background: var(--butter-100); }

.admin-table-wrap .row-actions .danger { color: var(--vermillion-600); }

/* Bulk-select action bar above an admin index table — appears once at
   least one row checkbox is checked (admin-bulk-actions.js). */
.admin-bulk-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-3);
  background: var(--butter-100);
  border: var(--rule-bold) solid var(--ink);
  border-radius: 0;
  flex-wrap: wrap;
}

.admin-bulk-bar[hidden] { display: none; }

.admin-bulk-bar__count {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
}

.admin-bulk-bar__actions {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.admin-table-summary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--space-3);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--muted);
}

.page-link {
  border: var(--rule-bold) solid var(--ink);
  color: var(--ink);
  background: var(--paper-white);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-weight: 600;
  border-radius: var(--radius-xs) !important;
  margin: 0 2px;
}

.page-item.disabled .page-link {
  background: transparent;
  color: var(--muted);
  border-color: var(--rule);
}

.page-link:hover {
  background: var(--ink);
  color: var(--paper-white);
}

/* =========================================================
   14d. Admin forms — grouped sections, bordered action bar
   ========================================================= */
.admin-form-section {
  border: none;
  border-radius: 0;
  background: var(--paper-white);
  padding: var(--space-5);
  margin-bottom: var(--space-4);
  box-shadow: none;
}

.admin-form-section__title {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  margin-bottom: var(--space-4);
  padding-bottom: var(--space-2);
  border-bottom: var(--rule-hair) solid var(--rule);
}

.admin-form-actions {
  display: flex;
  gap: var(--space-2);
  padding: var(--space-4);
  background: var(--paper-white);
  border: var(--rule-bold) solid var(--ink);
  border-radius: 0;
  box-shadow: none;
}

/* Small status readout beside an async-triggering button (e.g. "Fetch
   from URL"), driven by DialShareAdmin.withLoadingState. Hidden by
   default; JS reveals it and cycles the text while the request runs. */
.admin-async-status {
  display: block;
  margin-top: var(--space-2);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--ink-soft);
}

/* Tabbed record views — forms whose .admin-form-section blocks are wrapped
   in tabs, and detail pages whose related-record tables are wrapped in
   tabs. Reuses .admin-form-section__title's mono/uppercase treatment for
   visual continuity with the pre-tab layout. */
.admin-tabs__nav {
  display: flex;
  gap: var(--space-1);
  border-bottom: var(--rule-bold) solid var(--ink);
  margin-bottom: var(--space-4);
  overflow-x: auto;
}

.admin-tabs__tab {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  background: none;
  border: none;
  padding: var(--space-3) var(--space-4);
  border-bottom: 3px solid transparent;
  margin-bottom: -2px;
  cursor: pointer;
  white-space: nowrap;
}

.admin-tabs__tab:hover { color: var(--ink); }

.admin-tabs__tab.is-active {
  color: var(--ink);
  border-bottom-color: var(--vermillion-500);
}

.admin-tabs__tab:focus-visible {
  outline: 2px solid var(--vermillion-500);
  outline-offset: 2px;
}

.admin-tabs__badge {
  display: inline-block;
  margin-left: var(--space-2);
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--paper-deep);
  color: var(--ink-soft);
  font-size: 0.65em;
}

.admin-tabs__panel { background: var(--paper-white); padding: var(--space-5); }

.table { --bs-table-border-color: var(--rule); }

.table td, .table th { vertical-align: middle; }

.table .font-mono,
.table td[data-numeric] { font-family: var(--font-mono); }

/* =========================================================
   14e. Modal dialog chrome — squared, heavy ink border, no shadow, no
   blur, no bounce. The reusable <dialog> shell every entity's modal
   builds on. Entity-specific modals add --lg and/or their own body
   markup; the roaster "add" wizard additionally opts into
   .roaster-modal's step-transition rules below.
   ========================================================= */
.dial-modal {
  display: flex;
  flex-direction: column;
  width: min(560px, calc(100vw - var(--space-5)));
  max-height: min(680px, calc(100vh - var(--space-6)));
  padding: 0;
  border: var(--rule-heavy) solid var(--ink);
  border-radius: 0;
  background: var(--paper);
  box-shadow: none;
  color: var(--ink);
  opacity: 0;
  transform: translateY(10px);
  transition: opacity var(--duration-base) var(--ease-out-quart), transform var(--duration-base) var(--ease-out-quart),
    overlay var(--duration-base) allow-discrete, display var(--duration-base) allow-discrete;
}

.dial-modal--lg {
  width: min(760px, calc(100vw - var(--space-5)));
  max-height: min(820px, calc(100vh - var(--space-6)));
}

.dial-modal[open] { opacity: 1; transform: translateY(0); }

@starting-style {
  .dial-modal[open] {
    opacity: 0;
    transform: translateY(10px);
  }
}

.dial-modal::backdrop {
  background: rgba(58, 22, 32, 0);
  transition: background-color var(--duration-base) var(--ease-out-quart), overlay var(--duration-base) allow-discrete, display var(--duration-base) allow-discrete;
}

.dial-modal[open]::backdrop { background: rgba(58, 22, 32, 0.6); }

@starting-style {
  .dial-modal[open]::backdrop { background: rgba(58, 22, 32, 0); }
}

.roaster-modal[data-step="review"] {
  width: min(640px, calc(100vw - var(--space-5)));
}

.dial-modal__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: var(--rule-bold) solid var(--ink);
}

.dial-modal__eyebrow {
  font-family: var(--font-mono);
  font-size: 0.64rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--accent-text);
}

.dial-modal__close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin: -6px -6px -6px 0;
  border: none;
  border-radius: 0;
  background: none;
  color: var(--ink);
  font-size: 1.2rem;
  line-height: 1;
}

.dial-modal__close:hover { background: var(--paper-deep); }

.dial-modal__body { flex: 1 1 auto; padding: var(--space-5); overflow-y: auto; }

.dial-modal__footer {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-top: var(--rule-bold) solid var(--ink);
}

@media (max-width: 599.98px) {
  .dial-modal {
    width: 100vw;
    max-width: 100vw;
    height: 100vh;
    max-height: 100vh;
    border-radius: 0;
  }
}

/* Command palette — Cmd/Ctrl+K quick-jump, built on .dial-modal above.
   Result rows reuse .admin-search__result/__group-label/__empty from the
   topbar search dropdown so the two lists read as one system. */
.admin-cmdk-trigger {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: 6px 10px;
  border: var(--rule-hair) solid var(--rule-strong);
  background: var(--paper-white);
  color: var(--ink-soft);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  cursor: pointer;
}

.admin-cmdk-trigger:hover { color: var(--ink); background: var(--paper-deep); }

.admin-cmdk {
  width: min(560px, calc(100vw - var(--space-5)));
  max-height: min(70vh, 560px);
}

.admin-cmdk__input {
  width: 100%;
  border: none;
  border-bottom: var(--rule-bold) solid var(--ink);
  padding: var(--space-4) var(--space-5);
  font-size: 1rem;
  background: transparent;
  color: var(--ink);
}

.admin-cmdk__input:focus { outline: none; }

.admin-cmdk__results { overflow-y: auto; padding: var(--space-3) 0; }

.admin-cmdk__result.is-active { background: var(--butter-100); }

/* =========================================================
   14f. Add-roaster modal — search-first intake over a native <dialog>,
   built on .dial-modal above.
   ========================================================= */
.roaster-modal__body,
.roaster-modal__footer {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.16s var(--ease-out-quart), transform 0.16s var(--ease-out-quart);
}

.roaster-modal__body.is-leaving,
.roaster-modal__footer.is-leaving { opacity: 0; transform: translateY(-6px); }

.roaster-modal__body.is-entering,
.roaster-modal__footer.is-entering { opacity: 0; transform: translateY(6px); }

.roaster-modal__lede h2 { font-size: 1.35rem; margin-bottom: var(--space-2); }

.roaster-modal__lede p {
  color: var(--muted);
  font-size: 0.9rem;
  margin-bottom: var(--space-5);
}

.roaster-modal__search-input {
  width: 100%;
  padding: var(--space-3) var(--space-4);
  border: var(--rule-bold) solid var(--ink);
  border-radius: 0;
  background: var(--paper-deep);
  font-family: var(--font-mono);
  font-size: 1rem;
  color: var(--ink);
}

.roaster-modal__search-input::placeholder { color: var(--muted); }

.roaster-modal__search-input:focus {
  outline: none;
  border-color: var(--vermillion-500);
  background: var(--paper-white);
}

.roaster-modal__predictions {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  border: var(--rule-bold) solid var(--ink);
  border-radius: 0;
  overflow: hidden;
}

.roaster-modal__predictions[hidden] { display: none; }

.roaster-modal__prediction + .roaster-modal__prediction { border-top: var(--rule-hair) solid var(--rule); }

.roaster-modal__prediction label {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  font-size: 0.9rem;
  color: var(--ink);
  cursor: pointer;
}

.roaster-modal__prediction label:hover { background: var(--paper-deep); }

.roaster-modal__prediction input[type="checkbox"] {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  accent-color: var(--vermillion-500);
}

.roaster-modal__prediction-btn {
  display: block;
  width: 100%;
  padding: var(--space-3) var(--space-4);
  border: none;
  background: none;
  text-align: left;
  font-size: 0.9rem;
  color: var(--ink);
  cursor: pointer;
}

.roaster-modal__prediction-btn:hover,
.roaster-modal__prediction-btn:focus-visible { background: var(--paper-deep); }

.roaster-modal__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.roaster-modal__chips[hidden] { display: none; }

.roaster-modal__chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: var(--space-1) var(--space-1) var(--space-1) var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--state-good-fill);
  color: var(--state-good);
  font-size: 0.82rem;
}

.roaster-modal__chip-remove {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border: none;
  border-radius: 50%;
  background: none;
  color: inherit;
  line-height: 1;
  font-size: 1rem;
}

.roaster-modal__chip-remove:hover { background: rgba(58, 22, 32, 0.08); }

.roaster-modal__intake-actions { margin-top: var(--space-4); }

.roaster-modal__manual {
  margin-top: var(--space-4);
  font-size: 0.86rem;
  color: var(--muted);
}

.roaster-modal__manual button {
  border: none;
  background: none;
  padding: 0;
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 2px;
  font: inherit;
}

.roaster-modal__manual button:hover { color: var(--accent-text); }

.roaster-modal__status {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: var(--space-4);
  font-size: 0.86rem;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 0.18s var(--ease-out-quart), transform 0.18s var(--ease-out-quart);
}

.roaster-modal__status[hidden] { display: none; }
.roaster-modal__status.is-visible { opacity: 1; transform: translateY(0); }

.roaster-modal__status-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }

.roaster-modal__status--pending .roaster-modal__status-dot {
  background: var(--state-warn);
  animation: status-pulse 1.1s ease-in-out infinite;
}

.roaster-modal__status--pending { color: var(--state-warn); }

.roaster-modal__status--error .roaster-modal__status-dot { background: var(--state-bad); }
.roaster-modal__status--error { color: var(--state-bad); }

@keyframes status-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

/* Recessed "confirmed reading" strip: provenance for the auto-filled
   fields below, styled as an instrument sub-panel rather than a form
   field so it reads as reference, not input. */
.roaster-modal__reading {
  background: var(--paper-deep);
  border: none;
  border-radius: 0;
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-5);
}

.roaster-modal__reading-eyebrow {
  font-family: var(--font-mono);
  font-size: 0.64rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  margin-bottom: var(--space-2);
}

.roaster-modal__reading-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  font-size: 0.84rem;
}

.roaster-modal__reading-rating { font-family: var(--font-mono); color: var(--ink-soft); }
.roaster-modal__reading-row a { font-weight: 600; color: var(--accent-text); }

.roaster-modal__manual-note { font-size: 0.84rem; color: var(--muted); margin-bottom: var(--space-4); }

.roaster-modal__back {
  border: none;
  background: none;
  padding: 0;
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--muted);
}

.roaster-modal__back:hover { color: var(--accent-text); }

.roaster-modal__summary {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.roaster-modal__summary-item { padding: var(--space-3) var(--space-4); border-radius: 0; font-size: 0.9rem; }
.roaster-modal__summary-item--success { background: var(--state-good-fill); color: var(--state-good); }
.roaster-modal__summary-item--neutral { background: var(--paper-deep); color: var(--ink-soft); }
.roaster-modal__summary-item--error { background: var(--state-bad-fill); color: var(--state-bad); }

@media (max-width: 767.98px) {
  .admin-topbar-card { padding: var(--space-4) var(--space-5); }
  .admin-row { flex-direction: column; }
  .admin-rail { flex: 1 1 auto; width: 100%; min-height: auto; padding: 16px; border-right: none; border-bottom: var(--rule-bold) solid var(--ink); }
  .admin-rail__nav { position: static; }
  .admin-main { padding: var(--space-5) var(--space-4); }
  .admin-rail__nav { display: none; flex-wrap: wrap; flex-direction: row; gap: var(--space-1); }
  .admin-rail__nav.is-open { display: flex; }
  .admin-toggle {
    display: flex;
    width: 100%;
    justify-content: space-between;
    align-items: center;
    background: none;
    border: none;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--ink-soft);
    padding: var(--space-2) var(--space-1);
  }
  .admin-topbar { flex-direction: column; align-items: stretch; }
  .admin-search { flex-basis: auto; }
}

/* =========================================================
   15. Motion
   Everything here is meant to go unnoticed: short, ease-out, small
   distances. Nothing loops, nothing bounces, nothing blocks reading.
   All of it is disabled under prefers-reduced-motion.
   ========================================================= */
@keyframes ds-rise {
  from { opacity: 0; transform: translateY(18px); }
  to { opacity: 1; transform: none; }
}
@keyframes ds-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes ds-wipe-right {
  from { clip-path: inset(0 100% 0 0); }
  to { clip-path: inset(0 0 0 0); }
}
@keyframes ds-rule-grow {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}
@keyframes ds-stamp-in {
  from { opacity: 0; transform: rotate(0deg) scale(0.86); }
  to { opacity: 1; transform: rotate(var(--stamp-tilt, -8deg)) scale(1); }
}
@keyframes ds-draw {
  from { stroke-dashoffset: var(--draw-length, 800); }
  to { stroke-dashoffset: 0; }
}

/* Scroll reveal — .fade-in-up is kept as a legacy alias so templates not
   yet migrated to .ds-reveal keep animating; main.js toggles both
   "visible" classes on every observed element. */
.fade-in-up,
.ds-reveal { opacity: 0; }

.fade-in-up--visible,
.ds-reveal.is-in { animation: ds-rise 620ms var(--ease-out) both; }
.ds-reveal--fade.is-in { animation: ds-fade 520ms var(--ease-out) both; }
.ds-reveal--wipe.is-in { animation: ds-wipe-right 720ms var(--ease-out) both; }

.ds-rule-grow { transform-origin: left center; }
.ds-rule-grow.is-in { animation: ds-rule-grow 640ms var(--ease-out) both; }

.ds-draw path, .ds-draw line, .ds-draw circle, .ds-draw rect, .ds-draw polyline { opacity: 0; }
.ds-draw.is-in path, .ds-draw.is-in line, .ds-draw.is-in circle, .ds-draw.is-in rect, .ds-draw.is-in polyline {
  opacity: 1;
  stroke-dasharray: var(--draw-length, 800);
  animation: ds-draw 1100ms var(--ease-out) both, ds-fade 200ms linear both;
}
.ds-draw.is-in [data-nodraw] { animation: ds-fade 400ms var(--ease-out) 700ms both; stroke-dasharray: none; }

/* Save/favorite toggle. Squared, uppercase, mono — ink outline when off,
   marigold block when on — with a quick scale pop on state change, not
   a permanent hover effect, so it reads as feedback rather than chrome. */
.save-btn {
  --bs-btn-color: var(--ink);
  --bs-btn-border-color: var(--ink);
  --bs-btn-bg: transparent;
  --bs-btn-hover-color: var(--paper-white);
  --bs-btn-hover-bg: var(--ink);
  --bs-btn-hover-border-color: var(--ink);
  transition: transform 0.15s var(--ease-out-quart), background-color 0.15s var(--ease-out-quart), color 0.15s var(--ease-out-quart), border-color 0.15s var(--ease-out-quart);
}

.save-btn[data-saved="true"] {
  --bs-btn-color: var(--ink);
  --bs-btn-border-color: var(--butter-500);
  --bs-btn-bg: var(--butter-500);
  --bs-btn-hover-color: var(--ink);
  --bs-btn-hover-bg: var(--butter-600);
  --bs-btn-hover-border-color: var(--butter-600);
}

.save-btn.is-popping { animation: save-pop 0.35s var(--ease-out-quart); }
.pill.is-popping { animation: save-pop 0.35s var(--ease-out-quart); }

@keyframes save-pop {
  0% { transform: scale(1); }
  40% { transform: scale(1.18); }
  100% { transform: scale(1); }
}

/* Generic POST-form submit guard: disables the button and swaps in a
   pulse so double-clicks don't fire duplicate submissions. */
.is-submitting {
  position: relative;
  color: transparent !important;
  pointer-events: none;
}

.is-submitting::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.4);
  border-top-color: #fff;
  animation: spin 0.6s linear infinite;
}

.btn-outline-dark.is-submitting::after,
.btn-outline-secondary.is-submitting::after {
  border-color: rgba(58, 22, 32, 0.25);
  border-top-color: var(--ink);
}

@keyframes spin { to { transform: rotate(360deg); } }

/* Flashes render server-side with .show already applied, so a
   transition on that class change would never fire; use a keyframe
   instead so the entrance still plays on first paint. */
.alert { animation: alert-in 0.35s var(--ease-out-quart); }

@keyframes alert-in {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .fade-in-up, .ds-reveal,
  .fade-in-up--visible, .ds-reveal.is-in,
  .ds-reveal--fade.is-in, .ds-reveal--wipe.is-in,
  .ds-rule-grow.is-in,
  .ds-draw.is-in path, .ds-draw.is-in line, .ds-draw.is-in circle, .ds-draw.is-in rect, .ds-draw.is-in polyline,
  .ds-draw.is-in [data-nodraw] {
    animation: none !important;
    opacity: 1;
    transform: none;
    clip-path: none;
    stroke-dasharray: none;
  }
  .ds-draw path, .ds-draw line, .ds-draw circle, .ds-draw rect, .ds-draw polyline { opacity: 1; }
  .card-product:hover .card-product__image-wrap img { transform: none; }
  .feature-link:hover .feature-link__arrow { transform: none; }
  .btn:active { transform: none; }
  .calibration-scale--illustrative .calibration-scale__track,
  .calibration-scale--illustrative .calibration-scale__pip {
    animation: none;
    opacity: 1;
  }
  .calibration-scale--illustrative .calibration-scale__pip { transform: translate(-50%, 50%) rotate(45deg); }
  .save-btn.is-popping { animation: none; }
  .is-submitting::after { animation: none; }
  .alert { animation: none; }
  .is-navigating { transition: none; }
  .dial-modal { transition: none; }
  .dial-modal::backdrop { transition: none; }
  .roaster-modal__body, .roaster-modal__footer, .roaster-modal__status { transition: none; }
  .roaster-modal__status--pending .roaster-modal__status-dot { animation: none; opacity: 1; }
  .pill.is-popping { animation: none; }
}

/* =========================================================
   16. Form panels — login/register/forgot-password/reviews/write all
   share this single centred treatment: Anton title, underlined fields
   (a rule, not a box), mono-label divider. (Was .auth-panel; renamed/
   generalised since more than auth now uses it.)
   ========================================================= */
.form-panel {
  max-width: 460px;
  margin: 0 auto;
  padding: 0;
}

.form-panel--wide { max-width: 620px; }

.form-panel--offset { margin-top: var(--space-6); }

.form-panel__header { margin-bottom: var(--space-6); }

.form-panel__header h1 {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 400;
  font-size: var(--type-hero-size);
  line-height: var(--type-hero-line);
  letter-spacing: var(--type-display-tracking);
  margin-bottom: var(--space-2);
}

.form-panel__header p {
  color: var(--accent-text);
  margin-bottom: 0;
  font-family: var(--font-mono);
  font-size: var(--type-eyebrow-size);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--type-eyebrow-tracking);
  opacity: 1;
}

/* Underlined field treatment, scoped to form-panel so the rest of the
   site's forms (admin, recipe form, trip form) keep the boxed
   .form-control default defined in section 2. */
.form-panel label,
.form-panel .form-label {
  display: block;
  margin-bottom: 6px;
  font-family: var(--font-mono);
  font-size: 0.64rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-quiet);
}

.form-panel .form-control,
.form-panel .form-select {
  padding: var(--space-2) 0;
  font-family: var(--font-body);
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--ink);
  background: transparent;
  border: none;
  border-bottom: 2px solid var(--ink);
  border-radius: 0;
}

.form-panel .form-control:focus,
.form-panel .form-select:focus {
  border-bottom-color: var(--vermillion-500);
  background: transparent;
  box-shadow: none;
}

.form-panel .mb-3,
.form-panel .mb-4 { margin-bottom: var(--space-5) !important; }

.auth-divider {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-5) 0;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--text-quiet);
}

.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--rule);
}

.form-panel__foot {
  margin: var(--space-5) 0 0;
  color: var(--ink-soft);
  font-size: 0.92rem;
}

/* =========================================================
   17. Error panels — the code set enormous in Anton, a vermillion stamp
   above it, one line of plain explanation. No apology paragraph.
   ========================================================= */
.error-panel {
  max-width: 640px;
  margin: 0 auto;
  padding: 0;
}

.error-panel__status {
  display: inline-block;
  padding: var(--space-1) var(--space-2);
  background: var(--vermillion-500);
  color: var(--paper-white);
  font-family: var(--font-mono);
  font-size: 0.64rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
}

.error-panel__code {
  display: block;
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 400;
  font-size: var(--type-mega-size);
  line-height: 0.8;
  letter-spacing: -0.03em;
  color: var(--ink);
  margin: var(--space-4) 0;
}

.error-panel__message {
  font-family: var(--font-body);
  color: var(--text-body);
  font-size: var(--type-lead-size);
  max-width: 36ch;
  margin: 0 0 var(--space-5);
}

/* =========================================================
   18. New poster primitives — sticker badge, handwritten annotation,
   schematic diagram. None of these existed in the previous floating-
   card system; added so key pages (home, roast/recipe detail) can use
   the full poster vocabulary.
   ========================================================= */
/* Rotated circular stamp. Use once per band, never twice in view. */
.sticker-badge {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  width: 116px;
  height: 116px;
  flex: 0 0 auto;
  border-radius: var(--radius-circle);
  background: var(--butter-500);
  color: var(--ink);
  text-align: center;
  padding: var(--space-3);
  transform: rotate(var(--sticker-tilt));
  animation: ds-stamp-in 0.5s var(--ease-out) both;
}

.sticker-badge--vermillion { background: var(--vermillion-500); color: var(--paper-white); }
.sticker-badge--ink { background: var(--ink); color: var(--butter-500); }
.sticker-badge--outline { background: transparent; color: var(--ink); border: var(--rule-bold) solid var(--ink); }
.sticker-badge--sm { width: 88px; height: 88px; }

.sticker-badge__line {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  line-height: 1.25;
}

.sticker-badge__line:not(:first-child) { font-size: 0.58rem; font-weight: 500; }

/* Handwritten aside — Caveat, small, one line, beside or above set
   type, never inside a paragraph. At most twice per page. */
.annotate {
  display: inline-block;
  font-family: var(--font-annotate);
  font-size: var(--type-annotate-size);
  line-height: 1.15;
  color: var(--ink);
}

.annotate--accent { color: var(--vermillion-500); }

/* Schematic diagram figure: 1.6px strokes, no fills, a mono FIG. caption. */
.diagram { margin: 0; width: 100%; }

.diagram svg {
  display: block;
  width: 100%;
  height: auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.diagram figcaption {
  font-family: var(--font-mono);
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: currentColor;
  opacity: 0.75;
  margin-top: var(--space-3);
  text-align: center;
}

/* Pull quote — Instrument Serif italic, vermillion band only. */
.pull-quote {
  font-family: var(--font-editorial);
  font-style: italic;
  font-size: var(--type-quote-size);
  line-height: 1.15;
  color: var(--paper-white);
  margin: 0;
  max-width: 30ch;
}

/* Stat tiles set in a paper-white readout against a saturated band. */
.stats-strip--tri { grid-template-columns: repeat(3, 1fr); }
.stats-strip--paper .stat-tile:not(:first-child) { border-left-color: rgba(253, 246, 232, 0.35); }
.stats-strip--paper .stat-tile__value { color: var(--paper-white); }
.stats-strip--paper .stat-tile__label { color: rgba(253, 246, 232, 0.78); }

/* Numbered spec list — single-column stack of dt/dd rows, one hairline per row. */
.spec-panel__row {
  padding: var(--space-4) 0;
  border-bottom: var(--rule-hair) solid var(--rule);
}

.spec-panel__row:last-child { border-bottom: none; }

/* In-page section nav — jumps to the poster detail page's own bands
   (roast page: Details / Brew guide / Recipes / Where to buy). Reuses
   the admin tab strip's underline convention, restyled for a paper
   band instead of admin's dark chrome. */
.page-tab-strip {
  display: flex;
  gap: var(--space-1);
  border-top: var(--rule-hair) solid var(--rule);
  border-bottom: var(--rule-hair) solid var(--rule);
  overflow-x: auto;
}

.page-tab-strip__tab {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--text-quiet);
  padding: var(--space-3) var(--space-4);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
}

.page-tab-strip__tab:hover { color: var(--ink); }

.page-tab-strip__tab.is-active {
  color: var(--ink);
  border-bottom-color: var(--vermillion-500);
}

/* Content-switching page tabs (Account: Saved / My recipes / Equipment /
   Account). Same underline convention as .page-tab-strip, but the tabs are
   buttons that swap panel visibility instead of anchors that jump. */
.page-tabs__nav {
  display: flex;
  gap: var(--space-1);
  border-top: var(--rule-hair) solid var(--rule);
  border-bottom: var(--rule-hair) solid var(--rule);
  overflow-x: auto;
  background: transparent;
  border-left: none;
  border-right: none;
}

.page-tabs__tab {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--text-quiet);
  background: transparent;
  border: none;
  padding: var(--space-4);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease-out), border-color var(--duration-fast) var(--ease-out);
}

.page-tabs__tab:hover { color: var(--ink); }

.page-tabs__tab.is-active {
  color: var(--ink);
  border-bottom-color: var(--vermillion-500);
}
