/* ===== WEBFONTS =====
   Self-hosted so they satisfy functions/_middleware.js's `font-src 'self'`.
   Populate fonts/ by running `node scripts/fetch-fonts.mjs` on a machine
   with network access, then commit the directory — see that script's header
   for why self-hosting rather than relaxing the CSP.

   font-display: swap means text renders immediately in the fallback and
   reflows when the webfont lands; on a kitchen tool, showing the recipe
   late is worse than showing it briefly in Georgia.

   If fonts/ is absent these blocks simply fail to load and the fallback
   stacks in --serif/--sans take over. The site is fully functional without
   them; only the typography differs. */
@font-face {
  font-family: 'Literata';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../fonts/Literata-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC,
                 U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Literata';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/Literata-400Italic-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC,
                 U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('../fonts/Inter-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC,
                 U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ===== RESET ===== */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* ===== DESIGN TOKENS ===== */
/*
  Light/dark theming added 2026-07-26, using the "Cookbook" (light) and
  "Kitchen Evening" (dark) tokens locked in v2-planning.md — pulled forward
  into v1 at Jacob's direction rather than left as a v2-only decision.
  Only the color system moved; v1 keeps its existing system-ui/mono
  typography on purpose (the doc itself frames the warmer serif direction
  as a v2 family-facing redesign choice, not a v1 one — "V1: utilitarian
  is correct").

  Base/surface/border/text values are the exact locked hex codes. Category
  colors are NOT pulled from the doc's category table — that table predates
  the 2026-07-24 taxonomy redesign and only covers 6 of the current 13
  categories (several under retired names), so porting it in would mean an
  inconsistent patchwork of doc-sourced and improvised hues. Instead every
  light-mode category color below is derived from the existing live
  dark-mode hue (same hue, lightness pulled down ~16%, saturation boosted
  ~12%) so all 13 stay internally consistent. Revisit if/when v2-planning.md's
  category table gets updated for the current taxonomy.

  --accent functions as the doc's "Precision" token (scaling UI and scaled
  values only). The family-recipe badge deliberately does NOT use it —
  the logo renders in --text (white on dark, near-black on light) with no
  accent color, since it's a plain mark, not a scaling-state indicator.
*/
:root {
  /* ===== Kitchen Evening (dark) — default theme ===== */
  --bg:         #1B1815;
  --surface:    #26211D;
  --surface-hi: #312A24;

  --border:     #433B34;
  --border-hi:  #574D44;

  --text:       #F2ECE4;
  --text-2:     #B9AEA1;
  --text-3:     #8A8078;

  /* Precision — scaling UI and scaled values only. Nothing else should
     reach for this color; see note above. */
  --accent:     #C8750E;
  --accent-dim: rgba(200, 117, 14, 0.10);

  /* Advisory — uncertainty/warnings (scaling flags, batch-size notices).
     Deliberately not red: red is reserved for destructive actions, which
     v1's read-only cook-from-json UI doesn't have. */
  --advisory:     #B88A3B;
  --advisory-dim: rgba(184, 138, 59, 0.09);

  /* Favorite — per-person star/pin (migrations/0013_favorites.sql, added
     2026-08-04). Reserved in docs/v2-planning.md since 2026-07-26 with no
     feature attached; same hex both themes per that table, so it's defined
     once per theme block for consistency with every other token here
     rather than hoisted to a single shared value. Distinct from --accent
     (Precision, scaling only) and --advisory (uncertainty) — this is
     personal state, not a system signal. */
  --favorite:     #C6A14A;
  --favorite-dim: rgba(198, 161, 74, 0.10);

  /* ── Category FAMILY palette (dark) ──────────────────────────────────
     Replaced the 13 per-category hues 2026-07-31. Measured in OKLCH, the
     old set had six hues inside a single 54° band (pastry and sauce were
     4.4° apart — indistinguishable on a badge) and pastry sat 1.3° from
     the amber accent, quietly breaking "amber is sacred."

     13 categories now map to 6 families, minimum 53.3° apart, all ≥20°
     clear of amber, uniform chroma 0.090 against the accent's 0.143. The
     badge always renders the category NAME, so colour never needed to
     carry 13-way precision — it identifies the family, the label
     identifies the category. Mapping lives in the .cat-* rules below.

     Dark values are unchanged from the first derivation (~4.5:1). Light
     is "tier C" and is deliberately MORE chromatic — see the light block.
     Full reasoning: docs/v2-planning.md, Category Colors. */
  --fam-baked:   #9C7C3D;  /* bread, pastry                   hue  82.4° */
  --fam-mains:   #668B54;  /* main, pasta                     hue 135.7° */
  --fam-sides:   #2A8F88;  /* side, appetizer                 hue 189.1° */
  --fam-liquids: #4C86B1;  /* sauce, beverage, other          hue 242.4° */
  --fam-sweets:  #9474AC;  /* dessert, breakfast              hue 310.0° */
  --fam-pots:    #B46D6D;  /* soup, stew                      hue  20.0° */

  /* Badge fills, precomputed rather than color-mix() — color-mix has
     patchy support on older mobile Safari, and this site is used on
     phones in kitchens. */
  --fam-baked-bg:   rgba(156, 124,  61, 0.10);
  --fam-mains-bg:   rgba(102, 139,  84, 0.10);
  --fam-sides-bg:   rgba( 42, 143, 136, 0.10);
  --fam-liquids-bg: rgba( 76, 134, 177, 0.10);
  --fam-sweets-bg:  rgba(148, 116, 172, 0.10);
  --fam-pots-bg:    rgba(180, 109, 109, 0.10);

  /* Error — load/fetch failures, and (added 2026-08-01) edit-form save
     failures/conflicts — both genuine error states, not scaling advisory.
     Not the doc's "Destructive" token (no delete actions exist in v1); kept
     separate and unchanged from before. --error-dim follows the same
     precomputed-rgba pattern as --accent-dim/--advisory-dim rather than
     color-mix(), for consistency with how every other "-dim" token here is
     defined. */
  --error: #e06c75;
  --error-dim: rgba(224, 108, 117, 0.10);

  /* Typography — v2 direction, implemented 2026-07-31.
     Literata for recipe titles and step numbers, Inter for everything
     structural, system mono for data/metadata. Self-hosted from fonts/
     (see @font-face at the top of this file and scripts/fetch-fonts.mjs):
     functions/_middleware.js sets `font-src 'self'`, so a Google Fonts
     link would be blocked outright.

     Fallbacks are real, not decorative — if fonts/ hasn't been populated
     yet the site degrades to Georgia + system-ui and stays fully usable.
     No mono webfont is fetched on purpose: metadata is short, system mono
     is fine, and a third family isn't worth the bytes. */
  --serif: 'Literata', Georgia, 'Times New Roman', serif;
  --sans: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: ui-monospace, 'Cascadia Code', Menlo, Consolas, monospace;

  --radius: 2px;
  --ease:   140ms ease;
}

:root[data-theme="light"] {
  /* ===== Cookbook (light) ===== */
  --bg:         #F7F3EC;
  --surface:    #FFFCF8;
  --surface-hi: #F0EAE0;

  --border:     #E8DED0;
  --border-hi:  #D9C8B1;

  --text:       #2C251F;
  --text-2:     #756B60;
  --text-3:     #786D61;  /* was #9A8E82 (2.89:1) — retuned 2026-07-31 per
                              docs/v2-planning.md, applied to CSS 2026-08-04:
                              the decision was documented but never actually
                              shipped. Now 4.57:1. */

  --accent:     #D97706;  /* deliberate WCAG AA exception — see
                              docs/v2-planning.md "Precision" note. Unchanged. */
  --accent-dim: rgba(217, 118, 6, 0.10);

  --favorite:     #C6A14A;  /* same hex both themes — see dark block's note. */
  --favorite-dim: rgba(198, 161, 74, 0.12);

  --advisory:     #8E6828;  /* was #B88A3B (2.82:1) — retuned 2026-07-31 per
                                docs/v2-planning.md, applied to CSS 2026-08-04,
                                same never-shipped gap as Tertiary text above.
                                Now 4.56:1. */
  --advisory-dim: rgba(184, 138, 59, 0.12);

  /* ── Category FAMILY palette (light) — "tier C" ──────────────────────
     Chosen 2026-07-31 from a four-tier comparison and confirmed by the
     family on real devices. Contrast is INFORMATIONAL on this project,
     not a gate: these land at 2.88–3.28:1 against --bg, below WCAG AA's
     4.5:1, and that is deliberate. This is a private whitelisted family
     site with no compliance obligation, and the 4.5:1 versions were
     visibly dull.

     Two things not to "correct" here:
     • These are more chromatic than the dark values above (up to 0.161
       vs 0.090). Not an inconsistency — the same colour reads noticeably
       less saturated against cream than against near-black, so the extra
       chroma compensates rather than diverges.
     • Chroma is capped at 0.16 but NOT uniform: teal (--fam-sides) tops
       out at 0.111 because sRGB simply holds less chroma at 189° than
       anywhere else on the wheel. It reads softer than its neighbours by
       necessity. Fixing that means moving the family off 189°, which
       squeezes the 53° minimum separation the palette depends on. */
  --fam-baked:   #B38300;
  --fam-mains:   #5CA132;
  --fam-sides:   #00A19A;
  --fam-liquids: #0094E1;
  --fam-sweets:  #AA6CD4;
  --fam-pots:    #DC5D63;

  --fam-baked-bg:   rgba(179, 131,   0, 0.10);
  --fam-mains-bg:   rgba( 92, 161,  50, 0.10);
  --fam-sides-bg:   rgba(  0, 161, 154, 0.10);
  --fam-liquids-bg: rgba(  0, 148, 225, 0.10);
  --fam-sweets-bg:  rgba(170, 108, 212, 0.10);
  --fam-pots-bg:    rgba(220,  93,  99, 0.10);

  --error: #c0392b;
  --error-dim: rgba(192, 57, 43, 0.10);
}

/* ===== BASE ===== */
html {
  font-size: 15px;
  -webkit-text-size-adjust: 100%;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  line-height: 1.5;
  min-height: 100vh;
}

/* ===== GLOBAL FOCUS ===== */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ===== UTILITY ===== */
.hidden { display: none !important; }

/* ===== SITE HEADER ===== */
.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.5rem;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 20;
}

.wordmark {
  font-family: var(--mono);
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  color: var(--text);
}

/* Right-side header cluster (recipe-count/scale-badge + theme toggle) */
.header-right {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

/* Left-side header cluster — submit.html only, so far (back-link + wordmark).
   Groups them so .site-header's justify-content: space-between still splits
   into exactly two clusters instead of spreading three items evenly. */
.header-left {
  display: flex;
  align-items: center;
  gap: 0.9rem;
}
.back-link {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--text-2);
  text-decoration: none;
  white-space: nowrap;
}
.back-link:hover { color: var(--accent); }

@media (max-width: 600px) {
  .header-left { gap: 0.5rem; }
  .back-link { font-size: 0.66rem; }
}

/* Theme toggle — same bordered-button language as scale-badge/filter-btn. */
.theme-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: transparent;
  color: var(--text-2);
  cursor: pointer;
  transition: border-color var(--ease), color var(--ease), background var(--ease);
  flex-shrink: 0;
}
.theme-toggle:hover {
  border-color: var(--border-hi);
  color: var(--text);
}
.theme-toggle-icon {
  font-size: 0.85rem;
  line-height: 1;
}

.recipe-count {
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--text-2);
  letter-spacing: 0.04em;
}

/* ===== CONTROLS ===== */
.controls {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 0.85rem 1.5rem;
  border-bottom: 1px solid var(--border);
}

.search-input {
  display: block;
  width: 100%;
  max-width: 340px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--sans);
  font-size: 0.88rem;
  padding: 0.4rem 0.65rem;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
  transition: border-color var(--ease);
}
.search-input::placeholder { color: var(--text-3); }
.search-input:focus         { border-color: var(--border-hi); }
.search-input::-webkit-search-cancel-button { -webkit-appearance: none; }

/* ===== FILTER DISCLOSURE =====
   All three filter groups live inside one collapsible bar (2026-08-01).
   Before this, three always-open chip rows — 26 chips — wrapped to roughly
   nine rows on a 360px screen and pushed every recipe below the fold.

   The summary bar has two jobs: it toggles the disclosure, and it shows any
   ACTIVE filter as an amber pill. The second matters more — a collapsed
   filter quietly narrowing results is a worse bug than the space it saves,
   so an active filter is never hidden. */
.filter-bar {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.filter-summary {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  width: 100%;
  padding: 0.45rem 0.6rem;
  background: transparent;
  border: none;
  border-radius: var(--radius);
  color: var(--text-2);
  cursor: pointer;
  text-align: left;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}
.filter-summary:hover { color: var(--text); }
.filter-label { flex-shrink: 0; }

.filter-caret {
  margin-left: auto;
  font-size: 0.7rem;
  color: var(--text-3);
  transition: transform var(--ease);
}
.filter-bar.open .filter-caret { transform: rotate(180deg); }
.filter-bar.open .filter-summary { border-bottom: 1px solid var(--border); }

/* Active-filter pill on the summary bar. Amber is normally reserved for
   scaling state — this is a deliberate, narrow exception: it's the same
   semantic ("something is actively modifying what you're looking at") and
   the alternative was inventing a second attention colour. */
.filter-pill {
  font-family: var(--mono);
  font-size: 0.58rem;
  letter-spacing: 0.03em;
  text-transform: none;
  border-radius: 8px;
  padding: 0.04rem 0.4rem;
  border: 1px solid var(--accent);
  color: var(--accent);
  background: var(--accent-dim);
  white-space: nowrap;
}
.filter-pill.none {
  border-color: var(--border);
  color: var(--text-3);
  background: transparent;
}

.filter-body { display: none; padding: 0.55rem 0.6rem 0.65rem; }
.filter-bar.open .filter-body { display: block; }

.filter-group { margin-bottom: 0.6rem; }
.filter-group:last-child { margin-bottom: 0; }
.filter-group-label {
  font-family: var(--mono);
  font-size: 0.56rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-3);
  margin-bottom: 0.3rem;
}

.clear-filters {
  align-self: flex-start;
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.06em;
  padding: 0.22rem 0.55rem;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: transparent;
  color: var(--accent);
  cursor: pointer;
}
.clear-filters:hover { background: var(--accent-dim); }

.filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}

.filter-btn {
  font-family: var(--mono);
  font-size: 0.67rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: transparent;
  color: var(--text-2);
  cursor: pointer;
  transition: border-color var(--ease), color var(--ease), background var(--ease);
  white-space: nowrap;
}
.filter-btn:hover:not(.active) {
  border-color: var(--border-hi);
  color: var(--text);
}
.filter-btn.active {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-dim);
}

/* ===== RECIPE GRID =====
   Was an edge-to-edge shared-border table; now separated cards with real
   gutters, since variation A's identity comes from its own left edge rule
   and a shared 1px grid border would sit right beside it and read muddy. */
.recipe-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(272px, 1fr));
  gap: 0.85rem;
  padding: 1.25rem 1.5rem;
}

/* ===== RECIPE CARD — "index card", variation A =====
   Confirmed 2026-07-31 after comparing four layout families and then ten
   variations on this one (docs/v2-card-variations.html — retained as the
   reference library for future refreshes).

   Category is a 3px rule on the leading edge, reading like a tabbed divider
   in a recipe box. `--c` is set by the .cat-* mapping on the badge inside,
   but the rule needs it on the card itself — js/index.js sets it inline
   from the same category, which keeps CSS as the single source of the
   mapping while avoiding a wrapper element.

   Deliberately NO photo area. photos.hero is null on all 88 recipes and
   photo support isn't planned; reserving space for it would spend the most
   valuable part of the card permanently. */
.recipe-card {
  display: flex;
  flex-direction: column;
  gap: 0.28rem;
  padding: 0.9rem 1rem 0.85rem;
  border: 1px solid var(--border);
  border-left: 3px solid var(--c, var(--fam-liquids));
  border-radius: var(--radius);
  text-decoration: none;
  color: inherit;
  background: var(--surface);
  transition: background var(--ease), border-color var(--ease);
}
.recipe-card:hover {
  background: var(--surface-hi);
  border-color: var(--border-hi);
  border-left-color: var(--c, var(--fam-liquids));
}
.recipe-card:focus-visible { outline-offset: 2px; }

.card-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  min-height: 1rem;
}

/* Category badge.
   NOTE ON ORDER: the `--c` fallback below MUST stay above the .cat-*
   family mapping further down. A badge carries both classes
   (`cat-badge cat-bread`), they have equal specificity, so whichever
   declares --c last wins — if this rule came after the mapping, every
   category would collapse to the fallback colour. `color: var(--c)` is
   order-independent (custom properties resolve at computed-value time),
   so only the --c declarations need care. */
.cat-badge {
  --c: var(--fam-liquids);   /* fallback for any category not mapped below */
  color: var(--c);
  font-family: var(--mono);
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  flex-shrink: 0;
}
/* ── Category → family mapping ────────────────────────────────────────────
   The 13-value `category` enum is unchanged in schema/recipe.schema.json,
   in D1, and in every recipe JSON — this is purely a presentation mapping.
   js/index.js still emits `cat-${category}` and needs no knowledge of
   families; each class resolves to its family's colour here.

   Variation A renders the category as plain coloured text, not a filled
   pill — so these set only --c. The --fam-*-bg tint tokens stay defined
   but unused here; they're what variations D/H/J in
   docs/v2-card-variations.html need, and that file is retained as the
   design-refresh reference. */
.cat-bread,
.cat-pastry    { --c: var(--fam-baked); }
.cat-main,
.cat-pasta     { --c: var(--fam-mains); }
.cat-side,
.cat-appetizer { --c: var(--fam-sides); }
.cat-sauce,
.cat-beverage,
.cat-other     { --c: var(--fam-liquids); }
.cat-dessert,
.cat-breakfast { --c: var(--fam-sweets); }
.cat-soup,
.cat-stew      { --c: var(--fam-pots); }

/* Right-side badge cluster in card-header (family badge, baker's %, etc.) */
.header-badges {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  flex-shrink: 0;
}

/* Family recipe indicator — this collection's core identity marker.
   Deliberately NOT --accent (Precision is reserved for scaling state,
   see PROJECT.md's "Family recipe badge icon + theming" — an early pass
   used gold here and was corrected the same day it shipped): plain
   --text, same as body copy. Renders the antler/feather family mark
   (img/family-mark.png) as a CSS mask rather than an <img>, so it
   recolors via background-color instead of needing separate light/dark
   image assets. Added 2026-07-26, icon swap 2026-07-26; reused on the
   recipe detail page 2026-08-04 (previously list-card only). */
.family-badge {
  display: inline-block;
  width: 1.05rem;
  height: 0.86rem;
  flex-shrink: 0;
  /* Plain mark, not a scaling-state indicator — theme text color, not
     --accent (Precision). White on dark, near-black on light. */
  background-color: var(--text);
  -webkit-mask-image: url('../img/family-mark.png');
  mask-image: url('../img/family-mark.png');
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
}

/* Favorite/pin toggle — per-person, added 2026-08-04 (migrations/
   0013_favorites.sql). A real <button>, not a span like the badges above:
   this one is interactive, not a passive indicator. Plain star glyph
   rather than a new image asset, matching how .bakers-badge used a plain
   "%" character rather than an icon — one fewer asset to theme for
   light/dark. Unfavorited state uses --text-3 (same quiet weight as other
   inactive metadata); favorited uses --favorite, filled. */
.favorite-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  flex-shrink: 0;
  border: none;
  background: none;
  padding: 0;
  margin: 0;
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
  color: var(--text-3);
  transition: color var(--ease), transform var(--ease);
}
.favorite-btn:hover { color: var(--favorite); }
.favorite-btn:active { transform: scale(0.9); }
.favorite-btn.favorited { color: var(--favorite); }

/* Card version sits inside .header-badges, sized down to match the other
   badges there rather than the bigger touch target the detail page's
   standalone button gets. */
.card-header .favorite-btn {
  width: 1.1rem;
  height: 1.1rem;
  font-size: 0.85rem;
}

/* Author attribution indicator — who the recipe belongs to (Mom's, Nana's,
   etc). Neutral bordered-pill styling rather than a category color or
   --accent (accent is reserved for scaling state). */
.author-badge {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.04em;
  color: var(--text-3);
  border: 1px solid var(--border);
  border-radius: 2px;
  padding: 0.1rem 0.35rem;
  flex-shrink: 0;
  white-space: nowrap;
}

.card-title {
  font-family: var(--serif);
  font-size: 1.05rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--text);
}

/* Author moved out of the bordered pill it used to sit in (.author-badge,
   still used elsewhere) and onto its own line in italic serif, directly
   under the title — it reads as attribution rather than as another chip
   competing with the category and baker's-% badges. */
.card-author {
  font-family: var(--serif);
  font-style: italic;
  font-size: 0.82rem;
  color: var(--text-2);
  line-height: 1.35;
}

/* Yield + total time. Separated from the title block by a hairline so it
   scans as data, not prose. Total time is new on the card — index.json
   already carried total_time_minutes for the time filter, it just wasn't
   being shown. Formatted via scaling.js's shared formatHM(). */
.card-meta {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex-wrap: wrap;
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--text-2);
  margin-top: 0.4rem;
  padding-top: 0.45rem;
  border-top: 1px solid var(--border);
}
.card-meta .sep { color: var(--text-3); }

.card-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.4rem;
  margin-top: 0.35rem;
}

.tag {
  font-family: var(--mono);
  font-size: 0.62rem;
  color: var(--text-3);
  letter-spacing: 0.02em;
}

/* ===== STATUS MESSAGES ===== */
.status-msg {
  grid-column: 1 / -1;
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  padding: 2.5rem 1.5rem;
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--text-2);
  letter-spacing: 0.04em;
}
.status-msg.error { color: var(--error); }

/* ===== RECIPE DETAIL PAGE ===== */

/* A stray `.cat-sauce { color: var(--c-sauce); ... }` lived here, orphaned
   from the other .cat-* rules up in the card section. Removed 2026-07-31
   with the 13→6 family migration: it referenced --c-sauce/--c-sauce-bg,
   which no longer exist, and because it sat AFTER .cat-badge in source
   order at equal specificity it would have blanked the colour on every
   sauce badge. .cat-sauce is now handled with the rest of the mapping. */

/* Scale badge in header */
.scale-badge {
  font-family: var(--mono);
  font-size: 0.67rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  padding: 0.22rem 0.65rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: transparent;
  color: var(--text-2);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  transition: border-color var(--ease), color var(--ease), background var(--ease);
  white-space: nowrap;
}
.scale-badge:hover { border-color: var(--border-hi); color: var(--text); }
.scale-badge.active {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-dim);
}
.scale-badge .chevron {
  display: inline-block;
  transition: transform var(--ease);
  font-style: normal;
  font-size: 0.55rem;
}
.scale-badge.open .chevron { transform: rotate(180deg); }

/* Back link */
.back-link {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--text-2);
  text-decoration: none;
  letter-spacing: 0.05em;
  padding: 0.85rem 1.5rem 0;
  transition: color var(--ease);
}
.back-link:hover { color: var(--text); }

/* Recipe header block */
.recipe-header {
  padding: 0.75rem 1.5rem 1rem;
  border-bottom: 1px solid var(--border);
}
.recipe-meta {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.5rem;
}
.recipe-title {
  font-family: var(--serif);
  font-size: 1.7rem;
  font-weight: 700;
  line-height: 1.18;
  color: var(--text);
}
/* Matches the list card's author treatment — italic serif, not mono. */
.recipe-author {
  font-family: var(--serif);
  font-size: 0.92rem;
  font-style: italic;
  color: var(--text-2);
  margin-top: 0.25rem;
}
.recipe-yield-desc {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--text-2);
  letter-spacing: 0.04em;
  margin-top: 0.3rem;
}
.recipe-time-row {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--text-2);
  letter-spacing: 0.04em;
  margin-top: 0.35rem;
}
.time-label {
  font-size: 0.64rem;
  color: var(--text-3);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.time-sep { color: var(--text-3); }
.time-val.time-scaled { color: var(--accent); }

/* Scaling drawer */
.scale-drawer {
  border-bottom: 1px solid var(--border);
  overflow: hidden;
  max-height: 0;
  transition: max-height 240ms ease;
}
.scale-drawer.open { max-height: 400px; }

.drawer-inner {
  padding: 1rem 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

.scale-row {
  display: grid;
  grid-template-columns: 1.1rem 90px 1fr;
  align-items: center;
  gap: 0.6rem;
}
.scale-row input[type="radio"] {
  accent-color: var(--accent);
  width: 1rem;
  height: 1rem;
  cursor: pointer;
  flex-shrink: 0;
}
.scale-row-label {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  color: var(--text-2);
  white-space: nowrap;
}
.scale-row-inputs {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
}
.scale-input {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.82rem;
  padding: 0.3rem 0.55rem;
  width: 90px;
  outline: none;
  transition: border-color var(--ease);
  -moz-appearance: textfield;
}
.scale-input::-webkit-inner-spin-button,
.scale-input::-webkit-outer-spin-button { -webkit-appearance: none; }
.scale-input:focus { border-color: var(--border-hi); }
.scale-input-unit {
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--text-2);
  letter-spacing: 0.04em;
}
.scale-select {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.78rem;
  padding: 0.3rem 0.5rem;
  outline: none;
  cursor: pointer;
  transition: border-color var(--ease);
  max-width: 180px;
}
.scale-select:focus { border-color: var(--border-hi); }

.clear-scale-btn {
  font-family: var(--mono);
  font-size: 0.67rem;
  letter-spacing: 0.08em;
  color: var(--text-2);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.22rem 0.65rem;
  cursor: pointer;
  transition: color var(--ease), border-color var(--ease);
  align-self: flex-start;
  margin-top: 0.2rem;
}
.clear-scale-btn:hover { color: var(--text); border-color: var(--border-hi); }
.clear-scale-btn.hidden { display: none; }

/* Warning banners — Advisory (ochre), not red: these are scaling/batch-size
   uncertainty notices, not destructive actions. Recolored from red to
   --advisory 2026-07-26 to match the locked semantic color rule. */
.warnings-block {
  border-left: 3px solid var(--advisory);
  background: var(--advisory-dim);
  padding: 0.75rem 1.1rem;
  margin: 0 1.5rem 0;
  border-radius: 0 var(--radius) var(--radius) 0;
  display: none;
}
.warnings-block.visible {
  display: block;
  margin-top: 1rem;
}
.warnings-block ul {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.warnings-block li {
  font-family: var(--sans);
  font-size: 0.82rem;
  color: var(--advisory);
  line-height: 1.4;
}
.warnings-block li::before {
  content: '⚠ ';
  font-size: 0.75rem;
}

/* ===== MOBILE LAYOUT TOGGLE =====
   Hidden on desktop entirely: above 600px the page is always two-column, so
   the choice is meaningless there. Shown only in the mobile media query
   below. Neutral styling on purpose — this is a viewing preference, not
   scaling state, so it must not reach for --accent beyond the active pill. */
.layout-toggle {
  display: none;
  gap: 0;
  margin: 1rem 1.5rem 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  width: fit-content;
}
.layout-btn {
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.3rem 0.75rem;
  background: transparent;
  border: none;
  color: var(--text-3);
  cursor: pointer;
}
.layout-btn + .layout-btn { border-left: 1px solid var(--border); }
.layout-btn.active { color: var(--accent); background: var(--accent-dim); }

/* Tab strip — only rendered visible in tabbed mode on mobile. */
.panel-tabs { display: none; }
.panel-tab {
  flex: 1;
  padding: 0.55rem;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-3);
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  cursor: pointer;
}
.panel-tab.active { color: var(--accent); border-bottom-color: var(--accent); }

/* Main recipe body */
.recipe-body {
  display: grid;
  grid-template-columns: 320px 1fr;
  align-items: start;
  border-top: 1px solid var(--border);
  margin-top: 1rem;
}

/* Ingredients panel */
.ingredients-panel {
  border-right: 1px solid var(--border);
  padding: 1.25rem 1.5rem;
  position: sticky;
  top: 53px; /* header height */
  max-height: calc(100vh - 53px);
  overflow-y: auto;
}
.panel-heading {
  font-family: var(--mono);
  font-size: 0.67rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-2);
  margin-bottom: 1rem;
}

/* Individual ingredient row */
.ingredient-row {
  display: flex;
  flex-direction: column;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--border);
}
.ingredient-row:last-child { border-bottom: none; }

.ingredient-top {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.15rem;
}
.ingredient-name {
  font-family: var(--sans);
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--text);
  line-height: 1.4;
}
.ingredient-amount {
  font-family: var(--mono);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text);
  flex-shrink: 0;
}
.ingredient-amount.scaled {
  color: var(--accent);
}
.ingredient-flag {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  padding: 0.1rem 0.32rem;
  border-radius: 2px;
  flex-shrink: 0;
  vertical-align: middle;
}
.ingredient-flag.capped {
  /* Note: scaling_behavior "cautious"/capped was retired from the schema
     (see PROJECT.md) — this class is currently unreachable dead CSS, kept
     only so nothing breaks if it resurfaces. Not worth a dedicated token. */
  color: var(--accent);
  background: var(--accent-dim);
  border: 1px solid var(--accent);
}
.ingredient-flag.fixed {
  color: var(--text-3);
  border: 1px solid var(--border);
}
.ingredient-gram {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--text);
  white-space: nowrap;
  flex-shrink: 0;
}
.ingredient-gram.scaled {
  color: var(--accent);
}
.ing-amount-group {
  display: flex;
  align-items: baseline;
  gap: 0.35rem;
  flex-wrap: wrap;
}

.ingredient-note {
  font-size: 0.78rem;
  color: var(--text-2);
  margin-top: 0.25rem;
  line-height: 1.4;
}

/* Instructions panel */
.instructions-panel {
  padding: 1.25rem 1.5rem;
}

.step {
  display: grid;
  grid-template-columns: 2.5rem 1fr;
  gap: 0.5rem;
  padding: 0.7rem 0;
  border-bottom: 1px solid var(--border);
}
.step:last-child { border-bottom: none; }

/* Step numbers in italic serif per the locked v2 direction ("serif for
   recipe titles, collection titles, step numbers"). */
.step-num {
  font-family: var(--serif);
  font-style: italic;
  font-size: 1rem;
  color: var(--text-3);
  padding-top: 0.1rem;
  text-align: right;
}
.step-body {}
.step-text {
  font-family: var(--sans);
  font-size: 0.98rem;
  line-height: 1.6;
  color: var(--text);
}
.step-text .ing-ref {
  color: var(--text);
  font-weight: 500;
}
.ing-ref-amount {
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--text-2);
  margin-left: 0.2rem;
  white-space: nowrap;
}
.step-flags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin-top: 0.4rem;
}
.step-flag {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  color: var(--text-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 2px;
  padding: 0.1rem 0.35rem;
}

/* Error / loading state */
.recipe-error {
  padding: 3rem 1.5rem;
  font-family: var(--mono);
  font-size: 0.82rem;
  color: var(--error);
}

/* ===== RESPONSIVE ===== */
@media (max-width: 600px) {
  .site-header,
  .controls {
    padding-left: 1rem;
    padding-right: 1rem;
  }

  /* "REYES FAMILY RECIPES" is much longer than the old "RECIPES" —
     shrink size/tracking so it doesn't force the header into overflow
     or wrapping against header-right's badges/toggle at narrow widths. */
  .wordmark {
    font-size: 0.68rem;
    letter-spacing: 0.12em;
  }

  .recipe-grid {
    grid-template-columns: 1fr;
  }

  .search-input {
    max-width: 100%;
  }

  .back-link,
  .recipe-header,
  .drawer-inner,
  .warnings-block,
  .ingredients-panel,
  .instructions-panel {
    padding-left: 1rem;
    padding-right: 1rem;
  }

  .warnings-block { margin-left: 1rem; margin-right: 1rem; }

  .recipe-body {
    grid-template-columns: 1fr;
  }

  .ingredients-panel {
    border-right: none;
    border-bottom: 1px solid var(--border);
    position: static;
    max-height: none;
  }

  /* The layout choice only exists on phones — see .layout-toggle above. */
  .layout-toggle { display: flex; margin-left: 1rem; margin-right: 1rem; }

  /* ── Tabbed mode ──────────────────────────────────────────────────────
     Only one panel is in the document flow at a time. Each panel keeps its
     own scroll position for free, because switching tabs doesn't scroll the
     page — that's the whole ergonomic argument for this mode: re-checking an
     amount mid-step is one tap out and one tap back, landing where you were.

     The per-panel headings are redundant once the tab strip names them, so
     they're hidden in this mode only. */
  .recipe-body.tabbed .panel-tabs {
    display: flex;
    border-bottom: 1px solid var(--border);
  }
  .recipe-body.tabbed .ingredients-panel,
  .recipe-body.tabbed .instructions-panel { display: none; }
  .recipe-body.tabbed .ingredients-panel { display: block; }
  .recipe-body.tabbed.show-instructions .ingredients-panel { display: none; }
  .recipe-body.tabbed.show-instructions .instructions-panel { display: block; }

  .recipe-body.tabbed .ingredients-panel {
    border-bottom: none;
  }
  .recipe-body.tabbed > .ingredients-panel > .panel-heading,
  .recipe-body.tabbed > .instructions-panel > .panel-heading {
    display: none;
  }
}

.recipe-notes {
  padding: 1.25rem 1.5rem;
  border-top: 1px solid var(--border);
}
.recipe-source {
  padding: 0.75rem 1.5rem;
  border-top: 1px solid var(--border);
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--text-2);
}
.recipe-source a {
  color: var(--text-2);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.recipe-source a:hover {
  color: var(--text);
}
.source-photos {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.6rem;
}
.source-photo-link {
  display: block;
  line-height: 0;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--border);
  transition: border-color var(--ease);
}
.source-photo-link:hover { border-color: var(--border-hi); }
.source-photo-thumb {
  display: block;
  width: 76px;
  height: 76px;
  object-fit: cover;
}

/* ===== IN-PLACE EDITING (js/edit.js) =====
   Added 2026-08-01, replacing the modal edit-form block that briefly lived
   here. Family editing happens ON the recipe page rather than in an overlay,
   so most of this decorates elements js/app.js already rendered
   (.ingredient-row, .step, .recipe-title) rather than introducing a parallel
   set of components.

   --error is used for save failures and conflicts (genuine error states);
   --advisory stays reserved for scaling/batch uncertainty, and --accent for
   scaling state plus the unsaved-changes affordance, which is the same
   semantic: something is actively modifying what you're looking at. */

/* Entry point — deliberately at the bottom of the recipe and deliberately
   quiet. Editing is a considered act, not an always-present affordance. */
.edit-entry {
  margin: 2.5rem 1.5rem 3rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--border);
  text-align: center;
}
.edit-entry-btn {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  background: transparent;
  border: 1px solid var(--border-hi);
  border-radius: var(--radius);
  color: var(--text-2);
  padding: 0.5rem 1.1rem;
  cursor: pointer;
  transition: border-color var(--ease), color var(--ease);
}
.edit-entry-btn:hover { border-color: var(--accent); color: var(--accent); }
.edit-entry-note {
  font-family: var(--sans);
  font-size: 0.75rem;
  color: var(--text-3);
  margin-top: 0.55rem;
  line-height: 1.5;
}
.edit-exit {
  margin: 2.5rem 1.5rem 3rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--border);
  display: flex;
  gap: 0.6rem;
  justify-content: center;
  flex-wrap: wrap;
}
/* "Describe a change" — sits beside Cancel editing, visible to everyone.
   Uses --accent because a proposal is the same semantic as scaling state:
   something is actively modifying what you're looking at. */
.edit-describe-btn {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  background: transparent;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  color: var(--accent);
  padding: 0.5rem 0.9rem;
  cursor: pointer;
  transition: background var(--ease);
}
.edit-describe-btn:hover { background: var(--accent-dim); }

/* Rows a proposal touched. Deliberately louder than .edit-changed — a change
   you made yourself needs a reminder, a change something else made for you
   needs to be looked at. Clears when the row is opened. */
.edit-proposed {
  background: var(--accent-dim);
  box-shadow: inset 3px 0 0 var(--accent);
}
.edit-proposed::before {
  content: 'review';
  position: absolute;
  right: 1.6rem;
  top: 0.45rem;
  font-family: var(--mono);
  font-size: 0.58rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
}

.edit-advanced-link {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  background: transparent;
  border: 1px dashed var(--border-hi);
  border-radius: var(--radius);
  color: var(--text-3);
  padding: 0.5rem 0.9rem;
  cursor: pointer;
}
.edit-advanced-link:hover { color: var(--text-2); border-color: var(--text-3); }

/* Original read-only nodes that the editor replaces while in edit mode. */
.edit-hidden-original { display: none !important; }

/* Banners */
.edit-mode-banner {
  display: block;
  margin: 0.85rem 1.5rem 0;
  padding: 0.55rem 0.8rem;
  border-left: 3px solid var(--advisory);
  background: var(--advisory-dim);
  font-family: var(--sans);
  font-size: 0.78rem;
  color: var(--advisory);
  line-height: 1.45;
}
.edit-banner {
  margin: 0.85rem 1.5rem 0;
  padding: 0.6rem 0.85rem;
  border-left: 3px solid var(--error);
  background: var(--error-dim);
  font-family: var(--sans);
  font-size: 0.8rem;
  color: var(--error);
  line-height: 1.45;
}
.edit-banner-info {
  border-left-color: var(--accent);
  background: var(--accent-dim);
  color: var(--accent);
}

/* Tappable rows. The pencil only appears on hover/focus so the reading view
   in edit mode still reads as a recipe rather than a form. */
.edit-tappable {
  position: relative;
  cursor: pointer;
  border-radius: var(--radius);
  transition: background var(--ease);
}
.edit-tappable:hover { background: var(--surface-hi); }
/* The pencil is always visible in edit mode, not hover-only. A phone has no
   hover, so a hover-only affordance meant a first-time user saw a recipe that
   looked unchanged and no hint that tapping did anything. Faint at rest so
   fifteen of them don't shout; brightens on hover where hover exists. */
.edit-tappable::after {
  content: '✎';
  position: absolute;
  right: 0.45rem;
  top: 0.4rem;
  font-size: 0.72rem;
  color: var(--text-3);
  opacity: 0.4;
  transition: opacity var(--ease);
}
.edit-tappable:hover::after { opacity: 1; }
.edit-changed { box-shadow: inset 3px 0 0 var(--accent); }
.edit-placeholder { color: var(--text-3); font-style: italic; }

/* Inline expand container */
.edit-inline {
  margin: 0.3rem 0 0.6rem;
  padding: 0.75rem 0.85rem;
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-radius: var(--radius);
}
.edit-inline.panel-editor { margin: 0.3rem 1.5rem 0.6rem; }
.edit-form { display: flex; flex-direction: column; gap: 0.55rem; }
.edit-row-fields { display: flex; gap: 0.55rem; flex-wrap: wrap; }
.edit-fld { flex: 1; min-width: 96px; }
.edit-fld label {
  display: block;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.07em;
  color: var(--text-3);
  text-transform: uppercase;
  margin-bottom: 0.22rem;
}
.edit-fld input,
.edit-fld select,
.edit-form textarea {
  width: 100%;
  box-sizing: border-box;
  background: var(--bg);
  border: 1px solid var(--border-hi);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--sans);
  font-size: 0.88rem;
  padding: 0.38rem 0.5rem;
  outline: none;
  transition: border-color var(--ease);
}
.edit-form textarea { min-height: 4.4rem; resize: vertical; }
.edit-fld input:focus,
.edit-fld select:focus,
.edit-form textarea:focus { border-color: var(--accent); }
.edit-acts { display: flex; gap: 0.45rem; margin-top: 0.2rem; flex-wrap: wrap; }
.edit-btn {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  padding: 0.35rem 0.85rem;
  border-radius: var(--radius);
  cursor: pointer;
  border: 1px solid var(--border-hi);
  background: transparent;
  color: var(--text-2);
  transition: border-color var(--ease), color var(--ease), opacity var(--ease);
}
.edit-btn:hover:not(:disabled) { color: var(--text); border-color: var(--text-2); }
.edit-btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg);
  font-weight: 600;
}
.edit-btn.primary:hover:not(:disabled) { opacity: 0.88; color: var(--bg); }
.edit-btn.primary:disabled { opacity: 0.4; cursor: not-allowed; }
.edit-btn.danger { color: var(--error); border-color: var(--error); }
.edit-add-btn {
  width: 100%;
  margin-top: 0.5rem;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  background: transparent;
  border: 1px dashed var(--border-hi);
  border-radius: var(--radius);
  color: var(--text-2);
  padding: 0.45rem 0.8rem;
  cursor: pointer;
  transition: border-color var(--ease), color var(--ease);
}
.edit-add-btn:hover { border-color: var(--accent); color: var(--accent); }

/* Time editor. Storage is minutes; the echo shows what that means so nobody
   has to work out that 210 is three and a half hours. Total is a read-out,
   never an input — it's derived from the other three. */
.edit-time-group { margin-bottom: 0.7rem; }
.edit-sublabel {
  display: block;
  font-family: var(--sans);
  font-size: 0.82rem;
  color: var(--text);
  margin-bottom: 0.3rem;
}
.edit-echo {
  display: block;
  font-family: var(--mono);
  font-size: 0.66rem;
  color: var(--accent);
  margin-top: 0.2rem;
}
.edit-hint {
  font-family: var(--sans);
  font-size: 0.72rem;
  color: var(--text-3);
  line-height: 1.45;
  margin-top: 0.25rem;
}
.edit-total {
  margin-top: 0.3rem;
  padding: 0.5rem 0.65rem;
  background: var(--accent-dim);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-family: var(--sans);
  font-size: 0.8rem;
  color: var(--text-2);
}
.edit-total b { font-family: var(--mono); color: var(--accent); }

/* Category picker — each option carries its definition, so the choice is
   read rather than guessed. See PROJECT.md's Category Values. */
.edit-cat-list {
  display: flex;
  flex-direction: column;
  gap: 0.18rem;
  max-height: 16rem;
  overflow-y: auto;
}
.edit-cat-opt {
  display: flex;
  gap: 0.55rem;
  align-items: flex-start;
  padding: 0.4rem 0.5rem;
  border: 1px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
}
.edit-cat-opt:hover { background: var(--surface-hi); }
.edit-cat-opt.selected { border-color: var(--accent); background: var(--accent-dim); }
.edit-cat-opt .k {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--text);
  min-width: 5.2rem;
}
.edit-cat-opt.selected .k { color: var(--accent); }
.edit-cat-opt .d {
  font-family: var(--sans);
  font-size: 0.73rem;
  color: var(--text-3);
  line-height: 1.4;
}

/* Unsaved-changes bar */
.edit-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 75;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
  padding: 0.7rem 1.5rem;
  background: var(--surface);
  border-top: 1px solid var(--accent);
}
.edit-bar-txt {
  flex: 1;
  min-width: 9rem;
  font-family: var(--sans);
  font-size: 0.8rem;
  color: var(--text-2);
}
.edit-bar-txt b { font-family: var(--mono); font-size: 0.76rem; color: var(--accent); }

/* Clear the fixed bar's footprint so the page's own bottom controls
   ("Cancel editing", "Advanced fields") aren't sitting underneath it. The
   class is applied by renderBar() only while the bar exists, so the page
   doesn't carry dead space the rest of the time. Generous on narrow screens
   because the bar wraps to two lines there. */
body.has-unsaved #page-content { padding-bottom: 5rem; }

/* Save dialog */
.edit-modal-wrap {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  overflow-y: auto;
}
.edit-modal {
  width: min(28rem, 100%);
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-radius: var(--radius);
  padding: 1rem 1.1rem 1.2rem;
}
.edit-modal h3 {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-3);
  margin-bottom: 0.6rem;
}
.edit-modal-lead {
  font-family: var(--sans);
  font-size: 0.82rem;
  color: var(--text);
  margin-bottom: 0.3rem;
}
.edit-changed-list {
  margin: 0 0 0.8rem;
  padding: 0.55rem 0.7rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  list-style: none;
}
.edit-changed-list li {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--text-2);
  padding: 0.12rem 0;
}
.edit-changed-list li::before { content: '· '; color: var(--accent); }
.edit-modal textarea {
  width: 100%;
  box-sizing: border-box;
  min-height: 3.4rem;
  background: var(--bg);
  border: 1px solid var(--border-hi);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--sans);
  font-size: 0.86rem;
  padding: 0.45rem 0.55rem;
  resize: vertical;
  outline: none;
}
.edit-modal textarea:focus { border-color: var(--accent); }
.edit-modal-hint {
  font-family: var(--sans);
  font-size: 0.74rem;
  color: var(--text-3);
  margin-top: 0.35rem;
  line-height: 1.45;
}
.edit-modal-err {
  margin-top: 0.7rem;
  padding: 0.55rem 0.7rem;
  border-left: 3px solid var(--error);
  background: var(--error-dim);
  font-family: var(--sans);
  font-size: 0.8rem;
  color: var(--error);
  line-height: 1.5;
}

@media (max-width: 600px) {
  .edit-entry, .edit-exit { margin-left: 1rem; margin-right: 1rem; }
  .edit-mode-banner, .edit-banner { margin-left: 1rem; margin-right: 1rem; }
  .edit-inline.panel-editor { margin-left: 1rem; margin-right: 1rem; }
  .edit-bar { padding: 0.7rem 1rem; }
  body.has-unsaved #page-content { padding-bottom: 7rem; }
}

/* "Will read as …" — the resulting recipe line, shown live under the amount
   and unit fields. Read-only on purpose: it exists so nothing is rewritten
   out of sight, not as another thing to fill in. */
.edit-reads-as {
  font-family: var(--sans);
  font-size: 0.76rem;
  color: var(--text-3);
  margin: -0.1rem 0 0.1rem;
}
.edit-reads-as b {
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--text-2);
  font-weight: 600;
}

/* ===== EDIT HISTORY (js/app.js) =====
   "Updated by X — 'reason' — Aug 2", expanding to the full record. Quiet by
   default: provenance is reassuring to have but shouldn't compete with the
   recipe. Uses --text-3/--text-2 only — no --accent, which stays reserved for
   scaling state. */
.recipe-history { margin-top: 0.55rem; }
.history-summary {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
  width: 100%;
  background: transparent;
  border: none;
  padding: 0.15rem 0;
  cursor: pointer;
  text-align: left;
  font-family: var(--sans);
  font-size: 0.76rem;
  color: var(--text-3);
  transition: color var(--ease);
}
.history-summary:hover { color: var(--text-2); }
.history-line { flex: 1; min-width: 12rem; line-height: 1.45; }
.history-when { color: var(--text-3); }
.history-toggle {
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.06em;
  white-space: nowrap;
  color: var(--text-3);
}
.history-detail {
  margin-top: 0.5rem;
  padding-left: 0.75rem;
  border-left: 1px solid var(--border);
}
.history-entry { padding: 0.4rem 0; }
.history-entry + .history-entry { border-top: 1px solid var(--border); }
.history-entry-head {
  font-family: var(--sans);
  font-size: 0.76rem;
  color: var(--text-2);
  line-height: 1.45;
}
.history-fields { list-style: none; margin-top: 0.25rem; }
.history-fields li {
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--text-3);
  line-height: 1.6;
}
.history-fields li::before { content: '· '; }

/* ===== ARCHIVE =====
   Archiving is a family action; restoring is admin-only (2026-08-02). The
   archive button is styled as a quiet, slightly cautionary control rather
   than a destructive one — nothing is deleted, it's a visibility flag — but
   it's the one action on this page most people can't undo themselves, so it
   shouldn't look like the others. --advisory, not --error: this is
   uncertainty, not a failure. */
.edit-archive-btn {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  background: transparent;
  border: 1px solid var(--advisory);
  border-radius: var(--radius);
  color: var(--advisory);
  padding: 0.5rem 0.9rem;
  cursor: pointer;
  transition: background var(--ease), opacity var(--ease);
}
.edit-archive-btn:hover:not(:disabled) { background: var(--advisory-dim); }
.edit-archive-btn:disabled { opacity: 0.35; cursor: not-allowed; }

/* Shown on an archived recipe opened directly — it's still readable by URL,
   so "why isn't this in the list?" needs an answer. */
.archived-notice {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
  margin: 0.85rem 1.5rem 0;
  padding: 0.6rem 0.85rem;
  border-left: 3px solid var(--advisory);
  background: var(--advisory-dim);
  font-family: var(--sans);
  font-size: 0.8rem;
  color: var(--advisory);
  line-height: 1.45;
}
.archived-notice span { flex: 1; min-width: 12rem; }

/* Admin archived browser (index.html?archived=1) */
.archived-mode-banner {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
  margin: 0 1.5rem 0.9rem;
  padding: 0.6rem 0.85rem;
  border-left: 3px solid var(--advisory);
  background: var(--advisory-dim);
  font-family: var(--sans);
  font-size: 0.8rem;
  color: var(--advisory);
}
.archived-mode-banner span { flex: 1; min-width: 12rem; }
.archived-mode-banner a,
.archived-link a {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  color: var(--text-2);
  text-decoration: none;
  white-space: nowrap;
}
.archived-mode-banner a:hover, .archived-link a:hover { color: var(--text); }
.archived-link {
  margin: 1.5rem 1.5rem 2.5rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
  text-align: center;
}

@media (max-width: 600px) {
  .archived-notice, .archived-mode-banner { margin-left: 1rem; margin-right: 1rem; }
  .archived-link { margin-left: 1rem; margin-right: 1rem; }
}

/* ── Submission review (submit.html / js/submission.js) ──────────────────────
   Added 2026-08-02. Deliberately reuses the .edit-* classes above wholesale
   (edit-fld, edit-form, edit-btn, edit-add-btn, edit-modal*, edit-cat-*) —
   this page mirrors the family edit form's layout and visual language on
   purpose (PROJECT.md, "Scraper submission review screen"), it just isn't
   literally recipe.html, since a not-yet-published submission has no id/
   version for the edit path's write-freshness check to hang off of. Only
   what's genuinely new to this page — the source picker and confidence
   highlighting — gets new classes here. */

.submission-panel {
  max-width: 42rem;
  margin: 0 auto;
  padding: 1.5rem 1.5rem 3rem;
}
.submission-intro {
  font-family: var(--sans);
  font-size: 0.86rem;
  color: var(--text-2);
  line-height: 1.55;
  margin-bottom: 1.2rem;
}

/* The one line on the whole review screen that actually matters, so it's
   styled to look like it (Jacob, 2026-08-03: needed to "really stick out" —
   the plain-paragraph version was too easy to skim past). Bigger and bolder
   than .similarity-banner/.escalation-banner on purpose, and the only place
   on this page --advisory is used as a TEXT color rather than just a border/
   background accent — same token as the confidence flags this instruction
   is telling you to go check, so the color itself reinforces the message
   rather than being decoration. */
.review-instruction {
  margin: 0 0 1.3rem;
  padding: 0.9rem 1.1rem;
  border-left: 4px solid var(--advisory);
  background: var(--advisory-dim);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-family: var(--sans);
  font-size: 1.02rem;
  font-weight: 700;
  color: var(--advisory);
  line-height: 1.45;
}
.review-instruction-sub {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.78rem;
  font-weight: 400;
  color: var(--text-2);
}

.source-tabs { display: flex; gap: 0.4rem; margin-bottom: 0.9rem; }
.source-tab {
  flex: 1;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  text-align: center;
  padding: 0.5rem 0.4rem;
  border: 1px solid var(--border-hi);
  border-radius: var(--radius);
  background: transparent;
  color: var(--text-2);
  cursor: pointer;
}
.source-tab.selected { border-color: var(--accent); color: var(--accent); background: var(--accent-dim); }

.photo-drop {
  border: 1px dashed var(--border-hi);
  border-radius: var(--radius);
  padding: 1rem;
  text-align: center;
  font-family: var(--sans);
  font-size: 0.8rem;
  color: var(--text-3);
}
/* Real image previews via data: URLs (functions/_middleware.js's CSP widened
   to img-src 'self' data: for exactly this — blob: URLs were the first
   approach tried and aren't covered by 'self' either, see that file's
   comment). Each thumbnail carries its own remove button since photos
   accumulate across multiple picker invocations (js/submission.js's
   onPhotosChosen) rather than being replaced. */
.photo-thumbs { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.6rem; }
.photo-thumb {
  position: relative;
  width: 4.5rem;
  height: 4.5rem;
}
.photo-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  display: block;
}
.photo-thumb-remove {
  position: absolute;
  top: -0.4rem;
  right: -0.4rem;
  width: 1.3rem;
  height: 1.3rem;
  line-height: 1;
  border-radius: 50%;
  border: 1px solid var(--border-hi);
  background: var(--surface);
  color: var(--text-2);
  font-family: var(--sans);
  font-size: 0.85rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.photo-thumb-remove:hover { color: var(--error); border-color: var(--error); }

/* Extraction loading state — a magnifying glass sliding over squiggled
   "handwriting" lines, plus a rotating status line. Not a progress bar —
   there's no real progress signal for a single request to tie a bar to (see
   js/submission.js's renderExtractLoading() comment). Chosen over a plain
   spinner after mocking up a few options with Jacob (2026-08-03); picked for
   literally acting out "reading the card". Neutral colors on purpose:
   --accent is reserved for scaling UI/scaled values only and --advisory for
   scaling/confidence uncertainty, neither of which this is — the lines use
   --text-3 (faint, "the card"), the glass uses --text-2 (the thing doing the
   reading), same hierarchy as everywhere else neutral text is layered.
   Same reasoning ruled out the brighter reference Jacob linked (a Flaticon
   pot icon in pink/navy/coral) — proceeding with it as-is would have been
   the same mistake the family badge's --accent misuse already was and got
   corrected for. */
.extract-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  padding: 3rem 1rem;
  text-align: center;
}
.extract-glass {
  width: 90px;
  height: 60px;
}
.extract-glass-lines path {
  stroke: var(--text-3);
  stroke-width: 2;
  fill: none;
  stroke-linecap: round;
}
.extract-glass-scope {
  transform-origin: 45px 30px;
  animation: extract-glass-move 2.2s ease-in-out infinite;
}
.extract-glass-scope circle {
  fill: none;
  stroke: var(--text-2);
  stroke-width: 2.5;
}
.extract-glass-scope line {
  stroke: var(--text-2);
  stroke-width: 3;
  stroke-linecap: round;
}
@keyframes extract-glass-move {
  0%, 100% { transform: translateX(-14px) rotate(-5deg); }
  50% { transform: translateX(14px) rotate(5deg); }
}
@media (prefers-reduced-motion: reduce) {
  .extract-glass-scope { animation-duration: 4s; }
}
.extract-loading-word {
  font-family: var(--sans);
  font-size: 0.95rem;
  color: var(--text);
  min-height: 1.4em;
}

/* Confidence highlighting — stays inside --advisory on purpose (PROJECT.md
   locks --error to genuine error states and destructive actions; a data-
   quality flag is neither). Two tiers differentiated by WEIGHT, not a
   second color: medium gets a light left-border treatment, low gets the
   same ochre filled solid, same idea as .warnings-banner elsewhere on the
   site but scoped to a single field instead of the whole recipe. */
.confidence-field { position: relative; border-radius: var(--radius); padding: 0.3rem 0.5rem; margin: -0.3rem -0.5rem 0.15rem; }
.confidence-field.confidence-medium { border-left: 3px solid var(--advisory); background: var(--advisory-dim); }
.confidence-field.confidence-low {
  border-left: 3px solid var(--advisory);
  background: var(--advisory);
  background: color-mix(in srgb, var(--advisory) 22%, var(--advisory-dim));
}
.confidence-field.confidence-low .confidence-note,
.confidence-field.confidence-medium .confidence-note { display: block; }
.confidence-note {
  display: none;
  font-family: var(--sans);
  font-size: 0.72rem;
  color: var(--advisory);
  margin-top: 0.2rem;
  line-height: 1.4;
}
.confidence-badge {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--advisory);
  border: 1px solid var(--advisory);
  border-radius: var(--radius);
  padding: 0.05rem 0.35rem;
  margin-left: 0.4rem;
}

.similarity-banner, .escalation-banner {
  margin: 0 0 1rem;
  padding: 0.7rem 0.9rem;
  border-left: 3px solid var(--advisory);
  background: var(--advisory-dim);
  font-family: var(--sans);
  font-size: 0.84rem;
  color: var(--text);
  line-height: 1.5;
  border-radius: 0 var(--radius) var(--radius) 0;
}

.submission-success {
  padding: 1rem 1.1rem;
  border-left: 3px solid var(--accent);
  background: var(--accent-dim);
  font-family: var(--sans);
  font-size: 0.88rem;
  color: var(--text);
  border-radius: 0 var(--radius) var(--radius) 0;
}

@media (max-width: 600px) {
  .submission-panel { padding: 1.2rem 1rem 2.5rem; }
}

/* ===== ADMIN DASHBOARD (js/admin.js) =====
   Read-only rollup, admin.html only. Dense/quiet by default — --text-2/-3
   and --mono for metadata, same idiom as the recipe history block. --advisory
   marks rows/cards worth a look (never-logged-in family members, unreviewed
   submissions with untouched flags, open escalations) — never --accent,
   which stays reserved for scaling UI per the site-wide color rule. */

.admin-dashboard {
  max-width: 56rem;
  margin: 0 auto;
  padding: 1.5rem 1.25rem 4rem;
}

.admin-title {
  font-family: var(--sans);
  font-size: 1.3rem;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 1.5rem;
}

.admin-section { margin-bottom: 2.25rem; }

.admin-section-title {
  font-family: var(--sans);
  font-size: 0.86rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--text-2);
  text-transform: uppercase;
  border-bottom: 1px solid var(--border);
  padding-bottom: 0.4rem;
  margin-bottom: 0.8rem;
}

.admin-count-badge {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--advisory);
  border: 1px solid var(--advisory);
  border-radius: var(--radius);
  padding: 0.02rem 0.4rem;
  vertical-align: middle;
}

.admin-empty {
  font-family: var(--sans);
  font-size: 0.84rem;
  color: var(--text-3);
  font-style: italic;
}

.admin-empty-cell {
  color: var(--text-3);
  font-style: italic;
}

.admin-mono {
  font-family: var(--mono);
  font-size: 0.76rem;
  color: var(--text-2);
}

/* Tables — roster, review queue */
.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--sans);
  font-size: 0.82rem;
}
.admin-table th {
  text-align: left;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-3);
  padding: 0.35rem 0.6rem;
  border-bottom: 1px solid var(--border-hi);
}
.admin-table td {
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid var(--border);
  color: var(--text-2);
  vertical-align: top;
}
.admin-table tr:last-child td { border-bottom: none; }
.admin-table a { color: var(--text); text-decoration: none; }
.admin-table a:hover { text-decoration: underline; }
.admin-row-advisory td:first-child { box-shadow: inset 3px 0 0 var(--advisory); }

/* Escalation cards */
.admin-card {
  padding: 0.8rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: 0.7rem;
  background: var(--surface);
}
.admin-card-advisory { border-left: 3px solid var(--advisory); background: var(--advisory-dim); }
.admin-card-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.6rem;
  flex-wrap: wrap;
}
.admin-card-title { font-family: var(--sans); font-weight: 600; color: var(--text); font-size: 0.9rem; }
.admin-card-when { font-family: var(--mono); font-size: 0.7rem; color: var(--text-3); }
.admin-card-meta {
  font-family: var(--sans);
  font-size: 0.78rem;
  color: var(--text-3);
  margin-top: 0.25rem;
}
.admin-match-list {
  margin-top: 0.5rem;
  padding-left: 1.1rem;
  font-family: var(--sans);
  font-size: 0.8rem;
  color: var(--text-2);
  line-height: 1.5;
}

/* Activity feed */
.admin-activity-list { list-style: none; }
.admin-activity-item {
  font-family: var(--sans);
  font-size: 0.82rem;
  color: var(--text-2);
  line-height: 1.7;
  padding: 0.35rem 0;
  border-bottom: 1px solid var(--border);
}
.admin-activity-item:last-child { border-bottom: none; }
.admin-activity-item a { color: var(--text); text-decoration: none; }
.admin-activity-item a:hover { text-decoration: underline; }
.admin-activity-verb { color: var(--text-3); }
.admin-role-tag {
  color: var(--text-3);
  border: 1px solid var(--border-hi);
  border-radius: var(--radius);
  padding: 0.01rem 0.3rem;
  margin: 0 0.15rem;
}
.admin-activity-when {
  float: right;
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--text-3);
}

/* Audit backlog */
.admin-plain-list { list-style: none; font-family: var(--sans); font-size: 0.84rem; }
.admin-plain-list li { padding: 0.25rem 0; }
.admin-plain-list a { color: var(--text); text-decoration: none; }
.admin-plain-list a:hover { text-decoration: underline; }
.admin-more-note { font-family: var(--mono); font-size: 0.72rem; color: var(--text-3); margin-top: 0.4rem; }

/* Collection health */
.admin-stat-row { display: flex; gap: 1.75rem; flex-wrap: wrap; }
.admin-stat { display: flex; flex-direction: column; gap: 0.15rem; }
.admin-stat-value { font-family: var(--mono); font-size: 1.4rem; color: var(--text); }
.admin-stat-label { font-family: var(--sans); font-size: 0.72rem; color: var(--text-3); }

.admin-table-wrap { overflow-x: auto; }

@media (max-width: 600px) {
  .admin-dashboard { padding: 1.1rem 0.9rem 3rem; }
  .admin-activity-when { float: none; display: block; margin-top: 0.15rem; }
  .admin-table { font-size: 0.76rem; }
  .admin-table th, .admin-table td { white-space: nowrap; }
}
