/* =========================================================================
   NEO ALMANAK — DESIGN SYSTEM
   Editorial dark. Warm black + champagne hairlines.
   ========================================================================= */

/* ---------- DESIGN TOKENS ----------
   The --ink-* scale runs from "deepest background" (--ink-900) to
   "brightest text" (--ink-0). In light mode the entire scale flips —
   --ink-900 becomes cream paper and --ink-0 becomes deep ink — so any
   selector using these tokens auto-themes without further work.
   Gold accents stay roughly the same (champagne reads on both surfaces).
*/

/* Default = dark (also covers explicit data-theme="dark") */
:root,
[data-theme="dark"] {
  --ink-900: #0a0908;      /* deepest background */
  --ink-850: #100d0b;      /* page base */
  --ink-800: #14110f;      /* elevated surface */
  --ink-700: #1c1815;      /* card */
  --ink-600: #241f1a;      /* raised card */
  --ink-500: #2a251f;      /* hairline borders */
  --ink-400: #3a3328;      /* strong borders */
  --ink-300: #544b42;      /* dim text */
  --ink-200: #8a8178;      /* muted text */
  --ink-100: #c4b9ac;      /* soft text */
  --ink-50:  #e8e2d8;      /* paper text */
  --ink-0:   #f5efe4;      /* brightest paper */

  /* Theme-tinted overlays — same role, theme-aware values. The shade
     is what dims popovers/modals; in dark mode it's near-black, in light
     mode it's a warm-translucent cream darkening toward ink. */
  --shade-overlay: rgba(10, 9, 8, 0.4);
  --shade-overlay-strong: rgba(0, 0, 0, 0.55);
  --surface-glass: rgba(20, 17, 15, 0.92);     /* translucent floating surface (nav, sticky bars) */
  --sheet-bg: rgba(20, 17, 15, 0.7);            /* modal sheet backing */

  /* Accents — gold reads on both modes */
  --gold-dim:    #7a6a4c;
  --gold:        #c9a876;  /* champagne — sparingly used */
  --gold-bright: #e4c896;
  --gold-glow:   rgba(201, 168, 118, 0.18);
  --on-gold:     #0a0908;  /* text on gold-filled surfaces */

  --color-scheme: dark;

  /* Semantic */
  --ok:    #7a9178;
  --warn:  #c4935b;
  --due:   #d97757;
  --miss:  #9a5549;
}

/* Light mode — warm paper instead of warm dark */
[data-theme="light"] {
  --ink-900: #f5efe4;      /* paper background */
  --ink-850: #ede5d4;      /* page base */
  --ink-800: #e3d9c4;      /* elevated surface */
  --ink-700: #d8ccb3;      /* card */
  --ink-600: #ccbf9f;      /* raised card */
  --ink-500: #b8a87f;      /* hairline borders */
  --ink-400: #8a7a5a;      /* strong borders */
  --ink-300: #6b5d44;      /* dim text */
  --ink-200: #4a3f2e;      /* muted text */
  --ink-100: #2c241c;      /* soft text */
  --ink-50:  #14110f;      /* near-black */
  --ink-0:   #0a0908;      /* deepest ink */

  --shade-overlay: rgba(60, 48, 32, 0.18);
  --shade-overlay-strong: rgba(40, 28, 16, 0.45);
  --surface-glass: rgba(245, 239, 228, 0.92);
  --sheet-bg: rgba(245, 239, 228, 0.94);

  /* Gold tuned slightly richer on cream — pure --gold has weak contrast
     against light surfaces, so we deepen it for accents/text but keep
     the hue. Borders/glow can use the same alphas; the cream backdrop
     makes them subtler in a useful way. */
  --gold-dim:    #8a6e3e;
  --gold:        #a98a4f;  /* darker champagne for contrast on cream */
  --gold-bright: #6f5826;
  --gold-glow:   rgba(138, 110, 62, 0.18);
  --on-gold:     #0a0908;

  --color-scheme: light;

  /* Semantic — slightly tuned for readable on cream */
  --ok:    #4a6a48;
  --warn:  #8c5c2a;
  --due:   #a14a2a;
  --miss:  #6a3a30;
}

/* Auto = follow OS preference */
@media (prefers-color-scheme: light) {
  [data-theme="auto"] {
    --ink-900: #f5efe4;
    --ink-850: #ede5d4;
    --ink-800: #e3d9c4;
    --ink-700: #d8ccb3;
    --ink-600: #ccbf9f;
    --ink-500: #b8a87f;
    --ink-400: #8a7a5a;
    --ink-300: #6b5d44;
    --ink-200: #4a3f2e;
    --ink-100: #2c241c;
    --ink-50:  #14110f;
    --ink-0:   #0a0908;
    --shade-overlay: rgba(60, 48, 32, 0.18);
    --shade-overlay-strong: rgba(40, 28, 16, 0.45);
    --surface-glass: rgba(245, 239, 228, 0.92);
    --sheet-bg: rgba(245, 239, 228, 0.94);
    --gold-dim:    #8a6e3e;
    --gold:        #a98a4f;
    --gold-bright: #6f5826;
    --gold-glow:   rgba(138, 110, 62, 0.18);
    --on-gold:     #0a0908;
    --color-scheme: light;
    --ok:    #4a6a48;
    --warn:  #8c5c2a;
    --due:   #a14a2a;
    --miss:  #6a3a30;
  }
}

:root {
  color-scheme: var(--color-scheme, dark);

  /* Type */
  --font-display: 'Fraunces', 'Cormorant Garamond', Georgia, serif;
  --font-body:    'Hanken Grotesk', 'Söhne', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono:    'IBM Plex Mono', 'JetBrains Mono', ui-monospace, Consolas, monospace;

  /* Scale */
  --step--2: clamp(0.694rem, 0.691rem + 0.02vw, 0.703rem);
  --step--1: clamp(0.833rem, 0.822rem + 0.06vw, 0.875rem);
  --step-0:  clamp(1.000rem, 0.977rem + 0.12vw, 1.094rem);
  --step-1:  clamp(1.200rem, 1.156rem + 0.22vw, 1.367rem);
  --step-2:  clamp(1.440rem, 1.362rem + 0.39vw, 1.709rem);
  --step-3:  clamp(1.728rem, 1.595rem + 0.66vw, 2.136rem);
  --step-4:  clamp(2.074rem, 1.858rem + 1.08vw, 2.670rem);
  --step-5:  clamp(2.488rem, 2.148rem + 1.70vw, 3.338rem);
  --step-6:  clamp(2.986rem, 2.466rem + 2.60vw, 4.172rem);
  --step-7:  clamp(3.583rem, 2.813rem + 3.85vw, 5.215rem);

  /* Space */
  --space-3xs: 0.25rem;
  --space-2xs: 0.5rem;
  --space-xs:  0.75rem;
  --space-s:   1rem;
  --space-m:   1.5rem;
  --space-l:   2rem;
  --space-xl:  3rem;
  --space-2xl: 4.5rem;
  --space-3xl: 6.5rem;

  /* Motion */
  --ease-out-soft:  cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in-out:    cubic-bezier(0.65, 0, 0.35, 1);
  --dur-fast:   180ms;
  --dur-med:    360ms;
  --dur-slow:   560ms;

  /* Shell */
  --shell-max: 720px;
  --radius-s: 4px;
  --radius-m: 8px;
  --radius-l: 14px;
  --radius-xl: 22px;
}

/* ---------- RESET ---------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Belt-and-suspenders horizontal scroll guard — iOS Safari occasionally
     ignores `overflow-x: hidden` on body alone if a descendant overflows. */
  overflow-x: hidden;
  /* Tinted scrollbar — Firefox uses these two properties; WebKit
     browsers use the ::-webkit-scrollbar block below. Both fall back
     to gold-dim track + gold thumb for the page scroll, matching the
     hairlines and pills used throughout the app. */
  scrollbar-width: thin;
  scrollbar-color: var(--gold-dim) transparent;
}
/* WebKit (Chrome, Safari, Edge, mobile WebViews) */
html::-webkit-scrollbar,
body::-webkit-scrollbar { width: 8px; height: 8px; }
html::-webkit-scrollbar-track,
body::-webkit-scrollbar-track { background: transparent; }
html::-webkit-scrollbar-thumb,
body::-webkit-scrollbar-thumb {
  background: var(--gold-dim);
  border-radius: 999px;
  border: 2px solid var(--ink-900);  /* visual padding around the thumb */
  background-clip: padding-box;
}
html::-webkit-scrollbar-thumb:hover,
body::-webkit-scrollbar-thumb:hover { background: var(--gold); }
body {
  /* `vh` first as the universally supported baseline; `dvh` overrides on
     Safari 15.4+ / Chrome 108+ to avoid the address-bar jump. Older iOS
     ignores the dvh line cleanly, so the page still has full height. */
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--ink-900);
  color: var(--ink-50);
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: 400;
  line-height: 1.6;
  letter-spacing: -0.005em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}
img, svg, video { display: block; max-width: 100%; }
button { background: none; border: 0; color: inherit; font: inherit; padding: 0; cursor: pointer; }
input, select, textarea { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }
::selection { background: var(--gold); color: var(--on-gold); }

/* ---------- ATMOSPHERE ---------- */
body::before {
  /* Subtle warm radial glow behind everything */
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(1200px 800px at 80% -10%, rgba(201, 168, 118, 0.06), transparent 60%),
    radial-gradient(900px 700px at -10% 100%, rgba(201, 168, 118, 0.04), transparent 55%);
  z-index: 0;
}
body::after {
  /* Film grain for texture. mix-blend-mode forces a per-pixel blend on
     every paint of underlying content — measurable cost on iPhone PWA
     standalone where the WebView is more constrained than Safari proper.
     Off by default; restored on hover-capable / fine-pointer devices
     (desktop) where the GPU absorbs the cost without the page feeling
     sluggish. */
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  display: none;
}
@media (hover: hover) and (pointer: fine) {
  body::after {
    display: block;
    opacity: 0.035;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.5 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
    mix-blend-mode: overlay;
  }
}

/* ---------- TYPOGRAPHY ---------- */
.display {
  font-family: var(--font-display);
  font-weight: 300;
  font-style: italic;
  font-variation-settings: 'opsz' 144, 'SOFT' 100;
  letter-spacing: -0.035em;
  line-height: 1.05;            /* safe default — wraps without lines crashing */
}
.display--tight { line-height: 0.95; }   /* opt-in for hero headlines */
.overline {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-200);
  font-weight: 500;
}
.mono { font-family: var(--font-mono); letter-spacing: 0.02em; }
.caption {
  font-size: var(--step--1);
  color: var(--ink-200);
  line-height: 1.5;
}
.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--gold);
}
h1, h2, h3 { font-family: var(--font-display); font-weight: 400; margin: 0; letter-spacing: -0.025em; }

/* ---------- LAYOUT SHELL ---------- */
.shell {
  position: relative;
  z-index: 2;
  max-width: var(--shell-max);
  margin: 0 auto;
  padding: 0 var(--space-m) calc(var(--space-3xl) + env(safe-area-inset-bottom));
}
.view { padding-top: calc(var(--space-l) + env(safe-area-inset-top)); }
.hide { display: none !important; }

/* ---------- VIEW HEADER ---------- */
.view-head {
  padding: var(--space-l) 0 var(--space-s);
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}
.view-head__title {
  font-size: var(--step-5);
  margin: 0;
  letter-spacing: -0.03em;
  /* Long compound nouns (especially Danish — Børnevaccinationsprogram,
     Forebyggende undersøgelser) need permission to wrap on narrow
     phones. `overflow-wrap: anywhere` lets the browser break inside a
     single token when it has to; `hyphens: auto` adds rule-based break
     opportunities (and renders soft-hyphen U+00AD characters cleanly
     when our locale strings include them). */
  overflow-wrap: anywhere;
  hyphens: auto;
}
.view-head .caption { margin: 0; }
.view-head + .hairline { margin-top: var(--space-s); }

/* ---------- TOP BAR ---------- */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: calc(var(--space-s) + env(safe-area-inset-top)) var(--space-m) var(--space-s);
  /* Bumped solid stop from 70% → 92% so content reads cleanly when there
     is no backdrop-filter blur behind the topbar (touch devices below). */
  background: linear-gradient(to bottom, var(--ink-900) 92%, transparent);
}
/* backdrop-filter is reliable on desktop browsers but causes a known
   compositing bug on iOS standalone PWAs where the always-on sticky/
   fixed elements (topbar, nav, sheet-backdrop) intermittently render a
   full-viewport translucent black layer over the page (mother's iPhone
   QA report, May 2026). Restricting these blurs to hover-capable
   pointers keeps the frosted-glass look on desktop while phones and
   tablets fall back to solid translucent backgrounds. */
@media (hover: hover) and (pointer: fine) {
  .topbar {
    background: linear-gradient(to bottom, var(--ink-900) 70%, transparent);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
  }
}
.topbar__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-1);
  letter-spacing: -0.02em;
}
/* Live deploy-version indicator — small grey subscript so we can
   eyeball what's actually live on the server during QA without having
   to view-source. Hidden from screen readers (aria-hidden in markup). */
.topbar__build {
  display: inline-block;
  margin-left: 0.5em;
  font-family: var(--font-mono);
  font-style: normal;
  font-size: 0.55rem;
  letter-spacing: 0.08em;
  color: var(--ink-300);
  vertical-align: middle;
  opacity: 0.7;
}
.topbar__title::after {
  content: '';
  display: inline-block;
  width: 4px; height: 4px;
  margin-left: 0.5em; margin-bottom: 0.2em;
  background: var(--gold);
  border-radius: 50%;
  vertical-align: middle;
  box-shadow: 0 0 8px var(--gold-glow);
}
.topbar__actions { display: flex; gap: var(--space-xs); align-items: center; }
.lang-toggle {
  display: inline-flex;
  align-items: stretch;
  padding: 2px;
  border: 1px solid var(--ink-500);
  border-radius: 999px;
  background: var(--ink-800);
}
.lang-toggle__seg {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.15em;
  color: var(--ink-300);
  padding: 5px 12px;
  min-width: 36px;
  border-radius: 999px;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out-soft), background var(--dur-fast) var(--ease-out-soft);
}
.lang-toggle__seg:hover { color: var(--ink-100); }
.lang-toggle__seg.is-active {
  color: var(--on-gold);
  background: var(--gold);
}
.lang-toggle__seg.is-active:hover { color: var(--on-gold); }

/* Quick theme toggle — circular icon button next to the language pill.
   Cycles auto → light → dark → auto. The icon swaps to reflect the active
   value (sun / moon / split). Same visual weight as a lang segment so the
   topbar stays balanced. */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 999px;
  border: 1px solid var(--ink-500);
  background: var(--ink-800);
  color: var(--ink-100);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out-soft),
              border-color var(--dur-fast) var(--ease-out-soft),
              background var(--dur-fast) var(--ease-out-soft),
              transform var(--dur-fast) var(--ease-out-soft);
}
.theme-toggle:hover {
  color: var(--gold);
  border-color: var(--gold-dim);
}
.theme-toggle:active { transform: scale(0.94); }
.theme-toggle__icon { display: block; }
/* Subtle visual differentiation per state */
.theme-toggle[data-theme="light"] { color: var(--gold); }
.theme-toggle[data-theme="dark"]  { color: var(--ink-100); }
.theme-toggle[data-theme="auto"]  { color: var(--ink-200); }

/* Install-as-PWA button — appears only when the browser fires beforeinstallprompt */
/* Floating install affordance — lives in the topbar markup but lifts
   out of the flex flow so it never elbows the title, theme toggle, or
   language switch on narrow viewports. Lands in the bottom-right
   corner above the floating nav, on its own layer (z-index 19, below
   the topbar's 20 and the nav's 30 so it never overlaps either). */
.install-btn {
  position: fixed;
  right: calc(var(--space-m) + env(safe-area-inset-right));
  bottom: calc(var(--space-s) + 60px + env(safe-area-inset-bottom));
  z-index: 19;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 7px 14px 7px 11px;
  border: 1px solid var(--gold-dim);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold);
  /* Solid surface so the pill reads cleanly when scrolled over busy
     content. Mirrors the .nav surface treatment. */
  background: var(--surface-glass);
  box-shadow: 0 8px 24px -10px rgba(0, 0, 0, 0.55);
  cursor: pointer;
  transition: color var(--dur-fast), background var(--dur-fast), border-color var(--dur-fast);
}
.install-btn:hover {
  color: var(--on-gold);
  background: var(--gold);
  border-color: var(--gold);
}
.install-btn[hidden] { display: none; }

/* ---------- HAIRLINE DIVIDERS ---------- */
.hairline {
  height: 1px;
  background: linear-gradient(to right, transparent, var(--ink-500) 15%, var(--ink-500) 85%, transparent);
  margin: var(--space-l) 0;
}
.hairline--gold {
  background: linear-gradient(to right, transparent, var(--gold-dim) 40%, var(--gold-dim) 60%, transparent);
}

/* ---------- CARDS ---------- */
.card {
  position: relative;
  background: linear-gradient(180deg, var(--ink-700) 0%, var(--ink-800) 100%);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-l);
  padding: var(--space-m);
  transition: border-color var(--dur-fast) var(--ease-out-soft), transform var(--dur-fast) var(--ease-out-soft);
}
.card:hover { border-color: var(--ink-400); }
.card--elevated {
  background: linear-gradient(180deg, var(--ink-600) 0%, var(--ink-700) 100%);
  box-shadow: 0 12px 40px -20px rgba(0,0,0,0.8), 0 2px 0 rgba(255,255,255,0.02) inset;
}

.sched-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
}

/* Schedule card (used by /checkups and /vaccines) — tappable list item */
.sched-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  padding: var(--space-m);
  cursor: pointer;
  text-align: left;
  transition: border-color var(--dur-fast) var(--ease-out-soft), background var(--dur-fast) var(--ease-out-soft), transform var(--dur-fast) var(--ease-out-soft);
}
.sched-card:hover, .sched-card:focus-visible {
  border-color: var(--gold-dim);
  background: linear-gradient(180deg, var(--ink-600) 0%, var(--ink-700) 100%);
  outline: none;
}
.sched-card:active { transform: scale(0.99); }

/* Brief pulse to orient the user after a "scroll-to" navigation
   (e.g. tapping an upcoming event on the dashboard). The class is
   added by JS and removed after ~2.2s so the row settles back to
   normal styling once the user has registered the highlight. */
.is-focus-pulse {
  animation: focus-pulse 2.2s var(--ease-out-soft) 1;
}
@keyframes focus-pulse {
  0%   { box-shadow: 0 0 0 0 var(--gold-glow); border-color: var(--gold); }
  35%  { box-shadow: 0 0 0 6px transparent;   border-color: var(--gold); }
  100% { box-shadow: 0 0 0 0 transparent;     border-color: var(--ink-500); }
}
.sched-card__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-s);
}
.sched-card__title {
  flex: 1;
  min-width: 0; /* allow flex child to shrink and wrap */
}
.sched-card__date {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink-300);
}
.sched-card__name {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-2);
  color: var(--ink-50);
  margin: var(--space-3xs) 0 0;
  line-height: 1.15;
  overflow-wrap: anywhere;
  hyphens: auto;
}
.sched-card__pill { flex-shrink: 0; align-self: flex-start; }

/* Coverage indicator on vaccine cards — thin track + fill that grows as
   the dose's coverage period elapses, plus a meta line showing the dose's
   protection duration and (when given) the date it stops covering. */
.sched-card__coverage { margin-top: var(--space-s); }
.sched-card__coverage-meta {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-300);
  margin-bottom: 4px;
}
.sched-card__coverage-track {
  height: 4px;
  border-radius: 999px;
  background: var(--ink-700);
  overflow: hidden;
}
.sched-card__coverage-fill {
  height: 100%;
  background: linear-gradient(to right, var(--gold-dim), var(--gold));
  transition: width var(--dur-med) var(--ease-out-soft);
}
.sched-card__coverage-track.is-expiring .sched-card__coverage-fill {
  /* warm amber when the dose has used 80%+ of its window — booster heads-up */
  background: linear-gradient(to right, var(--gold), #d28a4a);
}
.sched-card__coverage-track.is-expired .sched-card__coverage-fill {
  background: var(--miss);
}

/* Admin-only "+ Add new schedule item" row above the schedule list. */
.sched-admin-row {
  display: flex;
  justify-content: flex-end;
  margin-bottom: var(--space-s);
}

/* Admin edit + delete affordances on each schedule card — small icons
   tucked into the header row next to the status pill. Hidden until
   hover/focus on hover-capable inputs so they don't compete visually
   with normal interactions; always visible on touch (no hover) so
   admins can still tap them. */
.sched-card__admin-edit,
.sched-card__admin-del {
  flex: 0 0 auto;
  align-self: flex-start;
  width: 26px; height: 26px;
  border-radius: 999px;
  background: transparent;
  border: 0;
  color: var(--ink-300);
  font-size: 1.05rem;
  line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: color var(--dur-fast), background var(--dur-fast), opacity var(--dur-fast);
}
.sched-card__admin-edit:hover,
.sched-card__admin-edit:focus-visible {
  color: var(--ink-900);
  background: rgba(0, 0, 0, 0.06);
}
.sched-card__admin-del:hover,
.sched-card__admin-del:focus-visible {
  color: var(--miss);
  background: rgba(154, 85, 73, 0.12);
}
@media (hover: hover) {
  .sched-card__admin-edit,
  .sched-card__admin-del { opacity: 0; }
  .sched-card:hover .sched-card__admin-edit,
  .sched-card:focus-within .sched-card__admin-edit,
  .sched-card:hover .sched-card__admin-del,
  .sched-card:focus-within .sched-card__admin-del { opacity: 1; }
}
.sched-card__focus {
  color: var(--ink-200);
  font-size: var(--step--1);
  line-height: 1.45;
  margin: 0;
}

@media (max-width: 480px) {
  .sched-card { padding: var(--space-s); gap: 6px; }
  .sched-card__name { font-size: var(--step-1); }
  .sched-card__head { gap: var(--space-2xs); }
  .pill { padding: 2px 8px; font-size: 0.6rem; letter-spacing: 0.1em; }
}

/* ---------- ALBUMS STRIP ----------
   Horizontal scroll row of album tiles above the photos grid. Each tile
   pairs a square cover (photo thumb or a glyph for the special "All" /
   "New" tiles) with the album's name and member count. Active tile gets a
   gold ring; inactive tiles desaturate slightly on hover. */
.album-strip {
  display: flex;
  gap: var(--space-2xs);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  padding: var(--space-2xs) 2px var(--space-s);
  margin: 0 calc(-1 * var(--space-s)) var(--space-s);
  padding-left: var(--space-s);
  padding-right: var(--space-s);
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}
.album-strip::-webkit-scrollbar { height: 4px; }
.album-strip::-webkit-scrollbar-thumb {
  background: var(--ink-500);
  border-radius: 2px;
}
.album-tile {
  flex: 0 0 84px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  padding: 0;
  background: transparent;
  border: 0;
  scroll-snap-align: start;
  cursor: pointer;
}
.album-tile__cover {
  width: 84px;
  height: 84px;
  border-radius: 12px;
  overflow: hidden;
  background: var(--ink-700);
  border: 1px solid var(--ink-500);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--gold-dim);
  transition: border-color .15s ease, transform .15s ease;
}
.album-tile__cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.album-tile__cover svg { width: 28px; height: 28px; }
.album-tile__cover--new {
  font-size: 1.6rem;
  font-weight: 300;
  color: var(--gold);
  border-style: dashed;
}
.album-tile:hover .album-tile__cover,
.album-tile:focus-visible .album-tile__cover { border-color: var(--gold-dim); }
.album-tile.is-active .album-tile__cover {
  border-color: var(--gold);
  box-shadow: 0 0 0 2px var(--gold-glow);
}
.album-tile__name {
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: .72rem;
  letter-spacing: .02em;
  color: var(--ink-100);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 84px;
  padding: 0 2px;
}
.album-tile__count {
  font-family: var(--font-mono);
  font-size: .58rem;
  letter-spacing: .08em;
  color: var(--ink-300);
  text-align: center;
}

/* Album-context actions row that appears when an album is selected. */
.album-actions {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  margin-bottom: var(--space-s);
  flex-wrap: wrap;
}
.album-actions__name {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: 1.05rem;
  color: var(--ink-50);
}
.album-actions__sep { color: var(--ink-300); }
.link-action {
  background: none;
  border: 0;
  padding: 0;
  font-family: var(--font-mono);
  font-size: .68rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--gold-dim);
  cursor: pointer;
  border-bottom: 1px solid transparent;
  transition: color .15s, border-color .15s;
}
.link-action:hover { color: var(--gold); border-bottom-color: var(--gold-glow); }
.link-action--danger { color: var(--miss); }
.link-action--danger:hover { color: var(--miss); border-bottom-color: rgba(154, 85, 73, 0.4); }

/* Album filter chips inside the photoPicker sheet — narrows the picker's
   grid by album so users can scope when attaching to a milestone/event. */
.album-chips {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
  margin: 0 0 .75rem;
}
.album-chip {
  font-family: var(--font-mono);
  font-size: .65rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-100);
  background: var(--ink-700);
  border: 1px solid var(--ink-500);
  border-radius: 999px;
  padding: .3rem .7rem;
  cursor: pointer;
  transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.album-chip:hover { border-color: var(--gold-dim); color: var(--gold); }
.album-chip.is-active {
  background: var(--gold);
  color: var(--on-gold);
  border-color: var(--gold);
}

/* "Add to album" picker — tile-style toggleable rows inside a sheet.
   Each tile pairs a cover thumb (or glyph fallback for empty albums)
   with the album name + count; tap toggles inclusion. The active state
   uses the same gold-ring + check pattern as the rest of the app's
   selection UIs (astro cards, wardrobe size pickers). */
.album-picker__hint {
  margin: -.25rem 0 var(--space-s);
  color: var(--ink-300);
}
.album-picker-grid {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 50vh;
  overflow-y: auto;
  padding-right: 4px;
}
.album-picker-tile {
  display: flex;
  align-items: center;
  gap: .85rem;
  padding: 8px;
  background: var(--ink-700);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
  cursor: pointer;
  text-align: left;
  width: 100%;
  position: relative;
  transition: border-color var(--dur-fast), background var(--dur-fast), transform var(--dur-fast);
}
.album-picker-tile:hover {
  border-color: var(--gold-dim);
  background: var(--ink-600);
}
.album-picker-tile.is-selected {
  border-color: var(--gold);
  background: rgba(201, 168, 118, 0.06);
}
.album-picker-tile__cover {
  flex: 0 0 auto;
  width: 48px; height: 48px;
  border-radius: 8px;
  overflow: hidden;
  background: var(--ink-800);
  border: 1px solid var(--ink-500);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--gold-dim);
}
.album-picker-tile__cover img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.album-picker-tile.is-selected .album-picker-tile__cover {
  border-color: var(--gold-dim);
}
.album-picker-tile__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.album-picker-tile__name {
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: .95rem;
  font-weight: 500;
  color: var(--ink-50);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.album-picker-tile__count {
  font-family: var(--font-mono);
  font-size: .6rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-300);
}
.album-picker-tile__check {
  flex: 0 0 auto;
  width: 26px; height: 26px;
  border-radius: 999px;
  border: 1px solid var(--ink-500);
  background: var(--ink-800);
  color: var(--ink-800);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background var(--dur-fast), border-color var(--dur-fast), color var(--dur-fast);
}
.album-picker-tile.is-selected .album-picker-tile__check {
  background: var(--gold);
  border-color: var(--gold);
  color: var(--on-gold);
}

/* Diff line shown above Save: "+2 added · −1 removed" so the user can
   confirm the net effect before committing. Hidden when no changes. */
.album-picker__diff {
  margin-top: var(--space-s);
  padding: .35rem .75rem;
  border-radius: 999px;
  background: var(--ink-700);
  border: 1px solid var(--gold-dim);
  color: var(--gold);
  font-family: var(--font-mono);
  font-size: .68rem;
  letter-spacing: .1em;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Empty-state for the picker — centered icon + message. Reuses gold-dim
   for the icon so it doesn't compete with the primary CTA below. */
.album-picker__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-s);
  padding: var(--space-l) 0 var(--space-s);
  color: var(--gold-dim);
  text-align: center;
}
.album-picker__empty p {
  margin: 0;
  color: var(--ink-200);
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: 1.05rem;
}

/* ---------- PHOTOS VIEW ---------- */
.photos-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
  flex-wrap: wrap;
  margin-bottom: var(--space-m);
}
.photos-filter {
  display: inline-flex;
  padding: 2px;
  border: 1px solid var(--ink-500);
  border-radius: 999px;
  background: var(--ink-800);
}
.photos-filter__seg {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-300);
  padding: 5px 14px;
  border-radius: 999px;
  cursor: pointer;
  transition: color var(--dur-fast), background var(--dur-fast);
}
.photos-filter__seg:hover { color: var(--ink-100); }
.photos-filter__seg.is-active {
  color: var(--on-gold);
  background: var(--gold);
}

.photos-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 4px;
  /* Same containment guard as the picker grid — keeps inner cell
     layout isolated from view-mount reflows on iOS Safari. */
  contain: layout style;
}
.photo-tile {
  /* iOS button-as-grid-item geometry — same bulletproofing as
     .photo-pick (see comment there). The native button rendering +
     `aspect-ratio` + a child <img loading="lazy"> with `height:100%`
     collapses to the image's intrinsic line-box height on iOS 16/17,
     which is the "thumbnails stack on top of each other" symptom.
     padding-bottom: 100% holds the square purely from layout, and
     the child <img> is position:absolute so its size never leaks
     into the cell's height calculation. */
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  position: relative;
  height: 0;
  padding: 0 0 100% 0;
  overflow: hidden;
  border-radius: var(--radius-m);
  border: 1px solid var(--ink-500);
  background: var(--ink-800);
  cursor: pointer;
  min-width: 0;
  font: inherit;
  color: inherit;
  transition: border-color var(--dur-fast) var(--ease-out-soft), transform var(--dur-fast) var(--ease-out-soft);
}
.photo-tile:hover, .photo-tile:focus-visible {
  border-color: var(--gold-dim);
  outline: none;
  transform: scale(1.01);
}
.photo-tile img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.photo-tile__lock {
  position: absolute;
  top: 6px; right: 6px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--miss);
  box-shadow: 0 0 6px rgba(154,85,73,0.6);
  font-size: 0;
}

/* Age stamp — bottom-left of the thumbnail. Mono so it reads as a
   discreet caption rather than competing with the photo itself; gold
   tint on the text + soft dark scrim behind so it stays legible over
   light and dark photo regions both. The scrim is a subtle gradient
   fading from the bottom-edge up, contained to the stamp's pixel
   area so a busy photo doesn't get unnecessary darkening overall. */
.photo-tile__age {
  position: absolute;
  left: 8px;
  bottom: 8px;
  padding: 3px 8px;
  font-family: var(--font-mono);
  font-size: .58rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--gold-bright);
  background: rgba(10, 9, 8, 0.55);
  border: 1px solid rgba(201, 168, 118, 0.16);
  border-radius: 999px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
  pointer-events: none;
  white-space: nowrap;
  -webkit-backdrop-filter: blur(2px);
          backdrop-filter: blur(2px);
}
/* The stamp always sits on a dark translucent scrim so the photo
   stays legible behind. In light theme our --gold-bright token flips
   to a deeper champagne (#6f5826) tuned for cream surfaces, which
   reads poorly on the dark scrim. Force white in light mode so the
   stamp keeps strong contrast regardless of the theme. */
[data-theme="light"] .photo-tile__age { color: #f5efe4; }
@media (prefers-color-scheme: light) {
  [data-theme="auto"] .photo-tile__age { color: #f5efe4; }
}
@media (hover: hover) and (pointer: fine) {
  /* Fade the stamp slightly on hover so the photo wins the spotlight
     when the user is actively browsing one tile. Touch devices keep
     full opacity since there's no hover state. */
  .photo-tile:hover .photo-tile__age { opacity: 0.7; }
}

@media (min-width: 720px) {
  .photos-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
}

/* Two-button picker (gallery + camera) in upload sheet */
.photo-pickers {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2xs);
  border: 1px dashed transparent;
  border-radius: var(--radius-l);
  transition: border-color var(--dur-fast) var(--ease-out-soft), background var(--dur-fast) var(--ease-out-soft);
}
.photo-pickers.is-dropping {
  border-color: var(--gold);
  background: rgba(201, 168, 118, 0.04);
}
.photo-picker {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: var(--space-m) var(--space-s);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-l);
  background: var(--ink-800);
  cursor: pointer;
  text-align: center;
  transition: border-color var(--dur-fast) var(--ease-out-soft), background var(--dur-fast) var(--ease-out-soft);
}
.photo-picker:hover, .photo-picker:focus-visible {
  border-color: var(--gold-dim);
  background: var(--ink-700);
  outline: none;
}
.photo-picker__icon {
  width: 28px;
  height: 28px;
  color: var(--gold);
  margin-bottom: 2px;
}
.photo-picker__title {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--step-0);
  color: var(--ink-50);
  letter-spacing: -0.005em;
  line-height: 1.2;
}
.photo-picker__hint {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-300);
  margin-top: 2px;
}
@media (max-width: 380px) {
  .photo-picker { padding: var(--space-s); }
  .photo-picker__icon { width: 24px; height: 24px; }
}

/* ---------- IMAGE VIEWER (shared, swipe-aware lightbox) ----------
   Used by moments / photo thumbnail surfaces via openImageViewer().
   The legacy .lightbox styling below is reused by photos.js's rich
   single-photo detail sheet (caption + comments + albums + edit). */
.iv { display: flex; flex-direction: column; gap: var(--space-s); }
.iv__stage {
  position: relative;
  background: var(--ink-900);
  border-radius: var(--radius-m);
  overflow: hidden;
  min-height: 220px;
  /* `touch-action: none` on the stage so two-finger pinch reaches our
     JS handler instead of being swallowed by the page-zoom default.
     Single-finger swipe still works because we read deltas in the
     handler — not relying on browser pan. */
  touch-action: none;
}
/* Pan wrapper — owns the CSS transform that our zoom + pan logic
   updates. Separating image from transform target lets the image
   render at intrinsic resolution while we manipulate the wrapper. */
.iv__pan {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  /* GPU-promote so the transform is composited, not painted, on every
     pinch frame. translateZ keeps the layer alive across all states. */
  transform-origin: center center;
  transition: transform 200ms var(--ease-out-soft);
  will-change: transform;
}
.iv__pan--zoomed {
  /* During active zoom we want instant feedback, not the transition. */
  transition: none;
  cursor: grab;
}
.iv__img {
  display: block;
  max-width: 100%;
  max-height: 70vh;
  max-height: 70dvh;
  width: auto;
  height: auto;
  object-fit: contain;
  user-select: none;
  -webkit-user-select: none;
  pointer-events: none;
}

/* Fullscreen mode.
   The .iv__stage element is detached from the sheet and re-parented to
   document.body when the user taps fullscreen. That's needed because the
   sheet itself uses `transform: translateY(...)` which makes it the
   containing block for any `position: fixed` descendant — so applying
   fixed+inset:0 INSIDE the sheet only fills the sheet's box, not the
   viewport. Reparenting escapes that trap entirely.

   Layout uses 100dvh / 100dvw which respond to orientation changes
   automatically — landscape ↔ portrait works without any JS resize
   listener for the image itself (the zoom-pan handler does reset zoom
   on orientationchange to keep transforms valid). */
.iv-fullscreen-root {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
}
.iv__stage--full {
  position: relative;
  width: 100vw;
  height: 100dvh;
  border-radius: 0;
  background: #000;
  /* Image area + overlay controls inside the existing stage element.
     Toolbar / counter / nav stay where they already are — no new rules
     needed, they just reposition relative to a viewport-sized box. */
}
.iv__stage--full .iv__pan {
  width: 100vw;
  height: 100dvh;
}
.iv__stage--full .iv__img {
  max-width: 100vw;
  max-height: 100dvh;
}
.iv__stage--full .iv__nav {
  background: rgba(255, 255, 255, 0.08);
}
.iv__toolbar--hidden { opacity: 0; pointer-events: none; transition: opacity 200ms; }
.iv__caption {
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--step-1);
  color: var(--ink-50);
  line-height: 1.3;
  margin: 0;
}
.iv__caption[hidden] { display: none; }
/* Counter chip — top-left, subtle, readable over any photo. */
.iv__counter {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 2;
  font-family: var(--font-mono);
  font-size: .65rem;
  letter-spacing: .12em;
  color: #f5efe4;
  background: rgba(10, 9, 8, 0.55);
  padding: 5px 10px;
  border-radius: 999px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
  pointer-events: none;
}

/* Floating toolbar — bottom-right of the image. One translucent pill
   that groups every action so the controls read as a single intentional
   affordance instead of four scattered chips. Subtle resting opacity
   nudges the image to the foreground; brighter on hover/tap. */
.iv__toolbar {
  position: absolute;
  bottom: 12px;
  right: 12px;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 4px;
  background: rgba(10, 9, 8, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 999px;
  z-index: 2;
  opacity: 0.85;
  transition: opacity var(--dur-fast), background var(--dur-fast);
  -webkit-backdrop-filter: blur(8px);
          backdrop-filter: blur(8px);
}
.iv__toolbar:hover,
.iv__toolbar:focus-within {
  opacity: 1;
  background: rgba(10, 9, 8, 0.75);
}
.iv__tool {
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 999px;
  background: transparent;
  /* Match the nav arrows' off-white — the viewer stage is always dark
     regardless of app theme, so the gold tint (--gold-bright) only read
     well in dark mode and turned washed-out next to the bright shell on
     light theme. The arrows already use #f5efe4 and read crisply on
     both themes; pin the toolbar icons to the same value. */
  color: #f5efe4;
  cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast), transform var(--dur-fast);
}
.iv__tool:hover,
.iv__tool:focus-visible {
  background: rgba(201, 168, 118, 0.18);
  color: #fff;
}
.iv__tool:active { transform: scale(0.94); }

/* In-image × close button — hidden in normal viewer mode (the sheet's
   own × in the top-right corner of the sheet covers that case and
   doubling the affordance just produced two adjacent × icons —
   May 2026 QA). Only surfaces in fullscreen mode where the sheet
   chrome is no longer visible. */
.iv__close-full {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 38px;
  height: 38px;
  display: none;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 999px;
  background: rgba(10, 9, 8, 0.65);
  color: #f5efe4;
  cursor: pointer;
  z-index: 3;
  transition: background var(--dur-fast), transform var(--dur-fast);
}
.iv__stage--full .iv__close-full { display: inline-flex; }
.iv__close-full:hover { background: rgba(10, 9, 8, 0.9); }
.iv__close-full:active { transform: scale(0.94); }
/* Left/right navigation arrows — only render when there's an array. */
.iv__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 60px;
  border: 0;
  background: rgba(10, 9, 8, 0.45);
  color: #f5efe4;
  font-size: 1.6rem;
  font-family: var(--font-display);
  cursor: pointer;
  transition: background var(--dur-fast), opacity var(--dur-fast);
  z-index: 2;
}
.iv__nav--prev { left: 0;  border-radius: 0 var(--radius-m) var(--radius-m) 0; }
.iv__nav--next { right: 0; border-radius: var(--radius-m) 0 0 var(--radius-m); }
.iv__nav:hover,
.iv__nav:focus-visible {
  background: rgba(10, 9, 8, 0.75);
}
.iv__nav:active { background: rgba(10, 9, 8, 0.9); }
.iv__nav[disabled] { opacity: .3; cursor: default; }
/* Smaller arrows on narrow viewports so they don't fight thumbs vs swipe. */
@media (max-width: 480px) {
  .iv__nav { width: 36px; height: 52px; font-size: 1.4rem; }
  .iv__tool { width: 34px; height: 34px; }
}

/* Lightbox (rendered inside the photo-detail sheet — secondary,
   metadata + comments + edit; the primary photo-browsing experience
   is the .iv__* viewer above.) */
.lightbox { display: flex; flex-direction: column; gap: var(--space-s); }
/* Compact preview height inside the detail sheet — the user already
   browsed the photo at full size in the viewer, so the detail sheet
   trims the image down so the metadata / comments are immediately
   visible without scrolling. */
.lightbox__img--compact {
  max-height: 38vh;
  max-height: 38dvh;
}
.lightbox__img {
  width: 100%;
  max-height: 70vh;
  object-fit: contain;
  border-radius: var(--radius-m);
  background: var(--ink-900);
}
.lightbox__caption {
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--step-1);
  color: var(--ink-50);
  line-height: 1.3;
  margin: 0;
}
.lightbox__meta {
  display: flex;
  gap: var(--space-s);
  align-items: center;
  flex-wrap: wrap;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-300);
}
.lightbox__attached { padding: var(--space-s) 0; border-top: 1px solid var(--ink-500); }
.lightbox__attached ul { margin: var(--space-2xs) 0 0; padding: 0; list-style: none; }
.lightbox__attached li { padding: 4px 0; color: var(--ink-100); }

/* Comments thread under the photo. Compact vertical list with a thin
   composer pinned at the bottom of the section — feels like an editorial
   "marginalia" rather than a chat panel. */
.lightbox__comments {
  padding: var(--space-s) 0;
  border-top: 1px solid var(--ink-500);
}
.lightbox__comments-list {
  list-style: none;
  margin: var(--space-2xs) 0 var(--space-s);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  max-height: 38vh;
  overflow-y: auto;
}
.lightbox__comments-empty,
.lightbox__comments-loading {
  text-align: center;
  padding: var(--space-s) 0;
  color: var(--ink-300);
}
.lightbox__comment {
  background: var(--ink-700);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
  padding: var(--space-2xs) var(--space-s);
  position: relative;
}
.lightbox__comment.is-mine {
  background: rgba(201, 168, 118, 0.06);
  border-color: var(--gold-dim);
}
.lightbox__comment-head {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  margin-bottom: 2px;
}
.lightbox__comment-author {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: .9rem;
  color: var(--ink-50);
}
.lightbox__comment-when {
  font-family: var(--font-mono);
  font-size: .58rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-300);
  margin-left: auto;
}
.lightbox__comment-del {
  background: transparent;
  border: 0;
  color: var(--ink-300);
  font-size: 1rem;
  line-height: 1;
  width: 22px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px;
  cursor: pointer;
  transition: color var(--dur-fast), background var(--dur-fast);
}
.lightbox__comment-del:hover {
  color: var(--miss);
  background: rgba(154, 85, 73, 0.12);
}
.lightbox__comment-body {
  margin: 0;
  font-size: .9rem;
  color: var(--ink-100);
  line-height: 1.45;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}
.lightbox__comment-form {
  display: flex;
  gap: .5rem;
  align-items: center;
}
.lightbox__comment-form input {
  flex: 1;
  min-width: 0;
  /* Match the rest of the app's input look — the form sits outside a
     `.field` wrapper so the generic `.field input` rules don't apply. */
  background: var(--ink-800);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
  padding: 10px 14px;
  color: var(--ink-50);
  font-family: var(--font-body);
  font-size: var(--step-0);
  transition: border-color var(--dur-fast), background var(--dur-fast);
}
.lightbox__comment-form input::placeholder { color: var(--ink-400); }
.lightbox__comment-form input:focus {
  outline: none;
  border-color: var(--gold-dim);
  background: var(--ink-700);
}
.lightbox__comment-form .btn { flex: 0 0 auto; }

/* Photo strip — used inside event cards and timeline entries */
.photo-strip {
  display: flex;
  gap: 4px;
  margin-top: var(--space-xs);
  overflow-x: auto;
  padding-bottom: 4px;
}
.photo-strip__tile {
  flex: 0 0 auto;
  width: 64px; height: 64px;
  border-radius: var(--radius-m);
  background: var(--ink-800);
  border: 1px solid var(--ink-500);
  overflow: hidden;
  cursor: pointer;
}
.photo-strip__tile img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---------- FUND CARDS (savings view) ---------- */
.fund-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
}
.fund-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  padding: var(--space-m);
  background: linear-gradient(180deg, var(--ink-700) 0%, var(--ink-800) 100%);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-l);
  cursor: pointer;
  text-align: left;
  transition: border-color var(--dur-fast) var(--ease-out-soft), background var(--dur-fast) var(--ease-out-soft);
}
.fund-card:hover, .fund-card:focus-visible {
  border-color: var(--gold-dim);
  outline: none;
}
.fund-card__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-s);
}
.fund-card__title { flex: 1; min-width: 0; }
.fund-card__name {
  display: block;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-2);
  color: var(--ink-50);
  letter-spacing: -0.02em;
  line-height: 1.15;
}
.fund-card__meta {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-300);
  margin-top: 4px;
  overflow-wrap: anywhere;
}
.fund-card__amount {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  flex-shrink: 0;
}
.fund-card__balance {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--step-1);
  color: var(--gold);
}
.fund-card__target {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--step--2);
  color: var(--ink-300);
  margin-top: 2px;
}
.fund-card__progress {
  position: relative;
  height: 6px;
  background: var(--ink-600);
  border-radius: 999px;
  overflow: hidden;
}
.fund-card__progress-fill {
  position: absolute;
  inset: 0 auto 0 0;
  background: linear-gradient(90deg, var(--gold-dim) 0%, var(--gold) 100%);
  border-radius: 999px;
  transition: width var(--dur-med) var(--ease-out-soft);
}
.fund-card__progress-label {
  position: absolute;
  right: 8px;
  top: -16px;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--gold);
}
.fund-card__actions {
  display: flex;
  gap: var(--space-2xs);
  justify-content: flex-end;
}

/* Fund detail balance display */
.fund-detail__balance {
  text-align: center;
  padding: var(--space-m) 0;
  border-top: 1px solid var(--ink-500);
  border-bottom: 1px solid var(--ink-500);
}
.fund-detail__balance-amount {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-5);
  color: var(--gold);
  letter-spacing: -0.02em;
  margin: var(--space-2xs) 0;
}

@media (max-width: 480px) {
  .fund-card { padding: var(--space-s); }
  .fund-card__name { font-size: var(--step-1); }
  .fund-card__balance { font-size: var(--step-0); }
  .fund-detail__balance-amount { font-size: var(--step-4); }
}

/* ---------- PEOPLE CHIP PICKER (event sheet → attendees) ---------- */
.people-picker {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
}
.people-chip {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 6px 12px;
  background: var(--ink-700);
  border: 1px solid var(--ink-500);
  border-radius: 999px;
  cursor: pointer;
  text-align: left;
  transition: border-color var(--dur-fast) var(--ease-out-soft),
              background var(--dur-fast) var(--ease-out-soft),
              color var(--dur-fast) var(--ease-out-soft);
}
.people-chip:hover { border-color: var(--gold-dim); }
.people-chip.is-selected {
  background: var(--gold);
  border-color: var(--gold);
  color: var(--on-gold);
}
.people-chip__name {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--step--1);
  line-height: 1.1;
}
.people-chip__sub {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-300);
  line-height: 1;
}
.people-chip.is-selected .people-chip__sub { color: var(--ink-700); }

/* ---------- FORM TOGGLE ROW (in-flow switch — e.g. Mark as completed) ---------- */
.form-toggle[hidden] { display: none; }
.form-toggle {
  display: flex;
  align-items: center;
  gap: var(--space-m);
  margin: 0 0 var(--space-s);
  padding: var(--space-s) 0;
  border-top: 1px solid var(--ink-500);
  border-bottom: 1px solid var(--ink-500);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out-soft);
}
.form-toggle.is-on {
  border-top-color: var(--gold-dim);
  border-bottom-color: var(--gold-dim);
}
.form-toggle__text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.form-toggle__title {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--step-0);
  color: var(--ink-50);
  letter-spacing: -0.005em;
  line-height: 1.25;
}
.form-toggle.is-on .form-toggle__title { color: var(--gold-bright); }
.form-toggle__help {
  font-size: var(--step--1);
  color: var(--ink-200);
  line-height: 1.4;
}

/* ---------- ACCOUNT TOGGLE CARD (person sheet → login access) ---------- */
.account-card {
  margin-top: var(--space-l);
  padding: var(--space-s) var(--space-m);
  background: var(--ink-800);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-l);
  transition: border-color var(--dur-fast) var(--ease-out-soft),
              background var(--dur-fast) var(--ease-out-soft);
}
.account-card.is-on {
  border-color: var(--gold-dim);
  background: linear-gradient(180deg, var(--ink-700) 0%, var(--ink-800) 100%);
  box-shadow: 0 0 0 1px var(--gold-dim) inset, 0 6px 24px -14px var(--gold-glow);
}
.account-card__head {
  display: flex;
  align-items: center;
  gap: var(--space-m);
  cursor: pointer;
  margin: 0;
  padding: var(--space-2xs) 0;
}
.account-card__text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.account-card__title {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--step-0);
  color: var(--ink-50);
  letter-spacing: -0.005em;
  line-height: 1.25;
}
.account-card__help {
  font-size: var(--step--1);
  color: var(--ink-200);
  line-height: 1.4;
}
.account-card__fields {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows var(--dur-med) var(--ease-out-soft),
              opacity var(--dur-med) var(--ease-out-soft);
}
.account-card__fields > * {
  overflow: hidden;
  min-height: 0;
}
.account-card.is-on .account-card__fields {
  grid-template-rows: 1fr;
  opacity: 1;
  margin-top: var(--space-s);
  padding-top: var(--space-s);
  border-top: 1px solid var(--ink-500);
}

/* iOS-style switch */
.switch {
  position: relative;
  display: inline-block;
  flex-shrink: 0;
}
.switch__input {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}
.switch__track {
  display: inline-block;
  width: 46px;
  height: 26px;
  background: var(--ink-700);
  border: 1px solid var(--ink-500);
  border-radius: 999px;
  position: relative;
  transition: background var(--dur-fast) var(--ease-out-soft),
              border-color var(--dur-fast) var(--ease-out-soft);
}
.switch__thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 20px;
  height: 20px;
  background: var(--ink-200);
  border-radius: 50%;
  transition: transform var(--dur-med) var(--ease-out-soft),
              background var(--dur-fast) var(--ease-out-soft);
  box-shadow: 0 2px 6px -1px rgba(0,0,0,0.5);
}
.switch__input:checked + .switch__track {
  background: var(--gold);
  border-color: var(--gold);
}
.switch__input:checked + .switch__track .switch__thumb {
  transform: translateX(20px);
  background: var(--ink-900);
}
.switch__input:focus-visible + .switch__track {
  box-shadow: 0 0 0 3px var(--gold-glow);
}

/* ---------- PEOPLE LIST (settings → familie) ---------- */
.people-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}
.person-row {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;        /* keep avatar + body + actions on one row */
  gap: var(--space-s);
  padding: var(--space-s) var(--space-m);
  background: var(--ink-700);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
  cursor: pointer;
  text-align: left;
  transition: border-color var(--dur-fast) var(--ease-out-soft), background var(--dur-fast) var(--ease-out-soft);
  min-width: 0;
  width: 100%;
}
.person-row:hover, .person-row:focus-visible {
  border-color: var(--gold-dim);
  background: var(--ink-600);
  outline: none;
}
.person-row__avatar {
  flex-shrink: 0;
  width: 38px; height: 38px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ink-800);
  border: 1px solid var(--ink-500);
  border-radius: 50%;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-0);
  color: var(--gold);
  letter-spacing: 0;
}
.person-row__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  overflow: hidden;
}
.person-row__name {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--step-0);
  color: var(--ink-50);
  letter-spacing: -0.01em;
  margin: 0;
  line-height: 1.25;
  /* Display-name + inline badges as a single flex line that can wrap to a
     second line on truly narrow screens, but the badges always stay close
     to the name they belong to. */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
}
.person-row__name-text {
  /* Don't break the display name itself, just truncate if absurdly long. */
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
/* Email on its own line — long addresses ellipsize so they never push the
   action buttons (resend / delete) out of the row. */
.person-row__email {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  color: var(--ink-300);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
  letter-spacing: 0;
}
.person-row__sub {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-300);
  margin-top: 2px;
}
.person-row__badge {
  display: inline-block;
  padding: 1px 6px;
  margin-left: 4px;
  border: 1px solid var(--gold-dim);
  border-radius: 999px;
  color: var(--gold);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
}
.person-row__del {
  flex-shrink: 0;
  width: 32px; height: 32px;
  border-radius: 999px;
  color: var(--ink-300);
  font-size: 1.4rem;
  line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  transition: color var(--dur-fast), background var(--dur-fast);
}

/* Role / status badges — small pills that sit inline with the display name.
   Admin reads as a gold-tinted accent; reader is a quieter outline. The
   pending invite badge stays warmer/attention-grabbing. */
.role-badge {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 0.6rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--gold-dim);
  color: var(--gold);
  background: rgba(201, 168, 118, 0.08);
  white-space: nowrap;
  flex-shrink: 0;
}
.role-badge--admin {
  border-color: var(--gold);
  background: rgba(201, 168, 118, 0.18);
}
.role-badge--reader {
  color: var(--ink-200);
  border-color: var(--ink-400);
  background: transparent;
}
.role-badge--self {
  color: var(--ink-200);
  border-color: var(--ink-500);
  background: transparent;
  letter-spacing: 0.04em;
  text-transform: none;
  font-family: var(--font-body);
  font-size: 0.65rem;
  font-style: italic;
}
.person-row__del:hover { color: var(--miss); background: rgba(154, 85, 73, 0.12); }

/* ---------- ASTROLOGY PANEL (settings) ---------- */
.astro-hint {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-300);
}

.astro-card.is-primary {
  border-color: var(--gold-dim);
  box-shadow: 0 0 0 1px var(--gold-dim) inset, 0 6px 28px -12px var(--gold-glow);
}
@media (hover: hover) and (pointer: fine) {
  .astro-card.is-primary .astro-card__glyph {
    filter: drop-shadow(0 0 16px var(--gold-glow));
  }
}

/* Button variant — used when the card is itself the radio control. The
   browser default <button> styling (text-align, font, etc.) needs to be
   neutralised so it inherits the existing card layout. */
.astro-card--button {
  position: relative;
  width: 100%;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  transition: border-color var(--dur-fast), box-shadow var(--dur-fast), transform var(--dur-fast);
}
.astro-card--button:hover {
  border-color: var(--gold-dim);
  transform: translateY(-1px);
}
.astro-card--button:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}
/* Subtle ring + check glyph in the corner so the active card reads as
   selected on first glance, not just "highlighted". */
.astro-card__check {
  position: absolute;
  top: 10px; right: 10px;
  width: 18px; height: 18px;
  border-radius: 999px;
  border: 1px solid var(--ink-500);
  background: var(--ink-800);
  transition: background var(--dur-fast), border-color var(--dur-fast);
}
.astro-card--button.is-primary .astro-card__check {
  background: var(--gold);
  border-color: var(--gold);
}
.astro-card--button.is-primary .astro-card__check::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 9px; height: 5px;
  border-left: 2px solid var(--on-gold);
  border-bottom: 2px solid var(--on-gold);
  transform: translate(-50%, -65%) rotate(-45deg);
}

.astro-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-s);
}
.astro-card {
  display: flex;
  align-items: flex-start;
  gap: var(--space-m);
  padding: var(--space-m);
  background: linear-gradient(180deg, var(--ink-700) 0%, var(--ink-800) 100%);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-l);
  min-width: 0;
}
.astro-card__glyph {
  flex-shrink: 0;
  font-size: 2.4rem;
  line-height: 1;
  color: var(--gold);
  font-family: var(--font-display);
  font-style: normal;
  text-shadow: 0 0 12px var(--gold-glow);
}
.astro-card__body {
  flex: 1;
  min-width: 0;
  /* Force column flow even when this element is a <span> inside a button — */
  /* its children must stack vertically the same way the old <div> did. */
  display: flex;
  flex-direction: column;
}
.astro-card__eyebrow {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-300);
  display: block;
  margin-bottom: 4px;
}
.astro-card__name {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-2);
  letter-spacing: -0.02em;
  color: var(--ink-50);
  margin: 0 0 var(--space-3xs);
  line-height: 1.1;
  overflow-wrap: anywhere;
}
.astro-card__desc {
  font-size: var(--step--1);
  color: var(--ink-100);
  line-height: 1.4;
  margin: 0 0 var(--space-2xs);
}
.astro-card__meta {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.1em;
  color: var(--ink-200);
  line-height: 1.5;
}
.astro-card__meta .sep { color: var(--ink-400); margin: 0 6px; }

@media (max-width: 540px) {
  .astro-grid { grid-template-columns: 1fr; }
  .astro-card { padding: var(--space-s) var(--space-m); }
  .astro-card__glyph { font-size: 2rem; }
  .astro-card__name { font-size: var(--step-1); }
}

/* ---------- CHRONICLE CARDS (recorded milestones / events) ---------- */
.chronicle-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  margin-bottom: var(--space-l);
}
.chronicle-card {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--space-s);
  padding: var(--space-m);
  background: linear-gradient(180deg, var(--ink-600) 0%, var(--ink-700) 100%);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-l);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out-soft);
}
.chronicle-card:hover, .chronicle-card:focus-visible {
  border-color: var(--gold-dim);
  outline: none;
}
.chronicle-card__body {
  flex: 1;
  min-width: 0;
}
.chronicle-card__meta {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-300);
}
.chronicle-card__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-2);
  color: var(--ink-50);
  line-height: 1.15;
  margin: var(--space-3xs) 0 var(--space-2xs);
  letter-spacing: -0.02em;
  overflow-wrap: anywhere;
  hyphens: auto;
}
.chronicle-card__desc {
  font-size: var(--step--1);
  color: var(--ink-200);
  line-height: 1.5;
  margin: var(--space-3xs) 0 0;
}
.chronicle-card__lbl {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-300);
  margin-right: 4px;
}
.chronicle-card__del {
  flex-shrink: 0;
  width: 32px; height: 32px;
  border-radius: 999px;
  color: var(--ink-300);
  font-size: 1.4rem;
  line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  transition: color var(--dur-fast), background var(--dur-fast);
}
.chronicle-card__del:hover { color: var(--miss); background: rgba(154, 85, 73, 0.12); }

/* Thumbnail strip on milestone cards — small square previews with click-to-
   expand. Each thumb is a real button so it can keyboard-focus and stop
   bubbling cleanly without competing with the parent card's edit handler. */
.chronicle-card__thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: var(--space-2xs);
}
.chronicle-thumb {
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  padding: 0;
  border: 1px solid var(--ink-500);
  border-radius: 8px;
  overflow: hidden;
  background: var(--ink-700);
  cursor: zoom-in;
  transition: border-color var(--dur-fast), transform var(--dur-fast);
}
.chronicle-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.chronicle-thumb:hover,
.chronicle-thumb:focus-visible {
  border-color: var(--gold-dim);
  transform: scale(1.04);
}

/* ---------- SUGGEST TILES (clickable type cards) ---------- */
.suggest-group { margin-bottom: var(--space-m); }
.suggest-group__head {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold);
  margin: 0 0 var(--space-2xs);
  font-weight: 500;
  font-style: normal;
}
.suggest-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--space-2xs);
  margin-bottom: var(--space-l);
}
.suggest-tile {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2xs);
  padding: var(--space-s) var(--space-s);
  min-height: 60px;
  background: var(--ink-700);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out-soft), background var(--dur-fast) var(--ease-out-soft);
}
.suggest-tile:hover, .suggest-tile:focus-visible {
  border-color: var(--gold-dim);
  background: var(--ink-600);
  outline: none;
}
.suggest-tile__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}
.suggest-tile__sub {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-300);
}
.suggest-tile__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-1);
  color: var(--ink-50);
  line-height: 1.15;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
  hyphens: auto;
}
.suggest-tile__add {
  flex-shrink: 0;
  width: 24px; height: 24px;
  border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--gold);
  font-size: 1.1rem;
  line-height: 1;
}

@media (max-width: 480px) {
  .suggest-grid { grid-template-columns: 1fr; }
  .suggest-tile { padding: var(--space-2xs) var(--space-s); }
  .chronicle-card { padding: var(--space-s); }
  .chronicle-card__title { font-size: var(--step-1); }
}
/* Private milestones — subtle dashed border + small lock chip in the
   meta line. The viewer is always either the author or an admin (the
   server filters before sending), so this is a "remember this isn't
   shared" cue, not access control. */
.chronicle-card--private {
  border-style: dashed;
  border-color: var(--gold-dim);
}
.chronicle-card__private {
  color: var(--gold-dim);
  font-family: var(--font-mono);
  font-size: .68rem;
  letter-spacing: .06em;
}

/* ---------- TILE GROUPS (radio-style option tiles) ---------- */
.tile-group {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: var(--space-2xs);
}
.tile-opt {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: 72px;
  padding: var(--space-2xs);
  background: var(--ink-700);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
  color: var(--ink-200);
  font-family: var(--font-body);
  font-size: var(--step--1);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out-soft), background var(--dur-fast) var(--ease-out-soft), color var(--dur-fast) var(--ease-out-soft);
}
.tile-opt:hover { border-color: var(--ink-400); }
.tile-opt.is-selected {
  border-color: var(--gold);
  background: var(--ink-600);
  color: var(--ink-100);
}
.tile-opt__glyph { font-size: 1.4rem; line-height: 1; color: var(--gold); }
.tile-opt__label {
  font-size: 0.75rem;
  text-align: center;
  line-height: 1.15;
  overflow-wrap: anywhere;
  hyphens: auto;
}

/* ---------- COMPACT TILE GROUP ----------
 * Used for the performer (who-did-it) picker — name-only chips, no glyph
 * line, smaller min-height than the standard tile-group. Lets a row of
 * 2-4 family members fit comfortably alongside the rest of the form
 * fields rather than dominating the sheet. */
.tile-group--compact {
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
}
.tile-opt--compact {
  min-height: 38px;
  padding: .35rem .5rem;
  gap: 2px;
}
.tile-opt--compact .tile-opt__label {
  font-size: .72rem;
  letter-spacing: .02em;
}

/* ---------- BUTTONS ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  min-height: 44px;
  padding: 0 var(--space-m);
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.02em;
  border: 1px solid var(--ink-500);
  color: var(--ink-50);
  background: var(--ink-700);
  transition: all var(--dur-fast) var(--ease-out-soft);
  -webkit-tap-highlight-color: transparent;
}
.btn:hover:not(:disabled) { border-color: var(--ink-400); background: var(--ink-600); }
.btn:active:not(:disabled) { transform: scale(0.97); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }
.btn--primary {
  background: var(--gold);
  color: var(--on-gold);
  border-color: var(--gold);
  font-weight: 600;
}
.btn--primary:hover:not(:disabled) {
  background: var(--gold-bright); border-color: var(--gold-bright);
  box-shadow: 0 0 30px var(--gold-glow);
}
.btn--ghost { background: transparent; border-color: var(--ink-500); }
.btn--small { min-height: 36px; padding: 0 var(--space-s); font-size: var(--step--2); }
.btn--block { width: 100%; }
.btn--icon { width: 40px; height: 40px; min-height: 40px; padding: 0; border-radius: 50%; }

/* ---------- FORM ---------- */
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: var(--space-m); }
/* `.field` is `display: flex`, which would otherwise win over the UA's
   `[hidden] { display: none }` (equal specificity, later rule wins). The
   checkups sheet relies on toggling the `hidden` attribute on .field rows,
   so re-assert the cascade for hidden fields. */
.field[hidden] { display: none; }
.field label {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ink-200);
}
.field input, .field select, .field textarea {
  width: 100%;
  background: var(--ink-800);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
  padding: 12px 14px;
  color: var(--ink-50);
  font-family: var(--font-body);
  font-size: var(--step-0);
  transition: border-color var(--dur-fast), background var(--dur-fast);
}
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none;
  border-color: var(--gold-dim);
  background: var(--ink-700);
}
.field--row { flex-direction: row; gap: var(--space-s); }
.field--row > * { flex: 1; }

/* ---------- STATUS PILLS ---------- */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border: 1px solid var(--ink-500);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-200);
  white-space: nowrap;
}
.pill::before {
  content: '';
  width: 5px; height: 5px;
  background: currentColor;
  border-radius: 50%;
}
.pill--done     { color: var(--ok); border-color: rgba(122, 145, 120, 0.35); }
.pill--upcoming { color: var(--ink-200); border-color: var(--ink-400); border-style: dashed; }
.pill--expected { color: var(--gold); border-color: var(--gold-dim); border-style: dotted; }
.pill--planned  { color: var(--gold-bright); border-color: var(--gold-dim); background: rgba(201, 168, 118, 0.06); }
.pill--due      { color: var(--due); border-color: rgba(217, 119, 87, 0.4); }
.pill--missed   { color: var(--miss); border-color: rgba(154, 85, 73, 0.4); }
.pill--postponed{ color: var(--warn); border-color: rgba(196, 147, 91, 0.4); }

/* ---------- BOTTOM NAV ---------- */
.nav {
  position: fixed;
  /* Centered via stretched anchors + auto margins, NOT left:50% +
     translateX(-50%). A transform on a position:fixed element gives it
     its own compositing layer, and iOS WebKit intermittently fails to
     re-anchor that layer after the on-screen keyboard dismisses or a
     sheet releases its overflow lock — the bar is then painted wherever
     the visual viewport last was, i.e. floating mid-screen (July 2026
     iPhone QA). Margin centering keeps the element on the normal
     fixed-positioning path with no layer promotion. */
  left: 0;
  right: 0;
  margin-inline: auto;
  width: fit-content;
  bottom: calc(var(--space-s) + env(safe-area-inset-bottom));
  z-index: 30;
  display: flex;
  gap: 2px;
  padding: 6px;
  /* Solid translucent floor for touch devices — backdrop-filter is gated
     to hover/fine-pointer below (see iOS PWA note on .topbar). */
  background: var(--surface-glass);
  border: 1px solid var(--ink-500);
  border-radius: 999px;
  box-shadow: 0 10px 40px -10px rgba(0,0,0,0.6);
}
/* While the iOS on-screen keyboard is open, position:fixed bottom
   elements pan with the visual viewport (by design in WebKit) and end
   up hovering over the content mid-screen. app.js toggles this class
   from a visualViewport resize listener; fading out beats fighting the
   viewport math. */
.nav.nav--kb-open {
  opacity: 0;
  pointer-events: none;
  transition: opacity .15s ease;
}
@media (hover: hover) and (pointer: fine) {
  .nav {
    backdrop-filter: blur(20px) saturate(150%);
    -webkit-backdrop-filter: blur(20px) saturate(150%);
  }
}
.nav__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  border-radius: 999px;
  color: var(--ink-300);
  transition: all var(--dur-fast) var(--ease-out-soft);
}
@media (max-width: 380px) {
  .nav__item { width: 40px; }
}
.nav__item:hover { color: var(--ink-100); }
.nav__item.is-active {
  background: var(--ink-700);
  color: var(--gold);
  box-shadow: inset 0 0 0 1px var(--gold-dim);
}
.nav__item svg { width: 20px; height: 20px; }

/* ---------- DASHBOARD HERO ---------- */
.hero {
  position: relative;
  padding: var(--space-xl) 0 var(--space-l);
  text-align: left;
}
/* Wraps the hero name + decorative zodiac silhouette. */
.hero__namefield {
  position: relative;
  isolation: isolate;          /* contain stacking */
  margin: var(--space-s) 0 var(--space-m);
  min-height: clamp(160px, 38vw, 260px);
  display: flex;
  align-items: center;
}
.hero__namefield .hero__name {
  position: relative;
  z-index: 10;                  /* above any decorative silhouette */
  margin: 0;
  padding-right: clamp(80px, 18vw, 200px);   /* breathing room before the silhouette */
  /* Soft dark halo so glyphs stay crisp even when overlapping the sigil */
  text-shadow: 0 0 18px rgba(10, 9, 8, 0.85);
}
/* Zodiac silhouette — anchored right, vertically centered behind the name.
   Pushed off the right edge with a strong gradient mask so long names
   never overlap dense parts of the silhouette. */
.hero__sigil {
  position: absolute;
  right: -2vw;
  top: 50%;
  transform: translateY(-50%);
  width: clamp(180px, 42vw, 300px);
  height: clamp(180px, 42vw, 300px);
  color: var(--gold);
  opacity: 0.28;
  pointer-events: none;
  user-select: none;
  z-index: 1;
  display: block;
  -webkit-mask-image: linear-gradient(to right, transparent 0%, transparent 30%, black 65%);
          mask-image: linear-gradient(to right, transparent 0%, transparent 30%, black 65%);
}
.hero__sigil svg,
.hero__sigil img { width: 100%; height: 100%; display: block; }
/* The detailed file-based SVGs ship with stroke="currentColor" which
   doesn't cross the <img> boundary, so without a filter the silhouette
   would render in default black. This chain converts that black to the
   gold accent (var(--gold) ≈ #c9a876). The inline-SVG onerror fallback
   uses currentColor directly and doesn't need tinting. */
.hero__sigil img {
  filter: brightness(0) saturate(100%) invert(72%) sepia(28%) saturate(530%) hue-rotate(358deg) brightness(94%) contrast(88%);
}

/* Element accent removed v1.44.27 — the textual "Ild / Vand / …"
   label in the hero__meta astro stat carries the element info, so
   doubling it with a decorative glyph just collided with the name's
   top-left corner. Sigil decal stays as the single hero ornament. */

@media (hover: hover) and (pointer: fine) {
  .hero__sigil {
    filter: drop-shadow(0 0 24px var(--gold-glow));
  }
}

@media (max-width: 480px) {
  .hero__sigil {
    right: -3vw;
    opacity: 0.26;
    width: clamp(150px, 50vw, 220px);
    height: clamp(150px, 50vw, 220px);
  }
}

/* Escape hatch for iOS standalone PWA only — if the stacked filter
   chain (drop-shadow + brightness/sepia tint) re-triggers the iOS
   compositor blank-middle bug, set <html data-ios-pwa="1"> from JS
   and the sigil hides only on that surface. Android PWAs, iOS Safari
   tabs, and desktop are unaffected. */
html[data-ios-pwa="1"] .hero__sigil { display: none; }
.hero__eyebrow {
  display: flex; align-items: center; gap: var(--space-xs);
  margin-bottom: var(--space-m);
}
.hero__eyebrow::after {
  content: ''; flex: 1; height: 1px;
  background: linear-gradient(to right, var(--ink-500), transparent);
}
.hero__name {
  font-size: var(--step-7);
  /* line-height 0.9 cropped descenders (g/j/y in names like "Stjernegaard")
     against the flex line-box on the namefield. Bumping to 1.0 + a touch of
     padding-bottom guarantees ink stays inside its container without
     adding visible spacing above the next element. */
  line-height: 1.0;
  letter-spacing: -0.02em;                                 /* less crowding so ascenders don't merge */
  font-variation-settings: 'opsz' 144, 'SOFT' 30;          /* less rounded so 'd' reads as 'd' */
  margin: var(--space-s) 0 var(--space-m);
  padding-bottom: 0.12em;
  background: linear-gradient(180deg, var(--ink-0) 0%, var(--ink-100) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}
.hero__meta {
  display: flex;
  gap: var(--space-l);
  flex-wrap: wrap;
  padding-top: var(--space-s);
  border-top: 1px solid var(--ink-500);
}
.hero__stat .k {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-200);
  margin-bottom: 2px;
}
.hero__stat .v {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-3);
  color: var(--ink-50);
  letter-spacing: -0.02em;
}
/* Sub-label inside a stat — used for the element name under the
   zodiac sign ("Vædderen / Ild"). Mono caps, gold-dim so it reads
   as supporting metadata rather than a second primary value. The
   element line-art glyph sits inline to the left of the label.
   `display: flex` (not inline-flex) so the whole row drops below the
   .v line — mirrors how .hero__stat-sub (birth time) sits below the
   .v birthdate — while keeping the icon + text on the same row. */
.hero__stat__sub {
  display: flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold-dim);
  margin-top: 2px;
}
/* Element line-art icon inline with its label. Sized to the cap
   height of the mono label so it reads as a small decorative bullet
   rather than a competing glyph. currentColor on the inline SVG
   means we tint via CSS color — no filter-chain needed. */
.hero__stat__sub-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 12px;
  height: 12px;
  color: var(--gold);
  flex: 0 0 auto;
}
.hero__stat__sub-icon svg {
  width: 100%;
  height: 100%;
  display: block;
}
/* Birth time-of-day under the date — small mono caption. Only appears
   when the stored DOB has a non-midnight time (renderBirthTime guards). */
.hero__stat-sub {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.12em;
  color: var(--ink-300);
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}

/* ---------- DEV-PHASE CITAT (current developmental phase, dashboard) ---------
 * Quiet contextual reminder shown just under the hero. Italic Fraunces
 * matches the timeline phase rows and hero typography so it reads as
 * an ambient note, not a notification banner. The button is full-width
 * but visually airy — minimal padding, transparent background, hairline
 * top + bottom — so it doesn't push or "land" against neighbouring
 * elements. Click opens the same detail sheet as timeline phases.
 */
.dev-citat {
  display: flex;
  align-items: flex-start;
  gap: var(--space-s);
  width: 100%;
  margin: var(--space-s) auto 0;
  padding: .85rem var(--space-s);
  background: transparent;
  border: none;
  border-top: 1px solid rgba(201, 168, 118, 0.15);
  border-bottom: 1px solid rgba(201, 168, 118, 0.15);
  text-align: left;
  cursor: pointer;
  color: inherit;
  font: inherit;
  transition: background var(--dur-fast) var(--ease-out-soft);
}
.dev-citat:hover { background: rgba(201, 168, 118, 0.04); }
.dev-citat:focus-visible { outline: 2px solid var(--gold-dim); outline-offset: -2px; }
.dev-citat__mark {
  flex-shrink: 0;
  font-size: 1.1rem;
  line-height: 1;
  color: var(--gold);
  margin-top: .15rem;
}
.dev-citat__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}
.dev-citat__eyebrow {
  font-family: var(--font-mono);
  font-size: .58rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--gold-dim);
}
.dev-citat__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: var(--step-0);
  color: var(--ink-50);
  line-height: 1.25;
}
.dev-citat__summary {
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--step--1);
  color: var(--ink-200);
  line-height: 1.4;
  margin-top: 2px;
}

/* ---------- ORBITAL DIAL ---------- */
.dial {
  position: relative;
  width: 100%;
  max-width: 320px;
  aspect-ratio: 1;
  /* Pulled tighter to the hero so the "Born" line and the age dial read
     as one composition; the bottom margin still breathes before the
     quick-log strip below. */
  margin: var(--space-2xs) auto var(--space-m);
}
.dial svg { width: 100%; height: 100%; }
.dial__track { fill: none; stroke: var(--ink-500); stroke-width: 0.5; }
.dial__progress {
  fill: none;
  stroke: var(--gold);
  stroke-width: 1.2;
  stroke-linecap: round;
  transition: stroke-dashoffset 1.2s var(--ease-out-soft);
}
@media (hover: hover) and (pointer: fine) {
  .dial__progress { filter: drop-shadow(0 0 6px var(--gold-glow)); }
}
.dial__tick { stroke: var(--ink-400); stroke-width: 0.5; }
.dial__tick--major { stroke: var(--ink-200); stroke-width: 0.6; }
.dial__label {
  font-family: var(--font-mono);
  font-size: 6px;
  fill: var(--ink-300);
  letter-spacing: 0.1em;
}
.dial__center {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center;
  pointer-events: none;
}
.dial__label {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-200);
  margin-bottom: 6px;
}
.dial__big {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-6);
  line-height: 1;
  letter-spacing: -0.04em;
}
.dial__sub {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-200);
  margin-top: 6px;
}
/* Second sub-line under the days count — shows total weeks, dimmer
   so the days line stays the visual primary. */
.dial__sub--weeks {
  margin-top: 2px;
  color: var(--ink-300);
}

/* ---------- TIMELINE ---------- */
.timeline {
  position: relative;
  padding: var(--space-l) 0;
}
.timeline::before {
  /* vertical hairline */
  content: '';
  position: absolute;
  left: 16px;
  top: 0;
  bottom: 0;
  width: 1px;
  background: linear-gradient(to bottom, transparent, var(--ink-500) 5%, var(--ink-500) 95%, transparent);
}
.tl-event {
  position: relative;
  padding: 0 0 var(--space-l) var(--space-xl);
  animation: tl-fade-in var(--dur-slow) var(--ease-out-soft) both;
}
@keyframes tl-fade-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}
.tl-event__node {
  position: absolute;
  left: 10px;
  top: 8px;
  width: 13px; height: 13px;
  border-radius: 50%;
  background: var(--ink-900);
  border: 1.5px solid var(--ink-400);
  transition: all var(--dur-fast) var(--ease-out-soft);
  z-index: 2;
}
.tl-event--done .tl-event__node {
  background: var(--gold);
  border-color: var(--gold);
  box-shadow: 0 0 0 4px var(--ink-900), 0 0 12px var(--gold-glow);
}
.tl-event--now .tl-event__node {
  background: var(--ink-900);
  border-color: var(--gold);
  box-shadow: 0 0 0 4px var(--ink-900), 0 0 16px var(--gold);
  animation: pulse 2.4s var(--ease-in-out) infinite;
}
.tl-event--due .tl-event__node { border-color: var(--due); }
.tl-event--missed .tl-event__node { border-color: var(--miss); }

/* Development phase row — same vertical rhythm as a regular event but
   visually muted: italic title, smaller text, no pill, hairline frame
   so it reads as "context", not "thing to action". Past phases dim
   further; the current phase tints the title gold. */
.tl-event.tl-phase {
  cursor: pointer;
  padding-bottom: var(--space-m);
}
.tl-phase__node {
  background: transparent !important;
  border-style: dashed !important;
  border-color: var(--gold-dim) !important;
  width: 11px !important; height: 11px !important;
  box-shadow: none !important;
  font-size: 0; /* hide the ∗ glyph; the dot itself is the marker */
}
.tl-phase__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: var(--step-0);
  color: var(--ink-100);
}
.tl-phase .tl-event__desc {
  color: var(--ink-200);
  font-size: var(--step--1);
}
.tl-phase--past { opacity: 0.55; }
.tl-phase--past .tl-phase__title { color: var(--ink-200); }
.tl-phase--now .tl-phase__title { color: var(--gold); }
.tl-phase--now .tl-phase__node {
  border-style: solid !important;
  border-color: var(--gold) !important;
  background: var(--gold-glow) !important;
}
.tl-phase:hover .tl-phase__node { border-color: var(--gold); }
.tl-phase:focus-visible { outline: 2px solid var(--gold-dim); outline-offset: 4px; border-radius: 6px; }
/* Developmental leap — same family as a phase, distinct accent.
   The ⤴ glyph reads as "step up / leap" which fits the metaphor. */
.tl-leap__node {
  background: var(--gold-glow) !important;
  border-style: dotted !important;
  font-size: .65rem !important;
  color: var(--gold) !important;
  /* Override the font-size:0 from .tl-phase__node so the ⤴ shows. */
}
.tl-leap--now .tl-leap__node {
  background: var(--gold) !important;
  color: var(--on-gold) !important;
  border-color: var(--gold) !important;
}
/* Dashboard leap citat — same template, modifier accent. */
.dev-citat--leap .dev-citat__mark {
  color: var(--gold);
  font-size: 1.1rem;
}
@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 4px var(--ink-900), 0 0 8px var(--gold); }
  50%      { box-shadow: 0 0 0 4px var(--ink-900), 0 0 20px var(--gold-bright); }
}
.tl-event__date {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-200);
  display: flex; gap: var(--space-xs); align-items: center;
  margin-bottom: 6px;
}
.tl-event__kind {
  color: var(--ink-300);
  padding-left: var(--space-xs);
  border-left: 1px solid var(--ink-500);
}
.tl-event__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-2);
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--ink-50);
  margin: 4px 0 6px;
}
.tl-event__desc {
  font-size: var(--step--1);
  color: var(--ink-200);
  line-height: 1.55;
}
.tl-event__meta {
  display: flex; flex-wrap: wrap; gap: var(--space-xs);
  margin-top: var(--space-xs);
}

/* Year/month separator chip on timeline */
.tl-sep {
  position: relative;
  margin: var(--space-l) 0 var(--space-m);
  padding-left: var(--space-xl);
}
.tl-sep__chip {
  display: inline-block;
  padding: 4px 14px;
  background: var(--ink-900);
  border: 1px solid var(--gold-dim);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold);
  position: relative;
  z-index: 2;
}

/* ---------- QUICK LOG FAB ROW ---------- */
.quick-log {
  display: grid;
  /* `minmax(0, 1fr)` (no hard floor) lets cells shrink to whatever the
     row can give them — important on very narrow phones where 6 cards
     × any positive minmax-min + gaps exceed the shell content width
     and the row breaks (overflows horizontally). Cards stay in a
     single line at every viewport; the per-button `max-width: 140px`
     and `justify-content: center` keep small rows (e.g. 2–3 active
     kinds) from stretching into wide rectangles. */
  grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
  justify-content: center;
  gap: var(--space-2xs);
  margin: var(--space-m) 0;
}
@media (max-width: 480px) { .quick-log { gap: 4px; } }
@media (max-width: 360px) { .quick-log { gap: 2px; } }
.quick-log__btn {
  position: relative;
  z-index: 1;
  /* `min-width: 0` lets the grid track shrink below intrinsic content
     width — without it, an un-breakable uppercase label expands the
     cell past 1fr and overflows horizontally on small screens.
     `max-width` paired with the parent's `justify-content: center`
     stops a row of e.g. 2 cards from growing into wide rectangles —
     they cap and the row centres in the available space. */
  min-width: 0;
  max-width: 140px;
  width: 100%;
  justify-self: center;
  display: flex; flex-direction: column; align-items: center;
  gap: 6px;
  padding: var(--space-s) 4px;
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
  background: var(--ink-700);
  transition: all var(--dur-fast) var(--ease-out-soft);
}
@media (max-width: 480px) {
  /* Tighter padding on narrow phones so a 6-card row keeps the icon and
     label visually balanced inside ~50px-wide cells. */
  .quick-log__btn { padding: 8px 2px; gap: 4px; }
  .quick-log__btn svg { width: 20px; height: 20px; }
}
.quick-log__btn:hover,
.quick-log__btn:focus-within {
  border-color: var(--gold-dim);
  background: var(--ink-600);
  /* Lift active card so its tooltip pseudo paints above sibling buttons that
     come later in the DOM (otherwise they cover the bubble's overflow). */
  z-index: 5;
}
.quick-log__btn svg { width: 22px; height: 22px; color: var(--gold); }
.quick-log__btn span {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-100);
  /* Long uppercase labels (e.g. "MEDICIN", "PUMPNING") would otherwise
     stretch the cell wider than 1fr; allow mid-word breaks rather than
     pushing the row past the viewport. */
  overflow-wrap: anywhere;
  hyphens: auto;
  text-align: center;
  max-width: 100%;
}
/* Since-last counter — bottom-center of the button, sits below the label.
   Top-right is reserved for the today's-count badge so the two pieces of
   information don't fight for the same anchor. The original 0.55rem mono
   read as illegible on phones in good light; bumped to 0.66rem with a
   500 weight + brighter --gold so the digits stand out without losing
   the hierarchy below the main button label. */
.quick-log__btn span.quick-log__since {
  position: absolute;
  bottom: 3px;
  left: 50%;
  transform: translateX(-50%);
  /* Proportional body font instead of mono — at this small size the
     mono digits read cramped, and a parent glancing at a fussy quick-
     log card has to actually parse "1h 23m" rather than count letters.
     Body sans gives more open glyphs + better legibility on iPhone
     viewports especially. Size + weight nudged up for the same reason. */
  font-family: var(--font-body);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--gold);
  background: var(--shade-overlay);
  padding: .12rem .55rem;
  border-radius: 999px;
  border: 1px solid rgba(201, 168, 118, 0.32);
  pointer-events: auto;  /* allow tooltip hover */
  cursor: help;
  white-space: nowrap;
}
.quick-log__btn span.quick-log__since--ongoing {
  /* "Currently sleeping" state — italic + warmer tint to distinguish from
     the static "since X ago" reading. */
  color: var(--gold, #c9a876);
  border-color: rgba(201, 168, 118, 0.45);
  font-style: italic;
}

/* Today's-count badge — top-right corner. Filled gold so the day's tally
   reads at-a-glance, distinct from the muted since-last counter below. */
.quick-log__btn span.quick-log__count {
  position: absolute;
  top: 4px;
  right: 5px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: .62rem;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--on-gold);
  background: var(--gold);
  border-radius: 999px;
  pointer-events: auto;
  cursor: help;
  line-height: 1;
}

/* Last feed-type glyph — top-left of the feed card, mirroring the count
   pill on the right at the same height/anchor so the two corners read
   as a symmetric pair. Ghost-styled (no fill) so it stays informational
   without competing with the today's-count emphasis. */
.quick-log__btn span.quick-log__type {
  position: absolute;
  top: 2px;
  left: 3px;
  min-width: 22px;
  height: 22px;
  padding: 0 3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.15rem;
  line-height: 1;
  letter-spacing: 0;
  text-transform: none;
  color: var(--gold);
  pointer-events: auto;
  cursor: help;
}

/* In-app notification badge — same anchor as the count pill (top-right)
   but red-coded so unread alerts read as action-required at a glance.
   Renders alongside renderCountBadge only conceptually: the dashboard
   suppresses the count badge while an alert is showing so they don't
   stack in the same corner. */
.quick-log__btn span.quick-log__alert {
  position: absolute;
  top: 4px;
  right: 5px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: 0;
  text-transform: none;
  color: #fff;
  background: #b85e5e;
  border-radius: 999px;
  pointer-events: auto;
  cursor: help;
  line-height: 1;
  z-index: 2;
  box-shadow: 0 0 0 2px var(--ink-700);
}
/* Left-anchored variant — used on the medicine card where the count
   badge already lives top-right and we need both visible at once. */
.quick-log__btn span.quick-log__alert--left {
  right: auto;
  left: 5px;
}
/* Subtle pulse on the whole card so the alert reads even when the user
   is glancing away from the corner. Animation is short + finite (six
   pulses then settle) to avoid an "always demanding" feeling — the
   badge stays after the pulse fades. */
.quick-log__btn.has-alert,
.tile.has-alert {
  animation: neoAlertPulse 1.6s ease-out 0s 6;
  position: relative;
}
@keyframes neoAlertPulse {
  0%   { box-shadow: 0 0 0 0   rgba(184, 94, 94, 0.45); }
  60%  { box-shadow: 0 0 0 8px rgba(184, 94, 94, 0); }
  100% { box-shadow: 0 0 0 0   rgba(184, 94, 94, 0); }
}

/* Tile-row alert badge — same look as the quick-log alert but anchored
   to the tile corner (tiles are larger so we can sit slightly outside
   the icon to avoid overlap). */
.tile .quick-log__alert {
  position: absolute;
  top: 8px;
  right: 8px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: .62rem;
  font-weight: 700;
  color: #fff;
  background: #b85e5e;
  border-radius: 999px;
  line-height: 1;
  z-index: 2;
  box-shadow: 0 0 0 2px var(--ink-700);
}

/* Vertical breathing room so the bottom-center since-badge has space below
   the label without clipping. */
.quick-log__btn { padding-bottom: calc(var(--space-s) + 12px); }

/* History link beneath the quick-log row — visually tethered to the card row
   above via a hairline that aligns with the card row's bottom edge, so the
   link reads as a tail/footer on the row, not a separate orphan element. */
.quick-log__history {
  /* Pull up so the link's top sits flush with the card row's gap baseline. */
  margin: -2px 0 var(--space-m);
  padding: .55rem .15rem 0;
  display: flex;
  align-items: center;
  gap: .5rem;
}
.quick-log__history::before {
  content: "";
  width: 18px;
  height: 1px;
  background: linear-gradient(to right, transparent, var(--gold-dim));
  flex: 0 0 18px;
}
.quick-log__history a {
  font-family: var(--font-mono);
  font-size: .65rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--gold-dim);
  text-decoration: none;
  position: relative;
  padding: .15rem 0;
  transition: color .18s ease, letter-spacing .18s ease;
}
.quick-log__history a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: var(--gold-glow);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .25s var(--ease-out-soft);
}
.quick-log__history a:hover {
  color: var(--gold);
  letter-spacing: .18em;
}
.quick-log__history a:hover::after { transform: scaleX(1); }

/* Custom tooltip on the badges (and anywhere else with [data-tooltip]). */
[data-tooltip] {
  position: relative;
}
[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  /* --tt-shift is set per-element by installTooltipClamp() in app.js to keep
     the bubble inside the viewport on edge triggers. Defaults to 0. */
  transform: translateX(calc(-50% + var(--tt-shift, 0px))) translateY(4px);
  background: var(--ink-700);
  color: var(--ink-50);
  border: 1px solid var(--gold-glow);
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: .72rem;
  font-weight: 400;
  font-style: normal;
  letter-spacing: 0;
  line-height: 1.35;
  text-transform: none;
  /* Width: hug short strings on one line; wrap only when the natural width
     exceeds the cap or the viewport. */
  width: max-content;
  max-width: min(220px, calc(100vw - 2rem));
  white-space: normal;
  word-break: normal;
  overflow-wrap: anywhere;
  padding: .4rem .65rem;
  border-radius: 6px;
  pointer-events: none;
  opacity: 0;
  transition: opacity .12s ease, transform .15s ease;
  z-index: 50;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
  text-align: center;
}
[data-tooltip]:hover::after,
[data-tooltip]:focus-visible::after {
  opacity: 1;
  transform: translateX(calc(-50% + var(--tt-shift, 0px))) translateY(0);
}
/* Off-screen measurer used by installTooltipClamp() — must mirror the
   tooltip's width-affecting styles so getBoundingClientRect returns the
   real rendered width. */
.tt-probe {
  position: fixed;
  visibility: hidden;
  pointer-events: none;
  left: -9999px;
  top: 0;
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: .72rem;
  font-weight: 400;
  line-height: 1.35;
  letter-spacing: 0;
  padding: .4rem .65rem;
  border: 1px solid transparent;
  border-radius: 6px;
  width: max-content;
  max-width: min(220px, calc(100vw - 2rem));
  white-space: normal;
  word-break: normal;
  overflow-wrap: anywhere;
  text-align: center;
  box-sizing: border-box;
}
/* Hide tooltips on touch — coarse pointer devices can't really hover */
@media (hover: none) {
  [data-tooltip]::after { display: none; }
}
@media (max-width: 380px) {
  .quick-log__btn span:not(.quick-log__since):not(.quick-log__type) { font-size: .58rem; letter-spacing: .08em; }
}

/* ---------- TILE ROW (secondary nav) ---------- */
.tile-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-s);
  margin: var(--space-s) 0 var(--space-m);
}
.tile {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  padding: var(--space-s) var(--space-m);
  background: linear-gradient(135deg, var(--ink-700) 0%, var(--ink-800) 100%);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
  transition: all var(--dur-fast) var(--ease-out-soft);
  text-align: left;
  min-width: 0;        /* allow grid cell to shrink below content size */
  overflow: hidden;
}
.tile > div {
  min-width: 0;        /* let ellipsis kick in on .tile__sub */
  flex: 1;
}
.tile:hover {
  border-color: var(--gold-dim);
  transform: translateY(-1px);
  box-shadow: 0 8px 30px -10px rgba(0,0,0,0.5);
}
.tile svg {
  width: 22px; height: 22px;
  color: var(--gold);
  flex-shrink: 0;
}
/* Right-aligned secondary content on a tile (e.g. the milk stock badge).
   Stays out of the flex flow's growing column so the label/sub block
   keeps its ellipsis behaviour. */
.tile__badge {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.04em;
  color: var(--ink-100);
}
.tile__badge-row {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}
.tile__badge-glyph { color: var(--gold); font-size: .9rem; line-height: 1; }

@media (max-width: 480px) {
  .tile { padding: var(--space-s); gap: var(--space-2xs); }
  .tile__sub { letter-spacing: 0.04em; }
}
.tile__label {
  display: block;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--step-0);
  color: var(--ink-50);
  letter-spacing: -0.01em;
  line-height: 1.2;
}
.tile__sub {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  color: var(--ink-300);
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------- DATA TABLE (history etc.) ---------- */
.data-table-wrap {
  background: linear-gradient(180deg, var(--ink-700) 0%, var(--ink-800) 100%);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-l);
  overflow-x: auto;
}
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--ink-100);
}
.data-table thead th {
  padding: 0.75rem 1rem;
  text-align: left;
  font-family: var(--font-body);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-weight: 500;
  color: var(--ink-300);
  border-bottom: 1px solid var(--ink-500);
  background: rgba(201, 168, 118, 0.04);
  white-space: nowrap;
}
.data-table tbody td {
  padding: 0.7rem 1rem;
  border-bottom: 1px solid var(--ink-600);
  vertical-align: middle;
}
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr:hover td { background: rgba(201, 168, 118, 0.04); }
.data-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.data-table .num--gold { color: var(--gold); }
.data-table .muted { color: var(--ink-400); }

/* ---------- SECTION HEADER ---------- */
.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin: var(--space-xl) 0 var(--space-m);
  gap: var(--space-s);
}
.section-head h2 {
  font-size: var(--step-3);
  font-style: italic;
  font-weight: 300;
  letter-spacing: -0.02em;
}
.section-head a, .section-head button {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-200);
  transition: color var(--dur-fast);
}
.section-head a:hover, .section-head button:hover { color: var(--gold); }

/* ---------- NEXT EVENTS ROW ---------- */
.next-list { display: flex; flex-direction: column; gap: var(--space-s); }
.next-item {
  display: grid;
  grid-template-columns: 56px 1fr auto;
  gap: var(--space-s);
  align-items: center;
  padding: var(--space-s);
  background: var(--ink-800);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
  transition: all var(--dur-fast);
  cursor: pointer;
}
.next-item:hover { border-color: var(--gold-dim); transform: translateX(2px); }
.next-item__date {
  text-align: center;
  padding: 6px 0;
  border-right: 1px solid var(--ink-500);
}
.next-item__day {
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--step-3);
  line-height: 1;
  color: var(--ink-50);
}
.next-item__month {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-200);
  display: block;
  margin-top: 2px;
}
/* min-width: 0 lets the body shrink instead of pushing the pill column off
   the right edge — without this, a long title forces overflow and clips
   the badge on narrow viewports. The h3 also gets word-break to wrap
   gracefully when the title still doesn't fit at small font sizes. */
.next-item__body { min-width: 0; }
.next-item__body h3 {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--step-0);
  line-height: 1.3;
  margin: 0 0 2px;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}
.next-item__body .caption { font-size: var(--step--1); }
.next-item .pill { flex-shrink: 0; align-self: center; }

/* On phones the pill text ("Planlagt", "Forventes") would still nudge
   the body column. Cut its tracking + padding so it sits compactly
   against the right edge without crowding the title. */
@media (max-width: 480px) {
  .next-item {
    grid-template-columns: 48px 1fr auto;
    gap: var(--space-2xs);
    padding: var(--space-2xs) var(--space-s);
  }
  .next-item .pill {
    padding: 2px 8px;
    font-size: 0.58rem;
    letter-spacing: 0.08em;
  }
}

/* ---------- MODAL / SHEET ---------- */
.sheet-backdrop {
  position: fixed; inset: 0;
  /* Slightly darker baseline (0.7 vs the previous 0.55 + blur) so the
     modal still reads as a focused dim layer on touch devices, where
     backdrop-filter is gated off (see iOS PWA note on .topbar). */
  background: rgba(0, 0, 0, 0.7);
  z-index: 40;
  opacity: 0;
  transition: opacity var(--dur-med);
}
@media (hover: hover) and (pointer: fine) {
  .sheet-backdrop {
    background: var(--shade-overlay-strong);
    -webkit-backdrop-filter: blur(6px);
            backdrop-filter: blur(6px);
  }
}
.sheet-backdrop.is-open { opacity: 1; }
.sheet {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 41;
  max-height: 90vh;
  max-height: 90dvh;
  overflow-y: auto;
  background: var(--ink-800);
  border-top: 1px solid var(--ink-500);
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  padding: var(--space-m) var(--space-m) calc(var(--space-xl) + env(safe-area-inset-bottom));
  transform: translateY(100%);
  transition: transform var(--dur-med) var(--ease-out-soft);
  box-shadow: 0 -20px 60px -10px rgba(0,0,0,0.8);
}
.sheet.is-open { transform: translateY(0); }
.sheet::before {
  content: '';
  display: block;
  width: 40px; height: 4px;
  background: var(--ink-400);
  border-radius: 999px;
  margin: 0 auto var(--space-m);
}
.sheet__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-3);
  margin-bottom: var(--space-s);
  letter-spacing: -0.02em;
  /* Reserve space so a long title never tucks under the close ×. */
  padding-right: var(--space-xl);
}

/* Close × tucked into the sheet's top-right corner. Tap-friendly hit area
   on touch (40×40 minimum), subtle hover/focus on desktop. The button is
   static markup inside #sheet so it's always present without each caller
   having to add it. */
.sheet__close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 999px;
  color: var(--ink-200);
  cursor: pointer;
  transition: color var(--dur-fast), background var(--dur-fast), border-color var(--dur-fast);
  z-index: 2;
}
.sheet__close:hover,
.sheet__close:focus-visible {
  color: var(--gold);
  background: rgba(201, 168, 118, 0.08);
  border-color: var(--gold-dim);
}

/* ---------- LOGIN ---------- */
.login {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: var(--space-l);
}
.login__card {
  width: 100%;
  max-width: 420px;
  padding: var(--space-xl) var(--space-l);
  background: linear-gradient(180deg, var(--ink-700), var(--ink-800));
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-xl);
  box-shadow: 0 30px 80px -30px rgba(0,0,0,0.8), 0 0 60px -30px var(--gold-glow);
  text-align: center;
}
.login__mark {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-6);
  letter-spacing: -0.04em;
  margin-bottom: var(--space-2xs);
}
.login__tagline {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: var(--space-l);
}
.login form { text-align: left; }
.login__error {
  color: var(--due);
  font-size: var(--step--1);
  margin-top: var(--space-s);
  min-height: 1.4em;
}

/* ---------- EMPTY STATES ---------- */
.empty {
  padding: var(--space-xl) var(--space-m);
  text-align: center;
  border: 1px dashed var(--ink-500);
  border-radius: var(--radius-l);
  color: var(--ink-200);
}
.empty h3 {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-2);
  color: var(--ink-100);
  margin-bottom: var(--space-xs);
}

/* ---------- CHART ---------- */
.chart {
  width: 100%;
  background: linear-gradient(180deg, var(--ink-700) 0%, var(--ink-800) 100%);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-l);
  padding: var(--space-s) var(--space-m) var(--space-m);
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}
.chart__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-s);
  flex-wrap: wrap;
}
.chart__value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--gold);
  font-size: var(--step-0);
  white-space: nowrap;
}
.chart__svg-wrap {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
}
/* Floating tooltip placed inside the chart wrapper. JS positions it via
   transform on pointermove and toggles `.is-visible` on enter/leave —
   opacity-based so it can fade smoothly (especially nice on mobile,
   where it auto-dismisses after a few seconds). */
.chart__tip {
  position: absolute;
  top: 0; left: 0;
  pointer-events: none;
  background: var(--ink-700);
  color: var(--ink-50);
  border: 1px solid var(--gold-glow);
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: .72rem;
  font-weight: 400;
  line-height: 1.35;
  padding: .4rem .6rem;
  border-radius: 6px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
  max-width: min(220px, calc(100% - 8px));
  white-space: normal;
  word-break: normal;
  overflow-wrap: anywhere;
  text-align: left;
  z-index: 5;
  opacity: 0;
  visibility: hidden;
  transition: opacity .18s ease, visibility .18s linear;
}
.chart__tip.is-visible {
  opacity: 1;
  visibility: visible;
}

/* Visible data points highlight while their tooltip is showing. CSS-only —
   the helper just toggles `.is-tip-active` on the matched element. We use
   filter (drop-shadow) + a thicker stroke so the dot stays in place; SVG
   r/transform changes are messier (transform-origin defaults to 0,0). */
.chart .data-point.is-tip-active,
.chart .data-point--birth.is-tip-active,
.logs-stat__dot.is-tip-active {
  stroke-width: 2.5;
  transition: stroke-width .12s ease, filter .12s ease;
}
.chart svg { width: 100%; height: 100%; display: block; }
.chart .grid-line { stroke: var(--ink-500); stroke-width: 0.5; stroke-dasharray: 2 4; }
.chart .axis-label { font-family: var(--font-mono); font-size: 9px; fill: var(--ink-300); letter-spacing: 0.1em; }
.chart .data-line { fill: none; stroke: var(--gold); stroke-width: 1.5; }
@media (hover: hover) and (pointer: fine) {
  .chart .data-point.is-tip-active,
  .chart .data-point--birth.is-tip-active,
  .logs-stat__dot.is-tip-active {
    filter: drop-shadow(0 0 6px var(--gold-glow, rgba(201, 168, 118, 0.5)));
  }
  .chart .data-line { filter: drop-shadow(0 0 6px var(--gold-glow)); }
}
.chart .data-point { fill: var(--gold); stroke: var(--ink-900); stroke-width: 1.5; }
.chart .data-area { fill: url(#areaGrad); opacity: 0.3; }
/* Default: most painted SVG layers should NOT trap the cursor — only data
   points and WHO bands carry meaningful tooltips. */
.chart .grid-line,
.chart .axis-label,
.chart .data-area-fill,
.chart .data-line,
.chart .data-point,
.chart .data-point--birth,
.chart .who-median { pointer-events: none; }

/* Invisible hit area around each data point. r=10 makes 3px dots reliably
   tappable on touch and pixel-friendly with a mouse. The fill is `none` so
   the visible dots paint underneath; pointer-events:all so SVG hit-testing
   still picks it up despite no fill. */
.chart .data-hit {
  fill: transparent;
  pointer-events: all;
  cursor: help;
}

/* Birth point — open ring on top of the chart so the user can tell which
   dot was mirrored from the profile (vs. logged after birth). */
.chart .data-point--birth {
  fill: var(--ink-800);
  stroke: var(--gold);
  stroke-width: 1.5;
}

/* WHO percentile overlay. Outer band = p3..p97; inner band = p15..p85
   (the central two-thirds), drawn slightly more opaque. Fill + opacity
   are inlined on the polygon's `style` attribute (see growth.js) so the
   bands survive a stale-cache CSS load — these CSS rules are kept as a
   secondary safety net but should NOT set `opacity`, since that would
   multiply against the inline `fill-opacity` and erase the band entirely.
   Colours stay neutral so they don't compete with the gold data line. */
.chart .who-band {
  fill: var(--ink-100);
  pointer-events: all;
  cursor: help;
}

/* WHO toggle pill in the chart head — flips active when the overlay is on. */
.who-toggle {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-300);
  background: transparent;
  border: 1px solid var(--ink-500);
  border-radius: 999px;
  padding: 3px 10px;
  cursor: pointer;
  transition: color var(--dur-fast), border-color var(--dur-fast), background var(--dur-fast);
}
.who-toggle:hover { color: var(--gold); border-color: var(--gold-dim); }
.who-toggle.is-on {
  color: var(--on-gold);
  background: var(--gold);
  border-color: var(--gold);
}

/* Editable history rows on the growth view. */
.growth-row {
  cursor: pointer;
  transition: background var(--dur-fast);
}
.growth-row:hover,
.growth-row:focus-visible { background: rgba(201, 168, 118, 0.08); }
.growth-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  font-family: var(--font-mono);
  font-size: .56rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--gold);
  border: 1px solid var(--gold-dim);
  border-radius: 999px;
}

/* Logs highlights — compact stat cards above the daily-area charts.
   Surfaces richer facts (favourite breast, busiest hour, longest sleep)
   that aren't easily readable from the line charts alone. Auto-fit grid
   so 2 cards fill a phone width and 4-5 spread across desktop. */
.logs-highlights {
  display: grid;
  /* Tight floor (145px) so the desktop view packs 4–5 cards per row
     instead of stretching 3 cards across the available width and
     orphaning the 4th onto its own line. The smaller --step-0 value
     font means compound strings still fit at this width; longer
     values that need a second line are handled by line-clamp on
     `.logs-highlight__value`. */
  grid-template-columns: repeat(auto-fit, minmax(145px, 1fr));
  gap: var(--space-2xs);
  margin: var(--space-s) 0 var(--space-m);
}
.logs-highlight {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: var(--space-s);
  background: var(--ink-700);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
  min-width: 0;  /* let the text ellipsise instead of stretching the cell */
}
.logs-highlight__label {
  font-family: var(--font-mono);
  font-size: .58rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--gold-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.logs-highlight__value {
  font-family: var(--font-display);
  font-style: italic;
  /* `var(--step-0)` (≈17px) reads as a display value at this size when
     italic serif, and — unlike --step-1 (≈22px) — leaves enough room
     in a 170px card for compound values like "21:00 – 22:00" or
     "Bryst højre · 67%" without truncation. */
  font-size: var(--step-0);
  color: var(--ink-50);
  line-height: 1.25;
  /* Allow up to two wrapped lines for the rare value that's longer
     than one card-width; longer than that ellipsises as a fallback. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}
@media (max-width: 480px) {
  .logs-highlights {
    grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
    gap: 4px;
  }
  .logs-highlight { padding: .55rem; }
  .logs-highlight__label { font-size: .52rem; letter-spacing: .18em; }
}

/* Logs stats — one card per metric. Each card is an area chart with an
   inline summary header (today / avg / peak) and a sparse date axis. */
.logs-stats {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  margin: 0 0 var(--space-m);
}
.logs-stat {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: var(--space-s) var(--space-m);
  background: linear-gradient(180deg, var(--ink-700) 0%, var(--ink-800) 100%);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-l);
}
.logs-stat__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: .5rem;
  flex-wrap: wrap;
}
.logs-stat__label {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-200);
  flex-shrink: 0;
}
.logs-stat__stats {
  display: inline-flex;
  gap: 14px;
  flex-wrap: wrap;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-variant-numeric: tabular-nums;
}
.logs-stat__stat {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  color: var(--ink-100);
}
.logs-stat__stat-k {
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-300);
}
.logs-stat__stat strong {
  font-weight: 500;
  color: var(--gold);
}
.logs-stat__wrap {
  /* Reuses .chart__svg-wrap as the tooltip-aware container, but the area
     chart should fill its own height — the 16:9 aspect from the growth
     chart is too tall for a glanceable stats strip. */
  aspect-ratio: auto;
  height: 64px;
}
.logs-stat__svg {
  display: block;
  width: 100%;
  height: 100%;
}
.logs-stat__baseline {
  stroke: var(--ink-500);
  stroke-width: 1;
  stroke-dasharray: 2 3;
  pointer-events: none;
}
.logs-stat__area { pointer-events: none; }
.logs-stat__line {
  fill: none;
  stroke: var(--gold);
  stroke-width: 1.4;
  stroke-linejoin: round;
  stroke-linecap: round;
  pointer-events: none;
  vector-effect: non-scaling-stroke;
}
.logs-stat__dot {
  fill: var(--gold);
  pointer-events: none;
}
.logs-stat__dot--today {
  fill: var(--gold);
  stroke: var(--ink-900);
  stroke-width: 1;
}
@media (hover: hover) and (pointer: fine) {
  .logs-stat__line { filter: drop-shadow(0 0 4px var(--gold-glow)); }
  .logs-stat__dot--today { filter: drop-shadow(0 0 6px var(--gold-glow)); }
}
.logs-stat__hit {
  fill: transparent;
  pointer-events: all;
  cursor: help;
}
.logs-stat__axis {
  display: flex;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink-300);
  padding-top: 2px;
}

@media (max-width: 480px) {
  .chart { padding: var(--space-2xs) var(--space-s) var(--space-s); }
  .chart__svg-wrap { aspect-ratio: 4 / 3; }
  .chart .axis-label { font-size: 8px; }
  .chart .data-point { stroke-width: 1; }
}

/* ---------- UTILITY ---------- */
.flex { display: flex; }
.flex-col { flex-direction: column; }
.gap-s { gap: var(--space-s); }
.gap-m { gap: var(--space-m); }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.text-muted { color: var(--ink-200); }
.text-gold { color: var(--gold); }
.mb-s { margin-bottom: var(--space-s); }
.mb-m { margin-bottom: var(--space-m); }
.mb-l { margin-bottom: var(--space-l); }

/* ---------- CONFIRM DIALOG ----------
   In-app replacement for window.confirm(). Stacks above the sheet
   layer (z-index 41) and the fullscreen image overlay (z-index 1000)
   so a delete-confirm inside a fullscreen photo viewer still wins. */
.confirm-root {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-m);
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms;
}
.confirm-root.is-open {
  opacity: 1;
  pointer-events: auto;
}
.confirm-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.7);
  cursor: pointer;
}
.confirm-card {
  position: relative;
  max-width: 420px;
  width: 100%;
  padding: var(--space-l);
  background: var(--ink-800);
  border: 1px solid var(--gold-dim);
  border-radius: var(--radius-l);
  box-shadow: 0 20px 60px -10px rgba(0, 0, 0, 0.8);
  transform: translateY(8px);
  transition: transform 200ms var(--ease-out-soft);
}
.confirm-root.is-open .confirm-card { transform: translateY(0); }
.confirm-card__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: var(--step-2);
  color: var(--ink-50);
  margin: 0 0 .5rem;
  line-height: 1.2;
}
.confirm-card__body {
  font-size: var(--step-0);
  color: var(--ink-100);
  line-height: 1.5;
  margin: 0 0 var(--space-m);
}
.confirm-card__actions {
  display: flex;
  gap: .5rem;
  justify-content: flex-end;
}
.confirm-card__actions .btn { flex: 0 1 auto; min-width: 100px; }
@media (max-width: 480px) {
  .confirm-card__actions { flex-direction: column-reverse; }
  .confirm-card__actions .btn { width: 100%; }
}

/* Destructive variant — used for the "Confirm delete" button so the
   user has a clear visual cue that this action removes data. */
.btn--danger {
  background: var(--miss);
  color: #f5efe4;
  border: 1px solid var(--miss);
}
.btn--danger:hover,
.btn--danger:focus-visible {
  background: rgba(154, 85, 73, 0.85);
  border-color: rgba(154, 85, 73, 0.85);
}

/* ---------- TOAST ---------- */
.toast {
  position: fixed;
  left: 50%;
  bottom: calc(96px + env(safe-area-inset-bottom));
  transform: translateX(-50%) translateY(20px);
  /* Above the fullscreen image-viewer overlay (z:1000) so the
     "Downloading photo…" / share-copied toasts surface even when
     the user is in fullscreen — otherwise they fired silently. */
  z-index: 1100;
  padding: 10px 18px;
  background: var(--ink-600);
  border: 1px solid var(--gold-dim);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.05em;
  color: var(--ink-50);
  opacity: 0;
  pointer-events: none;
  transition: all var(--dur-med) var(--ease-out-soft);
  white-space: nowrap;
  box-shadow: 0 10px 40px -10px rgba(0,0,0,0.6);
}
.toast.is-show { opacity: 1; transform: translateX(-50%) translateY(0); pointer-events: auto; }

/* ---------- DISCOVERABILITY NUDGE ----------
   Soft champagne-bordered card that surfaces "you can configure
   notifications" to admins/caretakers who haven't engaged with the
   notification system yet. Lives between the dev-phase citat and the
   quick-log row on the dashboard; dismissable via localStorage flag.
   Visual is intentionally non-urgent — same restrained palette as the
   dev-citat block so the dashboard reads as one cohesive surface. */
.nudge-card {
  display: flex;
  gap: var(--space-s);
  align-items: flex-start;
  padding: var(--space-s) var(--space-m);
  margin: var(--space-m) 0;
  background: var(--ink-800);
  border: 1px solid var(--gold-dim);
  border-radius: var(--radius-l);
  box-shadow: 0 4px 18px -10px var(--gold-glow);
}
.nudge-card__icon {
  flex: 0 0 auto;
  font-size: 1.25rem;
  line-height: 1;
  margin-top: 0.15rem;
  color: var(--gold);
}
.nudge-card__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.nudge-card__eyebrow {
  font-family: var(--font-mono);
  font-size: 0.58rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-dim);
}
.nudge-card__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: var(--step-1);
  margin: 0.1rem 0 0;
  color: var(--ink-50);
  line-height: 1.2;
}
.nudge-card__desc {
  font-size: var(--step--1);
  line-height: 1.4;
  margin: 0.35rem 0 0;
  color: var(--ink-200);
}
.nudge-card__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  flex: 0 0 auto;
  align-items: stretch;
  min-width: 110px;
}
.nudge-card__actions .btn--sm {
  padding: 6px 14px;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  white-space: nowrap;
}
@media (max-width: 480px) {
  .nudge-card { flex-wrap: wrap; padding: var(--space-s); }
  .nudge-card__actions { width: 100%; flex-direction: row; }
  .nudge-card__actions .btn--sm { flex: 1; }
}

/* ---------- EVENING MOOD CARD ----------
   Passive end-of-day prompt that slots in below the citats. Same warm
   --ink-800 surface as the nudge cards above but without the gold-dim
   border to read as quieter (it's a question, not a call to action). */
.mood-card {
  margin: var(--space-m) 0;
  padding: var(--space-s) var(--space-m);
  background: var(--ink-800);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-l);
}
.mood-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
}
.mood-card__eyebrow {
  font-family: var(--font-mono);
  font-size: .58rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--gold-dim);
}
.mood-card__dismiss {
  background: transparent;
  border: 0;
  font-size: 1.2rem;
  line-height: 1;
  color: var(--ink-300);
  cursor: pointer;
  padding: 0 .25rem;
  transition: color var(--dur-fast);
}
.mood-card__dismiss:hover { color: var(--ink-100); }
.mood-card__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: var(--step-1);
  color: var(--ink-50);
  margin: .35rem 0 .75rem;
}
.mood-card__row {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 6px;
}
.mood-card__btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: .55rem 0;
  background: transparent;
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
  color: var(--ink-100);
  cursor: pointer;
  transition: background var(--dur-fast), border-color var(--dur-fast), transform var(--dur-fast);
  min-width: 0;
}
.mood-card__btn:hover {
  background: rgba(201, 168, 118, 0.06);
  border-color: var(--gold-dim);
}
.mood-card__btn:active { transform: scale(0.96); }
.mood-card__btn.is-selected {
  background: rgba(201, 168, 118, 0.12);
  border-color: var(--gold);
  box-shadow: inset 0 0 0 1px var(--gold-dim);
}
.mood-card__emoji {
  font-size: 1.55rem;
  line-height: 1;
}
.mood-card__label {
  font-family: var(--font-mono);
  font-size: .55rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-200);
  text-align: center;
  /* Long labels (e.g. "Vidunderlig") clip to ellipsis on narrow phones
     rather than push the grid wider. */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mood-card__btn.is-selected .mood-card__label { color: var(--gold); }
.mood-card__add-note {
  display: inline-block;
  margin-top: .75rem;
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--gold-dim);
  font-family: var(--font-mono);
  font-size: .68rem;
  letter-spacing: .1em;
  cursor: pointer;
}
.mood-card__add-note:hover { color: var(--gold); }
.mood-card__add-note[hidden] { display: none; }
.mood-card__note {
  margin-top: .75rem;
}
.mood-card__note textarea {
  width: 100%;
  resize: vertical;
  min-height: 2.2rem;
  padding: .55rem .7rem;
  background: var(--ink-900);
  color: var(--ink-50);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
  font-family: var(--font-body);
  font-size: .9rem;
}
.mood-card__note[hidden] { display: none; }
@media (max-width: 380px) {
  .mood-card__label { font-size: .5rem; letter-spacing: .08em; }
  .mood-card__emoji { font-size: 1.35rem; }
}

/* ---------- STAGGERED REVEAL ----------
   The animation uses `both` so children hold the `from` state (opacity:0)
   during the staggered delay before they animate in. The risk: if the
   browser's animation timer never fires the keyframe (iPhone PWA standalone
   on iOS 18 intermittently drops queued animations under memory pressure
   AND when Reduce Motion is on), every `.reveal > *` stays at opacity:0
   forever and the user sees a blank middle (May 2026 QA: mother's iPhone
   16 Pro PWA — dashboard / vaccines / checkups middle goes blank on
   scroll/reload).

   Fix: gate the entrance animation to hover-capable / fine-pointer
   devices that explicitly do NOT prefer reduced motion. Touch devices and
   Reduce-Motion users see content immediately at its natural state — no
   reliance on the animation timer to make anything visible. */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .reveal > * { animation: reveal var(--dur-slow) var(--ease-out-soft) both; }
  .reveal > *:nth-child(1) { animation-delay: 50ms; }
  .reveal > *:nth-child(2) { animation-delay: 150ms; }
  .reveal > *:nth-child(3) { animation-delay: 250ms; }
  .reveal > *:nth-child(4) { animation-delay: 350ms; }
  .reveal > *:nth-child(5) { animation-delay: 450ms; }
  .reveal > *:nth-child(6) { animation-delay: 550ms; }
}
@keyframes reveal {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ---------- SETTINGS PAGE ---------- */
.settings-view { padding-bottom: 2rem; }

/* Group: shared eyebrow header above a stack of cards. */
.settings-group {
  margin: 1.75rem 0 0;
}
.settings-group:first-of-type { margin-top: 1rem; }
.settings-group__title {
  margin: 0 0 .5rem;
  padding: 0 .25rem;
  font-size: .7rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--gold-dim, #a18258);
  font-weight: 500;
}
.settings-group__cards {
  background: rgba(201, 168, 118, 0.04);
  border: 1px solid rgba(201, 168, 118, 0.16);
  border-radius: 14px;
  overflow: hidden;
}

/* Card: hairline-separated rows inside the group surface. */
.settings-card { padding: 0; }
.settings-card + .settings-card { border-top: 1px solid rgba(201, 168, 118, 0.12); }
.settings-card[open] {
  background: rgba(201, 168, 118, 0.025);
}
.settings-card__head {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Larger tap target — was 1.1rem .25rem; bump to ~56px touch height. */
  padding: 1.1rem 1.1rem;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  min-height: 56px;
}
.settings-card__head::-webkit-details-marker { display: none; }
.settings-card__head:active { background: rgba(201, 168, 118, 0.06); }
.settings-card__head:hover .settings-card__title { color: var(--gold); }

.settings-card__title {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: 1.25rem;
  letter-spacing: -0.005em;
  color: var(--ink-100, #f5efe4);
  transition: color .2s ease;
  line-height: 1.2;
}
.settings-card__chevron {
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: 1.4rem;
  color: var(--gold);
  transition: transform .25s ease;
  transform: rotate(90deg);
  line-height: 1;
  flex-shrink: 0;
  margin-left: .75rem;
}
.settings-card[open] > .settings-card__head .settings-card__chevron {
  transform: rotate(-90deg);
}
.settings-card__body {
  padding: .25rem 1.1rem 1.25rem;
  animation: settings-card-in .25s ease both;
}
@keyframes settings-card-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Quiet footer — version + sign out, away from the cards. */
.settings-foot {
  margin-top: 2rem;
  padding-top: 1.25rem;
  border-top: 1px solid rgba(201, 168, 118, 0.12);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.settings-foot__brand {
  font-family: 'IBM Plex Mono', monospace;
  font-size: .7rem;
  letter-spacing: .08em;
  color: var(--gold-dim, #a18258);
}
.settings-foot__signout {
  flex-shrink: 0;
}

/* Compact language switch (replaces the stretched two-button row). */
.lang-switch {
  display: inline-flex;
  background: var(--shade-overlay);
  border: 1px solid rgba(201, 168, 118, 0.18);
  border-radius: 999px;
  padding: 3px;
  width: 100%;
  max-width: 260px;
}
.lang-switch__btn {
  flex: 1;
  background: transparent;
  border: 0;
  padding: .5rem 1rem;
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: .85rem;
  color: var(--ink-100);
  border-radius: 999px;
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.lang-switch__btn.is-active {
  background: var(--gold);
  color: var(--on-gold);
  font-weight: 500;
}
/* 3-segment variant for theme picker (auto / light / dark) */
.lang-switch--3 { max-width: 320px; }

/* Mobile polish — looser group spacing, tighter card padding. */
@media (max-width: 540px) {
  .settings-group { margin-top: 1.5rem; }
  .settings-card__head { padding: 1rem .85rem; }
  .settings-card__body { padding: .25rem .85rem 1rem; }
  .settings-card__title { font-size: 1.15rem; }
  .settings-foot {
    flex-direction: column-reverse;
    align-items: stretch;
    gap: .75rem;
  }
  .settings-foot__brand { text-align: center; }
}
.settings-card__divider {
  border: 0;
  border-top: 1px solid rgba(201, 168, 118, 0.18);
  margin: 1.5rem 0 1rem;
}
.settings-card__subtitle {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: 1.1rem;
  margin: 0 0 .25rem;
}

/* ---------- NOTIFICATIONS PANEL: GROUPED LAYOUT ----------
   The Notifications card stacks three logical groups (channels /
   inventory alerts / reminders) so users can locate the toggle they
   want without scanning a six-section linear list. Each group is its
   own bordered "sub-card" inside the parent .settings-card__body —
   visually distinct but lighter than the parent, so the hierarchy
   reads as: settings page → notifications card → three groups → items.
   Inside a group, items are separated by a soft dashed line rather
   than the parent's gold-tinted full divider. */
.notify-group {
  /* --ink-800 is the theme-aware "elevated surface" tone — slightly
     warmer than the page (--ink-900) on both light and dark. Previously
     hardcoded to rgba(20,17,15,0.55), which read as a near-black blob
     against the cream surface in light theme. */
  background: var(--ink-800);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-l);
  padding: var(--space-s) var(--space-m);
  margin: 0 0 var(--space-m);
}
.notify-group:last-of-type { margin-bottom: 0; }
.notify-group__head {
  margin-bottom: .85rem;
  padding-bottom: .55rem;
  border-bottom: 1px solid rgba(201, 168, 118, 0.16);
}
.notify-group__eyebrow {
  font-family: var(--font-mono);
  font-size: .58rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--gold-dim);
}
.notify-group__sep {
  border: 0;
  border-top: 1px dashed rgba(201, 168, 118, 0.18);
  margin: 1.25rem 0;
}
/* Subsection title + help — slightly smaller than the old
   .settings-card__subtitle since it sits inside an already-titled
   group. Italic Fraunces matches the rest of the surface. */
.notify-sub__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: var(--step-0);
  margin: 0 0 .15rem;
  color: var(--ink-50);
}
.notify-sub__help {
  font-size: var(--step--1);
  color: var(--ink-200);
  margin: 0 0 .75rem;
  line-height: 1.45;
}
@media (max-width: 480px) {
  .notify-group { padding: var(--space-2xs) var(--space-s); }
}
/* Reminders / care-interval cards nested inside a notify-group share
   --ink-800 as their base; bump them to --ink-700 so they read as a
   stacked elevation instead of dissolving into the panel surface. */
.notify-group .account-card { background: var(--ink-700); }
.notify-group .account-card.is-on {
  background: linear-gradient(180deg, var(--ink-600) 0%, var(--ink-700) 100%);
}

/* Pending-invite badge */
.invite-badge {
  font-style: normal;
  display: inline-block;
  font-size: .65rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  padding: .12rem .5rem;
  border-radius: 999px;
  background: rgba(201, 168, 118, 0.18);
  color: var(--gold);
}

/* Resend button on user rows */
.user-row__resend {
  flex-shrink: 0;
  background: transparent;
  border: 1px solid rgba(201, 168, 118, 0.4);
  color: var(--gold);
  width: 32px; height: 32px;
  border-radius: 999px;
  font-size: 1rem;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background .15s ease, transform .15s ease;
}
.user-row__resend:hover {
  background: rgba(201, 168, 118, 0.18);
  transform: rotate(90deg);
}

/* iOS-style toggle row — label on left, switch on right.
   Used for boolean settings inside sheets (reminders, etc.) where checkboxes
   would look untidy. The .switch component is shared with the push toggle. */
.toggle-list {
  display: flex;
  flex-direction: column;
  border: 1px solid rgba(201, 168, 118, 0.18);
  border-radius: 12px;
  overflow: hidden;
  margin-bottom: .5rem;
  /* `.toggle-list` is sometimes a real <ul> (wardrobe-cats, savings,
     export). Drop the default disc markers + indent so the list reads
     as a clean container of rows rather than a bullet list. */
  list-style: none;
  padding: 0;
}
.toggle-list > li { list-style: none; }
.toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .85rem 1rem;
  cursor: pointer;
  transition: background .15s ease;
}
.toggle-row + .toggle-row {
  border-top: 1px solid rgba(201, 168, 118, 0.12);
}
.toggle-row:hover {
  background: rgba(201, 168, 118, 0.04);
}
/* Drag handle on the left edge of a draggable toggle-row. Two columns
   of dots evoke a "grip"; cursor: grab signals reorderability. The
   `touch-action: none` is critical on mobile — without it a touch on
   the handle scrolls the page instead of starting a drag. */
.toggle-row__drag {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 28px;
  margin-right: 4px;
  font-family: var(--font-mono);
  font-size: 1.05rem;
  letter-spacing: -0.05em;
  color: var(--gold-dim);
  cursor: grab;
  user-select: none;
  touch-action: none;
  border-radius: 4px;
  transition: color var(--dur-fast), background var(--dur-fast);
}
.toggle-row__drag:hover { color: var(--gold); background: rgba(201, 168, 118, 0.08); }
.toggle-row__drag:active { cursor: grabbing; }
.toggle-row.is-dragging {
  background: rgba(201, 168, 118, 0.08);
  outline: 1px solid var(--gold-dim);
  outline-offset: -1px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
  opacity: 0.92;
}
.toggle-row--draggable { list-style: none; }

.toggle-row__icon {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: 'Fraunces', Georgia, serif;
  font-size: 1.05rem;
  color: var(--gold);
  background: rgba(201, 168, 118, 0.06);
  border-radius: 999px;
  letter-spacing: 0;
}
.toggle-row__text {
  display: flex;
  flex-direction: column;
  gap: .15rem;
  min-width: 0;
  flex: 1;
}
/* Helper line stays out of the layout when empty so rows without a help
   key don't grow taller than rows with one. */
.toggle-row__help:empty { display: none; }
.toggle-row__title {
  font-size: .9rem;
  color: var(--ink-100, #f5efe4);
  letter-spacing: 0;
  text-transform: none;
}
.toggle-row__help {
  font-size: .75rem;
  color: var(--gold-dim, #a18258);
  letter-spacing: 0;
  text-transform: none;
  font-style: italic;
}

/* Link variant — entire row navigates somewhere. Used for "go to savings",
   "open export", etc. The chevron is a soft visual cue that the row is
   clickable; no underline since the row's hover state already affords it. */
.toggle-row--link {
  text-decoration: none;
  color: inherit;
}
.toggle-row--link:hover .toggle-row__chevron {
  transform: translateX(2px);
  color: var(--gold);
}
.toggle-row__chevron {
  flex-shrink: 0;
  font-size: 1.4rem;
  color: var(--gold-dim);
  line-height: 1;
  transition: transform var(--dur-fast) var(--ease-out-soft), color var(--dur-fast);
}

/* Inline action set on the right side of a toggle-row (used for
   wardrobe-categories edit/toggle/delete chips). Compact mono-uppercase
   so multiple actions read as a single control cluster. The buttons
   share a generous gap so we don't need bullet separators between
   them — the spacing alone reads as "discrete actions". */
.toggle-row__actions {
  display: inline-flex;
  align-items: center;
  gap: .9rem;
  font-family: var(--font-mono);
  font-size: .68rem;
  letter-spacing: .1em;
  color: var(--ink-300);
  flex-shrink: 0;
  cursor: default;
  flex-wrap: wrap;
  justify-content: flex-end;
}
.toggle-row__actions .link-action { padding: .2rem 0; }
.toggle-row.is-inactive { opacity: 0.55; }
/* The container disables its hover background when the row only carries
   actions (no overall click target) — feels less "tappable" but the
   actions themselves still highlight individually. */
.toggle-row--with-actions { cursor: default; }
.toggle-row--with-actions:hover { background: transparent; }
/* On phones the row becomes a 2-line layout: icon + title on top,
   actions on a full-width second line, right-aligned. Without this
   the action chips push the title off-screen on a 360px viewport. */
@media (max-width: 480px) {
  .toggle-row--with-actions {
    flex-wrap: wrap;
    row-gap: .25rem;
  }
  .toggle-row--with-actions .toggle-row__actions {
    width: 100%;
    margin-left: calc(28px + var(--space-s));  /* indent under the icon column */
    justify-content: flex-start;
    gap: 1rem;
  }
}

/* "Add new" button at the bottom of a toggle-list — visually attached to
   the list rather than floating below it. Matches the list's border-radius
   so they read as one continuous control group. */
.toggle-list-add {
  display: block;
  width: 100%;
  padding: .75rem 1rem;
  background: transparent;
  border: 1px dashed rgba(201, 168, 118, 0.25);
  border-radius: 12px;
  color: var(--gold);
  font-family: var(--font-mono);
  font-size: .72rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  cursor: pointer;
  margin-top: .5rem;
  transition: border-color var(--dur-fast), background var(--dur-fast);
}
.toggle-list-add:hover {
  border-color: var(--gold);
  background: rgba(201, 168, 118, 0.06);
}
.toggle-row .switch {
  flex-shrink: 0;
}

/* Notification category checklist */
.checkbox-list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .5rem .75rem;
}
.checkbox-row {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .5rem .75rem;
  border: 1px solid rgba(201, 168, 118, 0.18);
  border-radius: 8px;
  cursor: pointer;
  font-size: .9rem;
  transition: background .15s ease, border-color .15s ease;
}
.checkbox-row:has(input:checked) {
  background: rgba(201, 168, 118, 0.08);
  border-color: rgba(201, 168, 118, 0.45);
}
.checkbox-row input[type="checkbox"] {
  margin: 0;
  accent-color: var(--gold);
}
@media (max-width: 480px) {
  .checkbox-list { grid-template-columns: 1fr; }
}

/* ---------- TALLY CARD (dashboard "today" summary) ---------- */
.tally-card {
  margin: 1rem 0 1.5rem;
  padding: 1rem 1.25rem;
  border: 1px solid rgba(201, 168, 118, 0.18);
  border-radius: 14px;
  background: rgba(201, 168, 118, 0.04);
}
.tally-card__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: .75rem;
  flex-wrap: wrap;
  margin-bottom: .75rem;
}
.tally-card__more {
  font-size: .8rem;
  color: var(--gold);
  text-decoration: none;
  letter-spacing: 0;
  white-space: nowrap;
  flex-shrink: 0;
  transition: opacity .15s ease;
}
.tally-card__more:hover { opacity: .75; }
.tally-card__row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}
.tally-card__row--four {
  grid-template-columns: repeat(4, 1fr);
}
.tally-card__stat { min-width: 0; }
.tally-card__n {
  /* Long values like "12t 30m · 3×" must shrink rather than overflow the tile */
  word-break: break-word;
  hyphens: auto;
}
@media (max-width: 540px) {
  .tally-card { padding: .85rem 1rem; }
  .tally-card__row--four {
    grid-template-columns: repeat(2, 1fr);
    gap: .75rem 1rem;
  }
  .tally-card__n { font-size: 1.3rem; }
}
.tally-card__stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: .15rem;
}
.tally-card__n {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: 1.6rem;
  line-height: 1;
  letter-spacing: -0.01em;
}
.tally-card__k {
  font-size: .7rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--gold);
}
.tally-card__empty {
  text-align: center;
  margin: 0;
  padding: .5rem 0;
}

/* ---------- LOGS HISTORY VIEW ---------- */
.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  margin: 1rem 0 .5rem;
}
.chip {
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: .8rem;
  border: 1px solid rgba(201, 168, 118, 0.25);
  border-radius: 999px;
  padding: .35rem .85rem;
  background: transparent;
  color: var(--ink-100, #f5efe4);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.chip:hover { border-color: rgba(201, 168, 118, 0.5); }
.chip.is-selected {
  background: var(--gold);
  border-color: var(--gold);
  color: #0a0908;
}
.chip__glyph { opacity: .8; }

.logs-summary {
  margin: .75rem 0 .25rem;
  font-size: .85rem;
}
.logs-summary strong {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  color: var(--ink-100, #f5efe4);
  margin-right: .15rem;
}

.log-day {
  margin: 1.25rem 0;
}
.log-day__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: 1rem;
  margin: 0 0 .35rem;
  padding-bottom: .35rem;
  border-bottom: 1px solid rgba(201, 168, 118, 0.18);
}
.log-day__count {
  font-family: 'IBM Plex Mono', monospace;
  font-size: .7rem;
  color: var(--gold);
  font-style: normal;
}
.log-day__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* ---------- MILK STORAGE (pumped breast-milk inventory) ---------- */
.milk-group { margin-bottom: var(--space-l); }
.milk-group__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-s);
  margin-bottom: var(--space-s);
}
.milk-group__head h2 {
  font-family: var(--font-display);
  font-size: var(--step-1);
  margin: 0;
}
.milk-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2xs);
}
.milk-row {
  display: grid;
  grid-template-columns: 32px 1fr auto;
  align-items: center;
  gap: var(--space-s);
  padding: var(--space-s);
  background: var(--ink-700);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
}
.milk-row--expired,
.milk-row--expiring_soon { border-color: var(--gold); }
.milk-row--consumed,
.milk-row--discarded { opacity: .65; }
.milk-row__glyph { font-size: 1.4rem; text-align: center; color: var(--gold); }
.milk-row__head {
  display: flex;
  align-items: center;
  gap: .5rem;
  flex-wrap: wrap;
}
.milk-row__meta { margin-top: 2px; }
.milk-row__rel { color: var(--gold); }
.milk-row__actions {
  display: flex;
  gap: .25rem;
  flex-direction: column;
}
@media (min-width: 540px) {
  .milk-row__actions { flex-direction: row; }
}

.log-row {
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  gap: .75rem;
  align-items: center;
  padding: .65rem .25rem;
  border-bottom: 1px solid rgba(201, 168, 118, 0.08);
  cursor: pointer;
  transition: background .15s ease;
}
.log-row:hover, .log-row:focus-visible {
  background: rgba(201, 168, 118, 0.05);
  outline: none;
}
.log-row:last-child { border-bottom: 0; }
.log-row__time {
  font-family: 'IBM Plex Mono', monospace;
  font-size: .8rem;
  color: var(--gold);
  letter-spacing: -0.02em;
}
.log-row__glyph {
  font-size: 1.05rem;
  width: 1.5rem;
  text-align: center;
}
.log-row__body {
  display: flex;
  flex-direction: column;
  gap: .15rem;
  min-width: 0;
}
.log-row__title {
  font-size: .9rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.log-row__note {
  font-size: .75rem;
  color: var(--gold);
  opacity: .7;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.log-row__performer {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: .82rem;
  color: var(--gold-dim);
  margin-left: .3rem;
}
.log-row__chev {
  color: var(--gold);
  opacity: .5;
  font-size: 1.1rem;
}

.log-sheet__delete {
  color: #b85e5e !important;
  border-color: rgba(184, 94, 94, 0.4) !important;
}

/* ---------- PHOTO PICKER (sheet) ---------- */
.photo-pick-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(86px, 1fr));
  gap: .35rem;
  max-height: 50vh;
  max-height: 50dvh;
  overflow-y: auto;
  padding: .25rem 0;
  min-height: 0;
  -webkit-overflow-scrolling: touch;
  /* Stable column tracks for the layout engine — without this iOS
     Safari sometimes lays out grid cells before all images have
     reported intrinsic sizes, which is what causes the "thumbnails
     stack on top of each other" symptom inside a sheet content swap. */
  contain: layout style;
}
.photo-pick {
  /* iOS button-as-grid-item belt-and-braces:
       1) Reset the native button look so WebKit doesn't insert
          intrinsic padding / line-box height that fights aspect-ratio.
       2) display: block — button defaults to inline-block, which
          iOS Safari has occasionally treated as a baseline-aligned
          grid item, collapsing the row to text-line height (i.e. the
          "stacked images" bug).
       3) height-of-zero + padding-bottom: 100% to enforce the square
          geometrically. We deliberately do NOT use `aspect-ratio`
          here — that property has known interactions with <button>
          and lazy-loaded <img> children on iOS 16-17 that yielded
          the very symptom this rule is fixing. The padding-bottom
          hack is bulletproof on every browser since 2012.
  */
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  position: relative;
  height: 0;
  padding: 0 0 100% 0;
  background: var(--ink-700, #1a1816);
  border: 2px solid transparent;
  border-radius: 10px;
  overflow: hidden;
  cursor: pointer;
  min-width: 0;
  font: inherit;
  color: inherit;
  transition: border-color .15s ease, transform .12s ease;
}
.photo-pick:hover { transform: scale(1.02); }
.photo-pick img {
  /* Absolute-position the image so it never inflates the cell height
     during layout — the cell's geometry is owned by aspect-ratio /
     padding-bottom, the img just paints inside. This is the iOS
     Safari fix for the "thumbnails stack on top of each other"
     issue when the picker opens inside a sheet. */
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.photo-pick__placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--gold-dim, #a18258);
  font-size: 1.5rem;
}
.photo-pick__check {
  position: absolute;
  top: 6px; right: 6px;
  width: 22px; height: 22px;
  border-radius: 999px;
  background: var(--gold, #c9a876);
  color: #0a0908;
  display: flex; align-items: center; justify-content: center;
  font-size: .85rem;
  font-weight: 600;
  opacity: 0;
  transform: scale(.6);
  transition: opacity .15s ease, transform .15s ease;
}
.photo-pick.is-selected {
  border-color: var(--gold, #c9a876);
}
.photo-pick.is-selected .photo-pick__check {
  opacity: 1;
  transform: scale(1);
}

/* "N photos attached" pill (in milestone/event sheet) */
.attach-pill {
  display: inline-flex;
  align-items: center;
  gap: .65rem;
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: .85rem;
  color: var(--ink-100, #f5efe4);
}
.attach-pill--empty {
  color: var(--gold, #c9a876);
  font-style: italic;
  letter-spacing: 0;
}
.attach-pill__thumbs {
  display: inline-flex;
}
.attach-pill__thumbs img {
  width: 28px; height: 28px;
  border-radius: 6px;
  object-fit: cover;
  margin-left: -6px;
  border: 1px solid var(--ink-700, #1a1816);
}
.attach-pill__thumbs img:first-child { margin-left: 0; }
.attach-pill__count {
  font-family: 'IBM Plex Mono', monospace;
  font-size: .75rem;
  color: var(--gold, #c9a876);
}

/* ---------- IMPORT PANEL ---------- */
.import-state {
  display: flex;
  align-items: baseline;
  gap: .6rem;
  padding: 1rem 1.15rem;
  background: rgba(201, 168, 118, 0.04);
  border: 1px solid rgba(201, 168, 118, 0.18);
  border-radius: 12px;
}
.import-state__n {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: 2rem;
  line-height: 1;
  color: var(--gold, #c9a876);
}
.import-state__lbl {
  font-size: .8rem;
  letter-spacing: 0;
  color: var(--ink-100, #f5efe4);
}

/* ---------- WARDROBE ---------- */

.wardrobe-view { padding-bottom: 2.5rem; }

/* Hero — current size, next size, and arrival hint */
.ws-hero {
  margin: 1rem 0 1.5rem;
  padding: 1.25rem;
  border-radius: 18px;
  background:
    radial-gradient(ellipse at top right, rgba(201, 168, 118, 0.1), transparent 60%),
    rgba(201, 168, 118, 0.04);
  border: 1px solid rgba(201, 168, 118, 0.18);
}
/* The current/next pairing is a 3-column × 3-row grid with explicit
   grid-areas. `display: contents` on the wrappers lets their children
   land directly in the grid, so the two size numbers always share row 2
   (perfect baseline alignment) regardless of whether a sub-caption sits
   beneath either side. The columns are 1fr / auto / 1fr so they hug the
   center arrow and stay symmetric. */
.ws-hero__top {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  grid-template-rows: auto auto auto;
  grid-template-areas:
    "cur-lbl  .      next-lbl"
    "cur-size arrow  next-size"
    "cur-sub  .      next-sub";
  column-gap: 1.25rem;
  row-gap: .35rem;
  align-items: end;
  margin-bottom: 1rem;
  text-align: center;
}
.ws-hero__cur, .ws-hero__next { display: contents; }
.ws-hero__cur  .ws-hero__lbl  { grid-area: cur-lbl; }
.ws-hero__cur  .ws-hero__size { grid-area: cur-size; }
.ws-hero__next .ws-hero__lbl  { grid-area: next-lbl; }
.ws-hero__next .ws-hero__size { grid-area: next-size; }
.ws-hero__sub--cur { grid-area: cur-sub; }
.ws-hero__sub:not(.ws-hero__sub--cur) { grid-area: next-sub; }

/* Inline info-icon button next to the "PASSER NU" / "FITS NOW" label.
   Hovers/taps surface the [data-tooltip] explaining how the size is
   picked (recent measurement vs. age fallback). */
.ws-info {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  margin-left: 4px;
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--gold-dim, #a18258);
  cursor: help;
  vertical-align: middle;
  transition: color var(--dur-fast);
}
.ws-info:hover, .ws-info:focus-visible { color: var(--gold, #c9a876); }

/* Inactive matrix row — historical category that's been deactivated.
   Items still listed for editing but the row visually steps back so
   admins know this category isn't in the picker any more. */
.ws-row.is-inactive .ws-rowhead {
  opacity: 0.55;
}
.ws-row.is-inactive .ws-rowhead__name::after {
  content: ' (·)';
  color: var(--gold-dim);
}

/* (Wardrobe categories admin list now uses the shared .toggle-list /
   .toggle-row pattern — see the toggle-row__actions block above.) */
.ws-hero__lbl {
  font-family: var(--font-mono);
  font-size: .62rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--gold-dim, #a18258);
}
.ws-hero__size {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: 2.4rem;
  letter-spacing: -0.02em;
  color: var(--ink-100, #f5efe4);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.ws-hero__size--soft { color: var(--gold, #c9a876); }
.ws-hero__sub {
  font-family: var(--font-mono);
  font-size: .68rem;
  letter-spacing: .12em;
  color: var(--gold-dim, #a18258);
  font-variant-numeric: tabular-nums;
  margin-top: .15rem;
}
/* Arrow lives in row 2 alongside the numbers — gives the eye a line that
   reads left-to-right exactly across the size pair, not above or below. */
.ws-hero__arrow {
  grid-area: arrow;
  font-family: 'Fraunces', Georgia, serif;
  font-size: 1.4rem;
  color: var(--gold-dim, #a18258);
  align-self: center;
  padding: 0 .25rem;
  opacity: 0.75;
}

@media (max-width: 420px) {
  .ws-hero__size { font-size: 2rem; }
  .ws-hero__lbl  { font-size: .58rem; letter-spacing: .18em; }
  .ws-hero__top  { column-gap: .75rem; }
}
.ws-hero__bottom {
  display: flex;
  gap: .5rem;
  flex-wrap: wrap;
  padding-top: .85rem;
  border-top: 1px solid rgba(201, 168, 118, 0.12);
}
.ws-hero__chip {
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: .82rem;
  color: var(--ink-100, #f5efe4);
  background: var(--shade-overlay);
  border: 1px solid rgba(201, 168, 118, 0.18);
  padding: .35rem .85rem;
  border-radius: 999px;
}
.ws-hero__chip strong {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  color: var(--gold, #c9a876);
  margin-right: .2rem;
}

/* Tabs — owned vs. wishlist */
.ws-tabs {
  display: flex;
  gap: .35rem;
  margin: 1rem 0;
  padding: 4px;
  background: var(--shade-overlay);
  border: 1px solid rgba(201, 168, 118, 0.18);
  border-radius: 999px;
}
.ws-tab {
  flex: 1;
  background: transparent;
  border: 0;
  padding: .55rem 1rem;
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: .85rem;
  color: var(--ink-100, #f5efe4);
  border-radius: 999px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  transition: background .2s ease, color .2s ease;
}
.ws-tab.is-active {
  background: var(--gold, #c9a876);
  color: #0a0908;
  font-weight: 500;
}
.ws-tab__n {
  font-family: 'IBM Plex Mono', monospace;
  font-size: .72rem;
  opacity: .8;
}
.ws-tab.is-active .ws-tab__n { opacity: 1; }

/* Matrix — single shared horizontal scroller so rows pan together as one
   table. Sticky-left category column stays anchored relative to the matrix
   while the size cells scroll. */
.ws-mode-pane { animation: settings-card-in .3s ease both; }

.ws-matrix {
  background: rgba(201, 168, 118, 0.03);
  border: 1px solid rgba(201, 168, 118, 0.16);
  border-radius: 14px;
  overflow: hidden;
}
.ws-matrix__scroll {
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
  scrollbar-color: rgba(201, 168, 118, 0.3) transparent;
}
.ws-matrix__scroll::-webkit-scrollbar { height: 6px; }
.ws-matrix__scroll::-webkit-scrollbar-thumb { background: rgba(201, 168, 118, 0.3); border-radius: 3px; }
.ws-matrix__header { background: rgba(201, 168, 118, 0.06); }
.ws-row, .ws-matrix__header {
  /* width: max-content lets the row size to its actual content (rowhead + N
     cells); min-width: 100% ensures short rows still fill the viewport. */
  display: flex;
  flex-wrap: nowrap;
  align-items: stretch;
  width: max-content;
  min-width: 100%;
}
.ws-row + .ws-row { border-top: 1px solid rgba(201, 168, 118, 0.08); }

.ws-rowhead {
  position: sticky;
  left: 0;
  /* Themed sticky surface — matches the page so the column reads as part of
     the matrix rather than a "dark island". Solid (no alpha) so it cleanly
     occludes the cells scrolling behind it.
     Fixed flex-basis (not min-width) is important: a longer category name
     would otherwise stretch one row's rowhead and break column alignment
     with the rest of the matrix. */
  background: var(--ink-900);
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .65rem .75rem;
  border-right: 1px solid rgba(201, 168, 118, 0.12);
  z-index: 1;
  flex: 0 0 132px;
  width: 132px;
  box-sizing: border-box;
  overflow: hidden;
  /* Promote the sticky column to its own GPU layer so iOS Safari doesn't
     have to repaint the row on every horizontal scroll frame. Without this,
     the category names visibly drag/jitter behind the cells while scrolling. */
  transform: translateZ(0);
  will-change: transform;
}
.ws-row { contain: layout paint; }
.ws-rowhead--header {
  background: var(--ink-900);
}
.ws-rowhead__glyph { font-size: 1.1rem; flex-shrink: 0; }
.ws-rowhead__name {
  font-size: .82rem;
  color: var(--ink-100, #f5efe4);
  letter-spacing: 0;
  text-transform: none;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-rowhead__total {
  font-family: 'IBM Plex Mono', monospace;
  font-size: .72rem;
  color: var(--gold-dim, #a18258);
  flex-shrink: 0;
}
.ws-rowhead--header .ws-rowhead__name {
  font-size: .65rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--gold-dim, #a18258);
}

/* Cells + size headers sit inline next to the rowhead inside the unified
   scroller (.ws-matrix__scroll). No per-row overflow needed any more. */
.ws-size, .ws-cell {
  flex: 0 0 56px;
  min-height: 52px;
  scroll-snap-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border-right: 1px solid rgba(201, 168, 118, 0.06);
  position: relative;
}

.ws-size {
  /* Top-aligned so the size number sits at the same y across every column,
     regardless of whether the cell has a tag (NOW / NEXT) and/or arrival
     sub-line below it. Without this, vertically-centered cells with extra
     content visibly drift the number downward and break the row's visual
     baseline. */
  padding: .35rem 0 .25rem;
  gap: .15rem;
  justify-content: flex-start;
}
.ws-size__cm {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: 1rem;
  color: var(--ink-100, #f5efe4);
}
.ws-size__tag {
  font-family: 'IBM Plex Mono', monospace;
  font-size: .55rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #0a0908;
  background: var(--gold, #c9a876);
  padding: .05rem .35rem;
  border-radius: 999px;
}
.ws-size__tag--next {
  color: var(--gold, #c9a876);
  background: rgba(201, 168, 118, 0.18);
}
.ws-size__sub {
  font-family: 'IBM Plex Mono', monospace;
  font-size: .55rem;
  color: var(--gold-dim, #a18258);
  white-space: nowrap;
  /* Belt-and-braces against any future longer label form bleeding into the
     next column — text-overflow ellipses if anything ever exceeds the cell. */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 0 2px;
  box-sizing: border-box;
}
.ws-size.is-current::after {
  content: '';
  position: absolute;
  inset: auto 4px 4px 4px;
  height: 2px;
  background: var(--gold, #c9a876);
  border-radius: 1px;
}

.ws-cell {
  background: transparent;
  border: 0;
  border-right: 1px solid rgba(201, 168, 118, 0.06);
  cursor: pointer;
  font-family: 'IBM Plex Mono', monospace;
  font-size: .9rem;
  color: var(--ink-100, #f5efe4);
  transition: background .2s ease, transform .15s ease;
}
.ws-cell:hover { transform: scale(1.03); }
.ws-cell.is-current {
  background: rgba(201, 168, 118, 0.08);
}
.ws-cell.is-current::before {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 2px;
  background: var(--gold, #c9a876);
}

/* Heat-map — count translates to gold intensity */
.ws-cell--heat-0 .ws-cell__dot {
  color: rgba(201, 168, 118, 0.18);
  font-size: 1rem;
}
.ws-cell--heat-1 { background: rgba(201, 168, 118, 0.05); }
.ws-cell--heat-2 { background: rgba(201, 168, 118, 0.12); }
.ws-cell--heat-3 { background: rgba(201, 168, 118, 0.22); }
.ws-cell--heat-4 {
  background: rgba(201, 168, 118, 0.4);
  color: #0a0908;
  font-weight: 600;
}
.ws-cell--heat-4 .ws-cell__n { font-family: 'Fraunces', Georgia, serif; font-style: italic; }
.ws-cell.is-current.ws-cell--heat-4 { background: rgba(201, 168, 118, 0.55); }

/* Tighten on narrow phones — rowhead becomes a 108px sticky column instead
   of 132px, cells shrink to 48px wide. Width must be set as both flex-basis
   and width to preempt content-driven growth on long category names. */
@media (max-width: 480px) {
  .ws-rowhead { flex-basis: 108px; width: 108px; padding: .55rem .65rem; }
  .ws-rowhead__name { font-size: .78rem; }
  .ws-size, .ws-cell { flex: 0 0 48px; min-height: 48px; }
  .ws-size__cm { font-size: .9rem; }
  .ws-hero__size { font-size: 1.85rem; }
}

/* Add button hovers above floor */
.ws-add { margin-top: 1.25rem; }

/* ----- item list inside cell drill-in ----- */
.ws-itemlist {
  display: flex;
  flex-direction: column;
  border: 1px solid rgba(201, 168, 118, 0.18);
  border-radius: 12px;
  overflow: hidden;
}
.ws-itemrow {
  display: flex;
  align-items: center;
  gap: .85rem;
  padding: .75rem 1rem;
  background: transparent;
  border: 0;
  text-align: left;
  cursor: pointer;
  transition: background .15s ease;
  width: 100%;
}
.ws-itemrow-wrap {
  display: flex;
  flex-direction: column;
  gap: 4px;
  border-bottom: 1px solid rgba(201, 168, 118, 0.1);
  padding-bottom: .35rem;
}
.ws-itemrow-wrap:last-child { border-bottom: 0; padding-bottom: 0; }
.ws-itemrow + .ws-itemrow { border-top: 1px solid rgba(201, 168, 118, 0.1); }
.ws-itemrow:hover { background: rgba(201, 168, 118, 0.05); }

/* Reservation badge inline with the item label. Mine = filled gold,
   others (only seen by admins) = neutral outlined. */
.ws-resv {
  display: inline-block;
  margin-left: .35rem;
  padding: 1px 7px;
  font-family: var(--font-mono);
  font-size: .55rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  border-radius: 999px;
  vertical-align: middle;
}
.ws-resv--mine {
  background: var(--gold);
  color: var(--on-gold);
}
.ws-resv--other {
  border: 1px solid var(--ink-400);
  color: var(--ink-200);
}

/* Reserve / Release inline button — sits below the row so the call to
   action is reachable without competing with the row tap-to-edit. */
.ws-resv-btn {
  align-self: flex-end;
  margin: 0 1rem .25rem;
  padding: .35rem .85rem;
  font-family: var(--font-mono);
  font-size: .65rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--gold);
  background: transparent;
  border: 1px solid var(--gold-dim);
  border-radius: 999px;
  cursor: pointer;
  transition: color .15s, border-color .15s, background .15s;
}
.ws-resv-btn:hover {
  color: var(--on-gold);
  background: var(--gold);
  border-color: var(--gold);
}
.ws-resv-btn--cancel {
  color: var(--miss);
  border-color: rgba(154, 85, 73, 0.4);
}
.ws-resv-btn--cancel:hover {
  color: var(--ink-50);
  background: var(--miss);
  border-color: var(--miss);
}
.ws-itemrow__qty {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-size: 1.3rem;
  color: var(--gold, #c9a876);
  min-width: 2rem;
}
.ws-itemrow__body { display: flex; flex-direction: column; gap: .15rem; flex: 1; min-width: 0; }
.ws-itemrow__lbl {
  font-size: .92rem;
  color: var(--ink-100, #f5efe4);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-itemrow__sub {
  font-size: .75rem;
  color: var(--gold-dim, #a18258);
}
.ws-itemrow__cond {
  font-size: 1.1rem;
  color: var(--gold-dim, #a18258);
  flex-shrink: 0;
}

/* ----- visual pickers in add/edit sheet ----- */
.ws-pickgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(74px, 1fr));
  gap: .35rem;
}
.ws-pick {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .25rem;
  padding: .65rem .25rem;
  background: var(--shade-overlay);
  border: 1px solid rgba(201, 168, 118, 0.18);
  border-radius: 10px;
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease, transform .12s ease;
}
.ws-pick:hover { border-color: rgba(201, 168, 118, 0.4); transform: scale(1.02); }
.ws-pick.is-selected {
  border-color: var(--gold, #c9a876);
  background: rgba(201, 168, 118, 0.1);
}
.ws-pick__glyph { font-size: 1.4rem; }
.ws-pick__lbl {
  font-size: .68rem;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-100, #f5efe4);
  text-align: center;
}

.ws-sizepick {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
}
.ws-size-pick {
  flex: 0 0 auto;
  min-width: 48px;
  padding: .5rem .75rem;
  background: var(--shade-overlay);
  border: 1px solid rgba(201, 168, 118, 0.18);
  border-radius: 8px;
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-size: .95rem;
  color: var(--ink-100, #f5efe4);
  cursor: pointer;
  transition: all .15s ease;
}
.ws-size-pick:hover { border-color: rgba(201, 168, 118, 0.4); }
.ws-size-pick.is-selected {
  background: var(--gold, #c9a876);
  border-color: var(--gold, #c9a876);
  color: #0a0908;
}

/* Number stepper — replaces the awkward bare number input */
.ws-stepper {
  display: flex;
  align-items: stretch;
  border: 1px solid rgba(201, 168, 118, 0.18);
  border-radius: 8px;
  overflow: hidden;
}
.ws-stepper__btn {
  width: 38px;
  background: var(--shade-overlay);
  border: 0;
  color: var(--gold, #c9a876);
  font-size: 1.15rem;
  cursor: pointer;
  transition: background .15s ease;
}
.ws-stepper__btn:hover { background: rgba(201, 168, 118, 0.12); }
.ws-stepper input[type="number"] {
  flex: 1;
  border: 0;
  background: transparent;
  text-align: center;
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-size: 1.1rem;
  color: var(--ink-100, #f5efe4);
  outline: none;
  padding: .35rem 0;
  -moz-appearance: textfield;
}
.ws-stepper input[type="number"]::-webkit-inner-spin-button,
.ws-stepper input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none; margin: 0;
}

/* ---------- LIGHT-THEME TUNING ----------
   Tokens already swap automatically; this section addresses the few effects
   that need different treatment (overlay-blended grain, blend modes that
   were authored against a dark canvas, etc.). */

/* Film grain — white-on-overlay reads as warm light over the dark page,
   but white over cream is invisible. Use multiply with a dark tint instead. */
[data-theme="light"] body::after {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.1 0 0 0 0 0.08 0 0 0 0 0.06 0 0 0 0.5 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  mix-blend-mode: multiply;
  opacity: 0.06;
}
@media (prefers-color-scheme: light) {
  [data-theme="auto"] body::after {
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.1 0 0 0 0 0.08 0 0 0 0 0.06 0 0 0 0.5 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
    mix-blend-mode: multiply;
    opacity: 0.06;
  }
}

/* The radial glow in body::before is gold-tinted at low alpha — works on
   both modes but gets a touch more warmth on cream. */
[data-theme="light"] body::before,
[data-theme="auto"] body::before {
  /* same gold radial — alpha is tuned in dark mode for that candle look,
     here it can stay subtle */
}

/* Fine-tune photo-pick checkmark contrast — gold check on cream needs
   a darker glyph color so it reads at the small size. */
[data-theme="light"] .photo-pick__check,
@media (prefers-color-scheme: light) {
  [data-theme="auto"] .photo-pick__check { color: var(--ink-0); }
}

/* ---------- PANTRY BARCODE SCANNER ----------
   Fullscreen camera overlay rendered above the open Add-item sheet.
   We mount on document.body (z-index above the sheet at 41) so the
   parent gets a focused, full-viewport view of the camera with no
   half-form bleed-through. On scan success the overlay tears down
   ~350 ms after the success flash and the user is back on the sheet
   with name/kind/notes auto-filled. */
.pantry-scan-btn {
  margin-bottom: var(--space-m);
  font-weight: 600;
  letter-spacing: .04em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .4rem;
}
/* While the scanner overlay is mounted, take the bottom sheet and its
   backdrop OUT of the layout entirely. `visibility: hidden` was the
   gentler first attempt but proved insufficient on some Android Chrome
   builds where the sheet's open-state transform creates a compositing
   layer that paints over later siblings regardless of z-index.
   `display: none` removes the element from the box tree, so there's
   nothing left to compose in front of the camera. The state lives on
   <body> and is set from the scanner JS before the overlay mounts. */
body.pantry-scan-active #sheet,
body.pantry-scan-active #sheetBackdrop {
  display: none !important;
}

/* ---------- WARDROBE-CAT GLYPH PICKER ----------
   Browse-emoji affordance on the wardrobe-category Add/Edit sheet.
   The text input still works for paste-your-own; the Browse button
   reveals a grid the parent can scan visually. Grouped by garment
   kind with tiny eyebrow labels — easier to find "the one I want"
   than a flat 50-emoji wall. */
.glyph-input-row {
  display: flex;
  gap: .35rem;
  align-items: stretch;
}
.glyph-input-row input { flex: 1; min-width: 0; }
.glyph-input-row .btn--small {
  flex-shrink: 0;
  padding: 0 var(--space-s);
  white-space: nowrap;
}
.glyph-picker {
  margin: var(--space-s) 0;
  padding: var(--space-s);
  background: var(--ink-800);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
  max-height: 280px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.glyph-group + .glyph-group { margin-top: .65rem; }
.glyph-group__title {
  margin: 0 0 .3rem .15rem;
  font-family: var(--font-mono);
  font-size: .6rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--gold-dim);
  font-weight: 500;
}
.glyph-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(38px, 1fr));
  gap: 4px;
}
.glyph-tile {
  width: 100%;
  aspect-ratio: 1 / 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.4rem;
  line-height: 1;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 8px;
  cursor: pointer;
  transition: background var(--dur-fast), border-color var(--dur-fast), transform var(--dur-fast);
  /* Force consistent rendering of variation-selector emoji on iOS */
  font-variant-emoji: emoji;
}
.glyph-tile:hover {
  background: var(--ink-700);
  border-color: var(--gold-dim);
}
.glyph-tile:active { transform: scale(.94); }
.glyph-tile.is-selected {
  background: var(--ink-700);
  border-color: var(--gold);
  box-shadow: 0 0 0 1px var(--gold) inset;
}

.pantry-scan {
  position: fixed;
  inset: 0;
  /* High z-index (9999) AND a fresh stacking context (`isolation: isolate`)
     so the overlay always paints above the bottom-sheet's blurred backdrop.
     iOS Safari has a known quirk where elements with `-webkit-backdrop-filter`
     compose onto a layer that can sit above subsequent fixed-positioned
     siblings — the explicit isolation defangs it. `translate3d(0,0,0)`
     promotes the overlay to its own GPU layer, which both fixes the iOS
     compositing order and stops the live <video> from re-rendering jank. */
  z-index: 9999;
  isolation: isolate;
  transform: translate3d(0, 0, 0);
  background: #0a0908;
  color: #f5efe4;
  display: flex;
  flex-direction: column;
  padding:
    calc(env(safe-area-inset-top) + var(--space-m))
    var(--space-m)
    calc(env(safe-area-inset-bottom) + var(--space-m));
  gap: var(--space-m);
  animation: scanFadeIn 180ms ease-out;
}
.pantry-scan.is-closing {
  animation: scanFadeOut 160ms ease-in forwards;
}
@keyframes scanFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes scanFadeOut {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* Top bar — title left, close × right. Light type on dark so the
   contrast stays high above the live video stage. */
.pantry-scan__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--space-xs);
}
.pantry-scan__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-2);
  letter-spacing: -0.01em;
}
.pantry-scan__close {
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 999px;
  color: inherit;
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  transition: background .15s, transform .15s;
}
.pantry-scan__close:hover { background: rgba(255,255,255,0.16); }
.pantry-scan__close:active { transform: scale(.94); }

/* Stage: the live video + reticle frame. Fills the available height
   between header and footer; the video uses object-fit: cover so the
   frame is always full-bleed regardless of camera orientation. */
.pantry-scan__stage {
  position: relative;
  flex: 1;
  min-height: 240px;
  background: #000;
  border-radius: 18px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 20px 60px -10px rgba(0,0,0,0.6);
}
.pantry-scan__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Reticle — four corner brackets + horizontal sweep line. The corners
   are drawn with two thin borders each so they stay crisp at any
   zoom; the sweep is a narrow gradient bar that loops top↔bottom. */
.pantry-scan__frame {
  position: absolute;
  inset: 14%;
  pointer-events: none;
}
.pantry-scan__corner {
  position: absolute;
  width: 28px;
  height: 28px;
  border-color: var(--gold, #c9a978);
  border-style: solid;
}
.pantry-scan__corner--tl { top: 0;    left: 0;    border-width: 2px 0 0 2px; border-radius: 4px 0 0 0; }
.pantry-scan__corner--tr { top: 0;    right: 0;   border-width: 2px 2px 0 0; border-radius: 0 4px 0 0; }
.pantry-scan__corner--bl { bottom: 0; left: 0;    border-width: 0 0 2px 2px; border-radius: 0 0 0 4px; }
.pantry-scan__corner--br { bottom: 0; right: 0;   border-width: 0 2px 2px 0; border-radius: 0 0 4px 0; }

.pantry-scan__sweep {
  position: absolute;
  left: 6%;
  right: 6%;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--gold, #c9a978), transparent);
  box-shadow: 0 0 12px var(--gold, #c9a978);
  top: 0;
  animation: scanSweep 1.6s ease-in-out infinite alternate;
}
@keyframes scanSweep {
  from { top: 4%;  opacity: .6; }
  to   { top: 96%; opacity: 1; }
}

/* Status pill — overlaid at the bottom of the stage. Tiny by design so
   the parent's view of the actual product is uncluttered. */
.pantry-scan__status {
  position: absolute;
  bottom: 12px;
  left: 50%;
  transform: translateX(-50%);
  padding: 6px 14px;
  background: rgba(10, 9, 8, 0.78);
  color: #f5efe4;
  border-radius: 999px;
  font-size: .75rem;
  letter-spacing: .04em;
  text-transform: lowercase;
  backdrop-filter: blur(6px);
  white-space: nowrap;
  z-index: 2;
}

.pantry-scan__hint {
  margin: 0;
  text-align: center;
  font-size: .82rem;
  color: rgba(245, 239, 228, 0.62);
  font-style: italic;
}
/* Cancel button uses a translucent light fill to read against the
   dark background without stealing focus from the live camera. */
.pantry-scan__cancel.btn--ghost {
  background: rgba(255,255,255,0.06);
  border-color: rgba(255,255,255,0.16);
  color: #f5efe4;
}
.pantry-scan__cancel.btn--ghost:hover {
  background: rgba(255,255,255,0.12);
}

/* Success state: green border flash + freeze the video tint. The
   500ms keyframe gives the parent visual confirmation before the
   overlay tears down ~350ms later. */
.pantry-scan__stage.is-success {
  animation: scanSuccess 500ms ease-out;
}
.pantry-scan__stage.is-success .pantry-scan__corner {
  border-color: #6ba368;
  transition: border-color .2s ease-out;
}
.pantry-scan__stage.is-success .pantry-scan__sweep {
  opacity: 0;
  transition: opacity .2s ease-out;
}
@keyframes scanSuccess {
  0%   { box-shadow: inset 0 0 0 0   rgba(107, 163, 104, 0); }
  35%  { box-shadow: inset 0 0 0 6px rgba(107, 163, 104, 0.55); }
  100% { box-shadow: inset 0 0 0 3px rgba(107, 163, 104, 0.25); }
}

/* Narrow phones: tighten padding so the camera stage gets more height. */
@media (max-width: 480px) {
  .pantry-scan {
    padding:
      calc(env(safe-area-inset-top) + var(--space-s))
      var(--space-s)
      calc(env(safe-area-inset-bottom) + var(--space-s));
    gap: var(--space-s);
  }
  .pantry-scan__title { font-size: var(--step-1); }
}

/* ---------- READ-ONLY VIEW SHEETS ----------
   Shared layout for the reader-role detail sheets on /vaccines, /checkups,
   and /moments. A view sheet shows the same data the edit sheet would but
   as label/value rows — no inputs, no save, just a Close button.
   Each row is a single line with a muted label on the left and the value
   on the right; multiline content (notes, attendees) flips to a stacked
   layout via `view-row--block`. Visually echoes `.field` so the two
   surfaces feel like the same family of components. */
.view-rows { display: flex; flex-direction: column; gap: .55rem; }
.view-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-s);
  padding: .55rem 0;
  border-bottom: 1px solid var(--ink-500);
}
.view-row:last-child { border-bottom: 0; }
.view-row__lbl {
  font-family: var(--font-mono);
  font-size: .68rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--gold-dim);
  flex-shrink: 0;
}
.view-row__val {
  font-family: var(--font-body);
  font-size: var(--step-0);
  color: var(--ink-50);
  text-align: right;
  word-break: break-word;
}
.view-row--block {
  flex-direction: column;
  align-items: stretch;
  gap: .35rem;
}
.view-row--block .view-row__lbl { text-align: left; }
.view-row__notes {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--step-0);
  color: var(--ink-50);
  line-height: 1.5;
  white-space: pre-wrap;
}
/* When several view-rows blocks sit back-to-back (e.g. measurements after
   the main details), keep the gap tight — they're already separated by
   their own internal hairlines. */
.view-rows-group { margin-top: .25rem; }

/* ---------- VISITOR INSIGHTS (admin BI dashboard) ----------
   Single-page composite of summary tiles + SVG charts + heatmaps + per-
   user table + recent-activity stream. Visual language echoes the rest
   of the app (paper-cream cards on dark/light themes, gold accents,
   monospace eyebrow labels, italic display headings) so it doesn't read
   as a bolt-on. */

.ins-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-2xs);
  margin: var(--space-s) 0 var(--space-l);
}
.ins-tile {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: var(--space-s);
  background: var(--ink-700);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
  min-width: 0;
}
.ins-tile__lbl {
  font-family: var(--font-mono);
  font-size: .58rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--gold-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ins-tile__val {
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--step-1);
  color: var(--ink-50);
  line-height: 1.15;
}
.ins-tile__sub {
  font-size: .7rem;
  color: var(--ink-300);
  letter-spacing: .04em;
}

/* Card container shared by all insights sections. Echoes the highlight
   card pattern from /logs but a touch wider since most charts here
   want full-bleed horizontal space. */
.ins-card {
  margin: var(--space-m) 0;
  padding: var(--space-m);
  background: var(--ink-700);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-l);
}
.ins-card__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: var(--step-1);
  color: var(--ink-50);
  margin: 0 0 .35rem;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
.ins-card__hint {
  font-family: var(--font-mono);
  font-size: .65rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--gold-dim);
}
.ins-card__sub {
  margin: 0 0 var(--space-s);
  color: var(--ink-300);
  font-size: var(--step--1);
}

/* Two-column layout for paired sections. Stacks on narrow screens. */
.ins-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-m);
  margin: var(--space-m) 0;
}
.ins-row > .ins-card { margin: 0; }
@media (max-width: 720px) {
  .ins-row { grid-template-columns: 1fr; }
}

.ins-svg {
  width: 100%;
  height: auto;
  max-height: 240px;
  display: block;
}

/* Legend strip below the daily-activity stacked bar chart. */
.ins-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-s);
  margin-top: var(--space-s);
  font-family: var(--font-mono);
  font-size: .58rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-300);
}
.ins-legend__item {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.ins-legend__swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
}

/* 24-hour heatmap — 12 cells per row × 2 rows so it reads like a clock
   split AM/PM. Grows narrower on small screens. */
.ins-hours {
  display: grid;
  /* minmax(0, 1fr) is critical — bare 1fr defaults to minmax(auto, 1fr),
     which lets a cell's intrinsic content (2-digit hour + up to 3-digit
     count) push the column wider than its share, causing the grid to
     overflow .ins-card's padding box. minmax(0, 1fr) clamps the floor
     to zero so the columns always divide the container width equally. */
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 3px;
  margin-top: var(--space-2xs);
}
.ins-hour {
  aspect-ratio: 1;
  border-radius: 4px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  cursor: help;
  transition: transform .12s;
  min-width: 0;
  overflow: hidden;
}
.ins-hour:hover { transform: scale(1.06); }
.ins-hour__lbl {
  font-family: var(--font-mono);
  font-size: .55rem;
  color: var(--ink-50);
  letter-spacing: .04em;
}
.ins-hour__n {
  font-family: var(--font-mono);
  font-size: .6rem;
  font-weight: 600;
  color: var(--ink-50);
}

/* Weekday heatmap — 7 inline cells. */
.ins-weekdays {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 4px;
  margin-top: var(--space-2xs);
}
.ins-weekday {
  aspect-ratio: 1;
  border-radius: 6px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-width: 0;
  overflow: hidden;
}
.ins-weekday__lbl {
  font-family: var(--font-mono);
  font-size: .58rem;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-50);
}
.ins-weekday__n {
  font-family: var(--font-mono);
  font-size: .8rem;
  font-weight: 600;
  color: var(--ink-50);
}

/* Login-by-day strip — 30 cells in a horizontal flow, terra-cotta tints
   to differentiate from the gold writes-heatmap. */
.ins-logins {
  display: grid;
  grid-template-columns: repeat(15, minmax(0, 1fr));
  gap: 3px;
}
.ins-login-cell {
  aspect-ratio: 1;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  min-width: 0;
  overflow: hidden;
  font-size: .65rem;
  font-weight: 600;
  color: var(--ink-50);
  cursor: help;
}
@media (max-width: 480px) {
  .ins-logins { grid-template-columns: repeat(10, minmax(0, 1fr)); }
}

/* Per-user table — wide; horizontal scroll on narrow screens rather
   than wrapping cells. */
.ins-table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.ins-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--step--1);
}
.ins-table th,
.ins-table td {
  padding: .55rem .5rem;
  text-align: left;
  border-bottom: 1px solid var(--ink-500);
  white-space: nowrap;
}
.ins-table th {
  font-family: var(--font-mono);
  font-size: .58rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--gold-dim);
  font-weight: 500;
}
.ins-table .num { text-align: right; }
.ins-table .num--gold { color: var(--gold); font-weight: 600; }
.ins-table .muted { color: var(--ink-300); }

.ins-user__name {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.ins-user__display { color: var(--ink-50); font-weight: 500; }
.ins-user__role {
  align-self: flex-start;
  font-size: .55rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  font-family: var(--font-mono);
  padding: 1px 6px;
  border-radius: 999px;
  border: 1px solid var(--ink-500);
}

/* Domain-section stat grid — tile-like rows used by care/memories/inv/finance. */
.ins-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--space-2xs);
}
.ins-stat {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: var(--space-2xs) var(--space-s);
  background: var(--ink-800);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-m);
}
.ins-stat__lbl {
  font-family: var(--font-mono);
  font-size: .55rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--gold-dim);
}
.ins-stat__val {
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--step-0);
  color: var(--ink-50);
}
.ins-stat__sub {
  font-size: .65rem;
  color: var(--ink-300);
}

.ins-bullets {
  margin: var(--space-s) 0 0;
  padding: 0;
  list-style: none;
}
.ins-bullets li {
  padding: 4px 0;
  font-size: var(--step--1);
  border-bottom: 1px dashed var(--ink-500);
}
.ins-bullets li:last-child { border-bottom: 0; }

/* Recent-activity stream — scrollable list of glyph + body rows. */
.ins-recent {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 480px;
  overflow-y: auto;
}
.ins-recent__row {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  padding: .5rem 0;
  border-bottom: 1px solid var(--ink-500);
}
.ins-recent__row:last-child { border-bottom: 0; }
.ins-recent__glyph {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--ink-800);
  border: 1px solid var(--ink-500);
  border-radius: 999px;
  color: var(--gold);
  font-family: var(--font-mono);
  font-size: .9rem;
}
.ins-recent__body {
  flex: 1;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-s);
  min-width: 0;
}
.ins-recent__head {
  flex: 1;
  min-width: 0;
  font-size: var(--step--1);
  color: var(--ink-100);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ins-recent__when {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: .65rem;
  letter-spacing: .08em;
  color: var(--gold-dim);
}

/* ---------- MEDIA ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---------- SLEEP COACH ---------- */
.sleep-card {
  margin: var(--space-m) 0;
  padding: var(--space-s) var(--space-m);
  background: var(--ink-800);
  border: 1px solid var(--ink-500);
  border-radius: var(--radius-l);
}
.sleep-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
  margin-bottom: var(--space-s);
}
.sleep-card__date {
  font-size: .72rem;
  letter-spacing: .08em;
  color: var(--ink-300);
}
.sleep-today {
  display: flex;
  flex-direction: column;
  gap: .35rem;
}
.sleep-today__big {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-4);
  color: var(--ink-50);
  line-height: 1;
}
.sleep-today__target {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: .12em;
  color: var(--ink-300);
}
.sleep-today__bar {
  position: relative;
  height: 6px;
  border-radius: 4px;
  background: var(--ink-700);
  overflow: hidden;
  margin-top: .35rem;
}
.sleep-today__bar-fill {
  display: block;
  height: 100%;
  background: var(--gold);
  border-radius: 4px;
  transition: width .35s ease;
}
.sleep-today__bar[data-status="low"]  .sleep-today__bar-fill { background: var(--warn); }
.sleep-today__bar[data-status="high"] .sleep-today__bar-fill { background: var(--gold-dim); }

.sleep-wake {
  display: flex;
  flex-direction: column;
  gap: .35rem;
}
.sleep-wake__big {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: var(--step-3);
  color: var(--ink-50);
  line-height: 1;
}
.sleep-wake__hint {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: .1em;
  color: var(--ink-300);
}
.sleep-wake__next {
  margin-top: .35rem;
  font-size: var(--step--1);
  color: var(--ink-100);
}
.sleep-wake__next strong { color: var(--gold); font-style: italic; font-family: var(--font-display); }
.sleep-wake__next--over { color: var(--warn); }
.sleep-wake[data-status="near"] .sleep-wake__big { color: var(--gold-bright); }
.sleep-wake[data-status="over"] .sleep-wake__big { color: var(--warn); }
.sleep-wake--sleeping .sleep-wake__big { color: var(--gold); }

.sleep-bedtime {
  margin-top: .65rem;
  font-size: var(--step--1);
  color: var(--ink-200);
}
.sleep-bedtime strong { color: var(--gold); font-style: italic; font-family: var(--font-display); }

.sleep-chart {
  width: 100%;
  height: auto;
  max-height: 180px;
}
.sleep-chart__band {
  fill: var(--gold-glow);
  opacity: .9;
}
.sleep-chart__bar--ok {
  fill: var(--gold);
}
.sleep-chart__bar--off {
  fill: var(--ink-400);
}
.sleep-chart__label,
.sleep-chart__y-label {
  font-family: var(--font-mono);
  font-size: 9px;
  fill: var(--ink-300);
}

.sleep-reference__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: var(--step-0);
  color: var(--ink-50);
  margin: 0 0 .5rem;
}
.sleep-reference__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: .35rem;
}
.sleep-reference__list li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-s);
  font-size: var(--step--1);
}
.sleep-reference__list .mono {
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-300);
}
.sleep-reference__list strong {
  color: var(--ink-50);
  font-weight: 500;
}
.sleep-reference__notes {
  margin: .75rem 0 0;
  color: var(--ink-200);
  font-size: var(--step--1);
  line-height: 1.55;
}

/* Wardrobe item-row thumbnail. Compact 40px square so the row height
   stays inside the existing rhythm — bigger thumbs would push the
   reserve/release buttons off-screen on phones. */
.ws-itemrow__thumb {
  width: 40px;
  height: 40px;
  flex: 0 0 40px;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid rgba(201, 168, 118, 0.2);
  background: var(--ink-700);
  margin-right: .15rem;
}

/* ---------- ALBUM CONFIG PANEL (photos page) ---------- */
.album-config {
  margin: var(--space-s) 0 var(--space-m);
  padding: .85rem 1rem;
  background: linear-gradient(180deg, rgba(201, 168, 118, 0.04), rgba(201, 168, 118, 0.01));
  border: 1px solid var(--ink-500);
  border-radius: 14px;
}
.album-config__head {
  display: flex;
  flex-direction: column;
  gap: .25rem;
  margin-bottom: .65rem;
}
.album-config__title-wrap {
  display: flex;
  align-items: center;
  gap: .5rem;
  flex-wrap: wrap;
}
.album-config__title {
  margin: 0;
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: 1.15rem;
  color: var(--ink-50);
  letter-spacing: .005em;
}
.album-config__chip {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  padding: .15rem .5rem;
  font-family: var(--font-mono);
  font-size: .58rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--gold-dim);
  background: rgba(201, 168, 118, 0.08);
  border: 1px solid rgba(201, 168, 118, 0.25);
  border-radius: 999px;
}
.album-config__chip svg { color: var(--gold-dim); }
.album-config__desc {
  margin: 0;
  font-size: .78rem;
  color: var(--ink-200);
  line-height: 1.5;
}
.album-config__actions {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
}
.album-config__btn {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  padding: .45rem .7rem;
  background: var(--ink-700);
  border: 1px solid var(--ink-500);
  border-radius: 10px;
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-size: .72rem;
  font-weight: 500;
  color: var(--ink-50);
  cursor: pointer;
  transition: border-color .15s, color .15s, background .15s;
}
.album-config__btn svg { width: 14px; height: 14px; color: var(--gold-dim); transition: color .15s; }
.album-config__btn:hover {
  border-color: var(--gold-dim);
  color: var(--gold);
}
.album-config__btn:hover svg { color: var(--gold); }
.album-config__btn.is-on {
  background: rgba(201, 168, 118, 0.12);
  border-color: var(--gold-dim);
  color: var(--gold);
}
.album-config__btn.is-on svg { color: var(--gold); }
.album-config__btn--danger {
  color: var(--miss);
}
.album-config__btn--danger svg { color: var(--miss); }
.album-config__btn--danger:hover {
  border-color: var(--miss);
  background: rgba(154, 85, 73, 0.08);
  color: var(--miss);
}
.album-config__btn--danger:hover svg { color: var(--miss); }

/* Hidden-album visual on the album-strip tile — small pill in the
   top-right corner of the cover. Tile stays selectable; the badge is
   just a hint. */
.album-tile__cover { position: relative; }
.album-tile__hidden-badge {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(20, 20, 28, 0.78);
  border: 1px solid rgba(201, 168, 118, 0.35);
  border-radius: 999px;
  color: var(--gold-dim);
  backdrop-filter: blur(2px);
}
.album-tile__hidden-badge svg { width: 12px; height: 12px; }
.album-tile.is-hidden-from-all .album-tile__cover {
  border-style: dashed;
  opacity: .9;
}
