/* ──────────────────────────────────────────────────────────
   Feliz — base stylesheet
   Asian-minimalism: warm off-white, generous space, hairline
   dividers, single restrained accent. No shadows.
   ────────────────────────────────────────────────────────── */

/* v0.9.306 A11Y-2 — WCAG SC 2.3.3 / 2.2.2: honour the OS-level reduced-motion
   preference. Strips animation + transition + smooth-scroll across the whole
   SPA for users who set it (vestibular sensitivity, motion-sickness, AT). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* v0.9.306 A11Y-2 — WCAG SC 2.4.7: every focusable element gets a visible
   keyboard-focus ring. :focus-visible (not :focus) so mouse-clicks don't
   paint the ring; keyboard navigation does. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* THEME-1-FINISH-REGISTRY (2026-06-13) — finish engine per
   docs/briefs/THEME_FINISH_ENGINE_SPEC_2026-06-13.md. The two v0.9.362
   presets become a six-finish registry: a finish is a curated token
   bundle ONLY — layout/type/spacing/icons never change per finish.
   Six finishes locked by Jayy (mock: _MOCK_theme_finishes_v1.html):
   nacht (factory default), dag (factory), gauge / linen / volt / pulse
   (premium). Old ids map forward: lime-night→volt, blue-day→dag.

   :root carries the structural tokens AND the nacht (default) appearance
   bundle, so first paint before the boot script lands on brand navy.
   Each finish gets its own :root[data-theme-preset="<id>"] block below.
   JS additionally stamps data-theme-mode="dark|light" on <html> so
   component-level mode overrides (further down + strength.html) apply to
   every dark/light finish without per-finish selector duplication. */
:root {
  /* Brand Nacht appearance tokens — "Sassu Ice" (COLOR_SYSTEM_RESEARCH
     2026-08-05 §3). The accent moved from a pastel (#A8D8EA, C≈0.07) to an
     electric cyan on the same hue family (H≈195, C≈0.16): the pastel read as
     a haze against the near-black canvas, which was the "no vibrance"
     complaint. --on-accent stays near-black because the accent keeps a high
     lightness (L≈75%) and white text fails on it — same luminance rule the
     strength-bridge enforced inline at v0.9.185. */
  --bg:        #03040A;
  --surface:   #0A0F1A;
  --ink:       #F0F2F5;
  --ink-2:     #ACB3C1;
  --muted:     #6B7280;
  --line:      #1E2533;
  --line-2:    #151B26;
  --accent:    #2AF0EA;
  --accent-2:  #A0F7F4;
  --on-accent: #03040A;
  --temptation-line: #f59e0b;
  /* THEME-2 brand-surface tokens — finish-INVARIANT. Brand front-door
     surfaces (landing) render Deep Navy + Ice in every finish per
     APP_BRAND_MEDIA_UX_SPEC §0; these never get per-finish overrides. */
  --brand-navy: #080C14;
  --brand-ink:  #DCE8F0;
  --brand-ice:  #A8D8EA;
  --brand-dim:  #7A8AA0;
  /* BRAND-HORSE-HEAD plate + glyph — finish-INVARIANT navy disc behind the
     horse silhouette, and the canonical ice-blue silhouette itself. Both are
     brand-moment literals (the horse mark is a brand-moment artifact on its own
     fixed palette, identical to the auth-lockup brand treatment — no finish
     recolouring), same precedent as --brand-navy (lives in the :root token
     block, which css-hex-lint exempts). */
  --horse-plate: #0E1320;
  --horse-glyph: #A8D8EA;
  --danger:    #FF1744;
  /* v0.9.911 NUTRITION-PANTRY-METER-CLEANUP — semantic amber warning, used
     for the negative-stock clip border on pantry meters; lives in the :root
     token block (css-hex-lint exempt) so var(--warning) resolves everywhere. */
  --warning:   #D97706;
  /* v0.9.314 STRENGTH-BADGE-RED-RECOLOR — iOS-system-red notification dot.
     Preset-agnostic: the tabbar surface is dark in both presets, so the
     badge red is constant (no per-preset override). */
  --badge-bg:  #FF3B30;
  --on-badge:  #FFFFFF;
  --ok:        #00E676;
  /* v0.9.255 — semantic DOMAIN colours, independent of the preset --accent.
     COLOR-3: re-valued onto the chroma-capped Sassu Ice domain hues
     (training green H145, nutrition amber H55, mood lavender H300). Training
     left the blue family because the new signature --accent is cyan H195 —
     a blue domain hue sat close enough to read as "the accent, dimmed".
     Mood previously borrowed var(--accent) outright, which is exactly the
     collision the one-domain-hue-per-screen rule exists to stop; it now owns
     a hue. These hexes are the sRGB fallback — the @supports block below
     restates all three in oklch() for browsers that have it. */
  --training:  #67BB6B;
  --on-training: #0E1726;
  --nutrition: #E78A45;
  --mood:      #AE96DA;
  /* NUTRITION-HYDRATION-CARD-AND-INTAKE-WATER-GOAL — water = semantic blue,
     deliberately distinct from the amber --nutrition macro accent. Every finish
     restates this (SASSU-DESIGN-GOLF-A fix #2) — a theme-invariant blue was one
     of the three colours that stayed put while the rest of the app re-skinned. */
  --hydration: #4EA8DE;
  /* Low-but-valid progress (<60% of a goal). Deliberately NOT --danger: an
     unfinished ring is not a failure, and the alarm red next to the #FF3B30
     badge was the loudest false-signal on the start screen. Muted, in-family
     per finish so it recedes rather than shouts. */
  --progress-low: #5F7896;
  /* v0.9.326 A11Y-12 S38 — nutrition macro tokens lifted for dark bg.
     COLOR-3: the macro triad is now one harmonised set — equal lightness,
     chroma capped ~0.13, separated by hue alone — instead of three neons at
     three different lightnesses. --mac-p completes the set; the protein bar
     used to reach for the generic --ok, which is 0.21 chroma and the single
     loudest colour on the Nutrition screen. */
  --mac-p:     #57BC80;
  --mac-c:     #E1A035;
  --mac-f:     #E37F65;
  --fiber:     #4DB6AC;
  --protein-2: #3DDC84;
  --favorite:  #FF8F00;
  --on-danger: #FFFFFF;
  /* v0.9.328 — dark lift instead of dark darken. */
  --surface-muted: rgba(255, 255, 255, 0.04);
  /* v0.9.368 STRENGTH-EDIT-MODE-MULTI-EXERCISE-GROUPING — subtle accent tint
     used for the "selected" row highlight in Edit mode. The 2026-06-01
     contrast addendum: a full --accent fill made labels disappear in the
     prior pair-only flow, so the selected-row state lives at ~20% accent
     alpha on dark and ~14% on light. Label keeps --ink/--text legible. */
  --accent-subtle: rgba(42, 240, 234, 0.20);
  /* Group palette — group_id (1..N) → color_token + on-color_token. Eight
     slots cycle on group_id. None equals --accent so the group channel
     reads separately from the active-state accent. Tokens hold on both
     presets; on-group-N inverts because the swatch is used at high
     saturation on chip+row-bar. Visual lock: _MOCK_strength_edit_mode_
     grouping_v1.html (2026-06-02). */
  --group-1: #00E5FF; --on-group-1: #001517;
  --group-2: #FFB300; --on-group-2: #1A0C00;
  --group-3: #B388FF; --on-group-3: #0E0420;
  --group-4: #FF7A6B; --on-group-4: #2A0904;
  --group-5: #1DE9B6; --on-group-5: #002115;
  --group-6: #76FF03; --on-group-6: #0A1C00;
  --group-7: #FF4081; --on-group-7: #33000F;
  --group-8: #536DFE; --on-group-8: #001033;
  /* v0.9.347 SETTINGS-IOS-STYLE-RESTYLE — iOS-Dark grouped-list tokens.
     systemGroupedBackground (#000) sits BELOW the card in luminance — the
     inverse of the light pattern (light has the screen grey + card white). */
  --surface-bg:        #05070D;
  --surface-card:      #111827;
  --divider:           #232B3A;
  --toggle-track-off:  #29323F;
  --toggle-thumb-off:  #FFFFFF;
  --section-label:     #9CA3AF;
  color-scheme: dark;

  --font-sans: -apple-system, BlinkMacSystemFont, "Inter", "Helvetica Neue", Helvetica, Arial, sans-serif;

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

  /* SHAPE SCALE — the ONLY four corner radii in the app. Before
     SASSU-DESIGN-GOLF-A there were 34 distinct radius declarations including
     19 raw px literals (2,3,5,7,9,11,13,18,20,99,999,9999px) and both 999px
     AND 9999px for the same pill; a per-component free-for-all is the single
     loudest "generated, not designed" tell. The three scalar values are the
     three that were already dominant (4px / 8px / 12px), so the large majority
     of components render pixel-identical to before — this is a consolidation,
     not a redesign. All three sit on the 4px grid.
       --radius-sm    input, swatch, tight inner element
       --radius-md    button, chip-with-corners, list row, small tile
       --radius-lg    card, panel, sheet, modal, dialog
       --radius-pill  anything whose radius should equal half its height
     Percentage radii (50% circle, 22% brand-plate squircle) stay percentages
     on purpose: they scale with the element, which no fixed token can do. */
  --radius-sm:   4px;
  --radius-md:   8px;
  --radius-lg:   12px;
  --radius-pill: 999px;

  /* MOTION — one signature for the whole app (SASSU-DESIGN-GOLF-D).
     Before this there were 33 duration variants and 8 easing curves, so no
     two surfaces moved alike and the app read as assembled rather than
     designed. Motion is finish-INVARIANT: these live only here, never in a
     preset override, because how fast a sheet opens is not a colour choice.
       --dur-fast   status/feedback — hover, focus, chip, small state flip
       --dur-base   transitions — sheet, overlay, tab, bar, height/opacity
       --dur-slow   one-shot choreography — entrance, celebration, flash
     The two below are PERIODS, not transition speeds: a spinner's revolution
     and a pulse's breath are functional rates, so they are carved out of the
     two-speed rule rather than crushed into it (a 200ms spinner is a strobe).
       --dur-loop   one revolution / one tick-smoothing cycle
       --dur-pulse  one breath of a looping pulse
     Easing collapses to a single decelerate curve. `linear` survives as the
     only second curve because constant rate is a requirement, not a taste:
     rotation and progress fills stutter under any eased curve. */
  --dur-fast:  120ms;
  --dur-base:  200ms;
  --dur-slow:  500ms;
  --dur-loop:  800ms;
  --dur-pulse: 1600ms;
  --ease-standard: cubic-bezier(0.2, 0, 0, 1);

  /* M3 type scale (roles) — one fixed size + line-height per role so the
     nutrition surface normalises to a system instead of raw px 10-32. */
  --text-label:    11px;  --lh-label:    1.3;
  --text-body-sm:  12px;  --lh-body-sm:  1.4;
  --text-body:     14px;  --lh-body:     1.45;
  --text-title:    16px;  --lh-title:    1.3;
  --text-headline: 24px;  --lh-headline: 1.25;

  /* Overlay scrim — pure black alpha, NOT a tinted grey. A tinted scrim
     desaturates the white cards showing through it, which is why the Dag hub
     read washed-out (_AUDIT_hub_dag.png). Black alpha only darkens. */
  --scrim: rgba(0, 0, 0, 0.5);

  /* HAIRLINE + GLOW (COLOR-2-GLOW-ELEVATION, research §2/§3) — the two
     primitives the elevation ladder below is built from. A hairline is a
     1px light-on-dark (or ink-on-light) edge that says "this is a separate
     plane"; a glow is a large-blur, low-opacity accent bloom that says
     "this plane is live". Glows are derived from --accent so every preset
     gets its own signature bloom for free. They are deliberately expensive
     to spend: OLED burn, battery and gradient banding all scale with glowing
     area, so glow belongs on interactive / active / PR moments only, never
     as ambient decoration. */
  --hairline:        rgba(255, 255, 255, 0.06);
  --hairline-strong: rgba(255, 255, 255, 0.12);
  --glow-soft: 0 0 16px color-mix(in srgb, var(--accent) 20%, transparent);
  --glow-hard: 0 0 24px color-mix(in srgb, var(--accent) 35%, transparent);

  /* ELEVATION LADDER — three rungs plus one upward-cast rung, per finish.
     Before SASSU-DESIGN-GOLF-A there were 53 ad-hoc box-shadow variants of
     which exactly ONE ever reached the screen, so there was no readable
     depth order at all. Assignment is semantic, not decorative:
       --elev-1  resting card / row / chip
       --elev-2  raised transient: popover, dropdown, FAB, drag-lift
       --elev-3  modal / dialog / fullscreen overlay panel
       --elev-up bottom-docked surfaces that rise from the edge (sheet, tabbar)
     The neutral drop-shadow is retired as the elevation language: it read as
     generic-default chrome and barely registered on a near-black canvas.
     Depth is now hairline-at-rest, hairline+glow once a plane is raised, so
     the ladder is legible on both finishes without any grey haze. */
  --elev-1:  0 0 0 1px var(--hairline);
  --elev-2:  0 0 0 1px var(--hairline), var(--glow-soft);
  --elev-3:  0 0 0 1px var(--hairline-strong), var(--glow-soft);
  --elev-up: 0 -1px 0 0 var(--hairline), var(--glow-soft);

  /* FOCUS / SELECTION RINGS — zero-blur spread rings. Deliberately separate
     from the elevation ladder: a ring is an outline substitute (it never
     shifts layout the way border-width does), not a depth cue. The ring
     itself stays crisp; the glow rides behind it so focus reads at a glance
     without thickening the outline. */
  --ring-focus:  0 0 0 3px var(--accent-subtle), var(--glow-soft);
  --ring-accent: 0 0 0 2px var(--accent), var(--glow-soft);
  /* Inset twin — for cells that must not paint outside their own box, so it
     stays glow-free by definition. */
  --ring-accent-inset: inset 0 0 0 2px var(--accent);
  /* "You are here" double ring: surface gap, then accent halo. The single
     strongest positional signal in the app, so it carries the hard glow. */
  --ring-current: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent), var(--glow-hard);

  /* NAV-CHAT-TAB-HUB-KEBAB-AND-20PCT-SMALLER — bottom-nav shrunk ~20% (was 96px)
     so the menu is less present, especially on small phones. safe-area inset is
     added separately (padding-bottom: --safe-bottom) so the home-indicator gap
     is untouched. */
  --tabbar-h: 86px;
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  /* v0.9.309 — A11Y-4 S6: iPhone 14+ notch + Dynamic Island.
     Auth wordmark/Sign-in form was overlapping the camera cutout. */
  --safe-top:    env(safe-area-inset-top,    0px);
  --safe-left:   env(safe-area-inset-left,   0px);
  --safe-right:  env(safe-area-inset-right,  0px);

  /* v0.9.375 CARDIO-MAP-LEAFLET-THEMABLE — per-tenant map theming tokens
     (spec §4.2). Live polyline tracks --accent (preset-aware); current-
     position marker stays a constant dark slate so it reads on the lime
     accent without re-mapping per preset. Planned-route overlay (dashed)
     uses a soft slate-blue so it doesn't compete with the live polyline.
     --cardio-tile-filter is empty by default; a tenant override sets
     e.g. `sepia(0.3) hue-rotate(220deg)` to brand-shift the OSM raster
     tiles without touching JS. The PwaMapProvider reads these via
     getComputedStyle on the map root every call, so a runtime theme
     swap recolours the next stroke automatically. */
  --cardio-polyline:    var(--accent);
  --cardio-marker:      #1A1A2E;
  --cardio-planned:     #5B8DEF;
  --on-planned:         #FFFFFF;
  --cardio-tile-filter: none;

  /* DAY-CYCLE-REFINEMENTS (v0.9.466) — SLEEP phase rows render against a
     deliberately darker background so the "night band" reads as a
     separator between EVENING and MORNING. Dark preset leans into the
     night metaphor; light preset gets a soft cool-gray tint. */
  --day-cycle-sleep-bg: #070A12;

  /* DAY-CYCLE-FULL-TOOL-AND-WIDGET-VISUAL-POLISH (v0.9.484) — per-phase
     thin left-bar accents that segment the EVENING / SLEEP / MORNING
     cards. Dusk indigo / deep slate / warm amber — the temperature
     gradient nudges the visual story of the day. Slippage pill palette
     stays monotone within each kind: late = soft indigo bg + indigo fg,
     early = soft green bg + green fg. Nacht preset (default :root).
     css-hex-lint exempt — all hex sits inside the :root token block. */
  --day-cycle-evening-accent: #6E7BFF;
  --day-cycle-sleep-accent:   #3F4859;
  --day-cycle-morning-accent: #F4A946;
  --slip-late-bg:  rgba(110, 123, 255, 0.18);
  --slip-late-fg:  #BFC6FF;
  --slip-early-bg: rgba(0, 230, 118, 0.18);
  --slip-early-fg: #5EE19A;

  /* MOOD-ANALYTICS-RAINBOW-AND-POLISH (v0.9.474) — semantic mood encoding
     palette, mirrors public/assets/js/calendar/_helpers.js MOOD_COLOR_STOPS.
     Drives the scatter X-axis rainbow background and the dot fills. The
     six stops map to mood scores 1, 3, 5, 6, 8, 10 along the X axis
     (red → orange → yellow → green → blue). Background opacity is
     preset-aware: nacht reads punchier on the dark surface, dag stays
     muted-pastel against the light surface. */
  --mood-rainbow-1:  #DC3C3C;
  --mood-rainbow-3:  #EB8C28;
  --mood-rainbow-5:  #E6C81E;
  --mood-rainbow-6:  #B4D21E;
  --mood-rainbow-8:  #3CB950;
  /* COLOR-3 — stop 9 exists to route the scale AROUND sRGB's dead zone, not
     to add a new semantic step. An SVG <linearGradient> always interpolates
     in sRGB (there is no oklch option on that element), and the 8→10 leap is
     the one long hue arc in this scale: green H146 → blue H256. Straight-line
     sRGB across that arc dips through a desaturated slate around the midpoint,
     so moods 8.5–9.5 rendered greyer than moods on either side of them — the
     scale visibly lost saturation exactly where it should read "good". This
     stop is the OKLCH midpoint of 8 and 10, which keeps chroma up through the
     turn; with it in place the two short sRGB segments track the perceptual
     path closely enough that the dip is gone. */
  --mood-rainbow-9:  #069FA6;
  --mood-rainbow-10: #3282E6;
  --mood-rainbow-bg-opacity: 0.32;

  /* Oranje accent tokens — Rep Max notes in the DAW sequencer + Dag-theme override. */
  --accent-orange:       #FB923C;
  --accent-orange-light: #4A2E10;

  /* NUTRITION-STREAK-DOTS-GOAL-COLOR — "logged but missed kcal goal" color
     for the 7-day consistency dots. Nacht's accent is lime, so the missed
     state uses a calm blue so the eye separates achieved vs missed at a
     glance. The Dag override below switches to black (blue IS Dag's accent). */
  --goal-missed: #5B8DEF;

  /* SEQUENCER-V2-DAW-OVERHAUL (v0.9.526) — teal loop-brace token. No
     existing Feliz hue mapped cleanly to "loop range marker"; teal reads
     on both Dag and Nacht surfaces. allow-hex */
  --loop: #2dd4bf;
}

/* THEME-1-FINISH-REGISTRY — the six finish token blocks. Canon rows
   (bg / surface / accent / on-accent / ink / dim) come straight from the
   spec table; every extra app token is derived inside the finish's family
   with the SAME derivation logic across finishes (line = bg lifted,
   surface-bg below surface in luminance, accent-2 = accent lifted,
   accent-subtle = accent at 20% dark / 12-14% light). Dark finishes
   inherit the dark semantic tokens (training/nutrition/macros/groups/
   day-cycle/mood) from the :root nacht bundle; light finishes restate the
   light semantic set. */

/* nacht — explicit block mirrors the :root default so a stamped
   data-theme-preset="nacht" is self-describing. */
:root[data-theme-preset="nacht"] {
  --bg:        #03040A;
  --surface:   #0A0F1A;
  --ink:       #F0F2F5;
  --ink-2:     #ACB3C1;
  --muted:     #6B7280;
  --line:      #1E2533;
  --line-2:    #151B26;
  --accent:    #2AF0EA;
  --accent-2:  #A0F7F4;
  --on-accent: #03040A;
  --accent-subtle: rgba(42, 240, 234, 0.20);
  --surface-bg:        #05070D;
  --surface-card:      #111827;
  --divider:           #232B3A;
  --toggle-track-off:  #29323F;
  --section-label:     #9CA3AF;
  --day-cycle-sleep-bg: #070A12;
  --hydration:     #4EA8DE;
  --progress-low:  #5F7896;
  color-scheme: dark;
}

/* dag — Brand Dag (factory, light), "Sassu Ice" values (research §3). The
   near-black navy accent (#16263F) is retired: it had zero chroma, so filled
   buttons dissolved into the dark text around them. Its replacement is a deep
   teal on the Nacht hue family — deliberately deeper and less saturated than
   the Nacht cyan, which would read as neon on white. Old stored id blue-day
   maps here in JS. */
:root[data-theme-preset="dag"] {
  --bg:        #F1F5F9;
  --surface:   #FFFFFF;
  --ink:       #0F172A;
  --scrim:     rgba(0, 0, 0, 0.34);
  /* Light finish: the hairline flips from white-on-dark to ink-on-light and
     is carried a touch heavier, because a 1px edge has to survive against a
     near-white page. The ladder itself (--elev-*) is inherited from :root —
     it is expressed purely in terms of these two tokens plus the accent
     glow, so a preset only ever restates the hairline. */
  --hairline:        rgba(15, 23, 42, 0.08);
  --hairline-strong: rgba(15, 23, 42, 0.14);
  --ink-2:     #475569;
  --muted:     #64748B;
  --line:      #CBD5E1;
  --line-2:    #E2E8F0;
  --accent:    #006E7D;
  --accent-2:  #00A8B5;
  --on-accent: #F0F9FF;
  --ok:        #00C853;
  /* COLOR-3 — light-finish domain hues: same hue family as the dark preset,
     dropped in lightness so they stay legible ON white rather than glowing
     off it (the §3 "Dag = dieper, minder verzadigd" rule). --on-training
     stays white because the green keeps the same L as the blue it replaced. */
  --training:  #38853E;
  --on-training: #FFFFFF;
  --nutrition: #B3621E;
  --mood:      #725B9A;
  --mac-p:     #249057;
  --mac-c:     #B37903;
  --mac-f:     #BE563D;
  --fiber:     #26A69A;
  --protein-2: #00C853;
  --favorite:  #FF6D00;
  --surface-muted: rgba(15, 23, 42, 0.04);
  --accent-subtle: rgba(0, 110, 125, 0.12);
  /* v0.9.368 group palette — light-tuned hues, same slot semantics. */
  --group-1: #00B8D4; --on-group-1: #001517;
  --group-2: #FF8F00; --on-group-2: #1A0C00;
  --group-3: #7C4DFF; --on-group-3: #FFFFFF;
  --group-4: #FF5252; --on-group-4: #FFFFFF;
  --group-5: #00BFA5; --on-group-5: #002115;
  --group-6: #64DD17; --on-group-6: #0A1C00;
  --group-7: #F50057; --on-group-7: #FFFFFF;
  --group-8: #304FFE; --on-group-8: #FFFFFF;
  --surface-bg:        #E2E8F0;
  --surface-card:      #FFFFFF;
  --divider:           #CBD5E1;
  --toggle-track-off:  #E2E8F0;
  --toggle-thumb-off:  #FFFFFF;
  --section-label:     #64748B;
  color-scheme: light;
  --cardio-polyline:    var(--accent);
  --cardio-marker:      #1A1A2E;
  --cardio-planned:     #5B8DEF;
  --on-planned:         #FFFFFF;
  --cardio-tile-filter: none;
  --day-cycle-sleep-bg: #DDE4EC;
  --day-cycle-evening-accent: #5B6BFF;
  --day-cycle-sleep-accent:   #6B7280;
  --day-cycle-morning-accent: #E6A042;
  --slip-late-bg:  rgba(91, 107, 255, 0.14);
  --slip-late-fg:  #3D4BCC;
  --slip-early-bg: rgba(0, 200, 83, 0.14);
  --slip-early-fg: #00A347;
  --mood-rainbow-bg-opacity: 0.18;
  --accent-orange:       #D97706;
  --accent-orange-light: #FFE8D6;
  /* Soft blue, deliberately outside the teal accent family so the streak
     "missed" state never reads as an accent highlight. */
  --goal-missed: #5B8DEF;
  /* Deeper than the dark-finish blue — the dark value is used as TEXT on a
     white card and fails contrast there. */
  --hydration:     #1D7FC0;
  --progress-low:  #7A8A9C;
}

/* gauge — premium, dark, amber instrument-cluster accent. */
:root[data-theme-preset="gauge"] {
  --bg:        #101113;
  --surface:   #191B1E;
  --ink:       #E6E4E0;
  --ink-2:     #7E7C76;
  --muted:     #5F5E59;
  --line:      #2B2D31;
  --line-2:    #232529;
  --accent:    #FFB454;
  --accent-2:  #FFC679;
  --on-accent: #1A1107;
  --accent-subtle: rgba(255, 180, 84, 0.20);
  --surface-bg:        #0A0B0D;
  --surface-card:      #1D2024;
  --divider:           #34373C;
  --toggle-track-off:  #34373C;
  --section-label:     #8C8B85;
  --day-cycle-sleep-bg: #141518;
  /* Cooled-down against the warm amber cluster — water must still read as
     water, but desaturated so it doesn't fight the accent. */
  --hydration:     #6BADC9;
  --progress-low:  #857C6E;
  color-scheme: dark;
}

/* linen — premium, light, warm paper + soft umber accent. */
:root[data-theme-preset="linen"] {
  --bg:        #F7F4EE;
  --surface:   #FFFFFF;
  --ink:       #262219;
  --scrim:     rgba(0, 0, 0, 0.34);
  /* Warm light finish — same inherited ladder, hairline tinted into umber. */
  --hairline:        rgba(38, 34, 25, 0.08);
  --hairline-strong: rgba(38, 34, 25, 0.14);
  --ink-2:     #8A8273;
  --muted:     #9C9484;
  --line:      #E6E1D6;
  --line-2:    #EFEBE2;
  --accent:    #4A4438;
  --accent-2:  #5C5547;
  --on-accent: #F7F4EE;
  --ok:        #00C853;
  /* COLOR-3 — light-finish domain hues: same hue family as the dark preset,
     dropped in lightness so they stay legible ON white rather than glowing
     off it (the §3 "Dag = dieper, minder verzadigd" rule). --on-training
     stays white because the green keeps the same L as the blue it replaced. */
  --training:  #38853E;
  --on-training: #FFFFFF;
  --nutrition: #B3621E;
  --mood:      #725B9A;
  --mac-p:     #249057;
  --mac-c:     #B37903;
  --mac-f:     #BE563D;
  --fiber:     #26A69A;
  --protein-2: #00C853;
  --favorite:  #FF6D00;
  --surface-muted: rgba(38, 34, 25, 0.04);
  --accent-subtle: rgba(74, 68, 56, 0.12);
  --group-1: #00B8D4; --on-group-1: #001517;
  --group-2: #FF8F00; --on-group-2: #1A0C00;
  --group-3: #7C4DFF; --on-group-3: #FFFFFF;
  --group-4: #FF5252; --on-group-4: #FFFFFF;
  --group-5: #00BFA5; --on-group-5: #002115;
  --group-6: #64DD17; --on-group-6: #0A1C00;
  --group-7: #F50057; --on-group-7: #FFFFFF;
  --group-8: #304FFE; --on-group-8: #FFFFFF;
  --surface-bg:        #F1EDE4;
  --surface-card:      #FFFFFF;
  --divider:           #D8D2C4;
  --toggle-track-off:  #E6E1D4;
  --toggle-thumb-off:  #FFFFFF;
  --section-label:     #8A8273;
  color-scheme: light;
  --cardio-polyline:    var(--accent);
  --cardio-marker:      #1A1A2E;
  --cardio-planned:     #5B8DEF;
  --on-planned:         #FFFFFF;
  --cardio-tile-filter: none;
  --day-cycle-sleep-bg: #ECE7DC;
  --day-cycle-evening-accent: #5B6BFF;
  --day-cycle-sleep-accent:   #6B7280;
  --day-cycle-morning-accent: #E6A042;
  --slip-late-bg:  rgba(91, 107, 255, 0.14);
  --slip-late-fg:  #3D4BCC;
  --slip-early-bg: rgba(0, 200, 83, 0.14);
  --slip-early-fg: #00A347;
  --mood-rainbow-bg-opacity: 0.18;
  --accent-orange:       #D97706;
  --accent-orange-light: #FFE8D6;
  --goal-missed: #5B8DEF;
  /* Teal-leaning blue so water reads cool against warm paper without going
     saccharine; both deep enough for text on the white card. */
  --hydration:     #2E7E96;
  --progress-low:  #9A9080;
}

/* volt — premium, dark, lime. Pixel-identical to the retired "Lime Night"
   preset so existing users mapped lime-night→volt see ZERO change. */
:root[data-theme-preset="volt"] {
  --bg:        #121212;
  --surface:   #1E1E1E;
  --ink:       #E8E8E8;
  --ink-2:     #B0B0B0;
  --muted:     #666666;
  --line:      #333333;
  --line-2:    #2A2A2A;
  --accent:    #ADFF00;
  --accent-2:  #C5FF33;
  --on-accent: #0A0A0A;
  --accent-subtle: rgba(173, 255, 0, 0.20);
  --surface-bg:        #000000;
  --surface-card:      #1C1C1E;
  --divider:           #38383A;
  --toggle-track-off:  #39393D;
  --section-label:     #8E8E93;
  --day-cycle-sleep-bg: #131A22;
  --hydration:     #56B0E4;
  --progress-low:  #757A70;
  color-scheme: dark;
}

/* pulse — premium, dark, hot pink. */
:root[data-theme-preset="pulse"] {
  --bg:        #120A10;
  --surface:   #1E1119;
  --ink:       #F4E9EF;
  --ink-2:     #937A88;
  --muted:     #6B5663;
  --line:      #32202B;
  --line-2:    #281822;
  --accent:    #FF3B81;
  --accent-2:  #FF619A;
  --on-accent: #14060C;
  --accent-subtle: rgba(255, 59, 129, 0.20);
  --surface-bg:        #0A0509;
  --surface-card:      #231521;
  --divider:           #3C2834;
  --toggle-track-off:  #3C2834;
  --section-label:     #9C8492;
  --day-cycle-sleep-bg: #160D14;
  --hydration:     #4FA5D6;
  --progress-low:  #8A7280;
  color-scheme: dark;
}

/* ── COLOR-3-DATAVIZ-OKLCH — the perceptual layer ──────────────────────
   Every domain hex above is a gamut-clipped rendering of the oklch() value
   restated here. Two reasons this is a separate @supports block rather than
   oklch() written straight into the token rows:

   1. A custom property accepts any token sequence, so `--training: oklch(…)`
      would PARSE in a browser without oklch support and only fail later, at
      the var() use site — where the declaration is dropped entirely and the
      element loses its colour rather than falling back. Declaring hex first
      and overriding here is the only ordering where an old browser degrades
      to a colour instead of to nothing.
   2. It keeps the authored intent (L, C, H) readable next to the fallback,
      so the next chroma-cap audit reads the numbers it is auditing.

   Chroma is capped at ~0.14 by design: the accent (C 0.16) must stay the
   most saturated thing on any screen, or the "vibrance only in data" rule
   inverts and the domain colours start competing with the signature. */
@supports (color: oklch(50% 0.1 200)) {
  :root {
    --training:  oklch(72% 0.14 145);
    --nutrition: oklch(72% 0.14 55);
    --mood:      oklch(72% 0.10 300);
    --mac-p:     oklch(72% 0.13 155);
    --mac-c:     oklch(75% 0.14 75);
    --mac-f:     oklch(70% 0.13 35);
  }
  :root[data-theme-preset="dag"],
  :root[data-theme-preset="linen"] {
    --training:  oklch(55% 0.13 145);
    --nutrition: oklch(58% 0.13 55);
    --mood:      oklch(52% 0.10 300);
    --mac-p:     oklch(58% 0.13 155);
    --mac-c:     oklch(62% 0.13 75);
    --mac-f:     oklch(58% 0.14 35);
  }
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overscroll-behavior-y: none;
}

body {
  font-weight: 400;
  letter-spacing: 0.005em;
}

button, input, select, textarea {
  font: inherit;
  color: inherit;
}

a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
}
a:hover { border-bottom-color: var(--ink); }

/* ───────── Boot splash ───────── */
.boot {
  /* v0.9.309 — A11Y-4 S7: svh ladder (vh → svh → dvh).
     iOS Safari shrinks the layout viewport when the URL bar collapses,
     pushing buttons off-screen if min-height tracks vh. svh is the
     small-viewport fallback for engines that don't grok dvh. */
  min-height: 100vh;
  min-height: 100svh;
  min-height: 100dvh;
  display: grid;
  place-items: center;
}
/* APP-BOOT-SPLASH-HORSE-ONLY-NO-WORDMARK — the boot splash is nameless: the
   ice-blue horse silhouette inside an indeterminate ring on the navy brand
   plate, no letters. Brand-moment tokens (--horse-plate / --horse-glyph) are
   finish-invariant by design, so the splash reads identically in Dag and
   Nacht. Under prefers-reduced-motion the ring stops turning but stays. */
.boot {
  background: var(--horse-plate);
}
.boot__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-5);
}
.boot__ring {
  position: relative;
  width: 128px;
  height: 128px;
  border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--horse-glyph) 22%, transparent);
  display: grid;
  place-items: center;
}
.boot__ring::before {
  content: '';
  position: absolute;
  inset: -2px;
  border-radius: 50%;
  border: 2px solid transparent;
  border-top-color: var(--horse-glyph);
  border-right-color: var(--horse-glyph);
  animation: boot-ring-spin 1.4s linear infinite;
}
.boot__horse {
  width: 72px;
  height: 72px;
  background-color: var(--horse-glyph);
  -webkit-mask: url('/assets/img/brand/horse-head-alpha-512.png') center / contain no-repeat;
  mask: url('/assets/img/brand/horse-head-alpha-512.png') center / contain no-repeat;
}
@supports not ((-webkit-mask: url('x')) or (mask: url('x'))) {
  .boot__horse {
    background-image: url('/assets/img/brand/horse-head-512.png');
    background-size: contain;
    background-position: center;
    background-repeat: no-repeat;
    background-color: transparent;
    -webkit-mask: none;
            mask: none;
  }
}
@keyframes boot-ring-spin {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .boot__ring::before { animation: none; }
}

/* APP-BOOT-SPLASH-HOLD-UNTIL-READY — the same .boot markup, lifted out of #app
   by liftBootSplash() so render()'s innerHTML swap can't destroy it. It then
   covers the app shell while the landing tab mounts and fetches. z-index sits
   above the whole in-app scale (top of that scale is 9400) but below the
   LOGO-SPLASH-3D overlay at 99999, which must still play on top. No new visual
   design: the opaque navy brand plate + the horse-and-ring already in .boot. */
.boot--overlay {
  position: fixed;
  inset: 0;
  z-index: 9500;
  background: var(--horse-plate);
  opacity: 1;
  transition: opacity var(--dur-slow) var(--ease-standard);
}
.boot--overlay.boot--out {
  opacity: 0;
  pointer-events: none;
}

/* LOGO-SPLASH-3D — fullscreen brand-moment overlay on cold start. The IIFE in
   public/index.html toggles #splash[hidden] off, plays the mp4, then adds
   .splash--out for the fade-out before removing the element. The navy plate
   reuses --horse-plate (the finish-invariant brand-moment token) so it matches
   the video's own black-navy background — no bare hex, css-hex-lint clean. */
#splash {
  position: fixed;
  inset: 0;
  z-index: 99999;        /* above app shell, above iframes, above modals */
  background: var(--horse-plate);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  opacity: 1;
  transition: opacity var(--dur-slow) var(--ease-standard);
  /* iOS safe-area: cover the notch + home-indicator */
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
}
#splash[hidden] { display: none; }
#splash-video {
  width: 100%;
  height: 100%;
  object-fit: contain;  /* preserve the video's portrait 720×1280 framing without cropping */
  display: block;
  background: var(--horse-plate);
}
.splash--out { opacity: 0; }

/* NAV-TAB-SMOOTH-CONTENT-FADE — content-only tab crossfade. The freshly
   rendered shell starts at .tab-enter (faded out, lifted 12px) and settles to
   .tab-enter-active over 280ms; app.js strips both classes on transitionend so
   no transform lingers. Replaces the v0.9.652 solid var(--bg) overlay band,
   which read as a flash. .nav-transition below is retained as a no-op (no JS
   creates or animates it anymore) to avoid dangling references. */
.nav-transition {
  position: fixed;
  inset: 0;
  opacity: 0;
  pointer-events: none;
}
.tab-enter {
  opacity: 0;
  transform: translateY(-12px);
  will-change: opacity, transform;
}
.tab-enter-active {
  opacity: 1;
  transform: translateY(0);
  transition: opacity var(--dur-base) var(--ease-standard),
              transform var(--dur-base) var(--ease-standard);
}
@media (prefers-reduced-motion: reduce) {
  .tab-enter {
    transform: none;
  }
  .tab-enter-active {
    transform: none;
    /* 60ms is deliberate and stays: under reduce the tab cross-fade must be
       near-instant. Only the curve joins the app signature. */
    transition: opacity 60ms var(--ease-standard);
  }
}

/* ───────── App shell layout ───────── */
.app-shell {
  /* v0.9.309 — A11Y-4 S7: svh ladder, same rationale as .boot above. */
  min-height: 100vh;
  min-height: 100svh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

.app-main {
  flex: 1 1 auto;
  /* v0.9.309 — A11Y-4 S6: safe-area-aware horizontal padding so chrome
     doesn't sit under the rounded-corner clip on landscape notched devices. */
  padding-left:  var(--safe-left);
  padding-right: var(--safe-right);
  padding-bottom: calc(var(--tabbar-h) + var(--safe-bottom) + var(--space-5));
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
  /* NAV-SWIPE-TABS-WITH-PEEK-DURING-GESTURE — allow native vertical scroll but
     let the JS controller intercept horizontal drags (gesture-direction
     detection in the first 12px). The controller toggles the inline transform. */
  touch-action: pan-y;
}

/* NAV-SWIPE-TABS-WITH-PEEK-DURING-GESTURE — the peek overlay holds the incoming
   tab's identity pane sliding in from the edge while the outgoing .app-main is
   translated in real time. Positioned (via inline rect) over the live app-main;
   clipped so the off-screen pane only shows the part that has slid in. */
.tab-swipe-peek {
  position: fixed;
  overflow: hidden;
  z-index: 5;
  pointer-events: none;
  background: var(--bg);
}
.tab-swipe-pane {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  will-change: transform;
}
.tab-swipe-pane__id {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  opacity: 0.85;
}
.tab-swipe-pane__icon {
  width: 64px;
  height: 64px;
  object-fit: contain;
}
.tab-swipe-pane__label {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text, var(--ink));
  letter-spacing: 0.01em;
}

/* Auth layout (no tab bar) */
.app-shell--auth .app-main {
  padding-bottom: var(--space-5);
}

/* ───────── Top bar ───────── */
.topbar {
  /* NAV-CHAT-LAUNCH-MOVE-TO-TOPBAR-RIGHT (v0.9.696) — anchor the absolutely
     positioned chat-launch chip to the topbar. */
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* v0.9.309 — A11Y-4 S6: pad top by the device's safe-area inset so the
     Strength/Calendar/Settings topbar clears the iPhone 14+ Dynamic Island. */
  padding:
    calc(var(--space-5) + var(--safe-top))
    calc(var(--space-5) + var(--safe-right))
    var(--space-3)
    calc(var(--space-5) + var(--safe-left));
  border-bottom: 1px solid var(--line);
}
/* COACH-MODE-TABBAR-SHELL-SIX-TABS — de modus-hint. Eén token, afgeleid van
   --accent + --surface, dus Dag en Nacht krijgen allebei automatisch de juiste
   tint zonder een tweede kleurenset (het is bewust GEEN apart thema — concept
   §1). Alleen de header kleurt; de rest van de app blijft identiek. */
:root { --coach-tint: color-mix(in srgb, var(--accent) 12%, var(--surface)); }
body.coach-mode .topbar { background: var(--coach-tint); }
.topbar__title {
  margin: 0;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-2);
}
/* CHAT-LAUNCH-VISIBLE-ON-ALL-SURFACES-NO-OVERLAP — reserve the pill's footprint
   (pill width + gap) at the title's right edge so a long title can't run under
   the floating pill. Only when the pill is actually shown. */
body.chat-launch-on .topbar__title { padding-right: 92px; }
.topbar__action {
  background: none;
  border: none;
  padding: var(--space-2) var(--space-3);
  font-size: 13px;
  letter-spacing: 0.05em;
  color: var(--muted);
  cursor: pointer;
}
.topbar__action:hover { color: var(--ink); }
/* CHAT-LAUNCH-VISIBLE-ON-ALL-SURFACES-NO-OVERLAP (v0.9.701) — the chat-launch
   pill is now a GLOBAL fixed element appended to <body> (ensureGlobalChatLaunch
   in app.js), not a topbar child. A topbar-scoped pill was invisible on the
   Gym + Cardio tabs, which hide .topbar entirely and paint their iframes over
   it. Fixed to the top-right of the viewport above all chrome; hidden by
   default and revealed only on real authed tabs via body.chat-launch-on.
   Finish-invariant navy brand-moment chip (--horse-plate, brand-moment
   exemption covers this token) carrying the horse-head silhouette at 24px. */
.topbar__chat-launch {
  position: fixed;
  top: max(env(safe-area-inset-top), 12px);
  right: var(--space-4);
  z-index: 1000;
  display: none;
  align-items: center;
  gap: 8px;
  height: 44px;
  padding: 0 14px 0 10px;
  border-radius: var(--radius-pill);
  background: var(--horse-plate);
  color: #FFFFFF;  /* allow-hex: brand-moment — white-on-navy chip (precedent: nav__item--chat / horse-head); finish-invariant by design */
  border: 0;
  cursor: pointer;
}
/* Shown only on real authed tabs (render() sets chat-launch-on); absent on
   /chat, auth screens, and pre-app gates so the pill never floats over them. */
body.chat-launch-on .topbar__chat-launch { display: inline-flex; }
/* Belt-and-suspenders: /chat owns its own topbar (hamburger + close), so the
   pill stays hidden there even if chat-launch-on lingered. */
body.is-chat .topbar__chat-launch { display: none; }
/* CHAT-LAUNCH-HIDE-IN-TRAINER-SHEET-MODE — a coach genuinely acting-as a client
   (body.acting-as-client, set in render() via the strict impersonation guard) is
   coaching, not chatting with Sassu, so the pill is noise on the in-program
   strength sheet/day view. fz-strength-in-program is the iframe→host bridge from
   STRENGTH-PERIODIZATION-TILE-CARD-VIEW-ONLY (set only on the sheet/day-overview,
   absent on the program-overview card list + every other tab). Covers
   coach.sassu.fit automatically — it serves the same app shell + app.js, so the
   same render() sets both classes there. Own-data sheet (no acting-as) and card
   view keep the pill. */
body.acting-as-client.fz-strength-in-program .topbar__chat-launch { display: none; }
/* CHAT-LAUNCH-HIDE-IN-SHEET-MODE-GENERAL — hide chat-launch pill when ANY
   full-screen sheet is open (Strength/Nutrition/Diary). The pill overlaps
   sheet content and competes with sheet CTAs; user is in a different task. */
body.fz-strength-in-program .topbar__chat-launch,
body.fz-nutrition-sheet-open .topbar__chat-launch,
body.fz-diary-sheet-open .topbar__chat-launch { display: none; }
/* COACH-STRENGTH-EDIT-MODAL-OVER-COACH-CHROME — the coach-mode "Coaching:" bar
   (#fzCoachBar, injected by coach/index.php at z-index max-int) floats above the
   ENTIRE Strength iframe in the parent stacking context, so it overlaps the
   in-iframe edit modal (.mo) / rename modal (.ren-o), whose backdrop dims the
   rows but whose content/buttons sit behind the bar. strength.html is sealed, so
   strength-bridge.js mirrors the open state onto the host body as
   [data-strength-modal-open]; while set we hide the bar. visibility (not
   display) → no layout shift; modal closes → attribute drops → bar pops back.
   Harmless on app.sassu.fit (no #fzCoachBar there). */
body[data-strength-modal-open] #fzCoachBar {
  visibility: hidden;
  pointer-events: none;
}
.topbar__chat-launch:active { transform: scale(0.96); }
.topbar__chat-launch__label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.brand-horse-head--launch { width: 24px; height: 24px; }

/* ───────── Bottom tab bar ───────── */
.tabbar {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  height: calc(var(--tabbar-h) + var(--safe-bottom));
  /* v0.9.309 — A11Y-4 S6: respect landscape side insets so tabs don't fall
     under the rounded-corner clip; bottom inset stays as before. */
  padding-bottom: var(--safe-bottom);
  padding-left:   var(--safe-left);
  padding-right:  var(--safe-right);
  background: var(--surface);
  border-top: 1px solid var(--line);
  display: flex;
  z-index: 50;
}
.tabbar__btn {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  background: none;
  border: 0;
  /* NAV-CHAT-TAB-HUB-KEBAB-AND-20PCT-SMALLER — ~20% less vertical padding; the
     button still fills the (86px) bar via flex so the tap hitbox stays ≥44px.
     NAV-CHAT-TAB-POLISH-LABEL-SIZE-COLOR-AND-10PCT-UP — ~10% up: padding + font. */
  padding: 7px 2px;
  cursor: pointer;
  color: var(--muted);
  font-size: 9px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  position: relative;
  overflow: hidden;
}
.tabbar__btn span {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* ICON-WIRE-SHARED-INFRA-AND-NAV (v0.9.702) — central icon primitive. The brand
   line-art webp is rendered as a CSS mask filled with currentColor, so the icon
   inherits the parent text colour (--ink on body, --accent in a primary button,
   --chat-on-accent on the chat chip) and recolours automatically with the theme
   finish — no per-surface tint overrides. Kept ABOVE .tabbar__icon so the locked
   bottom-nav keeps its own 22px/display:block sizing for fz-icon tab glyphs. */
.fz-icon {
  display: inline-block;
  width: 24px;
  height: 24px;
  background: currentColor;
  -webkit-mask: var(--icon-url) center / contain no-repeat;
          mask: var(--icon-url) center / contain no-repeat;
  vertical-align: middle;
  flex: 0 0 auto;
}
.tabbar__icon {
  /* NAV-CHAT-TAB-POLISH-LABEL-SIZE-COLOR-AND-10PCT-UP — 20px (was 18px) line-art
     glyph size for the inline-SVG nav tabs (hub kebab, challenges). */
  width: 20px;
  height: 20px;
  display: block;
  opacity: 0.7;
}
/* ICON-NAV-BIGGER-AND-CLEARER (v0.9.707) — bump the bottom-nav line-art glyphs from the
   22px tabbar default to 28px so the silhouet reads clearly in the 64px strip;
   contrast(1.05) firms up the alpha-mask edges. Nav-scoped (.tabbar__btn) only:
   the topbar chat-launch pill and Voeding meal-row icons keep the 24px default.
   The 64px tabbar centres icon+label with room to spare, so padding is left as
   the existing balanced baseline (label stays clear of the icon). */
.tabbar__btn .fz-icon {
  /* NAV-CHAT-TAB-POLISH-LABEL-SIZE-COLOR-AND-10PCT-UP — 25px (was 22px). */
  width: 25px;
  height: 25px;
  filter: contrast(1.05);
}
/* NAV-ICONS-DROP-OPACITY-FADE-USE-UNDERLINE-INDICATOR (v0.9.812) — opacity/
   grayscale-fading op inactieve iconen voelde voor Jayy vervaagd op ALLE
   tabs (zelfs de active bleef matig vanwege de mix). Vervangen door: alle
   icons full-color, active = lichte 1.06 zoom + 24×3px accent-pill onder de
   tile. De label-laag (bold actief / muted inactief) blijft de tekstuele
   indicator zoals 'ie altijd was. */
.tabbar__btn__photo {
  /* NAV-CHAT-TAB-POLISH-LABEL-SIZE-COLOR-AND-10PCT-UP — 57px (was 51px), ~10% up;
     the scale() transforms below stay so the effective footprint moves in step. */
  width: 57px;
  height: 57px;
  object-fit: contain;
  border-radius: var(--radius-md);
  display: block;
  user-select: none;
  -webkit-user-drag: none;
  opacity: 1;
  /* NAV-TABBAR-ICONS-GRAYSCALE-WHEN-INACTIVE (v0.9.847) — inactive nav-icons
     desatureren zodat alleen het actieve tab-icon in kleur staat → gratis
     visuele hiërarchie. Active-variant zet filter terug op none. */
  filter: grayscale(100%);
  transition: transform var(--dur-fast) var(--ease-standard), filter var(--dur-fast) var(--ease-standard);
  /* shrink photo-style nav-icons to match HUB emblem footprint (visueel ~gelijk;
     photographic PNG zonder whitespace-padding oogt groter dan flat emblem) */
  transform: scale(0.8);
  transform-origin: center bottom;
}
.tabbar__btn[aria-current="page"] .tabbar__btn__photo {
  transform: scale(0.85);
  filter: none;
}
/* NAV-TABBAR-REMOVE-STRIPE-INDICATORS — boven/onder actieve-tab streepjes weg.
   Sinds v0.9.847 (grayscale-inactive) is kleur het active-signaal; de oude
   ::before (top-streep boven icoon) + ::after (bottom-streep onder label) zijn
   redundant. Color-on-active + bold-label (hieronder) blijven — kleur + bold is
   robuust genoeg voor a11y (kleurenblindheid ziet nog het bold-verschil). */
.tabbar__btn[aria-current="page"] {
  color: var(--ink);
  font-weight: 600;
}
.tabbar__btn[aria-current="page"] .tabbar__icon { opacity: 1; }
.tabbar__btn[aria-disabled="true"] {
  opacity: 0.35;
  cursor: not-allowed;
}
.tabbar__btn[aria-disabled="true"]:active,
.tabbar__btn[aria-disabled="true"]:hover { background: none; }

/* BRICKS-TAB-DISABLED-VISUAL (v0.9.542) — visible-but-disabled tabs.
   State-driven (not key-hardcoded): any tab in the coming-soon set —
   Bricks always, Cardio for the nutrition-only beta cohort — gets the
   same dimmed look. opacity is token-friendly (no hex literal — passes
   css-hex-lint). When the user is ON the coming-soon tab the dimming
   eases up so the active indicator is still legible. */
.tabbar__btn--coming-soon { opacity: 0.4; }
.tabbar__btn--coming-soon[aria-current="page"] { opacity: 0.6; }

/* NAV-DEACTIVATED-TAB-REAL-ICON-BLACK-WITH-WHITE-QUESTION-MARK — a globally-
   deactivated tab keeps its REAL nav icon but renders it fully black, with a
   white '?' badge overlaid top-right. brightness(0) collapses ANY colored PNG/
   SVG/mask glyph to solid black regardless of original colors; grayscale(100%)
   first neutralises hue so brightness(0) lands cleanly. The button is the
   positioning context for the ::after badge. Label treatment is untouched. */
.tabbar__btn--deactivated { position: relative; }
.tabbar__btn--deactivated .tabbar__icon,
.tabbar__btn--deactivated .fz-icon,
.tabbar__btn--deactivated .tabbar__btn__photo {
  filter: grayscale(100%) brightness(0);
  opacity: 1;
}
.tabbar__btn--deactivated::after {
  content: "?";
  position: absolute;
  top: 2px;
  right: 4px;
  font-size: 11px;
  font-weight: 700;
  color: #ffffff; /* allow-hex: white-on-black '?' badge — contrast-locked, not theme-tinted */
  background: rgba(0, 0, 0, 0.55);
  border-radius: 50%;
  width: 14px;
  height: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  pointer-events: none;
}

/* NAV-CHAT-TAB-POLISH-LABEL-SIZE-COLOR-AND-10PCT-UP — the Chat tab is now a
   PLAIN photo nav-tab: identical button markup, img box and label node as
   Dagboek/Gym/Cardio/Voeding. The old v0.9.687 navy-chip erfenis (separate
   tile background, extra margin/padding, white label, overflow-clipped label)
   is fully neutralised — no background, no border-radius, no colour override —
   so the shared .tabbar__btn geometry paints it. NAV-CHAT-ICON-GRAYSCALE-LIKE-
   THE-REST removed the last distinction: chat had a `filter: none` exemption
   justified by "the bottom-nav disappears on /chat so the tab is never active",
   which stopped being true once /chat became a full tab root. Chat now has ZERO
   exemptions — grey when inactive, colour when active, like every other tab.
   Do not reintroduce one. The .tabbar__btn--chat class still exists as the
   badge mount point (app.js paintChatBadge), not as a style hook. */

/* NAV-SETTINGS-TAB-CONVERT-TO-MIJ-HUB-MENU (v0.9.816) — the rightmost 'Me' tab
   shows the user's avatar (photo) or an accent-circle initial fallback, in the
   same 64×64 box as the photo nav-icons so the bar stays aligned. The shared
   .tabbar__btn[aria-current="page"]::after accent-pill already lights it on the
   /settings* surface; we add the same active scale-up the photo tiles get. */
.tabbar__btn__avatar {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--line-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 28px;
  font-weight: 800;
  color: var(--accent);
  user-select: none;
  -webkit-user-drag: none;
  /* NAV-TABBAR-ICONS-GRAYSCALE-WHEN-INACTIVE (v0.9.847) — HUB-emblem/avatar
     volgt dezelfde grayscale-when-inactive regel als de photo-icons. */
  filter: grayscale(100%);
  transition: transform var(--dur-fast) var(--ease-standard), filter var(--dur-fast) var(--ease-standard);
}
.tabbar__btn--hub[aria-current="page"] .tabbar__btn__avatar { transform: scale(1.06); filter: none; }
/* Emblem fallback: contain + padding so the silver-chrome 3D highlight keeps
   breathing room instead of being full-bleed cropped like a photo avatar. */
.tabbar__btn__avatar--emblem { object-fit: contain; padding: 4px; background: transparent; }

/* NAV-HUB-KEBAB-SIZE-AND-LABEL-ALIGN — the hub kebab (iconKebab, inline SVG with
   class .tabbar__icon) rendered at the 20px default, so its icon block was far
   shorter than the 57px photo-img box of the other tabs → the 'HUB' label floated
   ABOVE the DAGBOEK/GYM/CARDIO/VOEDING/CHAT baseline. Give the kebab the SAME
   layout box (57px) and the SAME scale-transform the photo icons carry, so:
   (a) the 57px layout height puts the label on the shared baseline, and
   (b) the identical scale keeps the rendered bounding box in step with the
   photos (transform is visual-only, so it doesn't disturb the baseline).
   The 24-unit kebab viewBox now fills a 57px box, so the three dots read at the
   same visual weight as the photo glyphs (~60%+ of box height) instead of a tiny
   glyph lost in whitespace. Muted-when-inactive stays via the inherited opacity
   (0.7); active lifts it to full ink + the shared scale-up, matching the hub. */
.tabbar__btn--hub .tabbar__icon {
  width: 57px;
  height: 57px;
  /* flex-shrink:0 — unlike the photo <img> (a replaced element whose intrinsic
     size resists column-flex shrink), a bare <svg> shrinks a couple px when the
     column is tight, which pulled the 'HUB' label ~2px above the shared
     baseline. Pin it so the layout box stays exactly 57px like the photos. */
  flex: 0 0 auto;
  transform: scale(0.8);
  transform-origin: center bottom;
}
.tabbar__btn--hub[aria-current="page"] .tabbar__icon {
  transform: scale(0.85);
  opacity: 1;
}

/* COACH-MODE-NAV-ICONS-INSTALL — the chat tab's inline-SVG scale exception is
   gone with the SVG itself: chat now renders nav-chat.png through the shared
   .tabbar__btn__photo box, so it carries the same 57px + scale(0.8) footprint as
   every other photo tab. Only the hub kebab still needs its own icon sizing. */

/* Hub bottom-sheet (the 'Me' menu). Overlay + slide-in sheet mirror the
   nutrition .mo-* primitives; rows reuse the settings .set-row vocabulary so
   they read as familiar settings rows. */
.hub-overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 20, 30, 0.5);
  z-index: 220;
  display: flex;
  align-items: flex-end;
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-standard);
}
.hub-overlay.hub-sheet--open { opacity: 1; }
.hub-sheet {
  background: var(--bg);
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  max-height: 85vh;
  border-top-left-radius: var(--radius-lg);
  border-top-right-radius: var(--radius-lg);
  display: flex;
  flex-direction: column;
  transform: translateY(100%);
  transition: transform var(--dur-base) var(--ease-standard);
  padding-bottom: var(--safe-bottom);
  overflow-y: auto;
}
.hub-sheet.hub-sheet--open { transform: translateY(0); }
.hub-grip {
  width: 40px;
  height: 4px;
  background: var(--line);
  border-radius: var(--radius-sm);
  margin: 8px auto;
  flex: 0 0 auto;
  cursor: pointer;
}
.hub-menu-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  padding: 12px 16px 16px;
}
.hub-menu-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 18px 12px;
  background: var(--line-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  min-height: 96px;
  cursor: pointer;
  position: relative; /* anchor for .hub-menu-btn__badge */
  transition: transform var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard);
}
.hub-menu-btn:active { transform: scale(0.97); background: var(--line); }
/* HUB-CHALLENGES-COUNT-BADGE — accent count-pill of open challenges. */
.hub-menu-btn__badge {
  position: absolute;
  top: 8px;
  right: 8px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  box-sizing: border-box;
  border-radius: var(--radius-md);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 11px;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
}
@media (prefers-reduced-motion: no-preference) {
  .hub-menu-btn__badge { animation: hub-menu-badge-in var(--dur-base) var(--ease-standard); }
  @keyframes hub-menu-badge-in {
    from { transform: scale(0.4); opacity: 0; }
    to   { transform: scale(1);   opacity: 1; }
  }
}
.hub-menu-btn__icon { width: 28px; height: 28px; flex-shrink: 0; color: var(--accent); }
.hub-menu-btn__icon svg { width: 28px; height: 28px; }
.hub-menu-btn__label {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  text-align: center;
  line-height: 1.25;
}
/* Odd final button (e.g. non-coach user with 5 buttons) spans full width. */
.hub-menu-grid > .hub-menu-btn:nth-child(odd):last-child { grid-column: 1 / -1; }

/* HUB-LOW-VISION-TILE — accessibility promotion. Solid-accent tile with an
   oversized eye so a low-vision user can find the toggle at a glance. Normal
   grid width (Jayy 2026-08-07: full-width was too big — "hij past er gewoon
   rustig in"); the accent fill + big eye carry the findability instead.
   --on-accent gives the required contrast on the accent fill. */
.hub-menu-btn--a11y {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.hub-menu-btn--a11y:active { background: var(--accent); filter: brightness(0.94); }
.hub-menu-btn--a11y .hub-menu-btn__icon,
.hub-menu-btn--a11y .hub-menu-btn__icon svg {
  width: 44px;
  height: 44px;
}
/* DIARY-METRICS-SELF-IMPORT-ENTRY — measurement-import sheet body. Rides the
   shared .hub-overlay / .hub-sheet / .hub-grip chrome; only the content is new. */
.mi-sheet { padding: 4px 16px 20px; display: flex; flex-direction: column; gap: 12px; }
.mi-sheet__title { margin: 0; font-size: 18px; font-weight: 700; color: var(--text); }
.mi-sheet__hint { margin: 0; font-size: 14px; line-height: 1.4; color: var(--muted); }
.mi-pick {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  border: 1px dashed var(--line);
  border-radius: var(--radius-md);
}
.mi-pick__label { font-size: 14px; font-weight: 600; color: var(--text); }
.mi-pick__input { font-size: 14px; color: var(--text); }
.mi-sheet__status { margin: 0; font-size: 14px; color: var(--muted); min-height: 1em; }
.mi-sheet__status--error { color: var(--danger); }
.mi-summary { display: flex; flex-direction: column; gap: 6px; }
.mi-summary__row { display: flex; gap: 12px; justify-content: space-between; font-size: 14px; }
.mi-summary__label { color: var(--muted); flex: 0 0 auto; }
.mi-summary__value { color: var(--text); text-align: right; }
.mi-summary__warn { margin: 0; font-size: 13px; line-height: 1.4; color: var(--muted); }
.mi-submit {
  margin-top: 4px;
  padding: 12px;
  border: 0;
  border-radius: var(--radius-md);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
}
.mi-submit:disabled { opacity: 0.45; cursor: default; }

.hub-menu-btn--a11y .hub-menu-btn__icon { color: var(--on-accent); }
.hub-menu-btn--a11y .hub-menu-btn__icon svg { stroke-width: 2; }
.hub-menu-btn--a11y .hub-menu-btn__label {
  color: var(--on-accent);
  font-size: 15px;
  font-weight: 700;
}
/* When low-vision mode is ON the whole hub-menu-btn set already stands out (the
   accent fill is now the "on" state); still keep the eye oversized regardless
   of state so the disable-tap is as easy to hit as the enable-tap. */

/* v0.9.320 A11Y-8 S23 — slide the tabbar off-screen while the on-screen
   keyboard is up. body.kb-open is toggled by the VisualViewport listener in
   app.js boot. Uses translateY(100%) so the existing transition (if any) and
   safe-bottom padding both come along; restores instantly when the keyboard
   dismisses. */
body.kb-open .tabbar {
  transform: translateY(100%);
  transition: transform var(--dur-base) var(--ease-standard);
  pointer-events: none;
}

/* v0.9.313 STRENGTH-BADGE-AND-COUNTFIX — countdown pill above the STRENGTH
   tabbar icon. Variant B (countdown): drops as the user logs exercises in
   today's session, hides at 0. v0.9.314 STRENGTH-BADGE-RED-RECOLOR: painted
   in iOS-system-red (#FF3B30) per Jayy — matches the universal notification-dot
   convention used by Instagram/Facebook/WhatsApp, so the visual language now
   pre-loads off the broader app ecosystem instead of brand accent. Painted/
   removed by paintStrengthBadge() in app.js. Position anchors to the icon
   strip (top-right of the icon area), not the button bounds, so the active-tab
   top accent bar doesn't compete with it. */
.tabbar__btn { position: relative; } /* (already set above; defensive — keeps absolute child contained even if the rule above is overridden by future refactors) */
.tabbar__badge {
  position: absolute;
  top: 4px;
  /* Half a tab width — 22px icon → ~12px right of icon centerline.
     Tab width varies (flex:1), so right is anchored as a fraction of the
     button. With 6 tabs on a 390px viewport that's ~65px/btn → 25% of
     btn width = ~16px right inset; clamps prevent extremes on narrow/wide. */
  right: clamp(14px, 22%, 22px);
  min-width: 16px;
  height: 16px;
  /* Geometric centering of the inner digit: inline-flex + line-height:1 +
     tabular-nums prevents the baseline-vs-geometric mismatch that left "7"
     visibly upper-left of the circle center (v0.9.317 → v0.9.318 fix). */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  background: var(--badge-bg);
  color: var(--on-badge);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  text-align: center;
  letter-spacing: 0;
  text-transform: none;
  box-shadow: var(--elev-1);
  pointer-events: none;
  opacity: 0;
  animation: tabbarBadgeFadeIn var(--dur-base) var(--ease-standard) forwards;
}
@keyframes tabbarBadgeFadeIn {
  to { opacity: 1; }
}

/* COACH-SHARED-RECIPE-UNSEEN-BADGE — numberless dot variant. Used on the
   Nutrition tab when there is no restock number to show but the client has an
   unseen coach-shared recipe. A small accent circle rather than a red count. */
.tabbar__badge.tabbar__badge--dot {
  min-width: 10px;
  width: 10px;
  height: 10px;
  padding: 0;
  background: var(--accent);
  box-shadow: var(--elev-1);
}

/* COACH-SHARED-RECIPE-UNSEEN-BADGE — dot on the "Mijn recepten" add-submenu
   tile while unseen coach-shares exist. Absolute accent circle, top-right. */
.nut-addmenu__tile { position: relative; }
.nut-addmenu__tile-dot {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 9px;
  height: 9px;
  border-radius: var(--radius-pill);
  background: var(--accent);
  box-shadow: var(--elev-1);
  pointer-events: none;
}

/* COACH-SHARED-RECIPE-UNSEEN-BADGE — brief highlight flash on a just-shared
   recipe row when the client opens My-recipes. Accent-tinted background that
   fades out over ~2.5s to say "this one is new from your trainer", then the
   row settles to its normal state and the dot clears permanently. */
.nut-recipe-shared-flash {
  animation: nutRecipeSharedFlash 2500ms var(--ease-standard) 1;
}
@keyframes nutRecipeSharedFlash {
  0%   { background: var(--accent); color: var(--on-accent); }
  70%  { background: var(--accent); color: var(--on-accent); }
  100% { background: transparent; color: inherit; }
}
@media (prefers-reduced-motion: reduce) {
  /* Static, non-animated fade: hold a subtle accent tint briefly then rely on
     JS removing the class. No motion, still communicates "new". */
  .nut-recipe-shared-flash {
    animation: none;
    background: var(--accent-subtle);
  }
}

/* v0.9.342 REST-TIMER-REWORK — tabbar STRENGTH-badge completion pulse.
   Fires once when fzRestTimer transitions to finished (chime/vibration moment).
   Single scale-pulse so a peripheral glance catches the rest completion. */
.tabbar__badge.tabbar__badge--pulse {
  animation: tabbarBadgePulse var(--dur-slow) var(--ease-standard) 1;
}
@keyframes tabbarBadgePulse {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.45); }
  100% { transform: scale(1); }
}

/* v0.9.342 REST-TIMER-REWORK — cross-tab thin strip + minimal text line.
   Singleton element appended to <body> by app.js fzMountRestStrip(). Shown
   only when a rest timer is active AND the inline pill in the Strength
   iframe is NOT visible (drawer closed, scrolled away, or different tab).
   Position: fixed top, accent-tinted progress bar + small body line. Tap
   navigates to /strength (the bridge restores pill into view on next tick). */
.fz-rt-strip {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  background: var(--surface);
  border-bottom: 1px solid var(--line-2);
  padding-top: var(--safe-top);
  display: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  box-shadow: var(--elev-1);
}
.fz-rt-strip.is-on { display: block; }
.fz-rt-strip__bar {
  height: 5px;
  background: var(--line-2);
  position: relative;
  overflow: hidden;
}
.fz-rt-strip__fill {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  background: var(--accent);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  width: 0%;
  transition: width var(--dur-loop) linear;
}
.fz-rt-strip__body {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-2);
  padding: 6px 14px 8px;
  letter-spacing: 0.01em;
  line-height: 1;
}
.fz-rt-strip.is-pulse .fz-rt-strip__fill {
  width: 100% !important;
  animation: fzRtStripPulse var(--dur-pulse) var(--ease-standard) 1;
}
@keyframes fzRtStripPulse {
  0%   { opacity: 1; }
  70%  { opacity: 1; }
  100% { opacity: 0; }
}
/* Shift the shell down when the strip is visible so the topbar doesn't
   sit underneath it. The strip height is fluid (5px bar + ~24px text +
   safe-top) — use 36px + safe-top as the offset; close enough for the
   minimal mode Jayy locked. */
body:has(.fz-rt-strip.is-on) .app-shell {
  padding-top: calc(36px + var(--safe-top));
}
/* MOBILE-COMPAT-HAS-SELECTOR-FALLBACK (F2) — plain-selector twin for engines
   without :has() (Chromium <105 / Samsung Internet <20 / Firefox <121). JS
   toggles body.fz-rt-on in lockstep with .fz-rt-strip.is-on (app.js). Identical
   declarations to the :has() rule above — do NOT diverge. */
body.fz-rt-on .app-shell {
  padding-top: calc(36px + var(--safe-top));
}

/* ───────── Auth screens ───────── */
.auth {
  /* v0.9.309 — A11Y-4 S6+S7: notch-aware vertical padding (--safe-top so the
     "sassu" wordmark clears the Dynamic Island) + svh ladder so the Sign-in
     button stays above the iOS Safari URL bar. */
  min-height: 100vh;
  min-height: 100svh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding:
    calc(var(--space-7) + var(--safe-top))
    calc(var(--space-5) + var(--safe-right))
    calc(var(--space-7) + var(--safe-bottom))
    calc(var(--space-5) + var(--safe-left));
  max-width: 440px;
  margin: 0 auto;
}
/* THEME-2 — auth-screen brand lockup: emblem above the W2 wordmark.
   The wordmark renders via the CSS-mask alpha technique: the element
   paints background var(--ink) clipped to the letterform alpha, so it
   recolors per finish with a single asset (NEVER the emblem at this
   size — wordmark letterforms only, per APP_BRAND_MEDIA spec). The
   'sassu' text stays in the DOM (transparent) so AT still announces
   the brand. */
.auth__brandwrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-7);
}
.auth__brandwrap--lg { gap: var(--space-5); }
.auth__emblem {
  width: 40px;
  height: 40px;
  object-fit: contain;
  /* The emblem asset carries its navy plate (no alpha master exists yet);
     rounding it makes the plate read as a deliberate brand badge on light
     finishes instead of a raw square. */
  border-radius: 22%;
}
.auth__brandwrap--lg .auth__emblem {
  width: 96px;
  height: 96px;
}
.auth__brand {
  /* Source alpha PNG is 900×120 (7.5:1); 180×24 keeps the ratio. */
  width: 180px;
  height: 24px;
  overflow: hidden;
  color: transparent;
  user-select: none;
  background: var(--ink);
  -webkit-mask: url('/assets/img/brand/wordmark-alpha.png') center / contain no-repeat;
  mask: url('/assets/img/brand/wordmark-alpha.png') center / contain no-repeat;
}
/* SETTINGS-TOP-WORDMARK-LOGO — Sassu wordmark at the top of /settings.
   Same alpha-mask recolor trick as .auth__brand so the wordmark tints to
   the theme --ink token in both Dag and Nacht. Modest header accent, not
   a hero. Source alpha PNG is 900×120 (7.5:1); 150×20 keeps the ratio. */
.set-brand {
  display: flex;
  justify-content: center;
  margin: var(--space-2) 0 var(--space-5);
}
.set-brand__wordmark {
  width: 150px;
  height: 20px;
  overflow: hidden;
  color: transparent;
  user-select: none;
  background: var(--ink);
  -webkit-mask: url('/assets/img/brand/wordmark-alpha.png') center / contain no-repeat;
  mask: url('/assets/img/brand/wordmark-alpha.png') center / contain no-repeat;
}
/* BETA-TESTER-IDENTITY-AND-ADMIN-CLARITY */
.set-beta-pill {
  display: inline-flex; align-items: center; gap: 6px;
  align-self: flex-start;
  margin: 4px 0 12px 16px;
  padding: 4px 10px 4px 8px;
  background: var(--accent-subtle);
  color: var(--ink);
  border: 1px solid var(--divider);
  border-radius: var(--radius-pill);
  font-size: 11px; font-weight: 600;
  letter-spacing: 0.02em; text-transform: uppercase;
}
.set-beta-pill__dot { width: 8px; height: 8px; color: var(--accent); }
.set-beta-pill__label { line-height: 1; }
.set-beta-chip {
  display: inline-block;
  margin-left: 8px;
  padding: 2px 8px;
  background: var(--accent-subtle);
  color: var(--accent);
  border-radius: var(--radius-md);
  font-size: 10px; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase;
  vertical-align: middle;
}
.premium-card__beta-note {
  margin: 8px 0 12px;
  padding: 10px 12px;
  background: var(--accent-subtle);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-md);
}
.premium-card__beta-note__body {
  margin: 0;
  font-size: 13px; line-height: 1.4;
  color: var(--ink);
}
.fz-admin-panel__link { margin-top: 12px; }
.fz-admin-panel__link-row {
  display: flex; gap: 8px; align-items: center;
  margin-top: 8px;
}
.fz-admin-panel__link-input {
  flex: 1; min-width: 0;
  font-family: monospace; font-size: 12px;
  padding: 8px 10px;
}
.fz-admin-invites__pending-row {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  padding: 10px 0;
  border-bottom: 1px solid var(--divider);
}
.fz-admin-invites__pending-row:last-child { border-bottom: 0; }
.fz-admin-invites__pending-meta {
  flex-basis: 100%;
  font-size: 11px; color: var(--muted);
  opacity: 0.7;
}
.auth__title {
  font-size: 22px;
  font-weight: 400;
  margin: 0 0 var(--space-2);
  letter-spacing: -0.005em;
}
.auth__sub {
  font-size: 14px;
  color: var(--muted);
  margin: 0 0 var(--space-6);
}
.auth__footer {
  margin-top: var(--space-6);
  text-align: center;
  font-size: 13px;
  color: var(--muted);
}
.auth__footer a {
  color: var(--ink);
  border-bottom: 1px solid var(--line);
}

/* BRAIN-CONSENT-REGISTRATION-OPTIN — default-unchecked research consent row on
   the signup + claim forms, and the one-time existing-user prompt overlay. */
.auth__consent {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  margin: var(--space-4) 0 var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface-2, var(--surface));
  cursor: pointer;
}
.auth__consent-box {
  flex: none;
  width: 18px;
  height: 18px;
  margin-top: 2px;
  accent-color: var(--accent);
}
.auth__consent-text {
  font-size: 13px;
  line-height: 1.45;
  color: var(--ink);
}
.fz-consent-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  background: color-mix(in srgb, var(--ink) 45%, transparent);
}
.fz-consent-sheet {
  width: 100%;
  max-width: 360px;
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--card, var(--surface));
  box-shadow: var(--elev-3);
}
.fz-consent-sheet__title {
  margin: 0 0 var(--space-2);
  font-size: 19px;
}
.fz-consent-sheet__body {
  margin: 0 0 var(--space-4);
  color: var(--muted);
  font-size: 14px;
  line-height: 1.5;
}
.fz-consent-sheet__actions {
  display: flex;
  gap: var(--space-3);
}
.fz-consent-sheet__actions .btn {
  flex: 1;
}

/* v0.9.319 — A11Y-7 S14: first-run landing at `/`. Two equal-weight CTAs +
   trust line. No form, no inputs — pure orient-and-route. Inherits .auth's
   notch-aware ladder so the headline clears the Dynamic Island and the CTAs
   stay above the iOS Safari URL bar. */
.auth--landing {
  justify-content: center;
  text-align: center;
}
/* THEME-2 — the unauthenticated landing is a BRAND surface: Deep Navy +
   void-horizon background in EVERY finish (scoped --brand-* token
   overrides, per APP_BRAND_MEDIA_UX_SPEC §0 — navy never leaks into
   working screens because the class only exists on the landing shell).
   The webp (14 KB) is a CSS background-image, so the browser loads it on
   demand after first paint; background-color paints instantly beneath it.
   Static image — nothing animated, no reduced-motion pairing needed. */
.app-shell--landing {
  --bg: var(--brand-navy);
  --surface: var(--brand-navy);
  --ink: var(--brand-ink);
  --ink-2: var(--brand-dim);
  --muted: var(--brand-dim);
  --accent: var(--brand-ice);
  --accent-2: var(--brand-ice);
  --on-accent: var(--brand-navy);
  --line: rgba(168, 216, 234, 0.18);
  background-color: var(--brand-navy);
  background-image: url('/assets/img/brand/bg-void-horizon-1280.webp');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  color: var(--ink);
}
.auth__hero {
  margin-bottom: var(--space-7);
}
.auth__hero-headline {
  font-size: 26px;
  font-weight: 400;
  line-height: 1.25;
  letter-spacing: -0.01em;
  margin: 0;
  color: var(--ink);
}
.auth__hero-sub {
  font-size: 15px;
  line-height: 1.5;
  margin: var(--space-3) 0 0;
  color: var(--muted);
}
.auth__cta-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.auth__trust {
  margin: var(--space-6) 0 0;
  font-size: 12px;
  color: var(--muted);
  text-align: center;
}
/* REGISTER-REQUEST-ACCESS-NO-CODE-PATH — the 'brievenbus': a quiet tertiary
 * text link for code-less visitors, so an empty invite field is never a dead
 * end. Muted + centred; not an accent control (no --on-accent needed). */
.auth__request-access {
  margin: calc(var(--space-3) * -1) 0 var(--space-5);
  font-size: 13px;
  color: var(--muted);
  text-align: center;
}
.auth__request-access a {
  color: var(--muted);
  border-bottom: 1px solid var(--line);
  cursor: pointer;
}
/* INVITE-LOCALE-FIRST (v0.9.657) — bilingual hint above the NL/EN picker
 * buttons. Self-labels in both languages by design (user has not chosen yet),
 * so it stays muted and centred and is never run through t(). */
.auth__locale-hint {
  margin: 0 0 var(--space-6);
  font-size: 13px;
  color: var(--muted);
  text-align: center;
}

/* Track A2 — "Sign in with Google". Empty until the server reports it's
 * configured, so .auth__google-wrap collapses to nothing when hidden. */
.auth__google-wrap:empty {
  display: none;
}

/* Beta Terms-of-Service gate (v0.9.169) — mandatory accept checkbox. */
.form-check {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  font-size: 14px;
  line-height: 1.45;
  color: var(--ink);
  margin: var(--space-5) 0 var(--space-6);
  cursor: pointer;
}
.form-check input[type="checkbox"] {
  width: 20px;
  height: 20px;
  margin-top: 1px;
  flex: 0 0 auto;
  accent-color: var(--accent);
}

/* Legal links in Settings (v0.9.170) — stacked, tappable rows.
   v0.9.508 SETTINGS-PADDING-AND-PROFILE-KERNING-V2 — horizontal padding was
   2px so the rows bled to the card's left edge. Aligned with the standard
   .set-row inset (var(--space-4) = 16px) + min-height 44px for tap target. */
.set-legal {
  display: flex;
  flex-direction: column;
}
.set-legal a {
  padding: 12px var(--space-4);
  min-height: 44px;
  display: flex;
  align-items: center;
  color: var(--ink);
  border-bottom: 1px solid var(--line);
  text-decoration: none;
  font-size: 15px;
}
.set-legal a:last-child { border-bottom: 0; }
.auth__divider {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-5) 0;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
}
.auth__divider::before,
.auth__divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}
.auth__google {
  display: flex;
  justify-content: center;
}

/* ───────── Form primitives ───────── */
.form-group {
  margin-bottom: var(--space-4);
}
.form-label {
  display: block;
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--space-2);
}
/* v0.9.329 — A11Y-11B S33: hairline underline stays (minimal Sassu), but the
   whole field gains a soft background tint on :focus-visible so the active tap
   target is unambiguous. Horizontal padding lifted from 0 → 12px so the tint
   reads as the whole field, not just under the caret. border-radius applied to
   the top corners so the tint clips against the underline cleanly. */
.form-input {
  display: block;
  width: 100%;
  padding: 14px 12px;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line);
  font-size: 16px;
  color: var(--ink);
  outline: none;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  transition: background var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard);
}
.form-input:focus,
.form-input:focus-visible {
  background: var(--surface-muted);
  border-bottom-color: var(--ink);
}
.form-input::placeholder { color: var(--muted); }
.form-input[aria-invalid="true"] { border-bottom-color: var(--danger); }

/* v0.9.311 — A11Y-6 S18: password reveal toggle. The wrap is the
   positioning context for the absolutely-placed eye button; the input keeps
   its full-width baseline, the button rides flush-right inside the field's
   own padding box so it never overlaps the typed characters at common
   screen widths. 44×44 hit-area meets WCAG SC 2.5.5 + 2.5.8. */
.form-input-wrap {
  position: relative;
  display: block;
}
.form-input-wrap .form-input {
  padding-right: 44px;
}
.form-input__reveal {
  position: absolute;
  top: 50%;
  right: 0;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--muted);
  cursor: pointer;
  border-radius: var(--radius-md);
}
.form-input__reveal:hover,
.form-input__reveal:focus-visible { color: var(--ink); outline: none; }
.form-input__reveal[aria-pressed="true"] { color: var(--ink); }
.form-input__reveal svg { width: 22px; height: 22px; }

/* v0.9.311 — A11Y-6 S19: ToS gate PDF tiles. Two equal-weight tiles stacked
   above the consent checkbox, replacing the inline "View ToS · Privacy" link
   pair. Each tile is a full-width tap target with the PDF name + a chevron;
   the visual weight makes "this is a document you should open" obvious. */
.tos-tiles {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: var(--space-4) 0 var(--space-5);
}
.tos-tile {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--ink);
  text-decoration: none;
  font-size: 14px;
  min-height: 44px;
}
.tos-tile:hover,
.tos-tile:focus-visible { border-color: var(--ink); outline: none; }
.tos-tile__label { display: flex; flex-direction: column; gap: 2px; }
.tos-tile__name { font-weight: 500; }
.tos-tile__meta {
  font-size: 11px;
  color: var(--muted);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.tos-tile__chev {
  flex: 0 0 auto;
  color: var(--muted);
}
.tos-tile__chev svg { width: 18px; height: 18px; display: block; }

.btn {
  display: inline-block;
  width: 100%;
  background: var(--ink);
  color: var(--bg);
  border: 1px solid var(--ink);
  padding: 16px 20px;
  font-size: 14px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 500;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
  border-radius: 0;
}
.btn:hover { background: var(--ink-2); border-color: var(--ink-2); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }
.btn--ghost {
  background: transparent;
  color: var(--ink);
}
.btn--ghost:hover {
  background: var(--ink);
  color: var(--bg);
}
.btn--accent {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.btn--accent:hover { background: var(--accent-2); border-color: var(--accent-2); }
/* Ghost-danger — destructive secondary action. Never a filled slab; calm red
   outline that reads as destructive without shouting. Disabled = subdued muted
   outline (transparent bg), not a grey slab. */
.btn--dangerghost {
  background: transparent;
  color: var(--danger);
  border-color: var(--danger);
}
.btn--dangerghost:hover { background: var(--danger); color: var(--on-danger); border-color: var(--danger); }
.btn--dangerghost:disabled { opacity: 1; background: transparent; color: var(--muted); border-color: var(--line); cursor: not-allowed; }
/* COACH-CLAIM-PAGE-ADD-SIGN-IN-WITH-EXISTING-ACCOUNT-OPTION (v0.9.511) —
   outline peer for the primary .btn. Used as the "Sign in with existing
   account" CTA below the divider on /claim. Token-driven so both dag + nacht
   inherit; same vertical rhythm as .btn (44px+ tap target via the inherited
   padding). */
.btn--secondary {
  background: transparent;
  color: var(--ink);
  border-color: var(--line);
}
.btn--secondary:hover {
  background: var(--surface-muted);
  border-color: var(--ink);
  color: var(--ink);
}

.btn-row { display: flex; gap: var(--space-3); }

.form-error {
  background: transparent;
  border-left: 2px solid var(--danger);
  padding: var(--space-2) var(--space-3);
  margin-bottom: var(--space-4);
  font-size: 13px;
  color: var(--danger);
}

.form-divider {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-5) 0;
  color: var(--muted);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.form-divider::before,
.form-divider::after {
  content: "";
  flex: 1 1 0;
  height: 1px;
  background: var(--line);
}

/* ───────── Generic page ───────── */
.page {
  padding: var(--space-5);
}
.page__title {
  font-size: 28px;
  font-weight: 300;
  margin: 0 0 var(--space-2);
  letter-spacing: -0.01em;
}
.page__lede {
  font-size: 14px;
  color: var(--muted);
  margin: 0 0 var(--space-6);
}

.placeholder {
  border: 1px dashed var(--line);
  padding: var(--space-7) var(--space-5);
  text-align: center;
  color: var(--muted);
}
.placeholder strong {
  display: block;
  color: var(--ink-2);
  margin-bottom: var(--space-2);
  font-weight: 500;
  font-size: 15px;
}

/* ───────── Settings (v0.9.160 UX pass) ───────── */
/* v0.9.347 SETTINGS-IOS-STYLE-RESTYLE — the Settings screen paints --surface-bg
   (slightly tinted neutral in light, near-black in dark) so the cards visually
   pop. Horizontal padding tightens because the per-card inner padding now
   provides the gutter. v0.9.490 SETTINGS-PADDING-BLEED — top reduced from
   --space-5 (24px) to --space-3 (12px) so the Profile header sits close to the
   tab nav; bottom reduced from --space-7 (48px) to --space-4 (16px) because
   .app-main already adds calc(--tabbar-h + --safe-bottom + --space-5) bottom
   clearance, the extra 48px was double-padding that left the danger Restore
   row sitting in dead space. The 16px breathing room before the parent nav-
   clearance kicks in gives the last section a calm trailing margin. */
.set { padding: var(--space-3) var(--space-4) var(--space-4); background: var(--surface-bg); min-height: 100%; }

/* Profile header */
.set-profile {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4);
  margin-bottom: var(--space-5);
  border-bottom: 0;
  background: var(--surface-card);
  border-radius: var(--radius-lg);
}
.set-avatar {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 19px;
  font-weight: 600;
  letter-spacing: 0.02em;
  flex: 0 0 auto;
}
/* v0.9.508 SETTINGS-PADDING-AND-PROFILE-KERNING-V2 — .set-id is a <span>;
   v0.9.490 added margin-top to the email but spans are inline by default,
   so the rule had no effect and the name + email rendered on a SINGLE line.
   Flex-column forces the children onto separate lines so the email reads
   as the secondary label below the display_name. */
.set-id {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.set-id__name {
  font-size: 18px;
  font-weight: 500;
  letter-spacing: -0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.set-id__email {
  font-size: 13px;
  color: var(--muted);
  margin-top: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.set-signout {
  background: none;
  border: 0;
  padding: var(--space-2);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
  flex: 0 0 auto;
}
.set-signout:hover { color: var(--danger); }

/* Groups — v0.9.347 SETTINGS-IOS-STYLE-RESTYLE: the wrapper itself is now
   fill-less; a child .set-card holds the rows in a rounded fill. The
   uppercase section label (.set-group__title) sits ABOVE the card. */
.set-group {
  padding: 0;
  border-bottom: 0;
  margin-bottom: var(--space-5);
}
.set-group:last-child { margin-bottom: 0; }
.set-group__title {
  font-size: 11px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--section-label);
  margin: 0 0 var(--space-2);
  padding: 0 var(--space-4);
  font-weight: 500;
}
/* Sub-label inside a card (e.g. "Accent" / "Theme" inside Appearance).
   v0.9.323 — A11Y-11A picker decouple needed two labelled rows in one group.
   Inside a card: pad to the card inset and use --section-label colour. */
.set-group__hint {
  font-size: 13px;
  color: var(--section-label);
  margin: 0;
  padding: var(--space-3) var(--space-4) var(--space-2);
}
.set-card > .set-group__hint:first-child { padding-top: var(--space-4); }
.set-group__hint--gap { margin-top: 0; }

/* SETTINGS-ACCORDION-AND-SECTION-REORG (v0.9.670) — each .set-group becomes a
   collapsible accordion. The h2.set-group__title stays the heading but wraps a
   full-width <button> (native role=button + Enter/Space); the card body lives
   in a grid-rows 0fr↔1fr panel that animates open in 200ms. Per-visit state
   only — Premium + App default open, everything else closed (set in JS). */
.set-acc__title { margin: 0; padding: 0; }
.set-acc__head {
  -webkit-appearance: none;
  appearance: none;
  background: none;
  border: 0;
  width: 100%;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 44px;
  padding: 9px var(--space-4);
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: var(--ink);
}
.set-acc__label {
  flex: 1 1 auto;
  font-size: 11px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--section-label);
  font-weight: 600;
}
.set-acc__caret {
  flex: 0 0 auto;
  color: var(--section-label);
  transition: transform var(--dur-base) var(--ease-standard);
}
.set-acc.is-open > .set-acc__title > .set-acc__head > .set-acc__caret { transform: rotate(90deg); }
.set-acc__panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-base) var(--ease-standard);
}
.set-acc.is-open > .set-acc__panel { grid-template-rows: 1fr; }
.set-acc__panelin { overflow: hidden; min-height: 0; }
@media (prefers-reduced-motion: reduce) {
  .set-acc__caret,
  .set-acc__panel { transition: none; }
}

/* SETTINGS-COLLAPSE-ALL-DEFAULT-AND-LANG-TOGGLE-PILL — Taal / Language
   promoted out of the accordion into a compact inline pill pair that sits
   between the profile-card and the first accordion section. Mirrors the
   INVITE-LOCALE-FIRST picker: active = navy (--accent) fill, inactive =
   outline. */
.set-lang-toggle {
  display: flex;
  gap: 8px;
  align-items: center;
  margin: var(--space-3) var(--space-4);
}
.set-lang-toggle__btn {
  flex: 0 0 auto;
  padding: 8px 16px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.set-lang-toggle__btn[aria-pressed='true'] {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}

/* v0.9.347 SETTINGS-IOS-STYLE-RESTYLE — rounded card. --radius-lg matches
   iOS 13+ grouped-list rhythm. No shadow — iOS uses flat fills since
   iOS 13. Every direct child of the card is a row position; hairline
   dividers appear BETWEEN siblings via the inset ::before pseudo. */
.set-card {
  background: var(--surface-card);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.set-card > * { position: relative; }
.set-card > * + *::before {
  content: '';
  position: absolute;
  top: 0;
  left: var(--space-4);
  right: 0;
  height: 1px;
  background: var(--divider);
  transform: scaleY(0.5);
  transform-origin: top;
  pointer-events: none;
}
/* Suppress the divider between a sub-label / description hint and whatever
   row follows it — they're a logical pair, NOT two row positions. Applies
   to .set-themes tile-rows AND .set-row pill-toggle rows (e.g. "Turn off
   pre-filled weight suggestions" + Manual-mode toggle). */
.set-card > .set-group__hint + *::before,
.set-card > .set-help + *::before { display: none; }

/* Appearance tiles — v0.9.347 SETTINGS-IOS-STYLE-RESTYLE: multi-choice
   picks (Theme, Accent, Strength view) stay as a tile-row but now sit
   INSIDE a card as one logical row position. Padding is supplied by the
   .set-themes wrapper so the tiles align with the card inset. Binary
   on/off toggles (Pinch Zoom, Sounds chime, Manual mode) are no longer
   tile-pairs — they're .set-toggle pill toggles, see below. */
.set-themes {
  display: flex;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4) var(--space-4);
}
.set-theme {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: var(--space-3) var(--space-3);
  background: transparent;
  border: 1px solid var(--divider);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
  color: var(--ink);
  font-family: inherit;
}
.set-theme:hover { border-color: var(--ink-2); }
/* Generic multi-choice ON state — paints with --accent so the active tile
   matches the user-chosen theme colour (Sassu deviation from iOS green).
   .set-theme-mode / .set-pinch-zoom / .set-rest-chime / .set-strength-view /
   .set-strength-engine inherit this. The Accent picker tiles have their
   own .on override below — they should NOT fill with --accent because the
   tile IS the picker for that very colour. */
.set-theme.on {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--on-accent);
}
.set-theme.on .set-theme__label { color: var(--on-accent); }
.set-theme__dot {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  position: relative;
}
.set-theme__dot::after {
  content: "";
  position: absolute;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 60% 60%;
}
.set-theme.on .set-theme__dot::after { display: flex; }
.set-theme__label { font-size: 13px; font-weight: 500; color: var(--ink-2); }
.set-theme.on .set-theme__label { color: var(--on-accent); }
/* Accent picker — its tiles are coloured DOTS, not filled tiles, so the
   ON state should outline (not fill) so the dot stays the visual focus. */
.set-card .set-themes .set-theme:has(.set-theme__dot).on {
  background: transparent;
  border-color: var(--accent);
  color: var(--ink);
}
.set-card .set-themes .set-theme:has(.set-theme__dot).on .set-theme__label {
  color: var(--ink);
}
/* MOBILE-COMPAT-HAS-SELECTOR-FALLBACK (F2) — plain-selector twins for engines
   without :has(). Keyed on a static .set-theme--dot marker class that mirrors
   the :has(.set-theme__dot) scoping. NOTE: this accent-dot picker is currently
   dormant (retired accent axis, THEME-PRESETS-UNIFY v0.9.362 — no live JS/DOM
   producer found via grep), so these twins are defensive/forward-compatible.
   Identical declarations to the :has() rules above — do NOT diverge. */
.set-card .set-themes .set-theme.set-theme--dot.on {
  background: transparent;
  border-color: var(--accent);
  color: var(--ink);
}
.set-card .set-themes .set-theme.set-theme--dot.on .set-theme__label {
  color: var(--ink);
}

/* SETTINGS-THEME-PRESETS-UNIFY (v0.9.362) — iOS grouped-list row for a
   single named theme preset. One card holds N preset rows separated by
   the standard inset hairline (.set-card__divider). Each row carries a
   24×24 split swatch (background half + accent half), the preset name
   and a one-line description, and a right-side checkmark that only
   renders on the active row. Min-height 44pt per WCAG 2.5.5 + Apple
   HIG. Selecting a row tints its background subtly via accent-mix so
   the checkmark + name read as the active state without flipping the
   whole row to --accent (which would compete with the swatch). */
.set-theme-preset {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 56px;
  padding: var(--space-3) var(--space-4);
  background: transparent;
  border: 0;
  cursor: pointer;
  text-align: left;
  color: var(--ink);
  font-family: inherit;
  transition: background var(--dur-fast) var(--ease-standard);
}
.set-theme-preset:hover { background: var(--surface-muted); }
.set-theme-preset.is-on { background: var(--surface-muted); }
.set-theme-preset__swatch {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-md);
  border: 1px solid var(--divider);
  overflow: hidden;
  position: relative;
  background: var(--swatch-bg, var(--surface));
}
.set-theme-preset__swatch::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 50%;
  background: var(--swatch-accent, var(--accent));
}
.set-theme-preset__main {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.set-theme-preset__name {
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
}
.set-theme-preset__sub {
  font-size: 12px;
  color: var(--section-label);
}
.set-theme-preset__check {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--accent);
  opacity: 0;
  transform: scale(.8);
  transition: opacity var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard);
}
.set-theme-preset__check svg { width: 22px; height: 22px; }
.set-theme-preset.is-on .set-theme-preset__check { opacity: 1; transform: scale(1); }
/* THEME-FOLLOW-THE-SUN — when the flag is on, the two finishes the sun rotates
   between get a hairline marker. Deliberately NOT a disabled/greyed state: the
   rows stay tappable, and such a tap is what switches the automatism off. */
.set-theme-preset--sun { box-shadow: inset 2px 0 0 0 var(--accent); }
/* THEME-1-FINISH-REGISTRY — premium finishes stay visible but lock-tagged
   for non-premium users (existing is_premium gate; tap shows the Premium
   upsell toast instead of switching). */
.set-theme-preset__lock {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--section-label);
}
.set-theme-preset__lock svg { width: 18px; height: 18px; }
.set-theme-preset.is-locked .set-theme-preset__name,
.set-theme-preset.is-locked .set-theme-preset__sub { opacity: .6; }
.set-theme-preset.is-locked .set-theme-preset__swatch { opacity: .75; }
/* Explicit hairline between two preset rows inside the same card. The
   generic .set-card > * + *::before rule already handles divider drawing,
   but the divider element is included as a sibling so JS row ordering
   stays explicit — re-orderable as future presets land without rewriting
   the cascade rule. */
.set-card__divider {
  height: 0.5px;
  background: var(--divider);
  margin-left: var(--space-4);
}

/* v0.9.508 SETTINGS-PADDING-AND-PROFILE-KERNING-V2 — wrapper for an
   inline button placed at the bottom of a Settings card (e.g. App →
   Force refresh). Supplies the same 16/12 gutter as .set-row so the
   button doesn't bleed to the card edges. */
.set-card__action {
  padding: var(--space-3) var(--space-4) var(--space-4);
}

/* v0.9.347 SETTINGS-IOS-STYLE-RESTYLE — pill toggle component. ~51pt ×
   31pt fully rounded with a 27pt circular thumb (2pt inset). OFF: track
   --toggle-track-off, thumb --toggle-thumb-off. ON: track --accent, thumb
   --on-accent (per feedback_accent_buttons_on_accent_token — never
   hardcoded white; respects per-brand swap). Smooth 200ms transition. */
.set-toggle {
  -webkit-appearance: none;
  appearance: none;
  position: relative;
  flex: 0 0 auto;
  width: 51px;
  height: 31px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--toggle-track-off);
  cursor: pointer;
  transition: background var(--dur-base) var(--ease-standard);
  font-family: inherit;
}
.set-toggle__thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 27px;
  height: 27px;
  border-radius: 50%;
  background: var(--toggle-thumb-off);
  box-shadow: var(--elev-1);
  transition: left var(--dur-base) var(--ease-standard), background var(--dur-base) var(--ease-standard);
}
.set-toggle.is-on { background: var(--accent); }
.set-toggle.is-on .set-toggle__thumb { left: 22px; background: var(--on-accent); }
.set-toggle:focus-visible { outline-offset: 4px; }

/* Disclosure rows (Change password, lists) — v0.9.347 SETTINGS-IOS-STYLE-
   RESTYLE: inside a card these become 44pt-min-height drilldown-style
   rows. The summary mimics a .set-row; the body unfolds beneath. */
.set-disclose { border-top: 0; }
.set-disclose:first-of-type { border-top: 0; }
.set-disclose__sum {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 11px var(--space-4);
  font-size: 15px;
  color: var(--ink);
}
.set-disclose__sum::-webkit-details-marker { display: none; }
.set-disclose__title { flex: 0 0 auto; }
.set-disclose__count { color: var(--section-label); flex: 1 1 auto; }
.set-disclose__sum::after {
  content: "›";
  color: var(--section-label);
  font-size: 20px;
  line-height: 1;
  transition: transform var(--dur-fast) var(--ease-standard);
  flex: 0 0 auto;
}
.set-disclose[open] > .set-disclose__sum::after { transform: rotate(90deg); }
.set-disclose__body { padding: 0 var(--space-4) var(--space-4); }

/* Search inside a list */
.set-search {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  padding: 10px 12px;
  margin-bottom: var(--space-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 14px;
  color: var(--ink);
}
.set-search::placeholder { color: var(--muted); }
.set-search:focus { outline: none; border-color: var(--ink-2); }

.set-list { display: block; }

/* Generic rows — v0.9.347 SETTINGS-IOS-STYLE-RESTYLE: 44pt min-height per
   Apple HIG touch target; horizontal padding matches the card inset; the
   line divider is supplied by the .set-card > * + *::before rule (inset
   to the label x-coord), so .set-row itself no longer carries a border. */
.set-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 44px;
  padding: 11px var(--space-4);
  border-top: 0;
  font-size: 15px;
  gap: var(--space-3);
}
.set-row:first-child { border-top: 0; }
.set-row__label { color: var(--ink); flex: 1 1 auto; }
/* SETTINGS-MCP-REMOVE-AND-MARKDOWN-EXPORT (v0.9.667) — two-line tappable row:
   title on top, muted subtitle beneath (data-export tile). */
.set-row__label--stack { display: flex; flex-direction: column; gap: 2px; }
.set-row__title { color: var(--ink); }
.set-row__sub { color: var(--section-label); font-size: 0.82rem; line-height: 1.3; }
.set-row__value { color: var(--section-label); font-variant-numeric: tabular-nums; }
.set-row__value.ok { color: var(--ok); }
/* SETTINGS-ROW-BUTTON-RESET-AND-CHROME-UNIFY (v0.9.1082, S-04) — any <button>
   built with the generic .set-row class inherited raw user-agent chrome
   (grey bevel, shrunk width) because only .set-row--nav carried a reset and
   .set-row--tappable had no rule at all. Reset button chrome here so every
   plain set-row button renders as a full-width flat row identical to the
   nav drill-in rows in both Dag and Nacht. Token-only, no raw hex. */
button.set-row,
.set-row--tappable {
  width: 100%;
  background: transparent;
  border: 0;
  text-align: left;
  font: inherit;
  font-size: 15px;
  color: var(--ink);
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
button.set-row:not(.set-theme-preset):not(.set-row--danger-action):hover,
.set-row--tappable:hover { background: var(--surface-2, var(--surface)); }

/* CHECKIN-METRICS-SETTINGS-SELECTION-AND-ORDER — per-row reorder controls +
   main-metric (hoofdmetric) badge in Settings → Metrics. */
.set-reorder { display: inline-flex; flex-direction: column; gap: 2px; flex: 0 0 auto; }
.set-reorder__btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 19px; padding: 0;
  background: transparent; border: 0; cursor: pointer;
  color: var(--section-label); border-radius: var(--radius-sm);
}
.set-reorder__btn svg { width: 16px; height: 16px; }
.set-reorder__btn:disabled { opacity: 0.3; cursor: default; }
.set-reorder__btn:not(:disabled):hover { color: var(--ink); }
.set-row__badge {
  flex: 0 0 auto; font-size: 0.68rem; font-weight: 600;
  padding: 2px 8px; border-radius: var(--radius-pill);
  background: var(--accent); color: var(--on-accent);
  text-transform: uppercase; letter-spacing: 0.04em;
}

/* COACH-SPRINT-7 — Privacy & Coaching permission matrix */
.set-coach-perm { padding: var(--space-2) 0; }
.set-coach-perm__row { gap: var(--space-3); }
.set-select {
  padding: 6px var(--space-3); font-size: 0.95rem; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm); box-sizing: border-box; min-width: 9.5rem;
}
.set-select:focus { outline: none; border-color: var(--accent); }
/* Sassu AI section (SASSU-AI-HARNESS-MEMORY-AND-MODEL-GATEWAY) */
.set-row--stack-full { align-items: flex-start; flex-wrap: wrap; gap: var(--space-2); }
.set-row--stack-full .set-select { min-width: 100%; }
.set-subhead { margin: var(--space-3) 0 0; font-size: 0.95rem; font-weight: 600; color: var(--ink); }
.set-chatai-memlist { display: flex; flex-direction: column; }
.set-chatai-memrow { align-items: flex-start; gap: var(--space-2); }
.set-chatai-badge {
  align-self: flex-start; margin-top: 3px; padding: 1px 8px;
  font-size: 0.72rem; text-transform: lowercase; color: var(--section-label);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-pill);
}
.set-icon-btn {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; color: var(--ink);
  background: transparent; border: 0; border-radius: var(--radius-sm); cursor: pointer;
}
.set-icon-btn svg { width: 18px; height: 18px; }
.set-icon-btn--danger { color: var(--danger); }
.set-icon-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.set-row__value.bad { color: var(--danger); }
/* v0.9.347 — drilldown chevron, lives on rows that navigate to a sub-screen. */
.set-row--drill::after {
  content: "›";
  color: var(--section-label);
  font-size: 20px;
  line-height: 1;
  margin-left: 2px;
  flex: 0 0 auto;
}

/* Link-style row with chevron (Switch to coach) — v0.9.347: 44pt-height
   row inside a card; the card supplies dividers, so the linkrow's own
   bottom-border is dropped. */
.set-linkrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  min-height: 44px;
  padding: 11px var(--space-4);
  border: 0;
  background: none;
  cursor: pointer;
  font-size: 15px;
  color: var(--ink);
  text-align: left;
  font-family: inherit;
}
.set-linkrow:hover { color: var(--ink); }
.set-linkrow::after { content: "›"; color: var(--section-label); font-size: 20px; line-height: 1; }
/* v0.9.317 — non-interactive variant for reserved-feature affordances (e.g.
   CONNECT API reference for non-trainers). Visible + readable but not a link.
   pointer-events:none is load-bearing — blocks future accidental click wires. */
.set-linkrow.is-disabled { opacity: 0.4; cursor: default; pointer-events: none; }
.set-linkrow.is-disabled:hover { color: var(--ink-2); }
.set-linkrow.is-disabled::after { color: var(--section-label); }

/* v0.9.190 — Switch-to-coach is a prominent themed CTA (was a plain link row).
   Painted in the selected theme accent with --on-accent for legibility on lime.
   v0.9.347 — inside a card it stays accent-painted but loses its own radius
   and shadow so it integrates with the card's flat fill + dividers. */
.set-coach-cta {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; padding: 14px 16px; margin-bottom: var(--space-4);
  border: 0; border-radius: var(--radius-lg);
  background: var(--accent); color: var(--on-accent);
  cursor: pointer; text-decoration: none;
  box-shadow: var(--elev-1);
  transition: background var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard);
}
.set-coach-cta:hover  { background: var(--accent-2); }
.set-coach-cta:active { transform: scale(.99); }
.set-coach-cta__main  { display: flex; flex-direction: column; gap: 2px; text-align: left; }
.set-coach-cta__label { font-size: 15px; font-weight: 700; }
.set-coach-cta__sub   { font-size: 12px; font-weight: 500; opacity: .85; }
.set-coach-cta__arrow { font-size: 22px; line-height: 1; font-weight: 600; }
.set-card > .set-coach-cta { border-radius: 0; margin-bottom: 0; box-shadow: none; }

/* v0.9.490 SETTINGS-PREMIUM-FEATURES — accent-highlighted upsell row used by
   the new Premium Features section (Switch to Coach + future Connect-with-AI).
   Subtle accent-tinted background so the eye scans these as paywall-gated
   features at a glance. Token-only — --accent-light is the soft fill, --accent
   is the icon/label color, both Dag (blauw) + Nacht (lijmgroen) inherit. */
.set-row--premium {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  min-height: 56px;
  padding: 12px var(--space-4);
  background: var(--accent-light);
  border: 0;
  text-align: left;
  font: inherit;
  font-size: 15px;
  color: var(--accent);
  cursor: pointer;
  text-decoration: none;
  gap: var(--space-3);
}
.set-row--premium:hover { background: var(--accent-light); filter: brightness(0.96); }
.set-row--premium .set-row__label { color: var(--accent); font-weight: 600; flex: 1 1 auto; }
.set-row--premium .set-row__chev { color: var(--accent); font-size: 22px; line-height: 1; flex: 0 0 auto; }

.set-help {
  font-size: 13px;
  color: var(--section-label);
  margin: 0 0 var(--space-3);
  line-height: 1.45;
}
.set-card > .set-help { padding: var(--space-3) var(--space-4); margin: 0; }

/* Item rows (archived programs, custom exercises) — v0.9.347: 44pt min
   height matches the card-row rhythm. Dividers inside lists are still
   per-item (full-width inside the disclosure body); divider colour
   token-driven. */
.set-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 44px;
  padding: 10px 0;
  border-top: 1px solid var(--divider);
}
.set-item:first-child { border-top: 0; }
.set-item__name { font-size: 14px; color: var(--ink); }
.set-item__meta { font-size: 11px; color: var(--section-label); margin-top: 2px; }
.set-item__actions { display: flex; gap: 6px; flex: 0 0 auto; }
.set-mini {
  background: none;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 6px 12px;
  font-size: 12px;
  color: var(--ink-2);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.set-mini:hover { border-color: var(--ink-2); }
.set-mini--danger:hover { border-color: var(--danger); color: var(--danger); }

/* PANTRY-ADMIN-MANAGEMENT-UI — shared-pantry membership roster. */
.set-role-chip {
  display: inline-block;
  padding: 1px 8px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line);
  font-size: 11px;
  color: var(--ink-2);
  margin-left: 6px;
  vertical-align: middle;
}
.set-role-chip--admin { border-color: var(--accent); color: var(--accent); }
.set-row--sub { padding-left: 44px; }
.set-row--actions { display: flex; gap: 8px; }
.set-priv-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 14px; }
.set-priv-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line);
  font-size: 12px;
  color: var(--ink-2);
}
.set-priv-chip svg { width: 14px; height: 14px; }
.set-priv-chip.is-on { border-color: var(--accent); color: var(--accent); }
.set-friend-picker { display: flex; flex-direction: column; margin: 4px 0 8px; }

/* MCP Sprint 4 (v0.9.359) — API & Integrations card.
   .set-apikey-reveal: shown once after a mint succeeds; carries the readonly
   key input + Copy button. .set-apikey-buttons: side-by-side Generate +
   Revoke buttons inside a .set-row, using the same gap rhythm as the
   surrounding set-mini affordances. Tokens only — no hardcoded hex. */
.set-apikey-reveal {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--divider);
}
.set-apikey-buttons {
  display: flex;
  gap: var(--space-2);
  flex: 0 0 auto;
}

/* MCP-CONNECTOR-2 — Settings → API & Koppelingen card.
   Three vertical sub-blocks inside one .set-card: Verbind met Claude (top),
   Verbonden apps (middle), Voor ontwikkelaars (bottom). The bottom block is
   intentionally lighter visual weight (smaller heading + muted copy) so the
   OAuth Verbind path reads as the canonical entry point and the bearer mint
   reads as the developer fallback. All tokens; --on-accent on accent fills. */
.mcp-connect { padding: var(--space-4); }
.mcp-connect__title {
  margin: 0 0 var(--space-2);
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}
.mcp-connect__copy {
  margin: 0 0 var(--space-4);
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-2);
}
.mcp-connect__ctas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.mcp-connect__cta {
  width: auto;
  flex: 0 1 auto;
  padding: 10px 16px;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: none;
  border-radius: var(--radius-sm);
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.mcp-connect__icon {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
}

.mcp-apps {
  padding: var(--space-4);
  border-top: 1px solid var(--divider);
}
.mcp-apps__title {
  margin: 0 0 var(--space-3);
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}
.mcp-apps__list { display: flex; flex-direction: column; gap: var(--space-2); }
.mcp-apps__empty {
  margin: 0;
  font-size: 13px;
  color: var(--ink-2);
  line-height: 1.5;
}
.mcp-apps__row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--surface-muted);
  border-radius: var(--radius-sm);
}
/* S-11 — top-align the Ontkoppelen button and let the name+meta column claim
   the remaining width so "gekoppeld … laatst gebruikt …" stacks under the
   name instead of wrapping mid-phrase against the button at 390 px. */
.mcp-apps__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
.mcp-apps__name { font-size: 14px; font-weight: 600; color: var(--ink); }
.mcp-apps__meta { font-size: 12px; color: var(--ink-2); }
.mcp-apps__revoke {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.mcp-apps__revoke-icon { width: 14px; height: 14px; flex: 0 0 auto; }

/* SETTINGS-KOPPELINGEN-SUBSCREEN-AND-CONNECTION-GROUPING — the /settings/
   koppelingen drill-in. Connector paste-URL row + connections grouped by
   client_name (one expandable row per app, count badge, per-connection
   Ontkoppelen inside) + the API-key mint/revoke block. Tokens only; no raw
   hex so css-hex-lint stays clean across all finishes. */
.kopp-linkrow { display: flex; gap: var(--space-2); padding: var(--space-3) var(--space-4); }
.kopp-link {
  flex: 1 1 auto;
  min-width: 0;
  background: var(--surface-muted);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 9px 10px;
  font-size: 12px;
  color: var(--ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.kopp-apps { display: flex; flex-direction: column; }

/* USER-CALENDAR-FEEDS-IMPORT-EXPORT — calendar feed import list + add form. */
.kopp-cal { display: flex; flex-direction: column; }
.kopp-cal-list { display: flex; flex-direction: column; }
.kopp-cal-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
}
.kopp-cal-row + .kopp-cal-row { border-top: 1px solid var(--line); }
.kopp-cal-row__text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.kopp-cal-row__name { font-size: 14px; color: var(--ink-1); }
.kopp-cal-row__meta {
  font-size: 12px; color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kopp-cal-add {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--line);
}
.kopp-cal-input {
  background: var(--surface-muted);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 9px 10px;
  font-size: 13px;
  color: var(--ink-1);
}
.set-mini--on { border-color: var(--accent); color: var(--accent); }

/* Grouped app — a <details>; summary is the browse-level row, body holds the
   per-connection detail rows revealed on tap. */
.kopp-group { border: 0; }
.kopp-group + .kopp-group { border-top: 1px solid var(--line); }
.kopp-group__sum {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  min-height: 44px;
  cursor: pointer;
  list-style: none;
}
.kopp-group__sum::-webkit-details-marker { display: none; }
.kopp-group__icon {
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
  border-radius: var(--radius-md);
  background: var(--accent-light);
  display: flex;
  align-items: center;
  justify-content: center;
}
.kopp-group__icon svg { width: 19px; height: 19px; color: var(--accent); }
.kopp-group__text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.kopp-group__name { font-size: 14.5px; font-weight: 600; color: var(--ink); }
.kopp-group__badge {
  display: inline-block;
  margin-left: 8px;
  font-size: 11px;
  font-weight: 600;
  color: var(--accent);
  background: var(--accent-light);
  border-radius: var(--radius-pill);
  padding: 1px 8px;
  vertical-align: middle;
}
.kopp-group__meta { font-size: 12px; color: var(--ink-2); }
.kopp-group__chev { flex: 0 0 auto; color: var(--ink-2); display: flex; transition: transform var(--dur-fast) var(--ease-standard); }
.kopp-group__chev svg { width: 20px; height: 20px; }
.kopp-group[open] .kopp-group__chev { transform: rotate(90deg); }
.kopp-group__body { background: var(--surface-muted); }

.kopp-conn {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 11px var(--space-4) 11px 62px;
}
.kopp-conn + .kopp-conn { border-top: 1px solid var(--line); }
.kopp-conn__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
.kopp-conn__name { font-size: 13px; font-weight: 600; color: var(--ink); }
.kopp-conn__meta { font-size: 11.5px; color: var(--ink-2); }
.kopp-conn__revoke { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; }
.kopp-conn__revoke-icon { width: 14px; height: 14px; flex: 0 0 auto; }

.kopp-dev { display: flex; flex-direction: column; }
.kopp-apikey-reveal { padding-top: var(--space-2); }

/* NUTRITION-HISTORY-IMPORT-UI-SETTINGS-AND-COACH — import block. Tokens only. */
.kopp-import { display: flex; flex-direction: column; gap: var(--space-2); padding: 0 var(--space-4) var(--space-3); }
.kopp-import__srcs { display: flex; flex-wrap: wrap; gap: var(--space-2); padding-top: var(--space-2); }
.kopp-import__guide { margin: 0; }
.kopp-import__filename { margin: 0; font-style: italic; }
.kopp-import__warn { margin: 0; color: var(--danger); }
.kopp-import__map { display: flex; flex-direction: column; gap: var(--space-2); }
.kopp-import__maprow { display: flex; align-items: center; gap: var(--space-2); }
.kopp-import__maplbl { flex: 0 0 90px; font-size: 13px; color: var(--ink-2); }
.kopp-import__mapsel {
  flex: 1 1 auto; min-width: 0;
  background: var(--surface-muted);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  font-size: 13px;
  color: var(--ink-1);
}
.kopp-import__actions { display: flex; gap: var(--space-2); flex-wrap: wrap; padding-top: var(--space-2); }
.kopp-import__result { display: flex; flex-direction: column; }
.kopp-import__preview {
  display: flex; flex-direction: column; gap: var(--space-2);
  background: var(--surface-muted);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  margin-top: var(--space-2);
}
.kopp-import__sum { margin: 0; font-size: 14px; color: var(--ink-1); font-weight: 600; }
.kopp-import__ok { margin: 0; font-size: 14px; color: var(--accent); font-weight: 600; }
.kopp-import__warns { margin: 0; padding-left: 18px; font-size: 12px; color: var(--ink-2); }
.kopp-import__table { width: 100%; border-collapse: collapse; font-size: 12px; }
.kopp-import__table th {
  text-align: left; color: var(--ink-2); font-weight: 600;
  padding: 4px 6px; border-bottom: 1px solid var(--line);
}
.kopp-import__table td {
  padding: 4px 6px; color: var(--ink-1);
  border-bottom: 1px solid var(--line);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 120px;
}
.kopp-import__batches { display: flex; flex-direction: column; }
.kopp-import__batch {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) 0;
}
.kopp-import__batch + .kopp-import__batch { border-top: 1px solid var(--line); }
.kopp-import__batch--muted { opacity: 0.55; }
.kopp-import__batch-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.kopp-import__batch-name { font-size: 14px; color: var(--ink-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kopp-import__batch-meta { font-size: 12px; color: var(--ink-2); }

/* NUTRITION-HISTORY-BULK-SCREENSHOT-IMPORT — the screenshot route rides the
 * kopp-import wizard shell; only the extra controls it adds live here. The
 * preview date is an EDITABLE input because a screenshot often shows
 * "Gisteren" and only the user knows which year that was. */
.kopp-shot { display: flex; flex-direction: column; gap: var(--space-2); }
.kopp-shot__anchor,
.kopp-shot__date {
  font: inherit;
  color: var(--ink-1);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 6px 8px;
  min-height: 44px;   /* thumb target — the date is meant to be corrected here */
}
.kopp-shot__date { min-height: 36px; font-size: 12px; padding: 2px 6px; }
.kopp-shot__count { margin: 0; }
/* A guessed date is marked with a warning rule, not a tinted fill: the fill
   colour would have to be a new token in all six finishes to stay readable. */
.kopp-shot__row--low td { background: var(--surface-muted); }
.kopp-shot__row--low td:first-child { box-shadow: inset 2px 0 0 var(--warning); }
.kopp-shot__flag {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-2);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* The provenance tag on an imported log row — quiet, never a second banner. */
.nut-entry__estimated {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--surface-muted);
  color: var(--ink-2);
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
}
.nut-empty__import {
  align-self: center;
  background: none;
  border: none;
  padding: var(--space-2);
  min-height: 44px;
  color: var(--ink-2);
  font: inherit;
  font-size: 13px;
  text-decoration: underline;
  cursor: pointer;
}

/* CHAT-GEMINI-1 (v0.9.656) — Settings → Premium → Klantbeheer · Chat-toegang.
 * Per-row layout mirrors .mcp-apps__row's two-column shape: name + state
 * label on the left, control (toggle or locked badge) on the right. Tokens
 * only — no raw hex so css-hex-lint stays clean across all six finishes. */
.chatgrant-list { display: flex; flex-direction: column; gap: var(--space-2); }
/* COACH-CLIENT-ROW-COMPACT-LAYOUT (v0.9.734) — with up to four trailing
   controls (Link to existing account + Send claim link + Premium badge + the
   chat-access toggle) the nowrap row overflowed on a phone. flex-wrap lets the
   secondary actions drop to a second line when they don't fit; the name+label
   block grows to hold the first line and keeps everything single-line when
   there's room. */
.chatgrant-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-1) var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--surface-muted);
  border-radius: var(--radius-sm);
}
.chatgrant-row__text { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.chatgrant-row__name { font-size: 14px; font-weight: 600; color: var(--ink); }
.chatgrant-row__label { font-size: 12px; color: var(--ink-2); }
.chatgrant-row__locked {
  flex: 0 0 auto;
  margin-left: auto;
  white-space: nowrap;
  padding: 2px var(--space-2);
  font-size: 11px;
  font-weight: 600;
  color: var(--on-accent);
  background: var(--accent);
  border-radius: var(--radius-pill);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* COACH-CLIENT-PREMIUM-TOGGLE (2026-06-22) — stacked Premium + Chat-access
   toggle pair on each client tile. Reuses .set-toggle + .chatgrant-row__locked
   styling; this only lays out the two labelled rows. */
.client-tile__toggles {
  flex: 0 0 auto;
  margin-left: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  align-items: flex-end;
}
.client-tile__toggles .toggle-row {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2);
}
.client-tile__toggles .toggle-label {
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-2);
}
.client-tile__toggles .chatgrant-row__locked { margin-left: 0; }

/* COACH-LINK-DUMMY (v0.9.692) — trainer-admin-only merge button. Neutral
   outline (not accent-filled) so it reads as a secondary action and sidesteps
   the accent-contrast lint rule. */
.chatgrant-row__merge {
  flex: 0 0 auto;
  white-space: nowrap;
  padding: var(--space-1) var(--space-2);
  font-size: 11px;
  font-weight: 500;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--divider);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.chatgrant-row__merge:hover { background: var(--surface-muted); }
.chatgrant-row__merge:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.mcp-dev {
  padding: var(--space-4);
  border-top: 1px solid var(--divider);
  opacity: 0.92;
}
.mcp-dev__title {
  margin: 0 0 var(--space-2);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--section-label);
  text-transform: uppercase;
}
.mcp-dev__copy {
  margin: 0 0 var(--space-3);
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-2);
}
.mcp-dev__guide { font-size: 13px; }

/* QR sheet — reuses .ui-dlg primitive; the QR is paint, not chrome, so its
   bg/fg are locked white/black inside the SVG for camera-decodable contrast.
   The mobile branch shows the URL in a copyable input + Kopieer CTA. */
.ui-dlg--qr { max-width: 360px; }
.mcp-qr__svgwrap {
  display: flex; justify-content: center;
  margin: 0 0 var(--space-3);
  padding: var(--space-3);
  background: #ffffff; /* allow-hex: QR quiet-zone must be white in both themes — camera contrast */
  border-radius: var(--radius-sm);
}
.mcp-qr__svgwrap svg { width: 100%; max-width: 240px; height: auto; display: block; }
.mcp-qr__urlnote {
  margin: 0 0 var(--space-4);
  font-size: 12px;
  color: var(--ink-2);
  word-break: break-all;
  text-align: center;
}
.mcp-qr__urlrow { display: flex; gap: var(--space-2); margin: 0 0 var(--space-4); }
.mcp-qr__url {
  flex: 1 1 auto;
  min-width: 0;
  padding: 10px 12px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-size: 13px;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
}
.mcp-qr__url:focus { outline: none; border-color: var(--ink-2); }

/* v0.9.330 — DELETE-ACCOUNT danger zone at the bottom of Settings. Calm
   red-token cues: section title in --danger, "Delete" button uses the
   existing .set-mini--danger pattern (border + text red, NOT fill). The
   wrapper adds a hairline top rule and breathing room so it visually
   separates from the dimmer-toned account-meta sections above. Visibility
   gated in renderSettings (is_trainer hides it). */
.set-group__title--danger { color: var(--danger); }
/* DIARY-METRICS-BODY-MEASUREMENTS (v0.9.495) — small-caps sub-eyebrow that
   sits INSIDE a .set-card to delineate a grouping of related rows (e.g.
   "Body measurements" within Diary metrics). Mirrors the look of
   .set-group__title but slightly smaller and padded to the card inset. */
.set-group__title-sub {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--section-label);
  font-weight: 700;
  margin: var(--space-2) 0 0;
  padding: var(--space-3) var(--space-4) 0;
}
.danger-zone {
  margin-top: var(--space-6);
  padding: 0;
  border-top: 0;
  margin-bottom: var(--space-5);
}
.danger-zone__hint {
  font-size: 13px;
  color: var(--section-label);
  line-height: 1.5;
  margin: 0;
  padding: 0 var(--space-4) var(--space-2);
}
.set-mini--danger { border-color: var(--danger); color: var(--danger); }

/* v0.9.330 — login-cancel banner for users who signed in during the 30-day
   deletion grace. role=status aria-live=polite. The cancel CTA mirrors the
   .set-mini--danger pattern (calm red outline, not red fill). */
.delbanner {
  margin: var(--space-3) var(--space-4);
  padding: 14px 16px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--danger);
  border-radius: var(--radius-sm);
  display: flex; flex-direction: column; gap: var(--space-3);
}
.delbanner__msg { font-size: 13px; color: var(--ink-2); line-height: 1.5; margin: 0; }
.delbanner__msg strong { color: var(--ink); font-weight: 600; }
.delbanner__btn {
  align-self: flex-start;
  padding: 8px 16px;
  background: transparent;
  border: 1px solid var(--danger);
  color: var(--danger);
  border-radius: var(--radius-sm);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  font-family: inherit;
}
.delbanner__btn:disabled { opacity: 0.5; cursor: progress; }

/* Error rows (nested disclosure) */
.set-err {
  margin-bottom: var(--space-2);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.set-err__sum {
  list-style: none;
  cursor: pointer;
  padding: var(--space-3);
  font-size: 13px;
  line-height: 1.35;
}
.set-err__sum::-webkit-details-marker { display: none; }
.set-err__meta { opacity: 0.6; margin-right: 6px; }
.set-err__body {
  margin: 0 var(--space-3) var(--space-3);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  white-space: pre-wrap;
  word-break: break-word;
  opacity: 0.8;
}

/* ───────── Themed dialog (replaces native confirm/prompt/alert) ───────── */
.ui-dlg-overlay { position: fixed; inset: 0; z-index: 1000; background: rgba(0,0,0,0.5);
  display: flex; align-items: center; justify-content: center; padding: var(--space-5); }
.ui-dlg { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm);
  width: 100%; max-width: 360px; padding: var(--space-5); }
.ui-dlg__title { font-size: 17px; font-weight: 600; color: var(--ink);
  margin: 0 0 var(--space-2); letter-spacing: -0.01em; }
.ui-dlg__msg { font-size: 14px; line-height: 1.5; color: var(--ink-2); margin: 0 0 var(--space-5); }
.ui-dlg__input { width: 100%; padding: 10px 12px; margin: 0 0 var(--space-4); background: var(--bg);
  border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: 16px; color: var(--ink); }
.ui-dlg__input:focus { outline: none; border-color: var(--ink-2); }
.ui-dlg__input--area { resize: vertical; min-height: 48px; line-height: 1.4; font-family: inherit; }
/* NUTRITION-SHARED-PANTRY-PARTNER-LINK — "Gedeeld met <partner>" pill on the
   Pantry + Shopping sub-tab headers. Uses --accent-subtle so it never fails the
   accent-on-accent contrast lint (no hardcoded white). */
.fz-share-pill { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px;
  border-radius: var(--radius-pill); background: var(--accent-subtle); color: var(--accent);
  font-size: 12px; font-weight: 600; line-height: 1.4; white-space: nowrap; }
.nut-groc__sharerow { display: flex; justify-content: flex-end; margin: var(--space-2) 0 0; }
.ui-dlg__row { display: flex; gap: var(--space-3); }
.ui-dlg__btn { flex: 1 1 0; padding: 12px; border-radius: var(--radius-sm); cursor: pointer;
  font-size: 13px; font-weight: 600; letter-spacing: 0.04em; border: 1px solid; font-family: inherit; }
.ui-dlg__btn--cancel { background: transparent; border-color: var(--line); color: var(--ink-2); }
.ui-dlg__btn--cancel:hover { color: var(--ink); border-color: var(--ink-2); }
.ui-dlg__btn--go { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.ui-dlg__btn--danger { background: var(--danger); border-color: var(--danger); color: var(--on-danger); }

/* v0.9.321 — A11Y-9 S24: iOS Add-to-Home-Screen instruction sheet. Reuses
   the .ui-dlg primitive (focus trap + Esc); adds an ordered step list with
   the inline Share-icon glyph in step 1. Compact + scannable; no second
   action button — single "Got it" closes. */
.ui-dlg--install { max-width: 380px; }
.install-steps { list-style: none; margin: 0 0 var(--space-4); padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.install-steps__item { display: flex; align-items: center; gap: var(--space-3); padding: 10px 12px;
  background: var(--bg); border: 1px solid var(--line-2); border-radius: var(--radius-sm); }
.install-steps__num { flex: 0 0 22px; height: 22px; width: 22px; display: inline-flex;
  align-items: center; justify-content: center; border-radius: 50%;
  background: var(--accent); color: var(--on-accent); font-size: 12px; font-weight: 700; line-height: 1; }
.install-steps__lbl { font-size: 14px; color: var(--ink); line-height: 1.4; display: inline-flex;
  align-items: center; flex-wrap: wrap; gap: 4px; }
.install-steps__glyph { width: 16px; height: 16px; color: var(--ink-2); display: inline-block;
  vertical-align: middle; flex: 0 0 auto; }

/* v0.9.321 — A11Y-9 S24: install-Sassu row in Settings. Calm secondary
   surface (not the prominent --accent CTA pattern used by switch-to-coach,
   so installs don't visually outweigh sign-out / change-password). */
.set-install {
  display: flex; align-items: center; gap: var(--space-3);
  width: 100%; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--surface); color: var(--ink);
  cursor: pointer; text-align: left; font-family: inherit;
  transition: border-color var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard);
}
.set-install:hover  { border-color: var(--ink-2); }
.set-install:active { transform: scale(.99); }
.set-install__icon { flex: 0 0 28px; height: 28px; width: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-md); background: var(--accent-light); color: var(--accent); }
.set-install__icon svg { width: 18px; height: 18px; }
.set-install__lbl   { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; }
.set-install__main  { font-size: 14px; font-weight: 600; color: var(--ink); }
.set-install__sub   { font-size: 12px; color: var(--muted); }
.set-install__arrow { flex: 0 0 auto; font-size: 22px; line-height: 1; color: var(--muted); }

/* v0.9.321 — A11Y-9 S32: one-shot Nutrition introduction coachmark.
   Non-modal speech-bubble pointing at the Nutrition tab in the tabbar; the
   rest of the screen is dimmed so the user knows where to look. Dismiss
   button + CTA. NEVER an emoji per feedback_no_emojis_in_ux. The bubble's
   bottom + arrow.left are positioned at runtime relative to the Nutrition
   tab's getBoundingClientRect(); the CSS handles look + feel only. */
.nut-intro {
  position: fixed; inset: 0; z-index: 950;
  background: rgba(0, 0, 0, 0.45);
  /* Avoid blocking tap-through on iOS rubber-band scroll. */
  touch-action: none;
}
.nut-intro__bubble {
  position: fixed;
  left: 50%; transform: translateX(-50%);
  max-width: min(360px, calc(100vw - 32px));
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 14px 16px 12px;
  box-shadow: var(--elev-3);
}
.nut-intro__title {
  font-size: 15px; font-weight: 700; color: var(--ink);
  margin-bottom: 4px; letter-spacing: -0.01em;
}
.nut-intro__body {
  font-size: 13px; line-height: 1.45; color: var(--ink-2);
  margin: 0 0 12px;
}
.nut-intro__row {
  display: flex; gap: 8px; justify-content: flex-end;
}
.nut-intro__dismiss,
.nut-intro__cta {
  border: 0; border-radius: var(--radius-md); padding: 9px 14px;
  font-size: 13px; font-weight: 600; cursor: pointer; font-family: inherit;
}
.nut-intro__dismiss {
  background: transparent; color: var(--ink-2);
  border: 1px solid var(--line);
}
.nut-intro__dismiss:hover { border-color: var(--ink-2); color: var(--ink); }
.nut-intro__cta {
  background: var(--accent); color: var(--on-accent);
}
.nut-intro__cta:hover { filter: brightness(0.95); }
/* Tail under the bubble pointing at the Nutrition tab. Arrow.left is set
   inline by JS so the tip aligns with the tab center. */
.nut-intro__arrow {
  position: absolute; bottom: -8px; left: 50%;
  width: 16px; height: 16px;
  background: var(--surface);
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  transform: translateX(-50%) rotate(45deg);
}

/* ──────────────────────────────────────────────────────────
   Strength tab — embedded prototype iframe
   ────────────────────────────────────────────────────────── */
.strength-iframe-wrap {
  /* STRENGTH-SHEET-BOTTOM-PADDING-FOR-WORKOUT-DONE-BUTTON (v0.9.896) — 100dvh
     (dynamic viewport height) i.p.v. 100vh: op mobile is 100vh de LARGE
     viewport (url-bar ingeklapt), waardoor de iframe-bodem (WORKOUT DONE knop)
     achter de bottombar verdween. 100dvh volgt de actueel zichtbare viewport
     zodat de iframe exact boven de tabbar eindigt en de knop bereikbaar blijft. */
  height: calc(100dvh - var(--tabbar-h) - var(--safe-bottom) - 1px);
  margin-top: -1px;
  width: 100%;
  /* Strength tab wrap: flex-column hosts the autoreg bell row above the iframe. */
  display: flex;
  flex-direction: column;
}
.strength-iframe-area {
  position: relative; /* v0.9.204 — anchor the FOUC cover over the iframe only */
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
}
.strength-iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

/* STRENGTH-PERIODIZATION-AS-LINK-IN-THIS-WEEK (v0.9.717) — the stand-alone
   Periodisering paneel that used to sit between the topbar and the iframe was
   removed; its periodisation overview is now reached via an inline link inside
   the iframe's "This week" card header (.this-week__periodization-link, styled
   in strength.html). This collapses the vertical gap above This Week. */

/* v0.9.204 — FOUC cover over the strength iframe until the bridge signals
   hydrate-complete (sassu:ready) or a fallback fires (see mountStrength()).
   Background tracks the parent theme so it matches the dark UI the iframe
   settles to — no light flash, no mock-card flash. */
.strength-iframe-cover {
  position: absolute;
  inset: 0;
  z-index: 5;
  background: var(--bg);
  pointer-events: none;
  opacity: 1;
  transition: opacity var(--dur-base) var(--ease-standard);
}
:root[data-theme-mode="dark"] .strength-iframe-cover { background: var(--bg); }
.strength-iframe-cover.is-hidden { opacity: 0; }
/* v0.9.309 — A11Y-4 S20: surface a calm label inside the cover so the user
   knows the app is alive during the 200-400 ms cold-start blank moment.
   Centered, muted, fades with the cover. */
.strength-iframe-cover__lbl {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 14px;
  letter-spacing: 0.02em;
  color: var(--muted);
  text-align: center;
  padding: 0 var(--space-5);
}
:root[data-theme-mode="dark"] .strength-iframe-cover__lbl { color: var(--ink-2); }
body.tab-strength .topbar { display: none; }
/* CHAT-LAUNCH-VISIBLE-ON-ALL-SURFACES-NO-OVERLAP (v0.9.701) reserveerde
   safe-area-inset-top + 52px om de floating chat-launch pill + autoreg bell
   + periodisering chip vrij van de back-button bar te houden. Jayy 2026-06-19
   (v0.9.810+): die 52px reserve voelde als overbodige witruimte op /strength.
   STRENGTH-IFRAME-TOP-PADDING-REMOVE-CHAT-PILL-RESERVE dropte de +52px;
   pill+bell+chip mogen visueel overlappen met de fz-sp-bar — accepted
   trade-off voor minder top-witruimte. */
body.tab-strength .strength-iframe-wrap {
  box-sizing: border-box;
  padding-top: env(safe-area-inset-top);
}

/* ──────────────────────────────────────────────────────────
   Coach hub — the coach app embedded fullscreen
   (COACH-IN-APP-3-GATED-TAB-MOUNT). Mirrors the strength-iframe geometry:
   the frame fills everything between the safe-area top and the tabbar, and
   the topbar is hidden because the coach SPA carries its own header.
   ────────────────────────────────────────────────────────── */
.coach-hub-wrap {
  height: calc(100dvh - var(--tabbar-h) - var(--safe-bottom) - 1px);
  margin-top: -1px;
  width: 100%;
  display: flex;
  flex-direction: column;
  background: var(--bg);
}
.coach-hub-iframe {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  border: 0;
  display: block;
}
body.tab-coach-hub .topbar { display: none; }
body.tab-coach-hub .coach-hub-wrap {
  box-sizing: border-box;
  padding-top: env(safe-area-inset-top);
  /* COACH-IN-APP-5-CONTINUITEIT-POLISH — the lateral insets are the parent's
     job: inside an iframe env(safe-area-inset-*) resolves to 0, so a landscape
     notch or a home indicator would clip the coach UI if we didn't reserve the
     gutters out here. */
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}

/* ──────────────────────────────────────────────────────────
   GYM-COACH-PERSISTENT-IFRAMES — the pool that keeps the Gym and Coach-Hub
   sub-documents warm across tab switches. The pool itself is an unstyled
   container at the end of <body>: giving it a stacking context (z-index:-1)
   would trap its fixed children behind the app shell, so the SLOTS carry the
   geometry and the pool carries nothing.
   visibility (not display:none) hides an inactive slot — display:none suspends
   rAF/IntersectionObserver/media in several engines, which is exactly the state
   we are trying to avoid.
   ────────────────────────────────────────────────────────── */
.fz-persistent-slot {
  position: fixed;
  top: 0; left: 0; right: 0;
  bottom: calc(var(--tabbar-h) + var(--safe-bottom));
  z-index: 10;
  visibility: hidden;
  pointer-events: none;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  height: auto;      /* overrides the 100dvh the wrap classes carry when in-flow */
  margin-top: 0;
  padding-top: env(safe-area-inset-top);
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}
body.tab-strength .fz-persistent-gym,
body.tab-coach-hub .fz-persistent-coach {
  visibility: visible;
  pointer-events: auto;
}
/* The in-#app stand-ins: the real surface paints from the pool, so these only
   have to occupy nothing and add no scroll. */
.strength-shell-ph,
.coach-shell-ph { height: 0; }

/* ──────────────────────────────────────────────────────────
   Cardio tab — persistent iframe (v0.9.611 CARDIO-GPS-PERSISTENT-IFRAME)
   The wrapper lives in document.body (outside #app) so it survives
   root.innerHTML='' on tab switch. body.tab-cardio shows it; all other
   tabs hide it. This keeps the GPS session alive across navigation.
   ────────────────────────────────────────────────────────── */
.fz-cardio-persistent {
  position: fixed;
  top: 0; left: 0; right: 0;
  bottom: calc(var(--tabbar-h) + var(--safe-bottom));
  z-index: 10;
  display: none;
}
body.tab-cardio .fz-cardio-persistent { display: block; }
.fz-cardio-persistent .cardio-iframe {
  width: 100%; height: 100%;
  border: 0; display: block;
}
/* Legacy .cardio-iframe-wrap kept for any refs that still target it. */
.cardio-iframe-wrap {
  height: calc(100vh - var(--tabbar-h) - var(--safe-bottom) - 1px);
  height: calc(100dvh - var(--tabbar-h) - var(--safe-bottom) - 1px); /* dvh wins on modern engines, 100vh above stays fallback */
  margin-top: -1px; width: 100%; position: relative;
}
.cardio-iframe { width: 100%; height: 100%; border: 0; }
body.tab-cardio .topbar { display: none; }
.cardio-iframe-cover {
  position: absolute; inset: 0; z-index: 5;
  background: var(--bg); pointer-events: none;
  opacity: 1; transition: opacity var(--dur-base) var(--ease-standard);
}
.cardio-iframe-cover.is-hidden { opacity: 0; }

/* ──────────────────────────────────────────────────────────
   CARDIO-TAB-DECISION-GATE (v0.9.663) — instant landing card painted on tab
   tap before any iframe mounts; the CTA initiates the deliberate, status-
   tracked load (spinner → live | error). Tokens only.
   ────────────────────────────────────────────────────────── */
.cardio-gate {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: calc(100vh - var(--tabbar-h) - var(--safe-bottom) - 2 * var(--space-5));
  min-height: calc(100dvh - var(--tabbar-h) - var(--safe-bottom) - 2 * var(--space-5)); /* dvh wins on modern engines, 100vh above stays fallback */
  /* CHAT-LAUNCH-VISIBLE-ON-ALL-SURFACES-NO-OVERLAP (v0.9.701) — the cardio gate
     has no topbar, so reserve a top strip for the floating pill above the
     centered hero. */
  padding: max(env(safe-area-inset-top), 56px) var(--space-5) var(--space-5);
  text-align: center;
}
.cardio-gate__hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  max-width: 360px;
}
.cardio-gate__title {
  margin: 0;
  font-size: 28px;
  font-weight: 700;
  color: var(--ink);
}
.cardio-gate__subline {
  margin: 0 0 var(--space-3);
  font-size: 15px;
  color: var(--ink-2);
}
.cardio-gate__cta {
  appearance: none;
  border: 0;
  border-radius: var(--radius-lg);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 16px;
  font-weight: 600;
  padding: 14px 28px;
  cursor: pointer;
}
.cardio-gate__cta:active { transform: scale(0.98); }

/* Loader — replaces the FOUC cover content while the iframe loads. */
.cardio-loader {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
}
.cardio-loader__spinner {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 3px solid var(--line);
  border-top-color: var(--accent);
  animation: cardioSpin var(--dur-loop) linear infinite;
}
@keyframes cardioSpin { to { transform: rotate(360deg); } }
.cardio-loader__label {
  font-size: 15px;
  font-weight: 600;
  color: var(--muted);
}

/* Error — 8s hard cap with no sassu:ready. Cover is pointer-events:none, so
   re-enable interaction for the retry affordance. */
.cardio-error {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  padding: var(--space-5);
  text-align: center;
  pointer-events: auto;
}
.cardio-error__msg {
  margin: 0;
  max-width: 320px;
  font-size: 15px;
  color: var(--ink-2);
}

@media (prefers-reduced-motion: reduce) {
  .cardio-loader__spinner {
    animation: none;
    border: 3px solid var(--accent);
  }
  .fz-cardio-routes__input-wrap.is-loading::after {
    animation: none;
    border: 2px solid var(--accent);
  }
}

/* GPS mini-player — shown above the tabbar when a GPS session is live
   on any tab other than Cardio. Tap returns to the tracker. */
.fz-gps-mini {
  position: fixed;
  left: 12px; right: 12px;
  bottom: calc(var(--tabbar-h) + var(--safe-bottom) + 8px);
  z-index: 45;
  display: none;
  align-items: center;
  gap: 8px;
  background: var(--accent);
  color: var(--on-accent);
  border-radius: var(--radius-lg);
  padding: 10px 14px;
  cursor: pointer;
  box-shadow: var(--elev-1);
  font-size: 13px;
  font-weight: 500;
  user-select: none;
}
.fz-gps-mini.is-active { display: flex; }
body.tab-cardio .fz-gps-mini { display: none !important; }
/* CARDIO-GPS-LIFECYCLE — split into body (open-cardio) + stop button so
   the trailing Stop is its own hit target. The padding is dropped from
   the wrapper and pushed onto each child so the divider lands cleanly. */
.fz-gps-mini { padding: 0; gap: 0; }
.fz-gps-mini__body {
  appearance: none; -webkit-appearance: none;
  background: transparent; color: inherit;
  border: 0; font: inherit; cursor: pointer;
  display: flex; align-items: center; gap: 8px;
  flex: 1 1 auto; padding: 10px 12px 10px 14px;
  text-align: left;
}
.fz-gps-mini__stop {
  appearance: none; -webkit-appearance: none;
  background: transparent; color: inherit;
  border: 0; border-left: 1px solid currentColor;
  font: inherit; font-weight: 700; cursor: pointer;
  padding: 10px 14px;
  letter-spacing: 0.06em; text-transform: uppercase;
  font-size: 12px;
  opacity: 0.95;
}
.fz-gps-mini__stop:active { opacity: 0.7; }
.fz-gps-mini__dot {
  flex: 0 0 auto;
  animation: fz-gps-pulse var(--dur-pulse) var(--ease-standard) infinite;
}
.fz-gps-mini__label { flex: 0 0 auto; }
.fz-gps-mini__stats { flex: 1 1 auto; text-align: center; opacity: 0.85; }
.fz-gps-mini__open { flex: 0 0 auto; font-size: 12px; opacity: 0.7; }
@keyframes fz-gps-pulse {
  0%, 100% { opacity: 1; }
  50%       { opacity: 0.4; }
}

/* v0.9.30 — Strength player overlays host chrome.
   When the iframe's `.hero-timer.active` fires, the bridge toggles
   `body.feliz-player-on` on this document. Iframe wrap expands to full
   viewport and the bottom tab bar is hidden so the player's top + bottom
   controls are never clipped.
   v0.9.35: 100vh → 100dvh. On iOS Safari `100vh` reports the LARGEST
   possible viewport (URL bar collapsed), so the bottom of the iframe sits
   below the current visible bottom when the URL bar is showing — that's
   why the player kept clipping at the bottom across v0.9.30 → 34. `100dvh`
   tracks the CURRENT visible viewport and resizes when Safari's URL bar
   shows/hides. Same on iPhone home-screen PWAs (no URL bar) where dvh ==
   vh. Also lock body scroll so the host page can't drift under the player. */
body.feliz-player-on .strength-iframe-wrap {
  height: 100dvh;
  height: 100vh; /* fallback for browsers without dvh */
  height: 100dvh;
}
body.feliz-player-on .tabbar {
  display: none;
}
body.feliz-player-on {
  overflow: hidden;
  position: fixed;
  inset: 0;
  width: 100%;
}

/* ──────────────────────────────────────────────────────────
   Strength tab (LEGACY — deprecated, kept for ref but unused)
   ────────────────────────────────────────────────────────── */

.strength {
  padding: var(--space-4) var(--space-4) var(--space-6);
}

/* ───── Session header ───── */
.sess-head {
  padding: var(--space-3) var(--space-1) var(--space-5);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--space-5);
}
.sess-name-input {
  display: block;
  width: 100%;
  background: transparent;
  border: 0;
  border-bottom: 1px dashed transparent;
  font-size: 24px;
  font-weight: 300;
  letter-spacing: -0.005em;
  color: var(--ink);
  margin-bottom: var(--space-3);
  padding: 2px 0;
  outline: none;
}
.sess-name-input:focus,
.sess-name-input:hover {
  border-bottom-color: var(--line);
}
.sess-stats {
  display: flex;
  gap: var(--space-5);
}
.sess-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.sess-stat__lbl {
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}
.sess-stat__val {
  font-size: 14px;
  color: var(--ink);
  font-feature-settings: 'tnum' 1;
}

/* ───── Exercise list ───── */
.exlist {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.exw {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.exw--ss {
  border-left: 3px solid var(--accent);
}

.ex-h {
  display: grid;
  grid-template-columns: 16px 1fr 118px 14px;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--line-2);
  cursor: pointer;
  user-select: none;
}
.ex-drag {
  letter-spacing: -2px;
  color: var(--muted);
  font-size: 13px;
  text-align: center;
  cursor: grab;
}
.ex-nm {
  font-size: 15px;
  color: var(--ink);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ex-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  width: 118px;
}
.ex-tb {
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--line);
  padding: 2px 6px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
}
.ex-sum {
  font-size: 12px;
  color: var(--muted);
}
.ex-chev {
  color: var(--muted);
  font-size: 18px;
  text-align: center;
}

/* ───── Sets ───── */
.ex-sets {
  padding: var(--space-2) 0;
}
.ex-si {
  display: flex;
  flex-direction: column;
}
.ex-empty {
  padding: var(--space-3) var(--space-4);
  font-size: 12px;
  color: var(--muted);
  text-align: center;
}

.sr {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--line-2);
  font-feature-settings: 'tnum' 1;
}
.sr:last-child { border-bottom: 0; }
.sr--done {
  background: rgba(58, 122, 77, 0.04);
}
.sr--done .sr-num { color: var(--ink); }

.sr-num-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  font-size: 11px;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 50%;
  font-weight: 500;
  flex-shrink: 0;
}
.sr-bw {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  font-size: 10px;
  letter-spacing: 0.05em;
  color: var(--muted);
  border: 1px solid var(--line);
  padding: 2px 4px;
  border-radius: var(--radius-sm);
}
.sr-bw.on {
  background: var(--ink);
  color: var(--bg);
  border-color: var(--ink);
}

.sr-w, .sr-r, .sr-d {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  min-width: 0;
}
.sr-w { width: 78px; justify-content: flex-end; }
.sr-r { width: 64px; justify-content: flex-end; }
.sr-d { width: 64px; justify-content: flex-end; }

.sr-num {
  font-size: 15px;
  color: var(--ink);
  font-weight: 500;
}
.sr-num--input {
  background: transparent;
  border: 0;
  border-bottom: 1px dashed transparent;
  outline: none;
  width: 56px;
  text-align: right;
  padding: 1px 2px;
  font-size: 16px;
  color: var(--ink);
  font-weight: 500;
  font-feature-settings: 'tnum' 1;
  border-radius: 0;
}
.sr-num--input:focus,
.sr-num--input:hover {
  border-bottom-color: var(--line);
}
.sr-num--input::placeholder { color: var(--muted); }
.sr-rpe-input { width: 36px; }
.sr-rpe-wrap {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  margin-left: var(--space-2);
}
.sr-rpe-lbl {
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--muted);
  text-transform: uppercase;
}
.sr-w.is-computed .sr-num {
  color: var(--ink-2);
  font-style: italic;
}

.sr-unit {
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--muted);
  text-transform: lowercase;
}

.sr-x {
  font-size: 12px;
  color: var(--muted);
}

.sr-combo {
  display: inline-flex;
  width: 18px;
  height: 18px;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 600;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 50%;
}

.sr-rpe {
  font-size: 11px;
  color: var(--muted);
  letter-spacing: 0.06em;
  margin-left: var(--space-2);
}

.sr-meta {
  font-size: 11px;
  color: var(--muted);
  margin-left: var(--space-2);
}

.sr-ck {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: 1px solid var(--line);
  border-radius: 50%;
  color: var(--muted);
  font-size: 14px;
  flex-shrink: 0;
  background: transparent;
  cursor: pointer;
}
.sr-ck.on {
  background: var(--ok);
  color: var(--bg);
  border-color: var(--ok);
}

/* ───── PAILs / RAILs block ───── */
.pr-block {
  padding: var(--space-3) var(--space-4) var(--space-4);
}
.pr-cfg {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-3);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--line-2);
  margin-bottom: var(--space-3);
}
.pr-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: center;
}
.pr-stat__lbl {
  font-size: 9px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}
.pr-stat__val {
  font-size: 14px;
  color: var(--ink);
  font-feature-settings: 'tnum' 1;
}
.pr-rounds {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.pr-round {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg);
}
.pr-round.done {
  border-color: var(--ok);
  background: rgba(58, 122, 77, 0.05);
}
.pr-round__n {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
}
.pr-round.done .pr-round__n { color: var(--ok); }
.pr-round__lbl {
  font-size: 12px;
  color: var(--muted);
}

/* ───── Add set / Add exercise ───── */
.sr-add {
  display: block;
  width: 100%;
  background: transparent;
  border: 0;
  border-top: 1px solid var(--line-2);
  padding: var(--space-3) var(--space-4);
  text-align: center;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
  font-weight: 500;
}
.sr-add:hover { color: var(--ink); background: var(--line-2); }

.ex-x {
  background: transparent;
  border: 0;
  width: 14px;
  height: 14px;
  font-size: 18px;
  line-height: 1;
  color: var(--muted);
  padding: 0;
  cursor: pointer;
}
.ex-x:hover { color: var(--danger); }

.sess-add-ex {
  margin-top: var(--space-5);
}

/* ───── Session actions ───── */
.sess-actions {
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  align-items: center;
}
.btn-link {
  background: transparent;
  border: 0;
  padding: var(--space-2) var(--space-3);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
}
.btn-link:hover { color: var(--ink); }
.btn-link--danger:hover { color: var(--danger); }

.strength-empty {
  text-align: center;
  padding: var(--space-7) var(--space-5);
}
.strength-empty .btn {
  margin-top: var(--space-5);
  max-width: 240px;
  margin-left: auto;
  margin-right: auto;
}

/* ──────────────────────────────────────────────────────────
   Calendar tab
   ────────────────────────────────────────────────────────── */
.cal-mount {
  padding: var(--space-3) var(--space-3);
}

/* CALENDAR-DAY-NIGHT-BACKGROUND-WITH-GLASS-PANELS — the Diary/Calendar tab is
   a full-bleed photo with the content as liquid-glass panels, same visual
   language as the Nutrition plate. Two variants: the day photo by default, the
   night photo once calIsNight() (calendar/index.js) puts .cal-night on <body>.
   The photo follows the CLOCK; the glass tint follows the THEME (the
   --glass-* tokens flip via html[data-theme-mode], see the Nutrition glass
   block) — all four combinations are readable.

   CALENDAR-BG-ADOPT-NUTRITION-PLATE-IDIOM-NO-FILL — the photo follows the
   `.nut-plate` idiom: a 3:2 box on the ordinary app background, its
   edges feathered into var(--bg) by a radial gradient painted OVER the image.
   No fill colour anywhere — a fill turned the whole page brown.

   CALENDAR-BG-PHOTO-FULL-VIEWPORT-WIDTH — the box is min(100vw, 720px), not the
   inherited 390px mock width: on any phone 100vw wins and the photo reaches both
   screen edges, while the 720px cap keeps it inside the .app-main column on
   desktop instead of spanning the whole window. No border-radius: at full bleed
   rounded corners read as clipped corners; the feather gradient alone carries
   the transition into the background.

   CALENDAR-BG-TRULY-FIXED-FULLBLEED-LAYER — iOS Safari ignores
   `background-attachment: fixed` and scrolls the image with the document, so
   the photo is a real `position: fixed` layer instead. It hangs off <body>,
   never off .app-main: the swipe-tabs controller (NAV-SWIPE-TABS-WITH-PEEK)
   puts an inline transform on .app-main, and a transformed ancestor would make
   `fixed` resolve against that ancestor — the photo would drag along with the
   horizontal swipe. z-index 0 + `.app-shell { position: relative; z-index: 1 }`
   (the .nut-plate-wrap idiom) rather than z-index -1: body is not a stacking
   context, so a negative-z child escapes to the root context and would paint
   UNDER the html background, which is painted (html, body { background }) and
   would hide the photo entirely.

   Every glass rule below is scoped under body.tab-calendar so nothing leaks to
   Nutrition / Gym / Settings. */
body.tab-calendar {
  --cal-bg-image: url('/assets/img/calendar-bg-day.webp');
  /* transparent, not var(--bg): the html background still paints the canvas, so
     the app keeps its base colour, but body's own box no longer covers ::before. */
  background: transparent;
}

/* DIARY-GLASS-LOWER-TINT-LET-THE-PHOTO-THROUGH — the global --glass-tint is
   tuned for the Nutrition plate (declared "done", must not move), where the
   photo is decoration behind dense numeric content. On the Diary tab the photo
   IS the surface, and at 0.80 light / 0.62 dark it was erased. These overrides
   are scoped to this tab only; the `html[data-theme-mode="light"] body...`
   selector (0,2,2) is what beats the global `html[data-theme-mode="light"]`
   (0,1,1), and the dark rule works because it sits after :root. The paired
   blur bumps below (16→22 / 24→30) are what keeps the text readable: less
   opacity has to buy more blur, or the photo's detail comes through as noise
   under the glyphs.

   0.42/0.34 was the design target. tests/visual/diary-glass-plate.spec.js
   measured it at 2.86:1 on the busiest band of the night photo, and the walk up
   in 0.04 steps landed here: 0.62/0.54 light and 0.60/0.52 dark is the LOWEST
   pair where all four secondary runs clear 4.5:1 in both themes over both
   photos — 0.58/0.56 still failed at 4.19:1. Do not lower these by eye; the
   harness is the referee. The light theme is where the win is: 0.80 → 0.62.
   ONE exception exists: DIARY-RING-PLATE-50PCT halves the tint on the ring
   plate alone by explicit Jayy instruction (2026-08-07), and that run carries
   its own lower threshold in diary-glass-plate.spec.js — every other run on
   this tab is still refereed at 4.5:1.

   DIARY-CHECKIN-CTA-FULL-WIDTH-SOLID re-walked the DARK pair 0.60/0.52 →
   0.66/0.58. Nothing about the glass itself got worse: the photo is a FIXED
   layer, so CONTENT HEIGHT decides which pixels sit behind a run. The CTA grew
   ~30px and slid .analytics-ring__sub into a brighter band of BOTH photos
   (3.65:1). The old pair had zero headroom, so any height change on this tab
   re-opens this walk. Light was unaffected and stays 0.62/0.54. */
body.tab-calendar {
  --glass-tint:   rgba(0, 0, 0, 0.66);
  --glass-tint-2: rgba(0, 0, 0, 0.58);
}
html[data-theme-mode="light"] body.tab-calendar {
  --glass-tint:   rgba(255, 255, 255, 0.62);
  --glass-tint-2: rgba(255, 255, 255, 0.54);
}

body.tab-calendar::before {
  content: '';
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  /* both photos are 1536x1024 = 1.5, so a box of the same ratio crops nothing */
  width: min(100vw, 720px);
  height: calc(min(100vw, 720px) / 1.5);
  z-index: 0;
  /* the gradient is listed FIRST so it paints above the photo and feathers its
     edges into the real app background, exactly like .nut-plate::after */
  background-image:
    radial-gradient(ellipse at center, transparent 38%, var(--bg) 78%),
    var(--cal-bg-image);
  background-position: center center;
  background-size: 100% 100%, cover;
  background-repeat: no-repeat;
  pointer-events: none;
}
body.tab-calendar .app-shell {
  position: relative;
  z-index: 1;
}
/* Full bleed to the very TOP: the topbar sits over the photo instead of over a
   flat band. Secondary glass recipe (16px) so the title stays readable, and the
   hard 1px rule goes — the blur edge is the separation. */
body.tab-calendar .topbar {
  background: var(--glass-tint-2);
  -webkit-backdrop-filter: blur(22px) saturate(155%);
  backdrop-filter: blur(22px) saturate(155%);
  border-bottom-color: transparent;
}
body.tab-calendar.cal-night {
  --cal-bg-image: url('/assets/img/calendar-bg-night.webp');
}

/* Primary panels: the week strip and every day-detail / mood / analytics card
   get the full 24px blur. */
body.tab-calendar .cal-grid--week,
body.tab-calendar .cal-card,
body.tab-calendar .cal-wk-total {
  background: var(--glass-tint);
  -webkit-backdrop-filter: blur(30px) saturate(165%);
  backdrop-filter: blur(30px) saturate(165%);
  border: 1px solid var(--glass-edge);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.14) inset;
}

/* DIARY-ANALYTICS-AND-LABEL-CHIPS-ADOPT-GLASS-IDIOM — the inline analytics
   goal cards (rendered by analytics/index.js into the Diary tab, class
   `.analytics-goal-card`) and the horizontal weekly-labels strip
   (`.diary-label-chip`) were both mounted on --surface-card BEFORE the
   Diary-tab glass extension, so the calendar background photo stopped at
   their top edge and they read as opaque slabs on top of the glassed
   .cal-card panels. Bringing them into the same 24px-blur token set as the
   .nut-meal / .cal-card idiom above makes the photo continuous behind the
   entire Diary surface. Scoped under body.tab-calendar so the standalone
   /analytics route (not a tab, doesn't set tab-calendar) stays unchanged. */
/* DIARY-RING-ON-GLASS — the compliance ring was the ONLY element on the tab
   with no panel under it: donut, percentage and caption sat bare on the
   background photo. It joins the same primary 24px-blur tier (it is the tab's
   head stat) at full CONTENT width, so it shares the left/right margin every
   other panel uses and the photo gutters stay intentional. */
body.tab-calendar .analytics-goal-card,
body.tab-calendar .analytics-ring--toggle,
body.tab-calendar .diary-label-chip {
  background: var(--glass-tint);
  -webkit-backdrop-filter: blur(30px) saturate(165%);
  backdrop-filter: blur(30px) saturate(165%);
  border: 1px solid var(--glass-edge);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.14) inset;
}

/* DIARY-RING-PLATE-50PCT — Jayy 2026-08-07 explicit override: exactly half
   the walked tint, ONLY on the ring plate. Owner accepted the readability
   tradeoff; do not 're-walk' this back up without a new Jayy instruction.
   Blur/border/shadow stay as-is — he asked for transparency, not compensation. */
body.tab-calendar .analytics-ring--toggle { background: rgba(0, 0, 0, 0.33); }
html[data-theme-mode="light"] body.tab-calendar .analytics-ring--toggle { background: rgba(255, 255, 255, 0.31); }

/* The ring track defaults to --surface-2 — an opaque hole on glass. It rides
   the existing --glass-hair token, which already flips with Dag/Nacht. */
body.tab-calendar .analytics-ring__track { stroke: var(--glass-hair); }
/* __pct is an SVG <text>, so it takes `fill`, not `color`. */
body.tab-calendar .analytics-ring__pct { fill: var(--glass-ink); }
body.tab-calendar .analytics-ring__sub,
body.tab-calendar .analytics-ring__chevron { color: var(--glass-ink-2); }

/* The direction chip inside a goal card and the "empty" placeholder in the
   labels strip both used --surface-muted for grouping against a flat
   background — over glass that reads as an opaque hole. Swap to a glass
   hairline, transparent fill. */
body.tab-calendar .analytics-goal-card__dir {
  background: transparent;
  border: 1px solid var(--glass-edge);
  color: var(--glass-ink-2);
}
body.tab-calendar .diary-label-chip--empty {
  border-color: var(--glass-edge);
  background: transparent;
  color: var(--glass-ink-2);
}

/* Text-on-glass — same rationale as the .cal-card__meta block below:
   secondary text on --ink-2 cannot reach 4.5:1 over a blurred mid-luminance
   photo, so it lifts onto --glass-ink-2. Primary labels lift to --glass-ink
   for parity with the rest of the glass surfaces. */
body.tab-calendar .analytics-goal-card__label,
body.tab-calendar .diary-label-chip__val { color: var(--glass-ink); }

body.tab-calendar .analytics-goal-card__sub,
body.tab-calendar .diary-label-chip__lbl { color: var(--glass-ink-2); }

@media (prefers-reduced-transparency: reduce) {
  body.tab-calendar .analytics-goal-card,
  body.tab-calendar .analytics-ring--toggle,
  body.tab-calendar .diary-label-chip {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background: var(--surface);
  }
}

/* The CHECK-IN band's surface-muted tint existed to group its cards against a
   flat --bg. Over a photo the cards already separate themselves, and stacking
   glass on glass would blur an already-tinted layer and crush the contrast, so
   the band goes fully transparent and only its children carry glass. */
body.tab-calendar .cal-band--checkin {
  background: transparent;
}

/* Secondary: the layer filter pills are small, so they take the cheaper 16px
   blur. `.on` re-asserts after this rule because body.tab-calendar .cal-layer
   (0,2,1) outranks the base .cal-layer.on (0,2,0). */
body.tab-calendar .cal-layer {
  background: var(--glass-tint-2);
  -webkit-backdrop-filter: blur(22px) saturate(155%);
  backdrop-filter: blur(22px) saturate(155%);
  border: 1px solid var(--glass-edge);
}
body.tab-calendar .cal-layer.on {
  background: var(--ink);
  color: var(--bg);
  border-color: var(--ink);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* Readability: --muted is mid-luminance and cannot reach 4.5:1 against a
   blurred mid-luminance photo at any tint — the same finding as the Nutrition
   glass block. Secondary text inside the glassed panels lifts onto
   --glass-ink-2, which flips with the theme. Never hardcoded. */
body.tab-calendar .cal-card__meta,
body.tab-calendar .cal-ex__sets,
body.tab-calendar .cal-kcal span,
body.tab-calendar .cal-note__k::after,
body.tab-calendar .cal-wk-total__sep {
  color: var(--glass-ink-2);
}

/* DIARY-TAB-CHECKIN-AND-ANALYTICS-GROUPING (v0.9.488) — two-band IA on
   the Diary tab. CHECK-IN (Day Cycle widget + mood entry + sleep/weight
   quick-log) and ANALYTICS (week stats + week goals + selected-day
   insights + mood analytics link) each get an eyebrow + tight in-band
   spacing; bands separate via a felt-distance gap. CHECK-IN carries a
   subtle surface-muted tint behind its cards so the cluster reads as one
   group; ANALYTICS keeps the default surface. Tokens-only — both dag and
   nacht inherit. */
.cal-band {
  margin: 0 calc(-1 * var(--space-3));
  padding: var(--space-3) var(--space-3) var(--space-4);
}
.cal-band + .cal-band {
  margin-top: var(--space-3);
}
.cal-band--checkin {
  background: var(--surface-muted);
  border-radius: var(--radius-lg);
}
.cal-band__body {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.cal-band__body > * {
  margin: 0;
}

/* "How was today?" mood entry card — chrome reuses .cal-card; the
   contained .diary-composer keeps its existing layout but no longer
   carries the chart-icon footer (the analytics path moved to the
   ANALYTICS-band link). */
.cal-mood-card .cal-card__hd {
  margin-bottom: 6px;
}

.cal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-2) var(--space-3) var(--space-3);
}
.cal-title {
  font-size: 18px;
  font-weight: 400;
  letter-spacing: 0.02em;
}
.cal-year {
  color: var(--muted);
  font-weight: 300;
  margin-left: 6px;
}
.cal-nav {
  background: transparent;
  border: 1px solid var(--line);
  width: 32px; height: 32px;
  border-radius: 50%;
  font-size: 16px;
  color: var(--ink-2);
  cursor: pointer;
}
.cal-nav:hover { color: var(--ink); }

/* v0.9.287 — align-items:center so the 32px mode-toggle button vertically
   centers with the ~26px filter pills (Jayy 2026-05-29 on-device: "align the
   calendar view button with that row over there with the options Strength
   /Cardio/Nutrition/Habits... so that everything will look a little bit
   more symmetric"). The mode-toggle's `margin-left: auto` (rule below)
   pushes it to the right end of the row. */
.cal-layers {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 var(--space-3) var(--space-3);
  flex-wrap: wrap;
}
/* v0.9.287 — mode-toggle button moved from its own .cal-modes row into the
   end of .cal-layers. margin-left:auto pushes it to the right end so the
   filter pills stay left-anchored and the toggle stays right-anchored. */
.cal-layers .cal-mode-toggle {
  margin-left: auto;
}
.cal-layer {
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  padding: 4px 12px;
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--muted);
  cursor: pointer;
  font-family: inherit;
}
.cal-layer.on {
  background: var(--ink);
  color: var(--bg);
  border-color: var(--ink);
}
.cal-layer--soon {
  opacity: 0.5;
  cursor: not-allowed;
}
/* DIARY-CALENDAR-LAYER-FILTER-VISIBLE-ADAPTIVE-DEFAULTS — a layer the user's
   own data says they don't use is dimmed, not removed; a tap still turns it on. */
.cal-layer--demoted {
  opacity: 0.55;
}

.cal-grid {
  position: relative;
}
/* v0.9.328 CAL-WEEKSTRIP-UNIFY — Week strip reads as ONE chip (Jayy 2026-05-30:
   "this calendar should look a little bit more unified as one thing"). The
   tinted container wraps the DOW header + dates row so the eye groups them
   as one block; the inner hairline divider and the inner top/bottom padding
   are dropped so the container provides the rhythm. Month grid is untouched
   (it uses .cal-grid--month — different surface, larger area). */
.cal-grid--week {
  background: var(--surface-muted);
  border-radius: var(--radius-lg);
  padding: 8px;
}
.cal-week {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  padding: 4px 0;
  border-bottom: 1px solid var(--line);
  text-align: center;
}
.cal-grid--week .cal-week {
  border-bottom: none;
  padding: 0 0 4px;
}
.cal-grid--week .cal-days {
  padding: 0;
}
.cal-week span {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.cal-days {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
  padding: 6px 0;
}
.cal-cell {
  position: relative;
  aspect-ratio: 1 / 1;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  /* Bottom padding always reserves the day-bar zone, even on empty days, so the
     day number sits at an identical height in every cell — no jumpy grid. */
  padding: 4px 4px 11px;
  font-family: inherit;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  color: var(--ink-2);
}
.cal-cell:hover { border-color: var(--line); }
.cal-cell--out { visibility: hidden; }
.cal-cell--today {
  background: var(--ink);
  color: var(--bg);
}
.cal-cell--today:hover { border-color: var(--ink-2); }
.cal-cell__n {
  font-size: 13px;
  font-weight: 500;
  font-feature-settings: 'tnum' 1;
}
.cal-cell__count {
  position: absolute;
  top: 4px;
  right: 4px;
  font-size: 9px;
  color: var(--muted);
}
.cal-cell--today .cal-cell__count { color: rgba(255,255,255,0.7); }
/* DIARY-DAY-BARS — replaces the loose per-day dots. COLOUR SAYS WHAT, LENGTH
   SAYS HOW FAR: row position + hue encode the domain (top = strength, always
   --training; bottom = nutrition, always --nutrition) and status lives in the
   FILL of the strength bar, never in a third hue — so colour is never the sole
   carrier of meaning and nobody has to memorise a legend. An empty day gets no
   bar and no rail: absence must read as absence. */
.cal-bars {
  position: absolute;
  left: 50%;
  bottom: 5px;
  transform: translateX(-50%);
  width: 26px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  pointer-events: none;
}
.cal-bar { height: 3px; border-radius: 99px; }
.cal-bar--str { background: var(--training); }
.cal-bar--planned { width: 33%; opacity: .45; }
.cal-bar--wip     { width: 66%; }
.cal-bar--done    { width: 100%; }
.cal-bar--nut { width: 100%; background: var(--nutrition); }

/* v0.9.x — Calendar Week View (Month/Week toggle + week-strip + agenda).
   v0.9.284 — segmented 2-up control collapsed to a single calendar-grid
   icon that opens a small dropdown (Jayy 2026-05-29 on-device screenshot).
   The row is `flex-end` so the icon sits slightly right of where the old
   segmented control was (the brief's "slightly to the RIGHT of the current
   spot"). The dropdown mirrors the Strength `.po-edit-menu` visual
   language (per feedback_reuse_sheet_ui_not_minimal_widgets) — same
   rounded white pill, soft shadow, plain-text items. */
/* v0.9.287 — .cal-modes (the standalone row that wrapped the mode-toggle
   button above the filter pills) removed; the button now lives at the end
   of .cal-layers per Jayy 2026-05-29. Rule deleted, not kept-inert, because
   the wrapper is gone entirely and the symbol shouldn't drift back into use. */
.cal-mode-toggle {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  color: var(--muted);
  cursor: pointer;
  font-family: inherit;
  padding: 0;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard);
}
.cal-mode-toggle svg {
  width: 18px;
  height: 18px;
  display: block;
}
.cal-mode-toggle:hover {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--ink-2);
}
.cal-mode-toggle:active { background: var(--line); }
.cal-mode-toggle:focus-visible {
  outline: 2px solid var(--ink-2);
  outline-offset: 2px;
}
.cal-mode-menu {
  position: fixed;
  z-index: 130;
  min-width: 160px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 6px;
  box-shadow: var(--elev-3);
  font-family: inherit;
}
.cal-mode-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 9px 10px;
  background: none;
  border: 0;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  border-radius: var(--radius-md);
  text-align: left;
  letter-spacing: 0;
  -webkit-tap-highlight-color: transparent;
}
.cal-mode-item:hover { background: var(--surface); }
.cal-mode-item:active { background: var(--line); }
.cal-mode-item.is-on { color: var(--ink); }
.cal-mode-item__lbl { flex: 1 1 auto; }
.cal-mode-item__ck {
  width: 14px;
  height: 14px;
  color: var(--ink);
  flex: 0 0 auto;
}
.cal-wk-total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 10px 12px;
  margin: var(--space-2) 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.cal-wk-total__lbl { font-size: 12px; letter-spacing: 0.04em; color: var(--ink-2); }
.cal-wk-total__val { font-size: 13px; font-weight: 600; color: var(--ink); font-feature-settings: 'tnum' 1; }
/* v0.9.255 — colour each metric by its domain (Jayy 2026-05-28). The "·"
   separators stay neutral so only the values carry colour. */
/* COLOR-3 — this row was the worst offender against the one-dominant-hue rule:
   four fully saturated colours inside a single line of 13px text. Two changes,
   both about hue COUNT rather than the domain coding Jayy asked for:
   protein is a sub-metric of nutrition, not a domain of its own, so it now
   reads as a quieter tone of the same amber instead of claiming a fourth hue
   (and a green that would have collided with training's new one); mood stops
   borrowing --accent, which made the signature colour look like just another
   metric. Browsers without color-mix drop that declaration and land on the
   --ink-2 line above it — muted, which is the intent anyway. */
.cal-wk-total__m--vol  { color: var(--training); }
.cal-wk-total__m--kcal { color: var(--nutrition); }
.cal-wk-total__m--pro  { color: var(--ink-2); }
.cal-wk-total__m--pro  { color: color-mix(in oklch, var(--nutrition) 60%, var(--ink-2)); }
.cal-wk-total__m--mood { color: var(--mood); }
.cal-wk-total__sep     { color: var(--muted); font-weight: 400; }

/* Week strip = day selector. Selected day fills; today (when not selected)
   shows an accent ring. Scoped to .cal-grid--week so the month grid's
   filled-today behaviour is untouched. */
.cal-grid--week .cal-cell--sel { background: var(--ink); color: var(--bg); }
.cal-grid--week .cal-cell--sel .cal-cell__n { color: var(--bg); }
.cal-grid--week .cal-cell--today:not(.cal-cell--sel) {
  background: transparent;
  box-shadow: var(--ring-accent-inset);
}
.cal-grid--week .cal-cell--today:not(.cal-cell--sel) .cal-cell__n { color: var(--accent); }

/* v0.9.325 — Month-grid day selector (state A → state B toggle). Same
   inverted-ink chip as Week strip so the visual rhythm reads the same in
   both modes. Today-ring sits ON TOP of the selection chip when the
   selected day IS today, so the user sees both signals. */
.cal-grid--month .cal-cell--sel { background: var(--ink); color: var(--bg); }
.cal-grid--month .cal-cell--sel .cal-cell__n { color: var(--bg); }

/* Selected / today fill the cell with --ink, which swallows both bar hues.
   Lift them to the light-on-dark variants and raise the planned bar's opacity
   so a planned day still reads inside the dark chip. */
.cal-cell--sel .cal-bar--str,
.cal-cell--today .cal-bar--str { background: #86D489; } /* allow-hex: on-dark-chip lift of --training; the chip is always --ink in both presets, so this pair is contrast-locked, not theme-tinted. COLOR-3 re-derived it in the new green family — left as the old blue it would have been the one place on the calendar still showing the retired training hue */
.cal-cell--sel .cal-bar--nut,
.cal-cell--today .cal-bar--nut { background: #F9B96C; } /* allow-hex: on-dark-chip lift of --nutrition — same rationale as the --training pair above */
.cal-cell--sel .cal-bar--planned,
.cal-cell--today .cal-bar--planned { opacity: .6; }

/* Selected-day summary (inline, no pop-up). */
.cal-sel { padding: var(--space-2) 0 var(--space-6); }
.cal-sel__day { font-size: 15px; font-weight: 600; color: var(--ink); padding: 6px 2px var(--space-2); }
.cal-sel__day .tdy { color: var(--accent); font-weight: 700; margin-left: 6px; font-size: 12px; }
.cal-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin-bottom: 10px;
}
.cal-card__hd { display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px; }
.cal-card__icon { color: var(--ink-2); display: inline-flex; flex: 0 0 auto; }
.cal-card__icon svg { width: 16px; height: 16px; }
.cal-card__icon.leaf { color: var(--ok); }
.cal-card__name { font-size: 15px; font-weight: 600; color: var(--ink); }
.cal-card__meta { font-size: 12px; color: var(--muted); margin-left: auto; font-feature-settings: 'tnum' 1; }
.cal-ex { display: flex; align-items: center; gap: 8px; padding: 6px 0; border-top: 1px solid var(--line-2); }
.cal-ex__name { font-size: 13px; color: var(--ink-2); }
.cal-ex__type {
  font-size: 9px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted);
  border: 1px solid var(--line); border-radius: var(--radius-pill); padding: 2px 7px; white-space: nowrap;
}
.cal-ex__sets { font-size: 12px; color: var(--muted); margin-left: auto; font-feature-settings: 'tnum' 1; white-space: nowrap; }
.cal-kcal { font-size: 22px; font-weight: 600; color: var(--ink); font-feature-settings: 'tnum' 1; }
.cal-kcal span { font-size: 12px; font-weight: 400; color: var(--muted); margin-left: 4px; }
.cal-macros { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 16px; margin-top: 8px; border-top: 1px solid var(--line-2); padding-top: 8px; }
.cal-macro { display: flex; justify-content: space-between; font-size: 13px; }
.cal-macro__k { color: var(--ink-2); }
.cal-macro__v { color: var(--ink); font-weight: 500; font-feature-settings: 'tnum' 1; }
.cal-open { background: transparent; border: 0; font-family: inherit; font-size: 12px; color: var(--accent); padding: 8px 2px 0; cursor: pointer; }
.cal-ag-rest { font-size: 12px; color: var(--muted); padding: 8px 2px; }

/* v0.9.300 — Week View per-day Strength card upgrade (Jayy 2026-05-29).
   PR highlights = a single inline row of comma-separated names under a small
   "PR" label pill; notes = key/value lines (Session: …, Exercise name: …)
   stacked under the PR block. Both blocks reuse the existing .cal-ex /
   .cal-macro typography rather than inventing new sizes — per
   feedback_reuse_sheet_ui_not_minimal_widgets. Each gets its own top border
   so the card reads as stacked sections (header → PR → notes → CTA) instead
   of free-floating text. Hidden when empty (rendered conditionally). */
.cal-prs {
  display: flex; align-items: baseline; gap: 8px;
  padding: 8px 0 6px; border-top: 1px solid var(--line-2);
}
.cal-prs__lbl {
  font-size: 9px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--accent); font-weight: 700;
  border: 1px solid var(--accent); border-radius: var(--radius-pill); padding: 2px 7px;
  flex: 0 0 auto;
}
.cal-prs__list { font-size: 13px; color: var(--ink); line-height: 1.35; }
.cal-notes { padding: 8px 0 4px; border-top: 1px solid var(--line-2); }
.cal-note { display: flex; gap: 8px; padding: 3px 0; font-size: 13px; line-height: 1.4; }
.cal-note__k {
  flex: 0 0 auto; max-width: 42%;
  color: var(--ink-2); font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal-note__k::after { content: ':'; margin-left: 1px; color: var(--muted); }
.cal-note__v { color: var(--ink-2); flex: 1 1 auto; }

/* v0.9.325 — Month-mode state A: stacked PR rows ("PRs this month" card).
   Each row = pill + exercise name (grow) + date (right). Pill reuses the
   same accent-ring style as .cal-prs__lbl so the visual language is
   identical between Week-card inline PRs and Month-card stacked PRs. */
.cal-pr-row {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 0; font-size: 13px;
  border-top: 1px solid var(--line-2);
}
.cal-pr-row:first-of-type { border-top: 0; }
.cal-pr-row__lbl {
  font-size: 9px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--accent); font-weight: 700;
  border: 1px solid var(--accent); border-radius: var(--radius-pill); padding: 2px 7px;
  flex: 0 0 auto;
}
.cal-pr-row__name { color: var(--ink); flex: 1 1 auto; }
.cal-pr-row__date { color: var(--muted); font-feature-settings: 'tnum' 1; flex: 0 0 auto; }

/* v0.9.441 DIARY-TODAY-UNIFIED-CARD — single "Today" card holds two stacked
   sections (Strength then Nutrition) separated by a thin token-coloured rule.
   Each section is hidden when its data is empty; the wrapping card is omitted
   entirely when both are empty. Token-only colours per css-hex-lint. */
.cal-card--today { display: flex; flex-direction: column; }
.cal-today-sec { padding-top: 10px; margin-top: 10px; border-top: 1px solid var(--line-2); }
.cal-today-sec:first-of-type { padding-top: 0; margin-top: 0; border-top: 0; }
.cal-today-sec__hd {
  font-size: 9px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--muted); font-weight: 700; margin-bottom: 8px;
}
.cal-today-sec--strength .cal-today-sess { padding-top: 8px; }
.cal-today-sec--strength .cal-today-sess + .cal-today-sess {
  margin-top: 8px; border-top: 1px solid var(--line-2);
}
.cal-today-sess__hd {
  display: flex; align-items: baseline; gap: 8px;
}
.cal-today-sess__name { font-size: 14px; font-weight: 600; color: var(--ink); }
.cal-today-sess__meta { font-size: 12px; color: var(--muted); margin-left: auto; font-feature-settings: 'tnum' 1; }

.cal-loading {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(248, 248, 246, 0.7);
  font-size: 13px;
  color: var(--muted);
}

/* ──────────────────────────────────────────────────────────
   Activity card (v0.9.427 DIARY-4-TRACKER-FRAMEWORK)
   Sleep / Burned / Net rows below a heart-rate icon header. Reuses the
   same .cal-card frame as Nutrition + Strength; the per-row layout
   mirrors .cal-macro (k on the left, v right-aligned, tnum). Token-only
   colours — no hex per the css-hex-lint rule.
   ────────────────────────────────────────────────────────── */
.cal-card--tracker { display: flex; flex-direction: column; }

/* USER-CALENDAR-FEEDS-IMPORT-EXPORT — external calendar events as a calm,
   dimmed, non-interactive background layer. Muted so Sassu's own cards lead. */
.cal-ext { opacity: 0.72; pointer-events: none; }
.cal-ext .cal-card__name { color: var(--ink-2); }
.cal-ext__row {
  display: flex;
  gap: var(--space-2);
  align-items: baseline;
  padding: 4px 0;
  font-size: 13px;
  color: var(--ink-2);
}
.cal-ext__time { flex: 0 0 auto; min-width: 54px; font-variant-numeric: tabular-nums; }
.cal-ext__title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* CALENDAR-CLIENT-SEES-COACH-SESSIONS — the coach's appointments. Full ink,
   NOT the dimmed .cal-ext treatment: this is first-line Sassu content. */
.cal-coach__row {
  display: flex;
  gap: var(--space-2);
  align-items: baseline;
  padding: 4px 0;
  font-size: 13px;
  color: var(--ink);
}
.cal-coach__time { flex: 0 0 auto; min-width: 54px; font-variant-numeric: tabular-nums; }
.cal-coach__title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-coach__chip {
  flex: 0 0 auto;
  font-size: 11px;
  line-height: 1.5;
  padding: 0 6px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--ink);
  background: var(--surface-2);
}
.cal-coach__chip--off { color: var(--ink-2); }
/* CALENDAR-CLIENT-CANCEL-CHAT-MAIL-NOTIFY — only an upcoming appointment is
   pressable; the affordance is the pointer + focus ring, no extra button. */
.cal-coach__row.is-tappable { cursor: pointer; }
.cal-coach__row.is-tappable:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cal-coach__row.is-cancelled .cal-coach__time,
.cal-coach__row.is-cancelled .cal-coach__title {
  color: var(--ink-2);
  text-decoration: line-through;
}
.cal-tracker-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 6px 0;
  border-top: 1px solid var(--line-2);
  font-size: 13px;
}
.cal-tracker-row:first-of-type { border-top: 0; padding-top: 4px; }
.cal-tracker-row__k { color: var(--ink-2); }
.cal-tracker-row__v {
  color: var(--ink);
  font-weight: 500;
  font-feature-settings: 'tnum' 1;
}

/* Tracker Settings tile (v0.9.427) — rows mirror .mcp-apps__row anatomy.
   v0.9.508 SETTINGS-PADDING-AND-PROFILE-KERNING-V2 — list + rows were
   horizontally bleeding to the card's left edge (Oura / Garmin / Apple /
   Google chips touching the edge). Standardised inset to the same 16px
   gutter every other Settings section uses, with min-height 44px per HIG. */
.tracker-list {
  display: flex; flex-direction: column;
  padding-bottom: var(--space-3);
}
.tracker-list__empty {
  color: var(--muted); font-size: 13px;
  padding: 6px var(--space-4);
}
.tracker-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 44px;
  padding: 11px var(--space-4);
  border-top: 1px solid var(--line-2);
}
.tracker-row:first-child { border-top: 0; }
.tracker-row__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tracker-row__name { font-size: 15px; color: var(--ink); }
.tracker-row__soon {
  font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--line); border-radius: var(--radius-pill); padding: 2px 7px;
  flex: 0 0 auto;
}


/* ──────────────────────────────────────────────────────────
   Diary metrics — week-header position (v0.9.455 DIARY-METRICS-ROW-PROMOTE-TO-TOP)
   Per-day metric inputs (weight, sleep, steps, water) toggled per-user in
   Settings → Dagboek metrics. Promoted to live directly under the "This
   week" totals strip — each metric is a tap-target with two states:
   EMPTY (accent CTA pill — "Log today's weight") and FILLED (calm surface
   card — "Weight 78.4 kg ›"). Empty-state colours follow the accent-on-token
   contrast protocol (var(--on-accent), never hardcoded white — lint rule g).
   Solo = single full-width pill; ≥2 = 2-column grid.
   ────────────────────────────────────────────────────────── */
.cal-diary-metrics {
  display: grid;
  gap: var(--space-2);
  margin: var(--space-2) 0;
}
.cal-diary-metrics--solo {
  grid-template-columns: 1fr;
}
.cal-metric {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 12px 14px;
  border-radius: var(--radius-lg);
  border: 1px solid transparent;
  font-size: 14px;
  min-height: 44px;
  box-sizing: border-box;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--dur-base) var(--ease-standard), border-color var(--dur-base) var(--ease-standard), color var(--dur-base) var(--ease-standard);
}
.cal-metric--empty {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.cal-metric--empty .cal-metric__lbl {
  flex: 1 1 auto;
  font-weight: 600;
  font-size: 14px;
  line-height: 1.2;
}
.cal-metric--empty .cal-metric__hint {
  flex: 0 0 auto;
  font-size: 20px;
  font-weight: 600;
  line-height: 1;
  opacity: 0.9;
}
.cal-metric--filled {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--line);
}
.cal-metric--filled .cal-metric__lbl {
  flex: 0 0 auto;
  color: var(--muted);
  font-size: 12px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.cal-metric--filled .cal-metric__val {
  flex: 1 1 auto;
  font-weight: 600;
  font-size: 15px;
  font-feature-settings: 'tnum' 1;
}
.cal-metric--filled .cal-metric__hint {
  flex: 0 0 auto;
  color: var(--muted);
  font-size: 18px;
  line-height: 1;
}
.cal-metric--editing {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--accent);
}
.cal-metric--editing .cal-metric__lbl {
  flex: 0 0 auto;
  color: var(--muted);
  font-size: 12px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.cal-metric__input {
  flex: 1 1 auto;
  min-width: 0;
  background: transparent;
  color: var(--ink);
  border: none;
  padding: 0;
  font-size: 15px;
  font-weight: 600;
  font-feature-settings: 'tnum' 1;
  outline: none;
  -moz-appearance: textfield;
  appearance: textfield;
}
.cal-metric__input::-webkit-outer-spin-button,
.cal-metric__input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.cal-metric__unit {
  flex: 0 0 auto;
  color: var(--muted);
  font-size: 12px;
  font-weight: 500;
}
/* CHECKIN-ADAPTIVE-METRIC-PRIORITY-YESTERDAY-GHOST — yesterday's value shown
   as a suggestion the user must ACCEPT. It is an overlay, never input.value:
   a placeholder disappears on iOS the moment the field takes focus, which
   would kill the one-tap accept before the user ever saw it. */
.cal-metric__field {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
}
.cal-metric__field .cal-metric__input { width: 100%; min-width: 0; }
.cal-metric__ghost {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  pointer-events: none;
  color: var(--muted);
  font-size: 15px;
  font-weight: 600;
  font-feature-settings: 'tnum' 1;
}
.cal-metric__ghosthint {
  flex: 1 0 100%;
  margin-top: 6px;
  background: transparent;
  border: 0;
  padding: 0;
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
}
.cal-metric--ghosted { flex-wrap: wrap; }
.cal-metric--saved {
  border-color: var(--ok);
}
/* NUTRITION-LOG-WATER-BUTTON-BLUE-COLOR (v0.9.483) — semantic water-blue
   override on the water_ml quick-log cell only. Replaces the generic --accent
   fill with a domain-coded water blue declared in :root (strength.html). The
   .cal-metric--water modifier is added by paintMetricCell when metricKey ===
   'water_ml', so the Cardio steps cell (also a quick-log) stays on --accent. */
.cal-metric--water.cal-metric--empty {
  background: var(--water-blue);
  color: var(--on-water-blue);
  border-color: var(--water-blue);
}

/* v0.9.463 WATER-LOG-VESSEL-PRESETS — popover with 4 vessel chips + manual
   '+ ml' fallback. Anchored to the water metric cell from JS (openWaterVesselSheet).
   Tokens only — both Dag + Nacht themes inherit correctly. */
.fz-water-vessels {
  position: fixed;
  z-index: 130;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 220px;
  max-width: 320px;
  padding: 12px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-3);
  font-family: inherit;
}
.fz-water-vessel {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 44px;
  width: 100%;
  padding: 8px 16px;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  font-family: inherit;
  cursor: pointer;
  text-align: center;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard);
}
.fz-water-vessel__lbl {
  font-size: 14px;
  font-weight: 600;
  line-height: 1.2;
}
.fz-water-vessel__sub {
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
  line-height: 1.2;
}
.fz-water-vessel:active {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.fz-water-vessel:active .fz-water-vessel__sub {
  color: var(--on-accent);
  opacity: 0.85;
}
.fz-water-vessels__sep {
  height: 1px;
  width: 100%;
  background: var(--line);
  margin: 2px 0;
}
.fz-water-vessel--manual {
  background: transparent;
  border-style: dashed;
}
/* v0.9.470 WATER-LOG-UNDO-LAST-STEP — Undo chip lives below a second
   separator beneath the manual '+ ml' chip. Same pill chrome as the other
   chips but with a slightly muted surface (it's a destructive-ish action,
   but NOT a destroy/clear button — so no red/destructive token; just a
   softer tone). Tokens only — Dag + Nacht inherit correctly. */
.fz-water-vessels__undo {
  background: var(--bg);
  color: var(--muted);
  border-style: dashed;
}
.fz-water-vessels__undo .fz-water-vessel__lbl {
  font-weight: 500;
}
.fz-water-vessels__undo .fz-water-vessel__sub {
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0.02em;
}
.fz-water-vessels__undo:active:not(.fz-water-vessels__undo--disabled) {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--ink);
}
.fz-water-vessels__undo:active:not(.fz-water-vessels__undo--disabled) .fz-water-vessel__sub {
  color: var(--ink);
  opacity: 0.7;
}
.fz-water-vessels__undo--disabled {
  opacity: 0.5;
  cursor: default;
  pointer-events: none;
}
.fz-water-delta-badge {
  position: fixed;
  z-index: 140;
  padding: 6px 10px;
  background: var(--ink);
  color: var(--bg);
  border-radius: var(--radius-pill);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  pointer-events: none;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity var(--dur-base) var(--ease-standard), transform var(--dur-base) var(--ease-standard);
}
.fz-water-delta-badge--show {
  opacity: 1;
  transform: translateY(0);
}

/* ──────────────────────────────────────────────────────────
   Diary entry rows (v0.9.422 DIARY-2 → DIARY-ENTRIES-CARD-MOVE-INTO-
   MOOD-ANALYTICS-PAGE v0.9.509). Entries no longer live on the Diary
   tab — they're rendered inside the `.diary-entries-section` on the
   Mood analytics page (mood-page.js). The row visual chrome below is
   shared via the renderDiaryEntryRow component. All colours reference
   design tokens — no hardcoded hex per the css-hex-lint rule.
   ────────────────────────────────────────────────────────── */
.diary-hashtag {
  color: var(--accent);
  font-weight: 500;
}
.diary-mood-badge {
  display: inline-flex;
  align-items: center;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 11px;
  font-weight: 600;
  border-radius: var(--radius-pill);
  padding: 2px 8px;
  font-feature-settings: 'tnum' 1;
  flex: 0 0 auto;
}
.diary-mood-bar {
  position: relative;
  height: 4px;
  background: var(--line);
  border-radius: var(--radius-sm);
  margin: 6px 0 6px;
  overflow: hidden;
  --mood-pct: 50%;
}
.diary-mood-bar::after {
  content: '';
  position: absolute;
  inset: 0;
  width: var(--mood-pct);
  background: var(--accent);
  border-radius: var(--radius-sm);
}
.diary-entry {
  padding: 8px 0;
  border-top: 1px solid var(--line-2);
}
.diary-entry:first-of-type { border-top: 0; padding-top: 4px; }
.diary-entry-body {
  font-size: 14px;
  line-height: 1.45;
  color: var(--ink);
  white-space: pre-wrap;
  word-break: break-word;
}
.diary-entry__meta {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--muted);
  margin-top: 4px;
}
.diary-entry__ts {
  font-feature-settings: 'tnum' 1;
  margin-left: 4px;
}
.diary-entry__del {
  margin-left: auto;
  background: transparent;
  border: 0;
  padding: 4px;
  color: var(--ink-2);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
}
.diary-entry__del:hover { color: var(--danger); }
.diary-entry__del svg { width: 16px; height: 16px; }
.diary-toggle {
  background: transparent;
  border: 0;
  color: var(--accent);
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  padding: 4px 0;
  cursor: pointer;
  width: 100%;
}
.diary-composer {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.diary-composer__ta-wrap {
  position: relative;
}
.diary-body-input {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  line-height: 1.45;
  padding: 8px 10px;
  resize: vertical;
  /* CHECKIN-PAGE-REORDER (v0.9.506) — bumped from 64px so the rows='4'
     multi-line placeholder (three reflective prompts) is visible without
     scrolling the textarea. */
  min-height: 96px;
  box-sizing: border-box;
  white-space: pre-wrap;
}
.diary-body-input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}
.diary-body-input.is-err {
  border-color: var(--danger);
  animation: diary-shake var(--dur-base) var(--ease-standard);
}
.diary-ac {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  margin-top: 4px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--elev-2);
  z-index: 5;
  max-height: 220px;
  overflow: auto;
}
.diary-ac[hidden] { display: none; }
.diary-ac-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  background: transparent;
  border: 0;
  padding: 8px 10px;
  font: inherit;
  font-size: 13px;
  color: var(--ink);
  cursor: pointer;
  text-align: left;
}
.diary-ac-item:hover, .diary-ac-item:focus-visible {
  background: var(--surface-muted);
}
.diary-ac-item__tag {
  color: var(--accent);
  font-weight: 600;
}
.diary-ac-item__uses {
  color: var(--muted);
  font-size: 11px;
  font-feature-settings: 'tnum' 1;
}
/* MOOD-HASHTAG-DEFAULT-SUGGESTIONS — horizontal scrollable chip-row that
   sits directly under the diary composer textarea. Sticky-to-input visual:
   no margin-top, padding above the chips. Touch scroll on iOS. */
.fz-mood-chips {
  display: flex;
  flex-wrap: nowrap;
  gap: 8px;
  padding: 8px 0;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.fz-mood-chips::-webkit-scrollbar { display: none; }
.fz-mood-chip {
  flex: 0 0 auto;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  padding: 4px 10px;
  font: inherit;
  font-size: 12px;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
}
.fz-mood-chip--on {
  background: var(--accent);
  color: var(--on-accent);
  border-color: transparent;
}
.diary-composer__slider {
  display: flex;
  align-items: center;
  gap: 10px;
}
.diary-mood-lbl {
  font-size: 12px;
  color: var(--muted);
  flex: 0 0 auto;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.diary-mood-slider {
  flex: 1 1 auto;
  width: 100%;
  accent-color: var(--accent);
}
.diary-mood-val {
  min-width: 2ch;
  text-align: right;
  font-feature-settings: 'tnum' 1;
  color: var(--accent);
  font-weight: 600;
  font-size: 14px;
}
.cal-diary-footer {
  display: flex;
  gap: 12px;
  align-items: stretch;
  margin-top: 12px;
}
/* DIARY-TRIGGER-INTERVENTION-LOOP-P1 — the "+ Trigger" toggle reuses the old
   temptation-toggle sizing/colour tokens so the composer slot is unchanged. */
.diary-composer__trigger {
  display: flex;
  align-items: center;
  justify-content: flex-start;
}
.diary-trigger-open {
  background: none;
  border: 0;
  padding: 4px 0;
  font-size: 12px;
  color: var(--muted);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  cursor: pointer;
  text-decoration: none;
}

/* ── Trigger intervention-loop sheet (full-screen overlay) ── */
html.trigger-sheet-open { overflow: hidden; }
.trigger-sheet {
  position: fixed;
  inset: 0;
  z-index: 100000;          /* above the bottombar (z 99999) */
  background: var(--surface);
  color: var(--ink);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.trigger-sheet__head {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--surface);
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: 8px var(--space-4);
}
.trigger-sheet__back {
  background: none;
  border: 0;
  color: var(--ink);
  cursor: pointer;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.trigger-sheet__back svg { width: 22px; height: 22px; }
.trigger-sheet__back-spacer { width: 32px; height: 32px; }
.trigger-affirmation {
  position: sticky;
  top: 48px;
  z-index: 1;
  background: var(--surface);
  margin: 0 auto;
  max-width: 32rem;
  text-align: center;
  color: var(--text);
  line-height: 1.45;
  padding: var(--space-3) var(--space-4);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
}
.trigger-affirmation__quote {
  font-family: Georgia, 'Times New Roman', 'Times', serif;
  font-style: italic;
  font-size: 1.05em;
  line-height: 1.45;
  color: var(--text);
  text-align: center;
  display: block;
}
.trigger-affirmation__quote::before { content: '\201C'; margin-right: 0.15em; }
.trigger-affirmation__quote::after  { content: '\201D'; margin-left: 0.15em; }
.trigger-affirmation__icon {
  display: block;
  width: 90%;
  max-width: 90%;
  height: auto;
  object-fit: contain;
  margin: 0 auto;
}
@media (max-height: 720px) {
  .trigger-affirmation__icon { width: 60%; max-width: 60%; }
}
.trigger-step {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  max-width: 32rem;
  width: 100%;
  margin: 0 auto;
}
.trigger-step__h {
  font-size: 20px;
  font-weight: 700;
  margin: 0;
  line-height: 1.25;
}
.trigger-step__sub {
  font-size: 13px;
  color: var(--muted);
  margin: 0;
}
.trigger-step__err {
  font-size: 13px;
  color: var(--danger);
  margin: 0;
}
.trigger-slider-row {
  display: flex;
  align-items: center;
  gap: 12px;
}
.trigger-intensity-slider {
  flex: 1 1 auto;
  width: 100%;
  accent-color: var(--accent);
}
.trigger-intensity-val {
  min-width: 2ch;
  text-align: right;
  font-feature-settings: 'tnum' 1;
  color: var(--accent);
  font-weight: 700;
  font-size: 18px;
}
.trigger-ta {
  width: 100%;
  box-sizing: border-box;
  resize: vertical;
  font: inherit;
  padding: 12px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--surface-card);
  color: var(--ink);
}
.trigger-step__foot {
  display: flex;
  margin-top: var(--space-2);
}
.trigger-step__foot--split { gap: 12px; }
.trigger-step__foot .trigger-btn { flex: 1 1 auto; }
.trigger-btn {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  padding: 12px 16px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.trigger-btn:active { background: var(--accent-subtle); }
.trigger-btn:disabled { opacity: 0.5; cursor: default; }
.trigger-btn--primary {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.trigger-picker-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
}
.trigger-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 110px;
  padding: 16px 12px;
  background: var(--surface-card);
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
  text-align: center;
}
.trigger-card:active { transform: scale(0.98); }
.trigger-card:disabled { opacity: 0.5; cursor: default; }
.trigger-card__icon {
  display: inline-flex;
  color: var(--accent);
}
.trigger-card__icon svg { width: 28px; height: 28px; }
.trigger-card__label {
  font-size: 13px;
  font-weight: 600;
  line-height: 1.2;
}

/* ── P2: intervention screens + outcome capture ── */
.trigger-intervention {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-4);
  padding: var(--space-4) 0;
  width: 100%;
}
.trigger-intervention .trigger-step__h { text-align: center; }
.trigger-intervention .trigger-step__sub { text-align: center; }
.trigger-intervention .trigger-btn--primary { width: 100%; }
.trigger-intervention .trigger-ta { text-align: left; }
/* DIARY-TRIGGER-MAKE-IT-PUBLIC-SHARE-SHEET — editable template + share/copy/anchor */
.trigger-go-public {
  gap: var(--space-3);
}
.trigger-share-ta {
  width: 100%;
  min-height: 8rem;
  font: inherit;
  font-size: 1em;
  text-align: left;
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  border: 1px solid var(--line);
  color: inherit;
  resize: vertical;
}
.trigger-share-ta:focus {
  outline: none;
  border-color: var(--accent);
}
.trigger-share-actions {
  display: flex;
  gap: var(--space-2);
  width: 100%;
}
.trigger-share-actions .trigger-btn {
  flex: 1 1 0;
  width: auto;
}
.trigger-share-fallback-link {
  align-self: center;
  font-size: 0.9em;
  color: var(--muted);
  text-decoration: underline;
  cursor: pointer;
}
.trigger-timer {
  font-size: 4rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.trigger-timer-presets {
  display: flex;
  gap: var(--space-2);
  justify-content: center;
}
.trigger-timer-preset {
  min-width: 48px;
  padding: 8px 12px;
}
.trigger-timer-preset.is-on {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.trigger-hold-progress {
  width: 100%;
  height: 4px;
  border-radius: var(--radius-sm);
  background: var(--line);
  overflow: hidden;
}
.trigger-hold-progress span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--accent);
  transition: width var(--dur-base) linear;
}
.trigger-outcome-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: var(--space-2);
}
.trigger-outcome-btn {
  padding: 14px 8px;
  font-size: 13px;
}
.trigger-outcome-btn.is-on {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.trigger-outcome-after {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.trigger-skip-outcome {
  background: none;
  border: 0;
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  text-decoration: underline;
  cursor: pointer;
  align-self: center;
  margin-top: var(--space-2);
}

/* ── P2: Recent triggers section in the Diary composer ── */
.diary-recent-triggers {
  margin-top: var(--space-3);
  border-top: 1px solid var(--line);
  padding-top: var(--space-3);
}
/* DIARY-MOOD-ANALYTICS-MOVE-RECENT-TRIGGERS-FROM-CHECKIN — compact entry-point
   on Check-in (where Recent Triggers used to live) linking to the Mood
   analytics page. Light chrome so it doesn't compete with the Save button. */
.checkin-mood-link {
  margin-top: var(--space-3);
  padding-top: var(--space-2);
}
.checkin-mood-link__btn {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  padding: var(--space-2) 0;
  color: var(--ink);
  font: inherit;
  font-size: 0.95em;
  cursor: pointer;
}
.checkin-mood-link__count { color: var(--muted); }
/* On the Mood analytics page the Recent Triggers list leads the page, so it
   needs no top divider. */
.trigger-patterns-recent {
  margin-top: 0;
  border-top: none;
  padding-top: 0;
  margin-bottom: var(--space-4);
}
.diary-recent-triggers__head {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--muted);
  margin-bottom: var(--space-2);
}
.diary-recent-triggers__count {
  color: var(--accent);
}
.diary-recent-trigger__group { margin-bottom: var(--space-2); }
.diary-recent-trigger__date {
  font-size: 11px;
  color: var(--muted);
  margin-bottom: 4px;
}
.diary-recent-trigger__row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  font-size: 13px;
}
.diary-recent-trigger__time {
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  min-width: 3.5ch;
}
.diary-recent-trigger__int {
  font-weight: 700;
  color: var(--accent);
  min-width: 2ch;
}
.diary-recent-trigger__iv {
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.diary-recent-trigger__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  font-size: 11px;
  font-weight: 600;
  padding: 4px 8px;
  border-radius: var(--radius-pill);
  border: 0;
}
.diary-recent-trigger__badge svg { width: 14px; height: 14px; }
.diary-recent-trigger__badge--succeeded { background: var(--accent-subtle); color: var(--ok); }
.diary-recent-trigger__badge--partial   { background: var(--accent-subtle); color: var(--warning, var(--nutrition)); }
.diary-recent-trigger__badge--failed    { background: var(--accent-subtle); color: var(--danger); }
.diary-recent-trigger__badge--untracked {
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  text-decoration: underline;
}
.recent-trigger-hashtag {
  flex: 0 0 auto;
  font-size: 10px;
  font-weight: 600;
  padding: 2px 6px;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--muted);
}

/* ── Hashtag pre-step (DIARY-TRIGGER-HASHTAG-PRE-STEP) ── */
.trigger-hashtag-input {
  width: 100%;
  box-sizing: border-box;
  background: var(--surface-2);
  color: inherit;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  font: inherit;
  margin-top: 8px;
}
.trigger-hashtag-input:focus {
  outline: none;
  border-color: var(--accent);
}
.trigger-hashtag-suggestions {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  padding: 4px 2px;
  margin-top: 8px;
}
.trigger-hashtag-chip {
  flex: 0 0 auto;
  background: var(--surface-2);
  color: inherit;
  border: 0;
  border-radius: var(--radius-pill);
  padding: 4px 12px;
  font: inherit;
  font-size: 0.9em;
  cursor: pointer;
  white-space: nowrap;
}
.trigger-hashtag-chip.is-on {
  background: var(--accent);
  color: var(--on-accent);
}
.trigger-hashtag-preview {
  text-align: center;
  color: var(--muted);
  font-size: 13px;
  min-height: 1.2em;
  margin-top: 6px;
}
.trigger-hashtag-skiplink {
  display: block;
  margin: 6px auto 0;
  background: none;
  border: 0;
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  text-decoration: underline;
  cursor: pointer;
}

/* ── P3: "See patterns" link in the Recent triggers header ── */
.diary-recent-triggers__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.diary-recent-triggers__patterns {
  background: none;
  border: 0;
  color: var(--accent);
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  cursor: pointer;
  padding: 0;
}

/* ── P3: recommended intervention card (Step 4 effectiveness ordering) ── */
.trigger-card--recommended {
  border: 2px solid var(--accent);
}
.trigger-card__hint {
  font-size: 11px;
  font-weight: 600;
  color: var(--accent);
  line-height: 1.1;
}
.trigger-show-all {
  display: block;
  width: 100%;
  margin-top: var(--space-3);
  background: none;
  border: 0;
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  text-decoration: underline;
  cursor: pointer;
}

/* ── P3: Trigger patterns page (/diary/triggers/patterns) ── */
.trigger-patterns-page {
  padding: var(--space-4);
}

/* DIARY-MOOD-ANALYTICS-VAULT-PASSWORD-PROTECTION */
.mood-vault-settings__head {
  display: flex;
  align-items: center;
  margin-bottom: var(--space-2);
}
.mood-vault-settings__title {
  font-size: 1rem;
  font-weight: 600;
  margin: 0;
}
.mood-vault-settings__toggle {
  cursor: pointer;
}
.mood-vault-settings__checkbox {
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
}
.mood-vault-settings__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-3);
}
.mood-vault-settings__body form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.mood-vault-gate {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-3);
  padding: var(--space-6) var(--space-4);
  max-width: 360px;
  margin: 0 auto;
}
.mood-vault-gate__icon {
  color: var(--muted);
}
.mood-vault-gate__icon svg {
  width: 36px;
  height: 36px;
}
.mood-vault-gate__title {
  font-size: 1.125rem;
  font-weight: 600;
  margin: 0;
}
.mood-vault-gate__sub {
  color: var(--muted);
  margin: 0;
}
.mood-vault-gate__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  width: 100%;
  margin-top: var(--space-2);
}
.mood-vault-gate__input {
  width: 100%;
}
.mood-vault-gate__err {
  color: var(--danger, var(--muted));
  font-size: 0.875rem;
}
.trigger-patterns-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  margin-top: var(--space-4);
}
.trigger-patterns-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.trigger-patterns-section__h {
  font-size: 14px;
  font-weight: 700;
  margin: 0 0 var(--space-1);
}
.trigger-pattern-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 10px 12px;
  background: var(--surface-card);
  border-radius: var(--radius-sm);
  font-size: 13px;
}
.trigger-pattern-row--high-risk {
  border-left: 4px solid var(--warning, var(--nutrition));
}
.trigger-pattern-row__text {
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
}
.trigger-pattern-row__chip {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 600;
  color: var(--warning, var(--nutrition));
  background: var(--accent-subtle);
  padding: 2px 8px;
  border-radius: var(--radius-pill);
}
.trigger-pattern-row__count {
  flex: 0 0 auto;
  font-weight: 700;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.trigger-patterns-chart {
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  padding: var(--space-2) 0;
}
.trigger-patterns-empty {
  color: var(--muted);
  font-size: 14px;
  line-height: 1.5;
  margin-top: var(--space-4);
}
/* CHECKIN-BUTTON-MINIMALIST-DEMOTION (v0.9.553) — demoted from accent-fill
   to outlined so /check-in stops fighting itself for attention. Class only
   renders inside mountMoodEntryCard, which is now mounted exclusively by
   /check-in (mood card moved out of /diary in v0.9.494), so a global
   demotion is scope-safe — no Diary surface still wears this class. */
.cal-diary-foot__save {
  flex: 1 1 auto;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--accent);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.cal-diary-foot__save:active { background: var(--accent-subtle); }
.cal-diary-foot__save:disabled { opacity: 0.6; cursor: default; }

/* CHECKIN-PAGE-REORDER-PLUS-MOOD-ANALYTICS-ICON-BUTTON (v0.9.506) — square
   analytics icon-button sitting LEFT of Save entry on the mood card footer.
   Border-only chrome so it reads as a secondary affordance against the
   accent-filled Save button. 44×44 minimum tap target both axes. */
.cal-diary-foot__analytics {
  flex: 0 0 44px;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 0;
  color: var(--muted);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.cal-diary-foot__analytics:hover,
.cal-diary-foot__analytics:active {
  background: var(--surface-muted);
  color: var(--ink);
}
.cal-diary-foot__analytics svg {
  width: 20px;
  height: 20px;
}

@keyframes diary-shake {
  0%   { transform: translateX(0); }
  20%  { transform: translateX(-6px); }
  40%  { transform: translateX(6px); }
  60%  { transform: translateX(-4px); }
  80%  { transform: translateX(4px); }
  100% { transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) {
  .diary-body-input.is-err { animation: none; }
}


/* ──────────────────────────────────────────────────────────
   Mood analytics (v0.9.426 DIARY-3)
   Mood line chart + hashtag bubble scatter, always visible
   below the week strip in Week mode. Inline SVG — no canvas.
   Bubble fill colour is set inline via fill="rgb(r,g,b)" on
   the SVG <circle> for semantic mood encoding; that's the
   ONLY hex-equivalent that lives outside this file and is
   exempt from css-hex-lint by virtue of not being in CSS.
   ────────────────────────────────────────────────────────── */
.diary-analytics {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin: 16px 0 8px;
  padding: 12px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
}
.diary-mood-chart,
.diary-bubble-section,
.mood-history {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* MOOD-ANALYTICS-HISTORY — the conversations under the charts. The turn idiom
   is lifted from the check-in itself: question dimmed to 0.45, answer at full
   weight. A day header is a button because a wall of transcript does not get
   read; only today opens itself. */
.mood-history__day {
  border-top: 1px solid var(--line);
  padding-top: 8px;
}
.mood-history__date {
  background: transparent;
  border: 0;
  padding: 6px 0;
  width: 100%;
  min-height: 44px;
  text-align: left;
  cursor: pointer;
  color: var(--ink);
  font-size: 13px;
  font-weight: 700;
}
.mood-history__body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 2px 0 10px;
}
.mood-history__tod {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  font-weight: 700;
}
.mood-history__q {
  margin: 8px 0 2px;
  font-size: 13px;
  line-height: 1.4;
  color: var(--ink);
  opacity: 0.45;
}
.mood-history__a {
  margin: 0;
  font-size: 14px;
  line-height: 1.45;
  color: var(--ink);
  white-space: pre-wrap;
}
.mood-history__scores {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.mood-history__score {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--mh-score-color, var(--line));
  border-radius: var(--radius-md);
  padding: 4px 8px;
}
.diary-chart-head {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  font-weight: 700;
  margin: 0;
}
.diary-chart-sub {
  font-size: 12px;
  color: var(--muted);
}
.diary-chart-loading,
.diary-chart-empty {
  font-size: 13px;
  color: var(--muted);
  padding: 12px 0;
  text-align: center;
  line-height: 1.4;
}
.diary-mood-svg,
.diary-bubble-svg {
  width: 100%;
  height: auto;
  display: block;
}
.diary-chart-label {
  font-size: 10px;
  fill: var(--muted);
}
.diary-bubble-label {
  font-size: 10px;
  fill: var(--ink);
  font-weight: 600;
  pointer-events: none;
  paint-order: stroke;
  stroke: var(--surface);
  stroke-width: 2px;
  stroke-linejoin: round;
}
.diary-bubble-leader {
  stroke: var(--muted);
  stroke-width: 0.6px;
  opacity: 0.6;
  pointer-events: none;
}
.diary-bubble { cursor: pointer; }
.diary-bubble:active circle { fill-opacity: 1; }

/* ── MOODCHART-DEPTH-IS-RECENCY-AND-BUBBLES-BREATHE ────────────────────────
   Depth means recency: close = used today, far = a year ago.

   PERSPECTIVE: 900px IS A CORRECTNESS GUARANTEE, NOT A LOOK. Against the z
   range of ±140px capped in mood-page.js it limits the apparent-size
   difference between the front and back plane to
       900 / (900 + 140) = 0.865  →  13.5 %
   which is less than one use_count step, so the depth axis can NEVER invert
   the size ordering (= frequency). At perspective 400px the same z range gives
   26 % and the chart starts lying about how often a tag was used. Redo that sum
   before changing this number, or the graph silently misreports its own data.

   Why divs over the SVG instead of preserve-3d inside it: measured. Inside
   <svg> the z translation is flattened outright (ratio 1.000 — no depth at
   all); the div layer measured exactly the 0.865 the geometry predicts. The SVG
   still owns background, axes, leader lines and beside-labels. */
.diary-bubble-space {
  position: relative;
  display: block;
  perspective: 900px;
  /* Lets the bubble text size itself in the SVG's own viewBox units (1 unit =
     0.3333cqw at viewBox width 300), so a label is the same size whether it is
     drawn by the SVG or by a div. */
  container-type: inline-size;
}
.diary-bubble-scene {
  position: relative;
  transform-style: preserve-3d;
  transform: rotateY(var(--fz-mc-ry, 0deg));
  /* Springs back to square on release. A chart left at an angle is unreadable,
     so the resting state is always 0. */
  transition: transform 400ms cubic-bezier(0.22, 1, 0.36, 1);
}
/* During the gesture the tilt must track the finger, not chase it. */
.diary-bubble-stage.is-gesturing .diary-bubble-scene,
.mood-explore__stage.is-gesturing .diary-bubble-scene { transition: none; }
.diary-bubble-layer {
  position: absolute;
  inset: 0;
  transform-origin: 0 0;
  transform-style: preserve-3d;
  /* --fz-mc-bake is 1 during the gesture (cheap GPU stretch of one texture) and
     1/scale at rest, when mood-page.js has grown the LAYOUT by that same factor
     so the compositor re-rasterises at native DPR instead of upscaling the 1x
     texture. --fz-mc-s never changes meaning, so every counter-scale divisor
     that reads it keeps working untouched. */
  transform: translate3d(var(--fz-mc-tx, 0px), var(--fz-mc-ty, 0px), 0) scale(calc(var(--fz-mc-s, 1) * var(--fz-mc-bake, 1)));
  pointer-events: none;
}
.diary-bubble-layer .diary-bubble {
  position: absolute;
  box-sizing: border-box;
  aspect-ratio: 1;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  pointer-events: auto;
  /* z is a 3D translation and scale() is 2D, so the composed depth is constant
     at every zoom level — the 13.5 % ceiling above holds at 1× and at 6×. */
  transform: translate3d(0, 0, var(--fz-z, 0px));
  animation-name: fz-mood-drift;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-play-state: paused;   /* only .diary-bubble--drift starts it */
}
.diary-bubble-layer .diary-bubble--drift { animation-play-state: running; }
.diary-bubble-layer .diary-bubble-inner {
  color: var(--fz-bub-ink, #FFFFFF);
  font-weight: 600;
  pointer-events: none;
  white-space: nowrap;
  /* Same on-screen size at every zoom, same rule as the SVG labels — but off
     --fz-mc-ls, which is only rewritten once the gesture ENDS. These two
     properties are the only per-bubble style that depends on the zoom, and with
     ~400 bubbles recomputing them per frame they cost the entire frame budget
     (measured: 24 fps dragging, 65 with the layer hidden). See
     applyLabelScale() in mood-page.js. */
  font-size: calc(var(--fz-fs, 9) * 0.33333cqw / var(--fz-mc-ls, 1));
}
/* The border is the ONE absolute-px thing inside the layer, so it is also the
   one divisor the bake has to override: baked, the layer is no longer scaled up
   and the border must be its native 1.5px. --fz-mc-bw is only set while baked. */
.diary-bubble-layer .diary-bubble { border-width: calc(1.5px / var(--fz-mc-bw, var(--fz-mc-ls, 1))); }
/* Both halts are deliberate: during a gesture two motion sources at once make
   the gesture imprecise, and a bubble you have tapped must hold still while you
   read its tooltip. */
.is-gesturing .diary-bubble-layer .diary-bubble,
.diary-bubble-layer .diary-bubble.is-frozen,
[data-motion="off"] .diary-bubble-layer .diary-bubble { animation-play-state: paused; }

/* will-change ONLY while a gesture runs, and only on the wrapper. A permanent
   per-bubble will-change costs texture memory around the clock for no gain. */
.is-gesturing .diary-bubble-layer { will-change: transform; }

/* One shared keyframe track; the character of each bubble comes entirely from
   four per-element variables. Primes of 41–67s (doubled by `alternate`) keep the
   cloud perceptually non-repeating for 80–130s — at 14s a human sees the loop
   within half a minute. NOTE: no requestAnimationFrame anywhere. A rAF loop
   writing 400 transforms per frame runs on the main thread, stutters on every
   garbage collect and dies the moment anything else happens; this runs on the
   compositor and costs zero JS per frame. */
@keyframes fz-mood-drift {
  0%   { transform: translate3d(0, 0, var(--fz-z, 0px)); }
  23%  { transform: translate3d(calc(var(--fz-ax, 0px) *  0.9), calc(var(--fz-ay, 0px) * -0.6), var(--fz-z, 0px)); }
  47%  { transform: translate3d(calc(var(--fz-ax, 0px) * -0.4), calc(var(--fz-ay, 0px) *  0.8), var(--fz-z, 0px)); }
  71%  { transform: translate3d(calc(var(--fz-ax, 0px) *  0.6), calc(var(--fz-ay, 0px) *  0.3), var(--fz-z, 0px)); }
  100% { transform: translate3d(0, 0, var(--fz-z, 0px)); }
}

/* WCAG 2.2 SC 2.2.2. All three conditions are met — starts automatically, never
   ends, sits beside other content — so this control is mandatory. */
.diary-bubble-motion-toggle {
  position: absolute;
  bottom: 6px;
  left: 6px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink-2);
  cursor: pointer;
}
.diary-bubble-motion-toggle:hover { color: var(--ink); border-color: var(--ink-2); }

/* prefers-reduced-motion is a MEDICAL boundary, not a taste setting: floating
   at different depths with parallax is the exact pattern that triggers nausea
   and disorientation in vestibular disorders, and half a dose is still a dose.
   So: full stop, never a reduced amplitude. Depth, opacity and colour all
   REMAIN, so the third axis stays fully readable and the picture does not feel
   dead — it only holds still. The parallax stops too: the planes no longer move
   relative to each other while dragging. */
@media (prefers-reduced-motion: reduce) {
  .diary-bubble-layer .diary-bubble { animation: none !important; }
  .diary-bubble-scene { transition: none !important; transform: none !important; }
}

/* ── MOODCHART-PINCH-ZOOM-PAN-AND-EXPLORE-OVERLAY ──────────────────────────
   The stage owns the gesture; the SVG only carries a transform.

   touch-action: pan-y is the hostage guarantee and is NOT a style choice. It
   hands a vertical swipe that starts on the chart back to the page, so the
   chart can never trap the scroll — the failure mode there has no error and no
   way out but leaving the tab. It does NOT include pinch-zoom, so two fingers
   still reach our handler. Same reasoning as .mdx__svg further down. Never
   `none` here; `none` is only correct in .mood-explore__stage, which is
   full-screen and has nothing behind it. */
.diary-bubble-stage {
  position: relative;
  overflow: hidden;
  touch-action: pan-y;
  border-radius: var(--radius-md);
}
.diary-bubble-stage .diary-bubble-svg,
.mood-explore__stage .diary-bubble-svg {
  transform-origin: 0 0;
  /* Same bake factor as .diary-bubble-layer — the two must stay locked. */
  transform: translate3d(var(--fz-mc-tx, 0px), var(--fz-mc-ty, 0px), 0) scale(calc(var(--fz-mc-s, 1) * var(--fz-mc-bake, 1)));
}
/* Escape hatch. Not decoration: without it someone who pinches to 6× by
   accident has no obvious way back to the whole picture. */
.diary-bubble-zoomreset {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 2;
  min-width: 34px;
  height: 28px;
  padding: 0 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink-2);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.diary-bubble-zoomreset[hidden] { display: none; }
/* The Verken affordance sits ON the preview, bottom-right, where the thumb
   already is — not in the section header a screen away from the chart. One
   line, icon + word (fz-lc-strip idiom). */
.diary-bubble-explore-chip {
  position: absolute;
  bottom: 6px;
  right: 6px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink-2);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}
.diary-bubble-explore-chip svg { width: 16px; height: 16px; }
.diary-bubble-explore-chip:hover { color: var(--ink); border-color: var(--ink-2); }
.diary-bubble-headrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
/* Progressive reveal. One attribute write on the stage swaps the visible set —
   the alternative is a JS loop over 400 text nodes per zoom step. */
.diary-bubble-label--tier1,
.diary-bubble-label--tier2,
.diary-bubble-label--tier3,
.diary-bubble-label--tier4 { transition: opacity 180ms ease; }
[data-zoom-tier="1"] .diary-bubble-label--tier2,
[data-zoom-tier="1"] .diary-bubble-label--tier3,
[data-zoom-tier="1"] .diary-bubble-label--tier4,
[data-zoom-tier="2"] .diary-bubble-label--tier3,
[data-zoom-tier="2"] .diary-bubble-label--tier4,
[data-zoom-tier="3"] .diary-bubble-label--tier4 { opacity: 0; }
/* Strokes and text must keep their ON-SCREEN size while the geometry grows.
   font-size is compensated inline per element (it varies per bubble); the
   stroke widths are compensated here. At 4× an uncompensated 2px halo reads
   8px and swallows the glyphs. */
.diary-bubble-stage .diary-bubble-label,
.mood-explore__stage .diary-bubble-label { stroke-width: calc(2px / var(--fz-mc-s, 1)); }
.diary-bubble-stage .diary-bubble-inner,
.mood-explore__stage .diary-bubble-inner { stroke-width: calc(1.5px / var(--fz-mc-s, 1)); }
.diary-bubble-stage .diary-bubble-leader,
.mood-explore__stage .diary-bubble-leader { stroke-width: calc(0.6px / var(--fz-mc-s, 1)); }
.diary-bubble-stage .diary-chart-label,
.diary-bubble-stage .diary-chart-axis-hint,
.mood-explore__stage .diary-chart-label,
.mood-explore__stage .diary-chart-axis-hint { font-size: calc(var(--fz-mc-fs, 10px) / var(--fz-mc-s, 1)); }
.diary-bubble-stage .diary-chart-axis-hint,
.mood-explore__stage .diary-chart-axis-hint { --fz-mc-fs: 7.5px; }

/* Verken-modus. Background follows var(--bg) so the Dag preset is genuinely
   light — a hardcoded dark panel would survive the sunset theme flip and look
   broken half the day. */
.mood-explore {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: var(--bg);
  display: flex;
  flex-direction: column;
}
.mood-explore__stage {
  position: relative;
  flex: 1 1 auto;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Correct here and ONLY here: full-screen, nothing behind it to scroll, and
     a permanently visible close button as the way out. */
  touch-action: none;
  overscroll-behavior: contain;
}
.mood-explore__stage .diary-bubble-svg { width: 100%; }
.mood-explore__stage .diary-bubble-space { width: 100%; }
.mood-explore__close {
  position: absolute;
  top: max(12px, env(safe-area-inset-top));
  left: 12px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
/* Compass. The field's own mood gradient IS the map — same stop offsets as
   #moodRainbowBg — so the colour under the viewport box reads as the mood you
   are standing in. Decorative: aria-hidden, no pointer events, no focus stop. */
.mood-explore__minimap {
  position: absolute;
  left: 12px;
  bottom: max(12px, env(safe-area-inset-bottom));
  z-index: 2;
  width: 112px;
  height: 72px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: linear-gradient(
    to right,
    var(--mood-rainbow-1) 0%,
    var(--mood-rainbow-3) 22.2%,
    var(--mood-rainbow-5) 44.4%,
    var(--mood-rainbow-6) 55.5%,
    var(--mood-rainbow-8) 77.7%,
    var(--mood-rainbow-9) 88.8%,
    var(--mood-rainbow-10) 100%
  );
  overflow: hidden;
  pointer-events: none;
  transition: opacity 180ms ease;
}
.mood-explore__minimap[hidden] { display: none; }
.mood-explore__minimap-view {
  position: absolute;
  box-sizing: border-box;
  border: 1.5px solid var(--accent);
  border-radius: 3px;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  transition: left 90ms linear, top 90ms linear, width 90ms linear, height 90ms linear;
}
.mood-explore__minimap-hint {
  position: absolute;
  bottom: 1px;
  font-size: 9px;
  line-height: 1;
  font-weight: 700;
  color: var(--on-accent);
  opacity: 0.75;
}
.mood-explore__minimap-hint--lo { left: 3px; }
.mood-explore__minimap-hint--hi { right: 3px; }
@media (prefers-reduced-motion: reduce) {
  .mood-explore__minimap,
  .mood-explore__minimap-view { transition: none; }
}
.diary-chart-axis-hint {
  font-size: 7.5px;
  fill: var(--muted);
  letter-spacing: 0.04em;
  pointer-events: none;
}
.diary-bubble-inner {
  fill: var(--fz-bub-ink, #FFFFFF); /* lint-hex-ok */
  font-weight: 600;
  paint-order: stroke;
  stroke: var(--fz-bub-halo, #101014); /* lint-hex-ok */
  stroke-width: 1.5px;
  stroke-linejoin: round;
  pointer-events: none;
}

/* Mood line chart nav — 4-tab toggle + period navigator (v0.9.579) */
.mood-line-nav {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  margin: 8px 0 12px;
}
.mood-line-toggle {
  display: flex;
  background: var(--surface-muted);
  border-radius: var(--radius-pill);
  padding: 2px;
}
.mood-line-toggle__btn {
  border: none;
  background: transparent;
  color: var(--ink-2);
  font-size: 12px;
  font-weight: 500;
  padding: 5px 11px;
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
  white-space: nowrap;
}
.mood-line-toggle__btn.is-active {
  background: var(--surface-card);
  color: var(--ink);
  box-shadow: var(--elev-1); /* lint-hex-ok */
}
.mood-line-period-nav {
  display: flex;
  align-items: center;
  gap: 4px;
}
.mood-line-period-label {
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  min-width: 140px;
  text-align: center;
}
.mood-line-nav__arrow {
  border: none;
  background: transparent;
  color: var(--ink-2);
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard);
  padding: 0;
}
.mood-line-nav__arrow svg { width: 16px; height: 16px; display: block; }
.mood-line-nav__arrow:hover:not(:disabled) { background: var(--surface-muted); }
.mood-line-nav__arrow--disabled,
.mood-line-nav__arrow:disabled { opacity: 0.3; cursor: default; pointer-events: none; }

/* MOOD-ANALYTICS-RAINBOW-AND-POLISH (v0.9.474) — line chart polish */
.diary-mood-line {
  stroke: var(--accent);
  stroke-width: 2.2;
}
/* CHECKIN-DUAL-GRAPH — legend + verleiding (temptation) line */
.diary-chart-legend { display:flex; gap:1rem; align-items:center; margin-bottom:.5rem; }
.diary-chart-legend__item { display:flex; align-items:center; gap:.3rem; font-size:.75rem; color:var(--muted); }
.diary-chart-legend__dot { width:8px; height:8px; border-radius: 50%; flex-shrink:0; }
.diary-chart-legend__dot--mood { background:var(--accent); }
.diary-chart-legend__dot--temptation { background:var(--temptation-line); }
.diary-tempt-line { stroke:var(--temptation-line); stroke-width:1.8; stroke-dasharray:5 3; fill:none; }
.diary-tempt-dot { fill:var(--temptation-line); }
.diary-mood-fill-stop--top { stop-color: var(--accent); stop-opacity: 0.42; }
.diary-mood-fill-stop--bot { stop-color: var(--accent); stop-opacity: 0; }
.diary-chart-dot { fill: var(--accent); }
.diary-chart-dot--today { fill: var(--accent); stroke: var(--surface); stroke-width: 1.2; }
.diary-chart-dot--empty {
  fill: none;
  stroke: var(--ink-2);
  stroke-width: 1;
  stroke-dasharray: 1.6 1.6;
  opacity: 0.45;
}
.diary-chart-refline { stroke: var(--line); stroke-width: 1; opacity: 0.8; }
.diary-chart-reflabel { fill: var(--muted); font-size: 9px; }

/* MOOD-ANALYTICS-RAINBOW-AND-POLISH (v0.9.474) — scatter rainbow stops.
   stop-color references the :root tokens so the lint stays clean. */
.diary-scatter-bg { fill-opacity: var(--mood-rainbow-bg-opacity, 0.28); }
.mood-rainbow-stop--1  { stop-color: var(--mood-rainbow-1); }
.mood-rainbow-stop--3  { stop-color: var(--mood-rainbow-3); }
.mood-rainbow-stop--5  { stop-color: var(--mood-rainbow-5); }
.mood-rainbow-stop--6  { stop-color: var(--mood-rainbow-6); }
.mood-rainbow-stop--8  { stop-color: var(--mood-rainbow-8); }
.mood-rainbow-stop--9  { stop-color: var(--mood-rainbow-9); }
.mood-rainbow-stop--10 { stop-color: var(--mood-rainbow-10); }


.diary-range-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.diary-date-in {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  color: var(--ink);
  font: inherit;
  font-size: 12px;
  padding: 6px 8px;
  flex: 1 1 0;
  min-width: 0;
}
.diary-date-in:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}
.diary-range-btn {
  background: var(--accent);
  color: var(--on-accent);
  border: 0;
  border-radius: var(--radius-md);
  padding: 6px 14px;
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  flex: 0 0 auto;
}
.diary-range-btn:active { opacity: 0.85; }
.diary-range-clear {
  background: transparent;
  color: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 5px 10px;
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.diary-range-clear:hover { color: var(--ink); border-color: var(--ink-2); }

.diary-bubble-tip {
  position: fixed;
  z-index: 200;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 8px 10px;
  font-size: 12px;
  line-height: 1.4;
  color: var(--ink);
  box-shadow: var(--elev-2);
  pointer-events: auto;
  max-width: 240px;
}
.diary-bubble-tip__tag {
  font-weight: 600;
  color: var(--accent);
  margin-bottom: 2px;
}
.diary-bubble-tip__row { color: var(--ink-2); }
.diary-bubble-tip__row strong { color: var(--ink); }

/* MOOD-TOPIC-TREND-CHART — merging one topic into another, from the tip. The
   chart geometry is untouched; this is only the detail panel. */
.diary-bubble-tip__note {
  color: var(--ink-2);
  font-size: 11px;
  margin-top: 4px;
}
.diary-bubble-tip__merge {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 8px;
}
.diary-bubble-tip__merge-btn {
  min-height: 36px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--on-accent);
  font: inherit;
  font-weight: 600;
  padding: 6px 10px;
  cursor: pointer;
}
.diary-bubble-tip__merge-btn:disabled { opacity: 0.55; cursor: default; }
.diary-bubble-tip__merge-sel {
  min-height: 36px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  padding: 6px 8px;
  max-width: 100%;
}

/* DIARY-MOOD-PAGE — dedicated /calendar/mood page chrome. The trigger
   moved into the Diary card footer (see .cal-diary-foot__analytics).
   The charts themselves reuse .diary-mood-chart / .diary-bubble-section
   above. MOOD-ANALYTICS-RAINBOW-AND-POLISH (v0.9.474) tightened the
   header into a single row with an eyebrow + title stack.
   MOOD-TRACKER-PADDING-AND-CSS-POLISH (v0.9.489) ships proper
   outer-edge padding so the page no longer bleeds to the viewport
   edges (overrides the earlier padding:0 that erased .cal-mount's
   built-in inset). 16px horizontal + 12px top + 16px bottom; the
   parent .app-main already adds nav-h + safe-bottom clearance so the
   trailing section never sits under the bottom nav. Inter-section
   gap bumped from 18px to 24px (felt-distance separator). */
.mood-page {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: var(--space-3) var(--space-4) var(--space-4);
}
.mood-page__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 0 12px;
  border-bottom: 1px solid var(--line);
}
.mood-page__back {
  background: transparent;
  border: 0;
  color: var(--accent);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  padding: 6px 8px 6px 0;
  cursor: pointer;
  flex: 0 0 auto;
}
.mood-page__back:active { opacity: 0.75; }
.mood-page__title-wrap {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.mood-page__eyebrow {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  color: var(--muted);
  line-height: 1;
}
.mood-page__title {
  font-size: 18px;
  font-weight: 700;
  color: var(--ink);
  margin: 0;
  line-height: 1.2;
  letter-spacing: -0.01em;
}


/* ──────────────────────────────────────────────────────────
   Add-Exercise modal (bottom sheet)
   ────────────────────────────────────────────────────────── */
.mo-overlay {
  position: fixed;
  inset: 0;
  background: var(--scrim);
  z-index: 200;
  display: flex;
  align-items: flex-end;
  opacity: 0;
  /* NUTRITION-M3-WAVE-3 — asymmetric M3 emphasized easing. The transition on the
     BASE rule governs the EXIT (mo--open removed): ~200ms out, matching the
     220ms JS teardown in closeSheet() so the fade completes before removal. */
  transition: opacity var(--dur-base) var(--ease-standard);
}
/* The transition on the OPEN rule governs the ENTER: ~300ms in. */
.mo-overlay.mo--open { opacity: 1; transition: opacity var(--dur-base) var(--ease-standard); }

.mo-sheet {
  background: var(--bg);
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  max-height: 85vh;
  border-top-left-radius: var(--radius-lg);
  border-top-right-radius: var(--radius-lg);
  /* NUTRITION-M3-WAVE-3 — M3 elevation-3, theme-aware, cast upward. */
  box-shadow: var(--elev-up);
  display: flex;
  flex-direction: column;
  transform: translateY(100%);
  /* EXIT motion (see .mo-overlay note): ~200ms out, inside the 220ms teardown. */
  transition: transform var(--dur-base) var(--ease-standard);
  padding-bottom: var(--safe-bottom);
}
/* ENTER motion: ~300ms M3 emphasized slide-up. */
.mo-sheet.mo--open { transform: translateY(0); transition: transform var(--dur-base) var(--ease-standard); }

.mo-grip {
  /* M3 drag handle: 32×4. Quiet by default, more prominent while the sheet is
     touched/hovered (opacity verloop) — signals the sheet is grab-to-dismiss. */
  width: 32px;
  height: 4px;
  background: var(--muted);
  opacity: 0.55;
  border-radius: var(--radius-sm);
  margin: var(--space-2) auto;
  flex: 0 0 auto;
  transition: opacity var(--dur-base) var(--ease-standard);
}
.mo-sheet:hover .mo-grip,
.mo-sheet:active .mo-grip { opacity: 0.85; }

/* NUTRITION-M3-WAVE-3 — reduced-motion: fade-only. The overlay's opacity fade
   still carries the appearance; the sheet itself does not slide (no transform),
   so there is no large translate motion. box-shadow stays (it is static). */
@media (prefers-reduced-motion: reduce) {
  .mo-sheet,
  .mo-sheet.mo--open { transform: none; transition: none; }
  .mo-grip { transition: none; }
}

.mo-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--space-5) var(--space-3);
  border-bottom: 1px solid var(--line);
}
.mo-title {
  font-size: var(--text-title);
  line-height: var(--lh-title);
  font-weight: 500;
  letter-spacing: 0.04em;
  flex: 1 1 auto;
  min-width: 0;
}

/* NUTRITION-LOG-FLOW-SINGLE-SHEET-NO-STACKING — the log flow is one sheet whose
   body swaps per stage. Two signifiers say where you are and how to leave:
   a back-chevron (one step back) and a three-dot stage indicator. */
.mo-back {
  background: none;
  border: 0;
  color: var(--muted);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  margin: calc(-1 * var(--space-2)) 0 calc(-1 * var(--space-2)) calc(-1 * var(--space-2));
}
.mo-back:hover { color: var(--ink); }
.mo-back[hidden] { display: none; }

.mo-steps {
  display: flex;
  gap: 6px;
  justify-content: center;
  padding: var(--space-3) 0 0;
  flex: 0 0 auto;
}
.mo-steps__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--line);
}
.mo-steps__dot.is-on {
  width: 18px;
  border-radius: 3px;
  background: var(--accent);
}
/* A stage the user stepped back from is kept in the DOM so its query, results
   and scroll position survive the return trip — [hidden] alone loses to the
   display rules on .mo-body and its variants. */
.mo-body[hidden] { display: none !important; }
.mo-close {
  background: none;
  border: 0;
  font-size: 24px;
  color: var(--muted);
  cursor: pointer;
  padding: 4px 8px;
  line-height: 1;
  /* WAVE-2 touch target: ≥44×44pt hitbox; negative margins keep the header
     height + right edge unchanged (HIG negative-margin pattern). */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  margin: calc(-1 * var(--space-2)) calc(-1 * var(--space-2)) calc(-1 * var(--space-2)) 0;
}
.mo-close:hover { color: var(--ink); }

.mo-error {
  margin: var(--space-3) var(--space-5) 0;
  padding: var(--space-2) var(--space-3);
  border-left: 2px solid var(--danger);
  color: var(--danger);
  font-size: 13px;
}

.mo-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--space-4) var(--space-5) var(--space-6);
}

.mo-section-lbl {
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  margin: var(--space-4) 0 var(--space-2);
}
.mo-section-lbl:first-child { margin-top: 0; }

.mo-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-2);
}
.mo-tm {
  position: relative;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-family: inherit;
}
.mo-tm:hover { border-color: var(--ink-2); }
.mo-tm__lbl {
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
}
.mo-tm__blurb {
  font-size: 11px;
  color: var(--muted);
  line-height: 1.4;
}
.mo-tm__pin {
  position: absolute;
  top: 6px;
  right: 8px;
  color: var(--muted);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}
.mo-tm__pin.on { color: var(--accent); }

.mo-config {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.mo-back {
  align-self: flex-start;
  background: transparent;
  border: 0;
  padding: var(--space-2) 0;
  font-size: 12px;
  color: var(--muted);
  cursor: pointer;
}
.mo-back:hover { color: var(--ink); }

.mo-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.mo-lbl {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}
.mo-input {
  width: 100%;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line);
  padding: 10px 0;
  font-size: 16px;
  color: var(--ink);
  outline: none;
  border-radius: 0;
  font-family: inherit;
}
.mo-input:focus { border-bottom-color: var(--ink); }
.mo-input--num { font-feature-settings: 'tnum' 1; }

.mo-lib {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 4px;
}
.mo-lib-chip {
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  padding: 4px 10px;
  font-size: 12px;
  color: var(--muted);
  cursor: pointer;
  font-family: inherit;
}
.mo-lib-chip:hover { color: var(--ink); border-color: var(--ink-2); }

.mo-config__box {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-3) var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
}

.mo-actions {
  display: flex;
  gap: var(--space-3);
  margin-top: var(--space-4);
}
.mo-actions .btn { flex: 1 1 0; }

/* ──────────────────────────────────────────────────────────
   Hero timer
   ────────────────────────────────────────────────────────── */
.hero-timer {
  position: fixed;
  top: 0; left: 0; right: 0;
  background: var(--ink);
  color: var(--bg);
  padding: var(--space-3) var(--space-5);
  z-index: 60;
  display: none;
  align-items: center;
  gap: var(--space-3);
  border-bottom: 1px solid var(--ink-2);
  font-feature-settings: 'tnum' 1;
}
.hero-timer.active { display: flex; }
.hero-timer.warn   { background: var(--accent); }
.hero-timer.alarm  { background: var(--ok); }
.hero-timer__phase {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.7);
  flex: 0 0 auto;
}
.hero-timer__time {
  font-size: 28px;
  font-weight: 300;
  letter-spacing: -0.02em;
  margin-left: auto;
}
.hero-timer__round {
  font-size: 12px;
  color: rgba(255,255,255,0.7);
  margin-left: var(--space-3);
}
.hero-timer__btns {
  display: flex;
  gap: var(--space-2);
}
.hero-timer button {
  background: rgba(255,255,255,0.15);
  color: var(--bg);
  border: 0;
  width: 30px; height: 30px;
  border-radius: 50%;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
}
.hero-timer button:hover { background: rgba(255,255,255,0.3); }
body.hero-active .app-main { padding-top: 64px; }

/* ──────────────────────────────────────────────────────────
   Per-set rest countdown + ex play
   ────────────────────────────────────────────────────────── */
.sr-rest {
  margin-left: 4px;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  padding: 2px 8px;
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
  font-family: inherit;
}
.sr-rest.on { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.sr-rest:hover { color: var(--ink); }
.sr-rest.on:hover { color: var(--bg); }

.ex-play {
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  padding: 4px 10px;
  margin-left: var(--space-2);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
  font-family: inherit;
}
.ex-play:hover { color: var(--ink); border-color: var(--ink-2); }
.ex-play.on    { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }

/* ──────── Update-available banner ──────── */
.update-banner {
  position: fixed;
  left: 50%;
  /* v0.9.339 — bump gap from space-3 → space-4 so the pill clearly floats
     above the tabbar (previously sat ~12px above and visually crowded the
     BRICKS/SETTINGS labels on a phone screenshot review). */
  bottom: calc(var(--tabbar-h) + var(--safe-bottom) + var(--space-4));
  transform: translate(-50%, calc(100% + var(--space-5)));
  background: var(--ink);
  color: var(--bg);
  /* v0.9.339 — symmetric padding. The bottom anchor above already clears the
     tabbar AND the home-indicator safe-area; doubling safe-bottom into the
     padding (v0.9.320) was inflating the pill height and looked off-balance. */
  padding: var(--space-2) var(--space-3) var(--space-2) var(--space-4);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  border-radius: var(--radius-pill);
  box-shadow: var(--elev-2);
  z-index: 200;
  font-size: 13px;
  letter-spacing: 0.02em;
  transition: transform var(--dur-base) var(--ease-standard);
  max-width: calc(100vw - var(--space-5) * 2);
}
.update-banner--show { transform: translate(-50%, 0); }
.update-banner__msg {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.update-banner__skip,
.update-banner__cta {
  background: transparent;
  border: 0;
  font: inherit;
  color: inherit;
  padding: 6px 10px;
  cursor: pointer;
  border-radius: var(--radius-pill);
}
/* v0.9.339 — opacity, not hardcoded white. The pill's text is var(--bg), which
   inverts with the theme (dark text in dark mode where the pill is white,
   light text in light mode where the pill is dark). The previous
   rgba(255,255,255,0.6) was invisible in dark mode (white-on-white). */
.update-banner__skip { opacity: 0.55; }
.update-banner__skip:hover { opacity: 0.9; }
.update-banner__cta {
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 500;
  padding: 6px 14px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-size: 11px;
}
.update-banner__cta:hover { background: var(--accent-2); }
.update-banner__cta:disabled { opacity: 0.6; cursor: default; }
body.tab-strength .update-banner { /* keep visible above iframe + tab bar */
  bottom: calc(var(--tabbar-h) + var(--safe-bottom) + var(--space-4));
}

/* ───── Offline pill (non-blocking) ───── */
/* OFFLINE-BANNER-NONBLOCKING-PILL — replaces the old full-width offline overlay
   (position:fixed;left:0;right:0;top:0;z-index:9999) which covered the chat
   notification bell. pointer-events:none is the whole point: the pill informs,
   it never intercepts a tap. Only the '?' help button opts back in. */
.fz-offline-pill {
  position: fixed;
  top: calc(var(--safe-top) + 8px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 90;            /* under toasts (100), chat notif sheet, topbar chip */
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: min(88vw, 360px);
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  background: var(--ink);
  color: var(--bg);
  font-size: 12px;
  line-height: 1.25;
  box-shadow: 0 1px 6px rgba(0, 0, 0, .20);
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-standard),
              max-width var(--dur-base) var(--ease-standard);
}
.fz-offline-pill[hidden] { display: none; }
.fz-offline-pill--visible { opacity: 1; }
.fz-offline-pill__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fz-offline-pill__icon { display: inline-flex; flex: 0 0 auto; }
.fz-offline-pill__icon svg { width: 14px; height: 14px; }
.fz-offline-pill .fz-lc-help {
  pointer-events: auto;   /* the only interactive affordance on the pill */
  margin-left: 6px;
  flex: 0 0 auto;
  width: 18px; height: 18px; line-height: 16px;
  border-radius: 50%;
  border: 1px solid currentColor;
  background: transparent;
  color: inherit;
  font-size: 12px;
  padding: 0;
  cursor: pointer;
}
/* Chat surface keeps its own header + bell at the top edge — clear it entirely. */
body.is-chat .fz-offline-pill { top: calc(var(--safe-top) + 56px); }
/* Coach hub renders its own sticky .cshell (8 + 40 + 8 + 1px border = 57px) at
   the top edge of the iframe; the pill has to clear that whole bar or it lands
   on the client search field. */
body.tab-coach-hub .fz-offline-pill { top: calc(var(--safe-top) + 65px); }
/* Narrow phones: the collapsed state drops to the icon alone. */
@media (max-width: 340px) {
  .fz-offline-pill--collapsed .fz-offline-pill__label { display: none; }
}

/* ───────── Toast ───────── */
.toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--tabbar-h) + var(--safe-bottom) + var(--space-4));
  transform: translateX(-50%);
  background: var(--ink);
  color: var(--bg);
  padding: var(--space-3) var(--space-5);
  font-size: 13px;
  letter-spacing: 0.04em;
  z-index: 100;
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-standard);
  pointer-events: none;
  max-width: 90vw;
}
.toast--show { opacity: 1; }
.toast--err { background: var(--danger); }

/* ───────── Utility ───────── */
.spinner {
  display: inline-block;
  width: 14px; height: 14px;
  border: 2px solid var(--line);
  border-top-color: var(--ink);
  border-radius: 50%;
  animation: spin var(--dur-loop) linear infinite;
  vertical-align: middle;
  margin-right: 8px;
}
.btn .spinner { border-color: rgba(255,255,255,0.3); border-top-color: var(--bg); }
@keyframes spin { to { transform: rotate(360deg); } }

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

/* v0.6.0 #2a — Calendar progress bars (month grid + day sheet).
   Mirrors classes used in the strength iframe so visuals stay consistent.   */
.fz-cal-prog { height: 3px; background: var(--line); border-radius: var(--radius-sm); width: 80%; margin: 2px auto 0; overflow: hidden; }
.fz-cal-prog__fill { height: 100%; background: var(--muted); border-radius: var(--radius-sm); transition: width var(--dur-base) var(--ease-standard); }
.cal-cell.fz-cal-today .fz-cal-prog { background: rgba(255,255,255,0.25); }
.cal-cell.fz-cal-today .fz-cal-prog__fill { background: var(--bg); }
.fz-sheet-prog { height: 4px; background: var(--line); border-radius: var(--radius-sm); margin-top: 6px; overflow: hidden; width: 100%; }
.fz-sheet-prog__fill { height: 100%; background: var(--accent); border-radius: var(--radius-sm); transition: width var(--dur-base) var(--ease-standard); }
.fz-sheet-prog__lbl { font-size: 10px; color: var(--muted); font-weight: 600; margin-top: 4px; }
/* Resume action button (replaces Repeat for in_progress sessions). */
.fz-resume-btn { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.fz-resume-btn:hover { background: var(--accent-2); border-color: var(--accent-2); }

/* ──────────────────────────────────────────────────────────
   Nutrition tab (v0.9.174, Ship 1 — the logging loop).
   Native SPA tab; reuses the .mo-* bottom-sheet + .ui-dlg-* dialog CSS.
   ────────────────────────────────────────────────────────── */
.nut-mount { padding: var(--space-4) var(--space-4) calc(var(--tabbar-h) + var(--safe-bottom) + var(--space-5)); }

/* Date header */
.nut-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-4); }
.nut-nav {
  background: none; border: 1px solid var(--line); color: var(--ink-2);
  width: 36px; height: 36px; border-radius: 50%; font-size: 20px; line-height: 1;
  cursor: pointer; flex: 0 0 auto;
}
.nut-nav:hover { border-color: var(--ink-2); color: var(--ink); }
.nut-date {
  background: none; border: 0; color: var(--ink); font-size: 17px; font-weight: 500;
  cursor: pointer; letter-spacing: 0.01em; padding: 6px 10px; border-radius: var(--radius-sm);
}
.nut-date.is-today { color: var(--accent); }
.nut-date__caret { color: var(--muted); font-size: 12px; }
/* v0.9.179 — transparent native date input overlaying the label; a direct tap
   opens the iOS picker (jump to any day). opacity:0 still receives pointer
   events, unlike display:none/visibility:hidden. */
.nut-datewrap { position: relative; display: inline-flex; }
.nut-dateinput {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; border: 0; margin: 0; padding: 0; cursor: pointer;
  -webkit-appearance: none; appearance: none; background: none;
}

.nut-loading { text-align: center; color: var(--muted); padding: var(--space-7) 0; }

/* SASSU-DESIGN-EMPTY-STATES-ACTIONABLE — the load-failure box states the recovery
   AND offers it. The button is the only accent element in an otherwise muted
   dashed placeholder, so the eye lands on the one thing worth pressing. */
.nut-loaderr__txt { margin: 0 0 var(--space-4); }
.nut-loaderr__retry {
  display: inline-block; min-height: 44px; padding: 0 var(--space-6);
  background: var(--accent); color: var(--on-accent);
  border: 0; border-radius: var(--radius-md);
  font-size: 15px; font-weight: 600; cursor: pointer;
}
.nut-loaderr__retry:hover { background: var(--accent-2); }
.nut-loaderr__retry:disabled { opacity: .5; cursor: default; }

/* DIARY-LOG-BUTTONS-RELOCATE-WATER-NUTRITION-STEPS-CARDIO (v0.9.473) — Water
   quick-log cell relocated from Diary onto Nutrition. The cell chrome is the
   .cal-metric--empty/--filled pair reused from the Diary surface; this wrap
   only owns the surrounding margin so the cell sits between the date header
   and the North Star without crowding either. */
/* NUTRITION-HYDRATION-CARD-AND-INTAKE-WATER-GOAL — dedicated water-tracking
   card under the macros North Star. Card chrome mirrors .nut-ns but carries the
   semantic blue --hydration accent (water ≠ macros amber). */
/* NUTRITION-M3-WAVE-4 — day-view cards share one outlined-card style:
   radius var(--radius-lg), padding var(--space-4)/var(--space-5), 1px --line. */
.hydration-card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5); margin-bottom: var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-3);
}
.hydration-card > header {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3);
}
.hydration-card h3 { margin: 0; font-size: var(--text-title); line-height: var(--lh-title); font-weight: 600; color: var(--ink); }
.hydration-amount { font-size: var(--text-body-sm); font-weight: 600; color: var(--hydration); white-space: nowrap; }
.hydration-bar {
  width: 100%; height: 8px; background: var(--surface-muted);
  border-radius: var(--radius-pill); overflow: hidden;
}
.hydration-bar__fill {
  height: 100%; width: 0; background: var(--hydration);
  border-radius: var(--radius-pill); transition: width var(--dur-base) var(--ease-standard);
}
.hydration-actions { display: flex; gap: var(--space-2); }
.hydration-btn {
  flex: 1; padding: var(--space-2) var(--space-3);
  background: var(--surface-muted); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  font-size: 0.85rem; font-weight: 600; cursor: pointer;
}
.hydration-btn:hover { border-color: var(--hydration); }
/* Intake water-goal preset chips reuse .onboarding__radio; this just keeps the
   row wrapping cleanly when the 5 chips don't fit one line. */
.water-goal-presets { flex-wrap: wrap; }

/* North Star */
.nut-ns {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5); margin-bottom: var(--space-4);
  /* SEQUENCER-V1E (v0.9.501) — positioning ancestor for the active-plan
     indicator that anchors to the card's top-right corner. */
  position: relative;
}
.nut-ns:hover { border-color: var(--ink-2); }
/* SEQUENCER-V1E (v0.9.501) — subtle refresh / cycle icon shown only when
   periodisation is overriding the stored target. Click-through is
   suppressed in JS so the parent goal-editor button doesn't open. */
.nut-ns__seq-ind {
  position: absolute; top: 8px; right: 8px;
  width: 26px; height: 26px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  color: var(--muted);
  background: transparent;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.nut-ns__seq-ind:hover, .nut-ns__seq-ind:focus-visible {
  color: var(--accent);
  outline: none;
}
.nut-ns__seq-ind > svg { width: 16px; height: 16px; display: block; }
.nut-ns--empty { text-align: center; padding: var(--space-5); }
.nut-ns__set { color: var(--accent); font-size: 15px; font-weight: 500; }
.nut-ns__hint { color: var(--muted); font-size: var(--text-body-sm); margin-top: 4px; }
/* Coach-set goals (v0.9.189): "Set by your coach" badge + coach-only Lock toggle. */
.nut-ns__by { color: var(--accent); font-size: 12px; font-weight: 600; margin-bottom: var(--space-3); }
.nut-lockrow { display: flex; align-items: center; gap: 8px; margin: var(--space-3) 0; cursor: pointer; }
.nut-lockrow__chk { accent-color: var(--accent); width: 18px; height: 18px; flex: 0 0 auto; }
.nut-lockrow__lbl { font-size: 13px; color: var(--ink); }
.nut-ns__cal { margin-bottom: var(--space-3); }
.nut-ns__cal:last-child { margin-bottom: 0; }
.nut-ns__calrow { display: flex; align-items: baseline; gap: 6px; }
/* NUTRITION-M3-WAVE-4 — type roles: kcal main number = headline; goal/remainder
   detail = body-sm. line-height:1 kept on the number for a tight numeric cap. */
.nut-ns__calnum { font-size: var(--text-headline); font-weight: 600; color: var(--ink); line-height: 1; }
.nut-ns__calgoal { font-size: var(--text-body-sm); color: var(--muted); }
.nut-ns__rem { font-size: var(--text-body-sm); color: var(--muted); margin-top: 4px; }
/* Ship A / rec #2 — over-target is calm + neutral, NEVER red (ED-safety + Feliz
   no-shame policy). The old `.is-over { color: var(--danger) }` shame state and
   the red bar fills below were removed wholesale. */
.nut-ns__rem--met { color: var(--ink-2); }
/* Gentle low-intake floor hint — supportive, accent-toned, never alarming. */
.nut-ns__floor { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--accent); margin-top: 6px; }
.nut-ns__floor .nut-ic { width: 14px; height: 14px; flex: 0 0 auto; }
.nut-ns__macros { display: flex; flex-direction: column; gap: var(--space-3); margin-top: var(--space-2); }

.nut-mac__top { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 4px; }
.nut-mac__lbl { font-size: var(--text-body); color: var(--ink-2); }
/* COLOR-3 — the gram readouts sitting directly above the macro bars were the
   one metric display on the Nutrition overview still on proportional figures,
   so the three values jittered against each other as the day's numbers moved. */
.nut-mac__val { font-size: var(--text-body-sm); color: var(--muted); font-variant-numeric: tabular-nums; }

/* NUTRITION-M3-WAVE-4 — macro progress bars thickened 6→8px with full pill
   rounding for glanceability; track stays var(--line), fill colour per macro
   token unchanged. */
.nut-bar { height: 8px; background: var(--line); border-radius: var(--radius-pill); overflow: hidden; }
.nut-bar__fill { height: 100%; background: var(--accent); border-radius: var(--radius-pill); transition: width var(--dur-base) var(--ease-standard); }
/* COLOR-3 — protein moved off the generic --ok onto the macro triad's own
   green. --ok is the app-wide success colour at C 0.21; sitting it next to
   --mac-c and --mac-f made the three bars read as two quiet ones and an alarm. */
.nut-mac--p .nut-bar__fill { background: var(--mac-p); }
.nut-mac--c .nut-bar__fill { background: var(--mac-c); }
.nut-mac--f .nut-bar__fill { background: var(--mac-f); }

/* Ship B (rec #3) — North Star reframe. Protein HERO leads (the one celebrated
   metric); fiber a calm secondary indicator; calories/carbs/fat a quiet band
   below a divider. No red anywhere — Ship A's no-shame invariant carried through.
   Protein green + fiber teal are fixed (like the macro fills above) with a
   slightly brighter dark-theme variant for contrast. */
.nut-ns__hero { margin-bottom: var(--space-4); }
.nut-ns__hero-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
/* NUTRITION-PROTEIN-BAR-MINIMALIST-REDESIGN-V2 — 6px time-axis bar; pills float
   above as lightweight neutral rectangles; colour belongs to the accent fill, not
   the data points. Token note: the brief names var(--text)/var(--surface-card); this
   theme defines neither, so --text→var(--ink) and --surface-2→var(--surface-2,
   var(--line)) (the established fallback). */
.nut-ns__hero-label { font-size: 11px; font-family: 'Geist Sans', sans-serif; text-transform: uppercase; color: var(--ink); opacity: 0.5; letter-spacing: 0.3px; }
.nut-ns__hero-num { display: flex; align-items: baseline; gap: 6px; }
/* COLOR-3 — the 32px protein total is the single largest metric on the
   Nutrition overview and was still proportional, so it visibly reflowed on
   every gram logged; at this size the shift is a wobble, not a nudge. */
.nut-ns__hero-total { font-size: 32px; font-family: 'Geist Sans', sans-serif; color: var(--accent); line-height: 1; font-variant-numeric: tabular-nums; }
.nut-ns__hero-goal { font-size: 14px; font-family: 'Geist Sans', sans-serif; color: var(--ink); opacity: 0.4; }
/* Bar: the 32px top margin reserves the floating pill zone (18px pill + 6px gap)
   plus the brief's 8px gap below the title row. */
/* NUTRITION-M3-WAVE-4 — protein time-axis bar thickened 6→8px, pill-rounded;
   the now-line height term tracks the new 8px bar so it still reaches bar-bottom. */
.nut-ns__hero-bar { position: relative; height: 8px; margin-top: 32px; }
.nut-ns__hero-bar__track { position: absolute; left: 0; top: 0; right: 0; bottom: 0; background: var(--surface-2, var(--line)); opacity: 0.6; border-radius: var(--radius-pill); }
.nut-ns__hero-bar__fill { position: absolute; left: 0; top: 0; bottom: 0; background: var(--accent); border-radius: var(--radius-pill); pointer-events: none; }
.nut-ns__hero-bar--complete .nut-ns__hero-bar__track { opacity: 1; }
/* NOW hairline — 1px accent @30%, spans pill-top→bar-bottom, behind the pills. */
.nut-ns__now-line { position: absolute; width: 1px; top: calc(-18px - 6px); height: calc(18px + 6px + 8px);
  background: var(--accent); opacity: 0.3; transform: translateX(-50%); pointer-events: none; z-index: 1; }
/* Pills — 28×18px neutral rectangles floating 6px above the bar. min-width keeps
   the nominal 28px terminal; content sizing lets the single-line label fit. */
.nut-ns__pill { position: absolute; min-width: 28px; height: 18px; box-sizing: border-box; padding: 0 5px;
  bottom: calc(100% + 6px); left: 0; transform: translateX(-50%);
  display: flex; align-items: center; justify-content: center;
  background: var(--bg); border: 1px solid var(--surface-2, var(--line)); border-radius: var(--radius-sm);
  cursor: pointer; z-index: 2; }
.nut-ns__pill::before { content: ''; position: absolute; inset: -12px; }
.nut-ns__pill--past { border-color: var(--accent); }
.nut-ns__pill--tap { border-color: var(--accent); }
.nut-ns__pill-label { font-family: 'Geist Sans', sans-serif; font-size: 11px; letter-spacing: 0.3px; color: var(--ink); white-space: nowrap; }
.nut-ns__pill-time { opacity: 0.9; }
.nut-ns__pill-grams { opacity: 1; }
.nut-ns__pill-super { position: absolute; top: -6px; right: -4px; font-size: 9px; color: var(--accent); font-family: 'Geist Sans', sans-serif; line-height: 1; }
/* Microcopy — 8px below the bar. */
.nut-ns__hero-micro { margin-top: 8px; font-size: 11px; font-family: 'Geist Sans', sans-serif; color: var(--ink); opacity: 0.45; display: flex; align-items: center; gap: 4px; }
.nut-ns__hero-micro--complete { color: var(--accent); opacity: 0.7; }
.nut-ns__hero-micro .nut-ic { width: 12px; height: 12px; flex: 0 0 auto; }
/* cluster sheet — opened by tapping a '+N' badge on a collapsed cluster */
.nut-clus-row { display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 12px 4px; background: transparent; border: 0; border-bottom: 1px solid var(--line);
  color: var(--ink); font-size: 14px; text-align: left; cursor: pointer; }
.nut-clus-row__t { font-variant-numeric: tabular-nums; color: var(--muted); min-width: 46px; }
.nut-clus-row__n { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nut-clus-row__g { font-weight: 600; color: var(--accent); }

.nut-ns__fiber { display: flex; align-items: center; gap: 10px; margin-bottom: var(--space-4); }
.nut-ns__fiber-main { flex: 1; min-width: 0; }
.nut-ns__fiber-top { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 4px; }
.nut-ns__fiber-lbl { font-size: var(--text-body); color: var(--ink-2); }
.nut-ns__fiber-val { font-size: var(--text-body-sm); color: var(--muted); }
.nut-ns__fiber-bar { height: 8px; background: var(--line); border-radius: var(--radius-pill); overflow: hidden; }
.nut-ns__fiber-bar > i { display: block; height: 100%; border-radius: var(--radius-pill); background: var(--fiber); transition: width var(--dur-base) var(--ease-standard); }

.nut-ns__band { border-top: 1px solid var(--line); padding-top: var(--space-3); margin-top: 2px;
                display: flex; flex-direction: column; gap: 10px; }
.nut-ns__band-cal { display: flex; align-items: baseline; justify-content: space-between; }
.nut-ns__band-cal-l { font-size: var(--text-body-sm); color: var(--ink-2); }
.nut-ns__band-cal-r { font-size: var(--text-body-sm); color: var(--muted); }
.nut-ns__band-cal-r b { color: var(--ink); font-weight: 600; }

/* "Your usual" — recents + favorites one-tap tiles above search (Ship A / rec #1). */
.nut-usual { margin-bottom: var(--space-2); }
.nut-usual__hd { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: var(--space-2); }
.nut-usual__lbl { font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-2); }
.nut-usual__hint { font-size: 11px; color: var(--muted); }
.nut-usual__list { display: flex; flex-direction: column; gap: var(--space-2); }
.nut-utile {
  display: flex; align-items: center; gap: var(--space-3); width: 100%; text-align: left;
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 10px var(--space-3); cursor: pointer;
}
.nut-utile:active { transform: scale(.99); }
.nut-utile__main { flex: 1; min-width: 0; }
.nut-utile__name { display: flex; align-items: center; gap: 6px; font-size: 14px; font-weight: 500; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nut-utile__name > span { overflow: hidden; text-overflow: ellipsis; }
.nut-utile__star { color: var(--accent); width: 14px; height: 14px; flex: 0 0 auto; }
.nut-utile__sub { font-size: 11px; color: var(--muted); margin-top: 2px; }
/* Ship G (rec #10) — time-of-day legibility: a faint accent tag on tiles that
   matched the current slot, and an accent header hint ("for your mornings"). */
.nut-utile__slot { color: var(--accent); font-weight: 600; }
.nut-usual__hint--slot { color: var(--accent); }
/* v0.9.257 — persoon-icoon next to user-uploaded foods. Muted, never accent —
   it sits beside fav stars and product thumbs and must not compete with them. */
.nut-user { display: inline-flex; align-items: center; color: var(--muted); margin-left: 6px; flex: 0 0 auto; }
.nut-user .nut-ic { width: 12px; height: 12px; }
.nut-user .nut-ic svg { width: 12px; height: 12px; }
.nut-utile__add {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%;
  background: var(--accent); color: var(--on-accent);
  display: flex; align-items: center; justify-content: center;
}
.nut-utile__add .nut-ic { width: 16px; height: 16px; }
/* NUTRITION-PIN-AND-LAST-USED-TOP — corner "pin to top" toggle. Sits in the
   tile's top-right corner (align-self, so it never overlaps the add circle) and
   above the tile's ::after ripple, which would otherwise eat the tap. Subtle by
   default; the pinned state fills and takes the accent — tokens only. */
.nut-utile__pin {
  position: relative; z-index: 1; flex: 0 0 auto; align-self: flex-start;
  width: 26px; height: 26px; margin: -4px -2px 0 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--muted); opacity: .55; cursor: pointer; border-radius: 50%;
}
.nut-utile__pin .nut-ic { width: 14px; height: 14px; }
.nut-utile__pin .nut-ic svg { width: 14px; height: 14px; }
.nut-utile__pin.is-on { color: var(--accent); opacity: 1; }
.nut-utile__pin:active { transform: scale(.92); }
.nut-usual__or { display: flex; align-items: center; gap: 10px; margin: var(--space-4) 0 var(--space-3); }
.nut-usual__or::before, .nut-usual__or::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.nut-usual__or-lbl { font-size: 11px; color: var(--muted); }

/* Forgiving consistency band (Ship E, rec #7) — "logged X of the last 7 days",
   an ADDITIVE count a single gap can't zero. NOT a streak; never red. */
.nut-consist {
  display: flex; align-items: center; gap: var(--space-3);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5); margin-bottom: var(--space-4);
}
.nut-consist__dots { display: flex; gap: 7px; flex: 0 0 auto; }
.nut-consist__dot {
  width: 13px; height: 13px; border-radius: 50%; box-sizing: border-box;
  border: 1.5px solid var(--line); background: transparent;
}
/* STREAK-DOTS-PARTIAL-FILL-PROGRESS — battery-fill (vertical, bottom→top). The
   --fill custom property is set by renderConsistency() on every logged dot;
   filled portion is var(--accent), unfilled is var(--line). 100% reads as a
   solid accent dot (= the old binary-achieved state). The accent border
   ensures even 0% logged days carry the "you logged something" signal vs the
   un-logged outline. Token-only — clears css-hex-lint across all six finishes. */
.nut-consist__dot.is-on {
  border-color: var(--accent);
  background: linear-gradient(to top, var(--accent) var(--fill, 0%), var(--line) 0);
}
.nut-consist__dot.is-current { box-shadow: var(--ring-current); }
.nut-consist__txt { font-size: var(--text-body-sm); color: var(--ink-2); line-height: 1.3; }
.nut-consist__txt b { color: var(--ink); font-weight: 700; }
.nut-consist--zero .nut-consist__txt { color: var(--muted); }
.nut-consist--zero .nut-consist__txt b { color: var(--ink-2); }

/* Warm re-entry (Ship E, rec #7) — empty TODAY after a gap. Gentle, never guilt. */
.nut-reentry {
  display: flex; gap: 10px; align-items: flex-start; text-align: left;
  background: var(--accent-light); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 13px 14px; margin-bottom: var(--space-4);
}
.nut-reentry__ic { flex: 0 0 auto; color: var(--accent); margin-top: 1px; }
.nut-reentry__ic svg { width: 20px; height: 20px; display: block; }
.nut-reentry__txt { font-size: 13.5px; color: var(--ink); line-height: 1.45; }
.nut-reentry__txt b { font-weight: 700; }
.nut-reentry__txt span { color: var(--ink-2); }

/* Sub-second reward tick (Ship E, rec #7) — fires on a successful log. No emoji
   (the `check` glyph). Pops + fades centered; static fade under reduced-motion. */
.nut-tick {
  position: fixed; inset: 0; z-index: 60; display: flex; align-items: center;
  justify-content: center; pointer-events: none;
}
.nut-tick__badge {
  width: 64px; height: 64px; border-radius: 50%; background: var(--ok);
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--elev-2);
  animation: nutTickPop var(--dur-slow) var(--ease-standard) both;
}
.nut-tick__badge svg { width: 34px; height: 34px; color: var(--on-accent); }
@keyframes nutTickPop {
  0%   { transform: scale(0.4); opacity: 0; }
  28%  { transform: scale(1);   opacity: 1; }
  72%  { transform: scale(1);   opacity: 1; }
  100% { transform: scale(0.96); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  /* 0.6s stays: this is an opacity-only dwell so the tick is readable without
     motion. Only the curve joins the app signature. */
  .nut-tick__badge { animation: nutTickFade 0.6s var(--ease-standard) both; }
  @keyframes nutTickFade { 0% { opacity: 0; } 20% { opacity: 1; } 80% { opacity: 1; } 100% { opacity: 0; } }
}

/* Meals */
.nut-empty { text-align: center; color: var(--muted); padding: var(--space-5) 0; }
/* Desktop-only since NUTRITION-LIQUID-GLASS-PLATE — the phone tree replaced this
   hero with the pinned plate wallpaper (.nut-plate-wrap, end of file). */
.nut-empty__hero { display: flex; justify-content: center; margin: var(--space-5) 0 var(--space-4); }
.nut-empty__hero-img { max-width: 95%; max-height: 200px; width: auto; height: auto; opacity: 0.95; border-radius: var(--radius-md); }
.nut-empty__title { font-size: var(--text-title); line-height: var(--lh-title); font-weight: 600; color: var(--ink); margin: 0 0 var(--space-1); }
.nut-empty__txt { font-size: var(--text-body-sm); line-height: var(--lh-body-sm); color: var(--muted); }
/* Copy-a-previous-day (Ship 2, v0.9.178) — prominent in the empty state, a quiet
   link when the day already has meals. */
.nut-copyday {
  margin-top: var(--space-4); background: none; border: 1px solid var(--line);
  border-radius: var(--radius-md); color: var(--accent); font-size: 14px; cursor: pointer;
  padding: var(--space-3) var(--space-4);
}
.nut-copyday:hover { border-color: var(--accent); background: var(--accent-light); }
.nut-copy__hint { font-size: 13px; color: var(--muted); margin-bottom: var(--space-3); }

/* Repeat-a-day HERO (Ship C, rec #4) — empty-day one-tap replay of the most
   recent logged day. Promotes the old buried copy link to the primary action. */
.nut-repeat {
  text-align: left; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: var(--space-4) var(--space-4) var(--space-3);
}
.nut-repeat__eyebrow {
  display: flex; align-items: center; gap: var(--space-1); color: var(--accent);
  font-size: 11px; letter-spacing: .06em; text-transform: uppercase; font-weight: 700;
  margin-bottom: var(--space-2);
}
.nut-repeat__eyebrow .nut-ic svg { width: 14px; height: 14px; }
.nut-repeat__q { font-size: 20px; font-weight: 700; color: var(--ink); line-height: 1.2; margin-bottom: var(--space-1); }
.nut-repeat__when { color: var(--accent); }
.nut-repeat__prev { font-size: 13px; color: var(--ink-2); margin: 0 0 var(--space-4); }
.nut-repeat__prev b { color: var(--ink); font-weight: 600; }
.nut-repeat__go {
  display: block; width: 100%; background: var(--accent); color: var(--on-accent);
  border: 0; border-radius: var(--radius-md); padding: var(--space-3); font-size: 15px; font-weight: 600; cursor: pointer;
}
.nut-repeat__go:hover { background: var(--accent-2); }
.nut-repeat__alt {
  display: block; margin: var(--space-2) auto 0; background: none; border: 0;
  color: var(--muted); font-size: 13px; cursor: pointer; padding: var(--space-1);
}
.nut-repeat__alt:hover { color: var(--accent); }

/* Undo banner after a repeat-a-day copy (Ship C, rec #4) — a persistent, tappable
   in-tab bar (the global toast auto-hides in 3s, too fast to catch a mistake). */
.nut-undobar {
  display: flex; align-items: center; gap: var(--space-3); margin-top: var(--space-4);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
}
.nut-undobar__txt { flex: 1 1 auto; font-size: 13px; color: var(--ink-2); }
.nut-undobar__undo { flex: 0 0 auto; background: none; border: 0; color: var(--accent); font-weight: 700; font-size: 14px; cursor: pointer; padding: var(--space-1) var(--space-2); }

/* Combo picker behind the "+" tile (NUTRITION-PLUS-TILE-COMBO-PICKER-SAVED-ONLY).
   One compact header line: label · "?". The explanation lives behind the "?" and
   reuses the SAME key as the Combos library empty state, so there is exactly one
   explanation of what a combo is, never two side by side. */
.nut-combopick__hd { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-2); }
.nut-combopick__lbl { flex: 1 1 auto; font-size: 12px; font-weight: 600; color: var(--muted); }
.nut-combopick__help {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; padding: 0; background: none; border: 0; cursor: pointer; color: var(--muted);
}
.nut-combopick__help.is-on { color: var(--accent); }
.nut-combopick__help svg { width: 16px; height: 16px; fill: none; stroke: currentColor; }
.nut-combopick__note { display: none; font-size: 12px; line-height: 1.45; color: var(--muted); margin-bottom: var(--space-3); }
.nut-combopick__note.is-open { display: block; }
.nut-combopick__list { display: flex; flex-direction: column; gap: var(--space-2); }
.nut-savedrow {
  display: flex; align-items: center; gap: var(--space-3); width: 100%; text-align: left;
  background: var(--accent-light); border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4); cursor: pointer;
}
.nut-savedrow:hover { border-color: var(--accent); }
.nut-savedrow .nut-ic { color: var(--accent); flex: 0 0 auto; }
/* Line glyph (clock), not the old filled star — a fill would turn it into a blob. */
.nut-savedrow .nut-ic svg { width: 18px; height: 18px; fill: none; stroke: currentColor; }
.nut-savedrow__main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.nut-savedrow__name { font-size: 15px; font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* One line, clipped — a six-item combo must not wrap the row open; the count
   line underneath tells the truth about what is hidden behind the ellipsis. */
.nut-savedrow__sub { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nut-savedrow__add { flex: 0 0 auto; display: inline-flex; align-items: center; color: var(--accent); }

/* NUTRITION-REMOVE-QUICK-LOG-CARDS-AND-DRAG-REORDER-MEALS — the .nut-qstart /
   .nut-qbtn block (Ship F's two empty-day quick-start tiles) lived here and was
   removed together with its markup on 2026-07-28. Both tiles duplicated the
   ready-made meal-slot rows above them and the global "+" FAB below. */

/* Rough-log sheet (rec #8) — one optional calories field, slot chips, a name. */
.nut-rl__hint { font-size: 13px; color: var(--ink-2); line-height: 1.45; margin: 0 0 var(--space-4); }
.nut-rl__cal {
  display: flex; align-items: baseline; gap: var(--space-2);
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: var(--space-3) var(--space-4);
}
.nut-rl__num { flex: 0 0 auto; min-width: 64px; width: 96px; background: none; border: 0; color: var(--ink); font-size: 30px; font-weight: 800; padding: 0; }
.nut-rl__num::placeholder { color: var(--muted); }
.nut-rl__num:focus { outline: none; }
.nut-rl__unit { flex: 1 1 auto; font-size: 14px; color: var(--muted); }
.nut-rl__opt { font-size: 11px; color: var(--muted); }
.nut-rl__lbl { font-size: 12px; font-weight: 600; color: var(--muted); margin: var(--space-4) 0 var(--space-2); text-transform: uppercase; letter-spacing: .05em; }
.nut-rl__slots { display: flex; gap: var(--space-2); }
.nut-rl__slot {
  flex: 1 1 0; text-align: center; background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius-md); padding: 9px 4px; font-size: 13px; color: var(--ink-2); cursor: pointer;
}
.nut-rl__slot.is-on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }
.nut-rl__name {
  width: 100%; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 11px var(--space-4); font-size: 14px; color: var(--ink); margin-top: var(--space-2);
}
.nut-rl__name:focus { outline: none; border-color: var(--accent); }
.nut-rl__go { width: 100%; margin-top: var(--space-4); }
.nut-rl__nodetail { display: block; width: 100%; background: none; border: 0; color: var(--accent); font-size: 13.5px; cursor: pointer; padding: var(--space-3); margin-top: 2px; }
.nut-rl__nodetail:hover { color: var(--accent-2); }

.nut-meals { display: flex; flex-direction: column; gap: var(--space-3); }
.nut-meal { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); overflow: hidden; position: relative; }
.nut-meal__ribbon { position: absolute; top: 0; right: 0; width: 96px; height: 96px; overflow: hidden; pointer-events: none; }
.nut-meal__ribbon span { position: absolute; display: block; width: 110px; text-align: center; transform: rotate(45deg); right: -38px; top: 7px; background: var(--accent-subtle); color: var(--accent); font-size: 7.5px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; padding: 2.5px 0; line-height: 1; }
.nut-meal__head { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-3) var(--space-4); cursor: pointer; }
.nut-meal__caret { background: none; border: 0; color: var(--muted); font-size: 13px; cursor: pointer; width: 18px; flex: 0 0 auto; }
.nut-meal__title { flex: 1 1 auto; display: flex; align-items: baseline; gap: var(--space-3); min-width: 0; }
.nut-meal__name { font-size: var(--text-title); line-height: var(--lh-title); font-weight: 500; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nut-meal__kcal { font-size: var(--text-body-sm); color: var(--muted); flex: 0 0 auto; }
.nut-meal__menu { background: none; border: 0; color: var(--muted); font-size: 20px; line-height: 1; cursor: pointer; padding: 2px 6px; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; margin: calc(-1 * var(--space-2)) 0; }
.nut-meal__menu:hover { color: var(--ink); }
.nut-meal__body { border-top: 1px solid var(--line-2); }
.nut-meal__add {
  display: block; width: 100%; text-align: left; background: none; border: 0;
  color: var(--accent); font-size: 14px; padding: var(--space-3) var(--space-4); cursor: pointer;
}
.nut-meal__add:hover { background: var(--accent-light); }

/* NUTRITION-DEFAULT-MEALS-KEEP-EMPTY-HERO — a standard meal with nothing in it
   yet is ONE line, never an opened empty section (that reads as a blank form).
   Same card chrome as a real meal so the list keeps one rhythm; softer border
   and a muted trailing hint so it still reads as waiting, not as logged. */
.nut-mealslot {
  display: flex; align-items: center; gap: var(--space-2); width: 100%;
  min-height: 48px; padding: var(--space-3) var(--space-4); text-align: left;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--radius-lg); cursor: pointer; font-family: inherit;
  -webkit-tap-highlight-color: transparent;
}
.nut-mealslot:hover { border-color: var(--line); }
.nut-mealslot__cat { flex: 0 0 auto; display: inline-flex; color: var(--muted); }
.nut-mealslot__main { flex: 1 1 auto; display: flex; align-items: baseline; gap: var(--space-2); min-width: 0; }
.nut-mealslot__name {
  flex: 0 0 auto; font-size: var(--text-title); line-height: var(--lh-title);
  font-weight: 500; color: var(--ink);
}
.nut-mealslot__hint {
  font-size: var(--text-body-sm); color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nut-mealslot__hint::before { content: '·'; margin-right: var(--space-2); }
.nut-mealslot__chev { flex: 0 0 auto; display: inline-flex; color: var(--muted); }
/* On the empty day the slots sit between the hero copy and the quick-start
   group, so they carry their own vertical rhythm. */
.nut-meals--slots { margin: var(--space-4) 0; }

/* Entry rows — v0.9.209 (Phase 4): each row swipes left to reveal an action tray.
   The wrap clips the tray; the row is the solid-background foreground that slides. */
/* NUTRITION-M3-WAVE-4 — entry rows within a meal card are separated by an
   INSET var(--line) hairline (HIG: divider begins at the row content, not
   edge-to-edge). Drawn as a ::before so it insets from the card edges; z-index
   lifts it above the row's opaque surface background. */
.nut-entry-wrap { position: relative; overflow: hidden; }
.nut-entry-wrap:not(:first-child)::before {
  content: ''; position: absolute; top: 0; left: var(--space-4); right: var(--space-4);
  height: 1px; background: var(--line); z-index: 1; pointer-events: none;
}
.nut-entry-actions { position: absolute; inset: 0 0 0 auto; display: flex; align-items: stretch; }
.nut-entry-act {
  width: 64px; min-height: 44px; flex: 0 0 auto; border: 0; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  font-size: 11px; font-family: inherit; line-height: 1; -webkit-tap-highlight-color: transparent;
}
.nut-entry-act .nut-ic { width: 18px; height: 18px; }
.nut-entry-act--edit { background: var(--line-2); color: var(--ink); }
.nut-entry-act--dup  { background: var(--line); color: var(--ink); }
.nut-entry-act--del  { background: var(--danger); color: var(--on-danger); }

.nut-entry {
  position: relative; display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4); background: var(--surface);
  touch-action: pan-y; will-change: transform; transition: transform var(--dur-base) var(--ease-standard);
}
/* Dim the CONTENT, not the row — the row must stay opaque so the swipe tray
   behind it never bleeds through a semi-transparent foreground. */
.nut-entry.is-uneaten > * { opacity: 0.55; }
/* NUTRITION-RECIPE-LOG-AS-SINGLE-AGGREGATE-ROW — a recipe logged as one
   aggregate row gets a subtle accent rail so it reads as "a whole recipe",
   distinct from the loose single-ingredient rows around it. */
.nut-entry--recipe { border-left: 3px solid var(--accent); padding-left: calc(var(--space-4) - 3px); }
.nut-check {
  flex: 0 0 auto; width: 24px; height: 24px; border-radius: 50%;
  border: 1.5px solid var(--line); background: none; color: var(--on-accent);
  font-size: 14px; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center;
}
.nut-check.on { background: var(--accent); border-color: var(--accent); }
.nut-entry__mid { flex: 1 1 auto; min-width: 0; cursor: pointer; }
.nut-entry__name { font-size: var(--text-body); line-height: var(--lh-body); color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nut-entry__meta { font-size: var(--text-label); color: var(--muted); margin-top: 2px; }
.nut-entry__kcal { flex: 0 0 auto; font-size: var(--text-body); color: var(--ink-2); font-variant-numeric: tabular-nums; }

/* Drag handle — v0.9.210 (Phase 4 Ship B). The handle alone gets touch-action:none
   so the drag never fights the page's vertical scroll (which the rest of the row
   leaves to the browser via pan-y). Press the grip → drag up/down to reorder within
   a meal, or across into another meal. */
.nut-entry__grip {
  flex: 0 0 auto; width: 28px; height: 28px; padding: 0; margin-left: -4px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; cursor: grab; color: var(--muted);
  touch-action: none; -webkit-tap-highlight-color: transparent;
}
.nut-entry__grip:active { cursor: grabbing; }
.nut-entry__grip svg { width: 18px; height: 18px; display: block; }

/* The lifted, finger-following copy of the row during a drag. */
.nut-entry--ghost {
  position: fixed; z-index: 300; pointer-events: none; box-sizing: border-box;
  border-radius: var(--radius-md); background: var(--surface);
  box-shadow: var(--elev-2); transform: scale(1.02);
  transition: none; opacity: .96;
}
/* The vacated slot left behind, shown as a dashed drop target that travels with
   the pointer (it IS the drop indicator — single, unambiguous). */
.nut-entry-wrap.is-dragging { background: var(--accent-light); outline: 2px dashed var(--accent); outline-offset: -2px; }
.nut-entry-wrap.is-dragging .nut-entry,
.nut-entry-wrap.is-dragging .nut-entry-actions { opacity: 0; }

/* NUTRITION-REMOVE-QUICK-LOG-CARDS-AND-DRAG-REORDER-MEALS — the same grip one
   level up, on the meal head. Quieter than the entry grip (it sits on a header
   that already carries a caret and a ⋯ menu) but the identical hit size, so the
   gesture stays reachable on a 390px phone. */
.nut-meal__grip {
  flex: 0 0 auto; width: 28px; height: 28px; padding: 0; margin-left: 2px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; cursor: grab; color: var(--muted); opacity: .55;
  touch-action: none; -webkit-tap-highlight-color: transparent;
}
.nut-meal__grip:hover { opacity: 1; }
.nut-meal__grip:active { cursor: grabbing; opacity: 1; }
.nut-meal__grip svg { width: 18px; height: 18px; display: block; }

.nut-meal--ghost {
  position: fixed; z-index: 300; pointer-events: none; box-sizing: border-box;
  border-radius: var(--radius-lg); background: var(--surface);
  box-shadow: var(--elev-2); transform: scale(1.02);
  transition: none; opacity: .96;
}
/* The vacated card: a dashed drop target that travels to whatever slot the
   pointer reaches, collapsed to the head's height so the list closes up. */
.nut-meal.is-dragging {
  background: var(--accent-light); outline: 2px dashed var(--accent); outline-offset: -2px;
}
.nut-meal.is-dragging > * { opacity: 0; }
.nut-meal.is-dragging .nut-meal__body { display: none; }

.nut-addmeal {
  display: block; width: 100%; margin-top: var(--space-4); padding: var(--space-3);
  background: none; border: 1px dashed var(--line); border-radius: var(--radius-md);
  color: var(--accent); font-size: 14px; cursor: pointer;
}
.nut-addmeal:hover { border-color: var(--accent); background: var(--accent-light); }

/* Add-food sheet body */
.nut-sheet-body { padding: var(--space-4) var(--space-5) var(--space-6); overflow-y: auto; }
/* NUTRITION-MEAL-KEBAB-BULK-CHANGE-TIME — bulk time-picker sheet */
.bulk-time__hint { margin-bottom: var(--space-3); color: var(--muted); }
.bulk-time__input {
  width: 100%;
  padding: var(--space-3);
  font-size: 1.5em;
  text-align: center;
  background: var(--surface-2, var(--line));
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  margin-bottom: var(--space-3);
}
.bulk-time__save { width: 100%; margin-bottom: var(--space-2); }

/* NUTRITION-MEAL-RECURRING-DELETE-INSTANCE-OR-SERIES — 3-option delete sheet */
.recurring-delete__hint { margin-bottom: var(--space-3); }
.recurring-delete__btn {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
  padding: var(--space-3);
  margin-bottom: var(--space-2);
  text-align: left;
}
.recurring-delete__btn-sub {
  font-size: 0.85em;
  color: var(--muted);
  margin-top: var(--space-1);
}
.recurring-delete__cancel { margin-top: var(--space-3); }
.nut-search {
  width: 100%; min-height: 44px; padding: var(--space-3); font-size: 16px; color: var(--ink);
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-md); box-sizing: border-box;
  transition: border-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard);
}
.nut-search:focus { outline: none; border-color: var(--accent); box-shadow: var(--ring-focus); }
/* NUTRITION-ADD-FOOD-ADAPTIVE-TOP3-MEER — scan-barcode affordance parked inside
   the search field; the input reserves the trailing space so text never runs
   under it. Absent (not disabled) on camera-less devices. */
.nut-search-wrap { position: relative; }
.nut-search-wrap .nut-search { padding-right: 48px; }
.nut-search__scan {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; padding: 0; cursor: pointer;
  background: transparent; border: 0; border-radius: var(--radius-md); color: var(--accent);
}
.nut-search__scan:hover { background: var(--line-2); }
.nut-search__scan .nut-ic svg { width: 22px; height: 22px; }

/* NUTRITION-LOG-INLINE-QUICK-LOG-WITH-MEAL-PICKER — inline entry-point on the
   Log page between the consistency band and the water cell. Reuses the add-food
   sheet's search bar + alt-row buttons; visually identical to flow A. */
.food-detail__meal-picker {
  display: flex; flex-direction: column; gap: var(--space-2); margin: var(--space-3) 0;
}
.food-detail__meal-picker label { font-size: 13px; color: var(--muted); }
.food-detail__meal-select {
  width: 100%; padding: var(--space-3); font-size: 16px; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm); box-sizing: border-box;
}
.food-detail__meal-select:focus { outline: none; border-color: var(--accent); }
/* NUTRITION-MY-RECIPES-LIST-IN-LOG-FOOD-SHEET — recipe list above meal-picker */
.log-food__recipes { display: flex; flex-direction: column; gap: var(--space-2); margin: var(--space-3) 0; max-height: 40vh; overflow-y: auto; }
.log-food__section-label { font-size: 13px; color: var(--muted); }
.log-food__recipe-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.log-food__recipe {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--surface); border: 2px solid var(--line); border-radius: var(--radius-sm);
  cursor: pointer; box-sizing: border-box;
}
.log-food__recipe:active { opacity: .7; }
.log-food__recipe[aria-checked="true"] { border-color: var(--accent); }
.log-food__recipe-thumb { flex: 0 0 auto; width: 48px; height: 48px; border-radius: var(--radius-sm); overflow: hidden; }
.log-food__recipe-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.log-food__recipe-thumb--empty { width: 100%; height: 100%; background: var(--line-2); }
.log-food__recipe-body { flex: 1 1 auto; min-width: 0; }
.log-food__recipe-name { font-size: 14px; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.log-food__recipe-macros { font-size: 12px; color: var(--muted); }
.log-food__recipe-check {
  flex: 0 0 auto; width: 24px; height: 24px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: transparent; color: transparent;
}
.log-food__recipe[aria-checked="true"] .log-food__recipe-check { background: var(--accent); color: var(--on-accent); }
.log-food__recipe-check .nut-ic svg { width: 16px; height: 16px; }
.log-food__recipes--empty { align-items: center; text-align: center; padding: var(--space-4) 0; }
.log-food__empty-text { color: var(--ink); font-style: italic; margin: 0 0 var(--space-2); }
.log-food__empty-help { color: var(--muted); font-size: 13px; margin: 0; }
/* Inline new-meal slot — input + check/× micro-icons (progressive disclosure). */
.food-detail__new-meal {
  display: flex; align-items: center; gap: var(--space-2);
}
.food-detail__new-meal-input {
  flex: 1 1 auto; min-width: 0; padding: var(--space-3); font-size: 16px; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm); box-sizing: border-box;
}
.food-detail__new-meal-input:focus { outline: none; border-color: var(--accent); }
.food-detail__meal-icon-btn {
  flex: 0 0 auto; width: 36px; height: 36px; border: 0; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-2, var(--line-2)); cursor: pointer; padding: 0;
}
.food-detail__meal-icon-btn .nut-ic svg { width: 18px; height: 18px; }
.food-detail__meal-icon-btn--confirm { background: var(--accent); color: var(--on-accent); }
.food-detail__meal-icon-btn--cancel { color: var(--muted); }
.food-detail__meal-icon-btn:active { opacity: .7; }
.food-detail__meal-icon-btn:disabled { opacity: .5; cursor: default; }
/* Helper line under the disabled ADD: explains why the primary CTA waits. */
.food-detail__meal-hint {
  font-size: 12px; color: var(--muted); margin: var(--space-1) 0 0; text-align: center;
}
.food-detail__meal-hint:empty, .food-detail__meal-hint[hidden] { display: none; }
/* NUTRITION-MEAL-PICKER-PROGRESSIVE-DISCLOSURE — collapsed until the primary
   CTA is tapped; expanding reveals the rows + chips (select equals save). */
.nut-subpick.is-collapsed { display: none; }
/* NUTRITION-MEAL-PICKER-PRESETS-AND-SUBFLOW-CREATE-REMOVE — sub-flow picker:
   existing-meal rows + 4 preset chips, no select / no free-form / no create. */
.nut-subpick__meals { display: flex; flex-direction: column; gap: var(--space-2); }
.nut-subpick__meal {
  width: 100%; text-align: left; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--ink); font-size: 15px; font-weight: 500;
  padding: 12px 14px; cursor: pointer; font-family: inherit;
}
.nut-subpick__meal:hover { border-color: var(--accent); color: var(--accent); }
.nut-subpick__meal.is-on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.nut-subpick__or { text-align: center; color: var(--muted); font-size: 12px; margin: var(--space-1) 0; }
.nut-subpick__presets { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.nut-subpick__chip {
  flex: 1 1 0; min-width: 0; text-align: center; background: var(--bg);
  border: 1px solid var(--line); border-radius: var(--radius-pill); color: var(--ink);
  font-size: 14px; font-weight: 500; padding: 10px 12px; cursor: pointer; font-family: inherit;
}
.nut-subpick__chip:hover { border-color: var(--accent); color: var(--accent); }
.nut-subpick__chip.is-on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.nut-subpick__chip:disabled { opacity: 0.6; cursor: default; }
.nut-manualbtn { margin-top: var(--space-3); background: none; border: 0; color: var(--accent); font-size: 14px; cursor: pointer; padding: 4px 0; }
.nut-srch-status { color: var(--muted); font-size: 13px; padding: var(--space-3) 0; }
.nut-srch-empty { padding: var(--space-4) 0; }
.nut-srch-empty__hd { color: var(--ink); font-size: var(--text-title); line-height: var(--lh-title); font-weight: 600; }
.nut-srch-empty__sub { color: var(--muted); font-size: var(--text-body-sm); line-height: var(--lh-body-sm); margin-top: var(--space-1); }
/* v0.9.191 — inline search spinner (reuses the existing @keyframes spin). */
.nut-spin {
  display: inline-block; width: 12px; height: 12px; vertical-align: -1px;
  border: 2px solid var(--line); border-top-color: var(--accent);
  border-radius: 50%; animation: spin var(--dur-loop) linear infinite;
}
.nut-results { display: flex; flex-direction: column; }
.nut-result { text-align: left; background: none; border: 0; border-top: 1px solid var(--line-2); padding: var(--space-3) 0; cursor: pointer; }
.nut-result:hover { background: var(--accent-light); }
.nut-result__name { font-size: 14px; color: var(--ink); }
.nut-result__sub { font-size: 12px; color: var(--muted); margin-top: 2px; }

/* Product image thumbnail in search rows (v0.9.231). The --img modifier turns the
   row into a flex line (thumb · text) — scoped to search results so other reusers
   of .nut-result (favorites / copy-from / destination rows) stay block-stacked. */
.nut-result--img { display: flex; align-items: center; gap: var(--space-3); }
.nut-result__body { flex: 1 1 auto; min-width: 0; }
.nut-result--img .nut-result__name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nut-result--img .nut-result__sub  { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nut-thumb {
  flex: 0 0 auto; width: 44px; height: 44px; border-radius: var(--radius-md); overflow: hidden;
  background: var(--bg); border: 1px solid var(--line-2);
  display: flex; align-items: center; justify-content: center; box-sizing: border-box;
}
.nut-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.nut-thumb--ph { background: var(--line-2); }
.nut-thumb--ph .nut-ic { width: 20px; height: 20px; color: var(--muted); }

/* Serving scaler + manual/goal fields */
/* Product hero image in the detail/scaler view (v0.9.231). */
.nut-hero {
  width: 200px; height: 200px; max-width: 100%; border-radius: var(--radius-lg);
  overflow: hidden; background: var(--surface-2, var(--bg)); border: 1px solid var(--line-2);
  margin: 0 auto var(--space-3); display: flex; align-items: center; justify-content: center;
  box-sizing: border-box;
}
.nut-hero img { width: 100%; height: 100%; object-fit: contain; display: block; }
@media (max-width: 360px) { .nut-hero { width: 70vw; height: 70vw; } }
/* NUTRITION-EDIT-SHEET-PRODUCT-AND-USER-PHOTO — compact photo row atop the Edit
   sheet: product photo + the user's own captured photo, same height, side by
   side. One present = that one alone; none = the block isn't rendered at all. */
.nut-photorow { display: flex; gap: var(--space-3); margin: 0 0 var(--space-3); }
.nut-photorow__tile {
  position: relative;
  flex: 0 1 130px; height: 130px; min-width: 0;
  border-radius: var(--radius-lg); overflow: hidden;
  background: var(--surface-2, var(--bg)); border: 1px solid var(--line-2);
  display: flex; align-items: center; justify-content: center; box-sizing: border-box;
}
.nut-photorow__tile img { width: 100%; height: 100%; display: block; }
.nut-photorow__tile--product img { object-fit: contain; }
.nut-photorow__tile--user img { object-fit: cover; }
/* SHARE-MEAL-PHOTO-BRANDED-EXPORT — the share affordance sits ON the user photo
   so it reads as "share THIS", and exists only where a photo does. The dark
   scrim is not an accent surface, so its foreground is a literal white by
   design (the --on-accent rule governs accent backgrounds only). */
.nut-photorow__share {
  position: absolute; right: 6px; bottom: 6px;
  width: 34px; height: 34px; border: 0; border-radius: 50%;
  background: rgba(0, 0, 0, 0.55); color: #fff; padding: 0; cursor: pointer; /* allow-hex: the scrim is theme-independent — it sits on the user's photo, not on an app surface */
  display: flex; align-items: center; justify-content: center;
}
.nut-photorow__share .nut-ic svg { width: 18px; height: 18px; display: block; }
.nut-sharecard__prev {
  border-radius: var(--radius-lg); overflow: hidden; margin: 0 0 var(--space-3);
  background: var(--surface-2, var(--bg)); border: 1px solid var(--line-2);
}
.nut-sharecard__prev img { width: 100%; display: block; }
.nut-sharecard__btnic { display: inline-flex; margin-right: var(--space-2); vertical-align: middle; }
.nut-sharecard__btnic svg { width: 17px; height: 17px; display: block; }
.nut-scale__brand { color: var(--muted); font-size: 13px; margin-bottom: var(--space-3); }
.nut-scale__row { display: flex; gap: var(--space-3); }
.nut-scale__row > * { flex: 1 1 0; min-width: 0; }
.nut-scale__save { width: 100%; margin-top: var(--space-4); }
/* NUTRITION-PORTION-AI-SUGGESTED-MEASURES-AND-ASKAI-CHIP — the bottom action row is
   ADD only again (full width); Ask AI moved up into the measure-chip row as its last
   chip, so it reads as "AI helps with PORTIONS" rather than a second way to add. */
.nut-scale__actions { display: flex; align-items: stretch; gap: var(--space-3); margin-top: var(--space-4); }
.nut-scale__actions .nut-scale__save { flex: 1 1 auto; width: auto; margin-top: 0; }
.nut-goal-seq-note { font-size: var(--text-body-sm); color: var(--muted); background: var(--surface-card); border-radius: var(--radius-md); padding: 8px 10px; margin-top: var(--space-3); line-height: 1.45; }
.nut-field { margin-bottom: var(--space-3); }
.nut-field__lbl { display: block; font-size: var(--text-body-sm); line-height: var(--lh-body-sm); color: var(--ink-2); margin-bottom: var(--space-1); }
.nut-field__in {
  width: 100%; min-height: 44px; padding: var(--space-3); font-size: 16px; color: var(--ink);
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-md); box-sizing: border-box;
  transition: border-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard);
}
/* Focus via box-shadow ring (no border-width change) so the field never shifts
   layout — matters for the sticky scaler thumb-zone. */
.nut-field__in:focus { outline: none; border-color: var(--accent); box-shadow: var(--ring-focus); }
/* GOAL-SHEET-KCAL-FROM-MACROS-CALC-BUTTON — calorie field + a small ghost helper
   button that computes kcal from the macro fields (Atwater 4/4/9). */
.nut-goal-calcrow { display: flex; align-items: flex-end; gap: 8px; margin-bottom: var(--space-3); }
.nut-goal-calcrow .nut-field { flex: 1; min-width: 0; margin-bottom: 0; }
.nut-goal-calcbtn {
  flex: 0 0 auto; min-width: 44px; min-height: 44px;
  display: flex; align-items: center; justify-content: center;
  background: transparent; color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  cursor: pointer; font-family: inherit;
}
.nut-goal-calcbtn:hover { border-color: var(--accent); color: var(--accent); }
.nut-goal-calcbtn[disabled] { opacity: 0.5; cursor: default; }
.nut-field--flash { animation: nutFieldFlash var(--dur-slow) var(--ease-standard); }
@keyframes nutFieldFlash {
  0% { box-shadow: 0 0 0 0 var(--accent); border-color: var(--accent); }
  40% { box-shadow: var(--ring-focus); border-color: var(--accent); }
  100% { box-shadow: 0 0 0 0 transparent; }
}

/* Add-a-meal dialog (v0.9.181) — small centered .ui-dlg card: a linear list of
   standard meal names (day rhythm) + a compact custom field. */
.nut-mealopts { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-4); }
.nut-mealopt {
  width: 100%; text-align: left; background: var(--bg); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--ink); font-size: 15px; font-weight: 500;
  padding: 12px 14px; cursor: pointer; font-family: inherit;
}
.nut-mealopt:hover { border-color: var(--accent); color: var(--accent); }
/* Select-then-add: a tapped preset stays visibly on until Add commits it. */
.nut-mealopt.is-on {
  border-color: var(--accent); background: var(--accent); color: var(--on-accent);
}
.nut-mealopt.is-on:hover { color: var(--on-accent); }
.nut-mealor { text-align: center; color: var(--muted); font-size: 12px; margin: 0 0 var(--space-3); }
.nut-mealcustom { align-items: stretch; }
.nut-mealcustom__in { flex: 1 1 auto; margin: 0; }
.nut-mealcustom__add { flex: 0 0 auto; padding-left: var(--space-5); padding-right: var(--space-5); }

/* Copy-from-yesterday suggestions inside the add-combo dialog: same row idiom as
   the presets, with a quiet second line naming what is in the combo. */
.nut-mealsug { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-4); }
.nut-mealsug .nut-mealor { margin: 0 0 var(--space-1); }
.nut-mealsug__row { display: flex; flex-direction: column; gap: 2px; }
.nut-mealsug__name { font-weight: 600; }
.nut-mealsug__sub {
  color: var(--muted); font-size: 12px; font-weight: 400;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nut-mealsug__row:hover .nut-mealsug__sub { color: var(--muted); }
.nut-mealsug__all { text-align: center; font-weight: 600; }
.nut-mealsug__row:disabled, .nut-mealsug__all:disabled { opacity: 0.55; cursor: default; }

/* In-flight create UX: all create controls disable together; the tapped one
   shows a subtle spinner; a slow-network hint appears (non-blocking) after ~4s. */
.nut-mealopt:disabled, .nut-savedrow:disabled, .nut-mealcustom__add:disabled,
.nut-mealcustom__in:disabled { opacity: 0.55; cursor: default; }
.nut-mealopt.is-loading, .nut-savedrow.is-loading, .nut-mealcustom__add.is-loading {
  position: relative; color: transparent;
}
.nut-mealopt.is-loading::after, .nut-savedrow.is-loading::after,
.nut-mealcustom__add.is-loading::after {
  content: ''; position: absolute; top: 50%; left: 50%;
  width: 16px; height: 16px; margin: -8px 0 0 -8px;
  border: 2px solid var(--line); border-top-color: var(--accent);
  border-radius: 50%; animation: nut-mealspin var(--dur-loop) linear infinite;
}
.nut-mealcustom__add.is-loading::after { border-top-color: var(--on-accent); }
@keyframes nut-mealspin { to { transform: rotate(360deg); } }
.nut-mealhint {
  min-height: 16px; margin: 0 0 var(--space-3); text-align: center;
  font-size: 12px; color: var(--muted); opacity: 0;
  transition: opacity var(--dur-base) var(--ease-standard);
}
.nut-mealhint.is-on { opacity: 1; }

.nut-chip {
  display: inline-block; margin-bottom: var(--space-3); padding: 6px 12px;
  background: var(--accent-light); border: 1px solid var(--line); border-radius: var(--radius-pill);
  color: var(--accent); font-size: 13px; cursor: pointer;
}
.nut-chip:hover { border-color: var(--accent); }
.nut-copyday { width: 100%; margin-top: var(--space-3); }
.nut-del { width: 100%; margin-top: var(--space-3); color: var(--danger); }

.nut-mpre { display: flex; gap: var(--space-2); margin: var(--space-2) 0; }
.nut-mpre__cell { flex: 1 1 0; text-align: center; background: var(--bg); border-radius: var(--radius-sm); padding: var(--space-2) 0; }
.nut-mpre__num { display: block; font-size: 16px; font-weight: 600; color: var(--ink); }
.nut-mpre__lbl { display: block; font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; margin-top: 2px; }

/* Ship D (rec #6) — portion picker: big readout + −/+ stepper + tap-presets in
   place of the gram/servings keyboard; the number pad is one tap deeper, and
   optional hand portions for by-sight logging. */
.nut-port { margin-bottom: var(--space-3); }
.nut-port__stepper { display: flex; align-items: stretch; gap: var(--space-3); margin-bottom: var(--space-2); }
.nut-port__btn {
  position: relative; /* anchors the velocity badge during hold-to-accelerate */
  flex: 0 0 64px; min-width: 64px; min-height: 64px; height: 64px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink); font-size: 26px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center; font-family: inherit; touch-action: none;
  -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
}
/* CHANGE 2 — hold-to-accelerate velocity badge */
.nut-port__velbadge {
  position: absolute; top: -20px; left: 50%; transform: translateX(-50%);
  font-size: 12px; color: var(--text-muted, var(--muted)); white-space: nowrap;
  opacity: 0; transition: opacity var(--dur-fast) var(--ease-standard); pointer-events: none;
}
.nut-port__velbadge.is-on { opacity: 1; }
/* CHANGE 1 — tap-to-edit gram field + non-editable unit suffix */
.nut-port__display--edit { cursor: text; }
.nut-port__gram {
  width: 100%; min-width: 0; max-width: 5ch; text-align: right; border: 0; background: none; padding: 0;
  font-size: 24px; font-weight: 700; color: var(--ink); font-family: inherit; line-height: 1.1;
  -moz-appearance: textfield;
}
.nut-port__gram::-webkit-outer-spin-button, .nut-port__gram::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.nut-port__gram:focus { outline: none; }
.nut-port__unit { font-size: 18px; font-weight: 600; color: var(--muted); }
/* CHANGE 3 — scrub affordance hint + CHANGE 6 history chip */
.nut-port__hintrow { display: flex; align-items: center; justify-content: center; gap: var(--space-3); min-height: 26px; margin-bottom: var(--space-2); }
.nut-port__scrubhint { font-size: 14px; letter-spacing: 0.3em; color: var(--text-muted, var(--muted)); transition: opacity var(--dur-slow) var(--ease-standard); }
.nut-port__scrubhint.is-fade { opacity: 0; }
.nut-port__histchip {
  background: none; border: 1px solid var(--line); border-radius: var(--radius-pill); padding: 4px 12px;
  color: var(--text-muted, var(--muted)); font-size: 13px; cursor: pointer; font-family: inherit;
}
.nut-port__histchip:hover { border-color: var(--accent); color: var(--accent); }
/* CHANGE 4 — sticky bottom thumb-zone */
.nut-scale--sticky.mo-body { display: flex; flex-direction: column; }
.nut-scale__sticky {
  position: sticky; bottom: 0; z-index: 1; margin-top: auto;
  background: var(--bg); border-top: 1px solid var(--line);
  padding: var(--space-3) 0 0; margin-left: calc(-1 * var(--space-5)); margin-right: calc(-1 * var(--space-5));
  padding-left: var(--space-5); padding-right: var(--space-5);
}
.nut-port__btn:hover { border-color: var(--accent); color: var(--accent); }
.nut-port__btn:disabled { opacity: 0.35; cursor: default; }
.nut-port__btn:disabled:hover { border-color: var(--line); color: var(--ink); }
.nut-port__display {
  flex: 1 1 auto; min-width: 0; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); display: flex; flex-direction: column; align-items: center;
  justify-content: center; padding: 8px 4px;
  cursor: pointer; transition: border-color var(--dur-fast) var(--ease-standard); -webkit-tap-highlight-color: transparent;
}
.nut-port__display:hover, .nut-port__display:focus-visible { border-color: var(--accent); outline: none; }
.nut-port__numrow { display: flex; align-items: center; gap: 6px; }
/* `display: flex` outranks the hidden attribute — unit mode needs it gone. */
.nut-port__numrow[hidden] { display: none; }
.nut-port__unitcount { font-size: 24px; font-weight: 700; color: var(--ink); line-height: 1.1; cursor: pointer; }
.nut-port__num { font-size: 24px; font-weight: 700; color: var(--ink); line-height: 1.1; }
.nut-port__edithint { display: inline-flex; color: var(--muted); opacity: 0.7; }
.nut-port__edithint svg { width: 13px; height: 13px; }
.nut-port__sub { font-size: 12px; color: var(--muted); margin-top: 3px; }
.nut-port__presets { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: var(--space-3); }
.nut-port__chip {
  flex: 1 1 64px; min-width: 0; min-height: 46px; padding: 9px 4px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink-2); font-size: 14px; cursor: pointer; font-family: inherit; text-align: center;
  overflow: hidden;
}
.nut-port__chip:hover { border-color: var(--accent); }
.nut-port__chip.is-active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.nut-port__chip.is-active .nut-port__chipsub { color: var(--on-accent); opacity: 0.85; }
.nut-port__chip:active { transform: scale(0.95); transition: transform var(--dur-fast) var(--ease-standard); }
/* INTELLIGENCE-POLISH CHANGE 1/2 — two-line preset chip (multiplier + computed grams) */
.nut-port__chiptop { display: block; font-size: 16px; font-weight: 600; line-height: 1.1; overflow-wrap: anywhere; word-break: break-word; }
/* NUTRITION-PORTION-SHEET-ASKAI-LAYOUT-AND-REPEAT — long single-word measure keys get a
   wider basis so the preset row wraps to a clean 2×2 (whole word, one line) instead of
   overflowing a cramped 1×4; short multiplier/gram chips keep the 64px basis (1×4). */
.nut-port__chip--wide { flex-basis: 140px; }
/* NUTRITION-PORTION-AI-SUGGESTED-MEASURES-AND-ASKAI-CHIP — Ask AI rides in the same
   chip row as the last item: accent outline so it reads as an offer rather than a
   value, filling on hover with --on-accent text (contrast protocol, lint rule g). */
.nut-port__chip--ai {
  flex: 1 1 120px; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  background: transparent; border-color: var(--accent); color: var(--accent);
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.nut-port__chip--ai .nut-port__chiptop { font-size: 14px; }
.nut-port__chip--ai .nut-ic svg { width: 16px; height: 16px; }
.nut-port__chip--ai:hover { background: var(--accent); color: var(--on-accent); }
.nut-port__chipsub { display: block; font-size: 11px; color: var(--text-muted, var(--muted)); margin-top: 2px; line-height: 1.1; }
/* CHANGE 3 — one-tap shortcut accent flash (preset chip) */
.nut-port__chip.is-flash {
  background: var(--accent); color: var(--on-accent); border-color: var(--accent);
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
/* CHANGE 9 — portion-definition sub-label under the gram readout */
.nut-port__portiedef { display: block; font-size: 11px; color: var(--text-muted, var(--muted)); margin-top: 2px; }
/* CHANGE 8 — reset-to-default pill */
.nut-port__reset {
  display: inline-flex; align-items: center; gap: 5px; background: none;
  border: 1px solid var(--line); border-radius: var(--radius-pill); padding: 4px 10px;
  color: var(--text-muted, var(--muted)); font-size: 13px; cursor: pointer; font-family: inherit;
}
.nut-port__reset:hover { border-color: var(--accent); color: var(--accent); }
/* Same trap as .nut-port__numrow[hidden]: the author display:inline-flex above
   beats the UA [hidden] rule, so resetPill.hidden=true left the pill on screen. */
.nut-port__reset[hidden] { display: none; }
.nut-port__reset .nut-ic svg { width: 14px; height: 14px; }
/* CHANGE 5 — live daily kcal goal micro-bar */
.nut-port__goal { display: flex; align-items: center; gap: var(--space-2); margin: 0 0 var(--space-3); }
.nut-port__goal-lbl { font-size: 11px; color: var(--text-muted, var(--muted)); white-space: nowrap; }
.nut-port__goal-track { flex: 1 1 auto; height: 4px; border-radius: var(--radius-sm); background: var(--line); overflow: hidden; }
.nut-port__goal-fill { height: 100%; background: var(--accent); border-radius: var(--radius-sm); transition: width var(--dur-fast) var(--ease-standard); }
.nut-port__goal-num { font-size: 11px; color: var(--text-muted, var(--muted)); white-space: nowrap; font-variant-numeric: tabular-nums; }
/* CHANGE 4 — macro values fade-through on each gram change (perceptible, calm) */
@keyframes nutMpreFade { 0% { opacity: 0.25; } 100% { opacity: 1; } }
.nut-scale__preview .nut-mpre__num { animation: nutMpreFade var(--dur-fast) var(--ease-standard); }
/* CHANGE 7 — brief confirm pulse on TOEVOEGEN */
@keyframes nutSavePulse { 0% { transform: scale(1); } 50% { transform: scale(1.04); } 100% { transform: scale(1); } }
.nut-scale__save.is-pulse { animation: nutSavePulse var(--dur-fast) var(--ease-standard); }
/* CHANGE 6 — vitamines disclosure accordion */
.nut-vitacc { border: 1px solid var(--line); border-radius: var(--radius-sm); margin-bottom: var(--space-3); overflow: hidden; }
.nut-vitacc__head {
  display: flex; align-items: center; justify-content: space-between; width: 100%;
  min-height: 48px; padding: 0 var(--space-3); background: var(--surface); border: 0;
  color: var(--ink); font-size: 14px; font-family: inherit; cursor: pointer;
}
.nut-vitacc__lbl { font-weight: 600; }
.nut-vitacc__chev { color: var(--muted); transition: transform var(--dur-fast) var(--ease-standard); }
.nut-vitacc__chev svg { width: 18px; height: 18px; }
.nut-vitacc.is-open .nut-vitacc__chev { transform: rotate(180deg); }
.nut-vitacc__body { padding: 0 var(--space-3) var(--space-3); }
.nut-port__more { display: flex; justify-content: space-between; align-items: center; gap: var(--space-3); }
.nut-port__more--solo { justify-content: flex-end; }
.nut-port__link {
  background: none; border: 0; color: var(--accent); font-size: 13px; cursor: pointer; padding: 6px 0;
  font-family: inherit; display: inline-flex; align-items: center; gap: 5px;
}
.nut-port__link .nut-ic svg { width: 15px; height: 15px; }
.nut-port__exact { margin-top: var(--space-3); }
.nut-port__askprompt { margin: 0 0 var(--space-2); font-size: 13px; font-weight: 600; color: var(--ink); text-align: center; }

/* Goal mode segmented control */
.nut-seg { display: flex; gap: 0; border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden; margin-bottom: var(--space-4); }
.nut-seg__btn { flex: 1 1 0; background: none; border: 0; border-left: 1px solid var(--line); color: var(--ink-2); font-size: 14px; padding: var(--space-3) 0; cursor: pointer; }
.nut-seg__btn:first-child { border-left: 0; }
.nut-seg__btn.on { background: var(--accent); color: var(--on-accent); box-shadow: var(--glow-soft); }

/* Action sheet rows (meal menu) */
.nut-act { display: block; width: 100%; text-align: left; background: none; border: 0; border-top: 1px solid var(--line-2); color: var(--ink); font-size: 15px; padding: var(--space-4) 0; cursor: pointer; }
.nut-act:first-child { border-top: 0; }
.nut-act--danger { color: var(--danger); }

/* NUTRITION-PARTNER-MEAL-PROPOSE-SENDER-FLOW — sender surface. The marker is a
   glyph inside the meal head, never a row of its own; the picker reuses .nut-act
   so a friend row is the same tap target as every other sheet action. */
.nut-meal__prop { display: inline-flex; align-items: center; flex: 0 0 auto; margin-right: 2px; }
.nut-meal__prop .nut-ic,
.nut-meal__prop .nut-ic svg { width: 14px; height: 14px; }
.nut-meal__prop--pending  { color: var(--accent); }
.nut-meal__prop--accepted { color: var(--ok); }
.nut-meal__prop--declined { color: var(--danger); }
.nut-meal__prop--expired  { color: var(--muted); }
.nut-prop__summary { margin: 0 0 var(--space-2); color: var(--ink); font-size: 15px; line-height: 1.4; }
.nut-prop__consent { margin: 0 0 var(--space-4); color: var(--muted); font-size: 13px; line-height: 1.4; }
.nut-prop__status  { margin: 0; color: var(--muted); font-size: 14px; }
.nut-prop__err     { margin: 0 0 var(--space-3); color: var(--danger); font-size: 14px; line-height: 1.4; }
.nut-prop__friend  { min-height: 48px; }

/* NUTRITION-PARTNER-MEAL-ACCEPT-RECEIVER-FLOW — the recipient side.
   The banner is an invitation, not an alert: accent-outlined rather than
   filled, so a waiting proposal reads as something offered and never competes
   with the day's own numbers for attention. */
.nut-mp-banner {
  display: flex; flex-direction: column; gap: var(--space-2);
  margin: 0 0 var(--space-3);
}
.nut-mp-banner__row {
  display: flex; align-items: stretch; gap: 0;
  background: var(--surface);
  border: 1px solid var(--accent);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.nut-mp-banner__open {
  flex: 1 1 auto; min-height: 56px;
  display: flex; align-items: center; gap: var(--space-2);
  padding: 10px 12px; text-align: left;
  background: none; border: 0; cursor: pointer;
  color: var(--ink); font: inherit;
}
.nut-mp-banner__open .nut-ic,
.nut-mp-banner__open .nut-ic svg { width: 20px; height: 20px; flex: 0 0 auto; }
.nut-mp-banner__open .nut-ic { color: var(--accent); }
.nut-mp-banner__txt  { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.nut-mp-banner__lead {
  font-size: 15px; font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nut-mp-banner__meta { font-size: 13px; color: var(--muted); }
.nut-mp-banner__hide {
  flex: 0 0 auto; width: 48px; min-height: 48px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; border-left: 1px solid var(--line);
  color: var(--muted); cursor: pointer;
}
.nut-mp-banner__hide .nut-ic,
.nut-mp-banner__hide .nut-ic svg { width: 16px; height: 16px; }

/* The accept sheet. */
.nut-mp__from { margin: 0 0 var(--space-3); color: var(--ink); font-size: 15px; line-height: 1.4; }

/* One-line portion strip (fz-lc-strip idiom): label + text chips, never icons —
   a measure is a number, and drawing it makes it read as a different kind of
   thing than the quantity on the row below it. */
.nut-mp__strip {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 0 0 var(--space-3); padding: 8px 10px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.nut-mp__striplbl {
  font-size: 11px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em; margin-right: 2px;
}
.nut-mp__chip {
  min-width: 44px; min-height: 36px; padding: 0 12px;
  background: transparent; color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  font: inherit; font-weight: 700; font-feature-settings: "tnum";
  cursor: pointer;
}
.nut-mp__chip.is-on {
  background: var(--accent); color: var(--on-accent); border-color: var(--accent);
}

/* NUTRITION-PORTION-THUMBS-FEEDBACK-PERSONAL — one strip under the measure chips.
   The thumbs are CONTROLS and live below the row; the chips themselves stay
   text-only. Modelled on .nut-mp__strip above so the sheet keeps one idiom. */
.nut-port__fb {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 0 0 var(--space-3); padding: 8px 10px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
/* Same trap as .nut-port__numrow[hidden]: this author `display: flex` beats the
   UA [hidden] rule, so the thumbs strip stood under the bare multiplier chips
   even though the JS had never revealed it — the strip must only appear once
   AI-authored portions are actually on screen. */
.nut-port__fb[hidden] { display: none; }
.nut-port__fblbl {
  font-size: 11px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em; margin-right: 2px;
}
.nut-port__fbbtn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 36px; padding: 0 12px;
  background: transparent; color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  font: inherit; cursor: pointer;
}
.nut-port__fbbtn:disabled { opacity: .45; cursor: default; }
.nut-port__fbpick {
  flex: 1 0 100%; display: flex; flex-wrap: wrap; gap: 8px;
}
.nut-port__fbpick[hidden] { display: none; }
.nut-port__fbrm {
  min-height: 36px; padding: 0 12px;
  background: transparent; color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  font: inherit; cursor: pointer;
}
.nut-port__fbrm.is-on {
  background: var(--accent); color: var(--on-accent); border-color: var(--accent);
}
.nut-port__fbnote {
  flex: 1 0 100%; min-height: 60px; padding: 8px 10px; resize: vertical;
  background: var(--bg); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  font: inherit;
}
.nut-port__fbsend {
  min-width: 44px; min-height: 36px; padding: 0 14px;
  background: var(--accent); color: var(--on-accent);
  border: 1px solid var(--accent); border-radius: var(--radius-sm);
  font: inherit; font-weight: 700; cursor: pointer;
}
.nut-port__fbsend:disabled { opacity: .45; cursor: default; }
/* NUTRITION-RAGE-CLICK-FRICTION — the in-flight guards existed but were invisible, so
   a slow AI/server response read as a dead button and the user hammered it. Same
   spinner idiom as the meal-create controls (.nut-mealopt.is-loading). */
.nut-pest__send:disabled { opacity: .55; cursor: default; }
.nut-port__fbsend.is-loading, .nut-pest__send.is-loading {
  position: relative; color: transparent;
}
.nut-port__fbsend.is-loading::after, .nut-pest__send.is-loading::after {
  content: ''; position: absolute; top: 50%; left: 50%;
  width: 16px; height: 16px; margin: -8px 0 0 -8px;
  border: 2px solid var(--line); border-top-color: var(--on-accent);
  border-radius: 50%; animation: nut-mealspin var(--dur-loop) linear infinite;
}
/* NUTRITION-PORTION-REGENERATE-FROM-CORRECTION — reads as a text link, not a third
   button: it REPLACES the "Thanks" label in this strip rather than adding a control. */
.nut-port__fbregen {
  min-height: 36px; padding: 0 2px;
  background: transparent; color: var(--accent);
  border: 0; border-radius: var(--radius-sm);
  font: inherit; font-weight: 700; text-decoration: underline;
  cursor: pointer;
}
.nut-port__fbregen:disabled { opacity: .55; cursor: default; text-decoration: none; }

/* Ghost lines — dimmed BECAUSE they are not in the log yet. The dimming is the
   message (same idiom as the workout player's prefill), so it stays a step
   quieter than ordinary secondary text and the dashed rule says "outline". */
.nut-mp__lines {
  border: 1px dashed var(--line); border-radius: var(--radius-sm);
  padding: 4px 10px; margin: 0 0 var(--space-2);
}
.nut-mp__line {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 0; border-bottom: 1px solid var(--line-2);
}
.nut-mp__line:last-child { border-bottom: 0; }
.nut-mp__line--ghost { opacity: .62; }
.nut-mp__lname { flex: 1 1 auto; min-width: 0; color: var(--ink); font-size: 14px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nut-mp__lqty  { flex: 0 0 auto; color: var(--muted); font-size: 13px; font-feature-settings: "tnum"; }
.nut-mp__lkcal { flex: 0 0 auto; color: var(--muted); font-size: 13px; font-feature-settings: "tnum"; }
.nut-mp__total { margin: 0 0 var(--space-3); color: var(--ink); font-size: 14px; font-weight: 600; }
.nut-mp__hint  { margin: 0 0 var(--space-3); color: var(--muted); font-size: 13px; line-height: 1.4; }
.nut-mp__err   { margin: 0; color: var(--danger); font-size: 14px; line-height: 1.4; }
.nut-mp__err:empty { display: none; }
.nut-mp__actions { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-3); }
.nut-mp__actions .btn { min-height: 48px; }

/* Provenance (§3.5) — one quiet line at the top of the meal body, so reading a
   log back weeks later still answers "did I measure this, or did someone else?" */
.nut-meal__via {
  display: flex; align-items: center; gap: 6px;
  padding: 4px 0 8px; color: var(--muted); font-size: 12px;
}
.nut-meal__via .nut-ic,
.nut-meal__via .nut-ic svg { width: 14px; height: 14px; }

/* Favorites list rows (Ship 2, v0.9.178) — reuse .nut-result for the tap target,
   add a trailing delete affordance. */
.nut-favrow { display: flex; align-items: stretch; border-top: 1px solid var(--line-2); }
.nut-favrow:first-child { border-top: 0; }
.nut-favrow .nut-result { border-top: 0; flex: 1 1 auto; }
.nut-favrow__del {
  flex: 0 0 auto; background: none; border: 0; color: var(--muted);
  font-size: 22px; line-height: 1; cursor: pointer; padding: 0 var(--space-3);
}
.nut-favrow__del:hover { color: var(--danger); }

/* Custom reusable foods (v0.9.182). Search results that are the user's own
   saved foods get a ★ tag + a trailing delete; the tap target reuses
   .nut-result inside a flex row (same shape as .nut-favrow). */
.nut-result__tag {
  margin-left: var(--space-2); font-size: 10px; font-weight: 600;
  color: var(--accent); text-transform: uppercase; letter-spacing: 0.04em;
  vertical-align: 1px;
}
.nut-result-row { display: flex; align-items: stretch; border-top: 1px solid var(--line-2); }
.nut-result-row:first-child { border-top: 0; }
.nut-result--inrow { border-top: 0; flex: 1 1 auto; }
.nut-result__del {
  flex: 0 0 auto; background: none; border: 0; color: var(--muted);
  font-size: 18px; line-height: 1; cursor: pointer; padding: 0 var(--space-3);
}
.nut-result__del:hover { color: var(--danger); }

/* NUTRITION-SERVING-SCALER-ALWAYS-IN-FLOW — inline per-row quick-add "+". A
   round accent-filled affordance on the right of a search result. Now rendered
   ONLY in the recipe/meal builder collect flow (meal.collect); it was removed
   from normal logging search because it instant-logged the default serving and
   let users skip the amount step. In logging search, row-tap → serving scaler. */
.nut-result__quickadd {
  flex: 0 0 auto; align-self: center; margin-right: var(--space-2);
  width: 36px; height: 36px; border-radius: 50%; border: 0; cursor: pointer;
  background: var(--accent); color: var(--on-accent);
  display: inline-flex; align-items: center; justify-content: center;
}
.nut-result__quickadd:hover { background: var(--accent-2); }

/* Global "+" FAB on the Log tab — fixed bottom-right, above the tabbar, hidden
   while any nutrition sheet is open. Glyph uses --on-accent (contrast policy). */
.nut-fab {
  position: fixed; right: var(--space-4);
  bottom: calc(var(--tabbar-h) + var(--safe-bottom) + var(--space-4));
  width: 56px; height: 56px; border-radius: 50%; border: 0; cursor: pointer;
  background: var(--accent); color: var(--on-accent);
  display: inline-flex; align-items: center; justify-content: center;
  box-shadow: var(--elev-2);  /* allow-hex — neutral drop shadow, not a themed surface colour */
  z-index: 40;
}
body.fz-nutrition-sheet-open .nut-fab { display: none; }

/* Shared add-flow submenu — a 3-column labelled-icon grid in the bottom sheet. */
.nut-addmenu { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-3); margin-top: var(--space-3); }
.nut-addmenu__tile {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-2);
  padding: var(--space-4) var(--space-2); cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  color: var(--accent); text-align: center;
}
.nut-addmenu__tile:hover { border-color: var(--accent); }
.nut-addmenu__tile--disabled { opacity: 0.5; }
.nut-addmenu__tile .nut-ic svg { width: 24px; height: 24px; }

/* NUTRITION-ADD-FOOD-ADAPTIVE-TOP3-MEER — the user's own top-3 routes render as
   larger primary tiles in a two-column band (the #1 route spans the row); every
   demoted route keeps its original 3-column grid behind the "Meer" row. Modifier
   classes only — same .nut-addmenu__tile component throughout. */
.nut-addmenu--primary { grid-template-columns: repeat(2, 1fr); }
.nut-addmenu__tile--primary { padding: var(--space-5, 20px) var(--space-3); border-color: var(--accent); }
.nut-addmenu__tile--wide { grid-column: 1 / -1; }
.nut-addmenu__tile--primary .nut-ic svg,
.nut-addmenu__tile--primary .fz-icon { width: 30px; height: 30px; }
.nut-addmenu__tile--primary .nut-addmenu__lbl { font-size: 14px; font-weight: 600; color: var(--ink); }
.nut-addmenu__more {
  display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  width: 100%; min-height: 44px; margin-top: var(--space-3); padding: var(--space-3);
  background: transparent; border: 1px solid var(--line); border-radius: var(--radius-pill);
  color: var(--ink-2); font-size: 13px; font-weight: 600; cursor: pointer;
}
.nut-addmenu__more:hover { border-color: var(--accent); }
.nut-addmenu__more-chev { transition: transform var(--dur-fast) var(--ease-standard); }
.nut-addmenu__more--open .nut-addmenu__more-chev { transform: rotate(180deg); }
.nut-addmenu--rest[hidden] { display: none; }

/* AI-PREMIUM-UI-COMMUNICATION-BADGES-PLUS-UPSELL — small lock badge marking an AI
   entry point as a Premium feature for non-premium accounts. Accent fill with the
   --on-accent glyph token (contrast protocol; never hardcoded white). Reused by
   the nutrition add-menu tiles, the recipe AI-parse button, and the day-cycle CTA;
   each host is already position:relative or gets it via its --premium modifier. */
.fz-premium-badge {
  position: absolute; top: 6px; right: 6px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--accent); color: var(--on-accent);
  pointer-events: none;
}
.fz-premium-badge svg { width: 11px; height: 11px; display: block; }
.nut-rb__aisubmit--premium { position: relative; }
.fz-day-cycle-empty__cta--premium { position: relative; }
.nut-addmenu__lbl { font-size: 12px; line-height: 1.2; color: var(--ink-2); }
.nut-addmenu__bound {
  display: inline-block; align-self: flex-start;
  font-size: 12px; font-weight: 600; color: var(--ink);
  background: var(--line-2); border-radius: var(--radius-pill);
  padding: var(--space-1) var(--space-3); margin-bottom: var(--space-3);
}

/* "Save to my foods" opt-in checkbox in the manual-entry dialog. */
.nut-reuse { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-3); cursor: pointer; }
.nut-reuse__box { width: 18px; height: 18px; accent-color: var(--accent); flex: 0 0 auto; }
.nut-reuse__lbl { font-size: 13px; color: var(--ink-2); }

/* NUTRITION-MANUAL-PACK-WEIGHT (v0.9.327): pack-weight hint + two-button row. */
.nut-manual__hint { font-size: 12px; font-style: italic; color: var(--muted); margin-top: calc(-1 * var(--space-2)); margin-bottom: var(--space-3); }
.nut-manual__btnrow { display: flex; gap: var(--space-3); margin-top: var(--space-4); }
.nut-manual__btnrow > .nut-scale__save { flex: 1 1 0; margin-top: 0; }

/* Add-food alternatives row (Scan / Manual) + barcode scanner (Ship 2, v0.9.175) */
.nut-altrow { display: flex; gap: var(--space-3); margin-top: var(--space-3); }
.nut-altbtn {
  flex: 1 1 0; min-width: 0; background: none; border: 1px solid var(--line);
  border-radius: var(--radius-md); color: var(--accent); font-size: 13px; cursor: pointer;
  padding: var(--space-3) var(--space-2); text-align: center;
  /* v0.9.195 — vertical icon-button: icon/glyph on its own row, label below.
     Was an inline row, which made the icon float beside 2-line-wrapped labels
     like an emoji typed into the text (Jayy 2026-05-26). */
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
}
.nut-altbtn:hover { border-color: var(--accent); }
.nut-altbtn__glyph { font-size: 19px; line-height: 1; display: block; }
.nut-altbtn__lbl { line-height: 1.2; }
.nut-altbtn .nut-ic svg { width: 21px; height: 21px; }

/* v0.9.192 — inline SVG icons (no emoji). Sized to the surrounding text. */
.nut-ic { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
.nut-ic svg { width: 1.05em; height: 1.05em; display: block; }
/* nut-ns__by stays block so its longer lock-note sentence wraps; the icon aligns
   inline with the first line. */
.nut-ns__by .nut-ic { vertical-align: -0.12em; margin-right: 4px; }
.nut-ns__by .nut-ic svg { width: 13px; height: 13px; }
.nut-result__del .nut-ic svg { width: 18px; height: 18px; }
.cal-nut__icon svg { width: 1em; height: 1em; vertical-align: -0.15em; }
.nut-manualbtn { margin-top: 0; } /* override the standalone-link spacing when in the row */

.nut-scan {
  position: relative; width: 100%; aspect-ratio: 4 / 3; background: #000;  /* allow-hex — barcode viewfinder needs absolute black behind the live video frame */
  border-radius: var(--radius-md); overflow: hidden; margin-bottom: var(--space-3);
}
.nut-scan__video { width: 100%; height: 100%; object-fit: cover; display: block; }
.nut-scan__frame {
  position: absolute; left: 12%; right: 12%; top: 50%; transform: translateY(-50%);
  height: 52%; border: 2px solid var(--accent); border-radius: var(--radius-md);
  box-shadow: 0 0 0 100vmax var(--scrim); pointer-events: none;
}
.nut-scan__status { color: var(--muted); font-size: 13px; text-align: center; padding: var(--space-2) 0; }
.nut-scan__status.is-err { color: var(--danger); }
.nut-scan__samsung { color: var(--muted); font-size: 13px; line-height: 1.45; text-align: center; padding: var(--space-2) var(--space-3); margin: 0; }

/* v0.9.315 — Scanner permission-denied recovery surface (ANDROID-CAMERA-PERMISSION-RECOVERY).
   Replaces the dead-end red `.is-err` string with a calm, platform-detected
   guide. Headline is NOT red — red stays reserved for transient errors. */
.nut-camperm { display: flex; flex-direction: column; gap: var(--space-3); }
.nut-camperm__hd { font-size: 14px; font-weight: 600; color: var(--ink); line-height: 1.35; margin: 0; }
.nut-camperm__steps { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 10px; }
.nut-camperm__steps li { display: flex; gap: 10px; align-items: flex-start; }
.nut-camperm__num {
  flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%;
  background: var(--bg); border: 1px solid var(--line); color: var(--ink-2);
  font-size: 12px; font-weight: 600; display: inline-flex;
  align-items: center; justify-content: center; margin-top: 1px;
}
.nut-camperm__txt {
  flex: 1 1 auto; min-width: 0; font-size: 13px; color: var(--ink); line-height: 1.4;
}
.nut-camperm__txt b { font-weight: 600; color: var(--ink); }
.nut-camperm__glyph {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border: 1px solid var(--line); border-radius: var(--radius-md);
  color: var(--ink-2); background: var(--bg);
  vertical-align: -0.35em; margin: 0 2px;
}
.nut-camperm__glyph svg { width: 14px; height: 14px; display: block; }
.nut-camperm__retry { gap: 8px; margin-top: 2px; }
.nut-camperm__cta-ic { display: inline-flex; align-items: center; justify-content: center; }
.nut-camperm__cta-ic svg { width: 16px; height: 16px; display: block; }

/* v0.9.315 — primary variant of .nut-altbtn (accent fill, --on-accent text).
   Used by the scanner Try-again CTA and any other surface where one of the
   alternative-row buttons is the headline action. */
.nut-altbtn.is-primary {
  background: var(--accent); border-color: var(--accent); color: var(--on-accent);
  flex-direction: row;
}
.nut-altbtn.is-primary:hover { background: var(--accent-2); border-color: var(--accent-2); }

/* NUTRITION-ADD-TO-DAY-FLOW-BUTTON-STYLE-HARMONIZE — the New-meal button swaps
   between the filled step-1 idiom (empty day: it is the only way forward) and
   the quiet alt-button (meal rows present). Only the filled state needs a row
   layout; the alt-button keeps its stacked glyph/label. */

/* v0.9.1255 — NUTRITION-M3-WAVE-5: recurrence + photo/voice/AI-estimator chrome
   moved off inline style.cssText onto tokenised classes so the Dag/Nacht presets
   reach them. DOM + behaviour unchanged; radius/type/spacing snap to wave-1 tokens
   (marginal tightening only); grey fallbacks become theme-aware --line/--line-2. */
.nut-rec__src { font-weight: 600; margin-bottom: var(--space-4); }
.nut-rec__chiprow { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-4); }

.nut-field__lblrow { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
textarea.nut-field__in { resize: vertical; overflow: auto; }

.nut-pest__thumb {
  border-radius: var(--radius-lg);
  background-size: cover; background-position: center;
  margin-bottom: var(--space-4);
}
.nut-pest__thumb--result { margin-bottom: var(--space-3); }
.nut-pest__loading { display: flex; align-items: center; justify-content: center; gap: var(--space-2); padding: var(--space-4) 0; }
.nut-pest__note { font-size: var(--text-body-sm); line-height: var(--lh-body-sm); opacity: .7; margin: var(--space-2) 0 0; }
.nut-pest__hint { opacity: .75; margin: 0 0 var(--space-4); }
.nut-pest__chat { margin-top: var(--space-4); border-top: 1px solid var(--line); padding-top: var(--space-3); }
.nut-pest__chathist { max-height: 120px; overflow-y: auto; display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-2); }
.nut-pest__chatrow { display: flex; align-items: center; gap: var(--space-2); }
.nut-pest__send {
  flex: 0 0 auto; width: 40px; height: 40px;
  border: 0; border-radius: 50%;
  background: var(--accent); color: var(--on-accent);
  font-size: 18px;
}
.nut-pest__turn { font-size: 13px; padding: 6px 10px; border-radius: var(--radius-lg); max-width: 85%; }
.nut-pest__turn--ai { align-self: flex-start; background: var(--line-2); }
.nut-pest__turn--me { align-self: flex-end; background: var(--accent); color: var(--on-accent); }

/* ─── Nutrition sub-tab strip + Library (v0.9.198, NUTRITION_LIBRARY_SPEC.md) ─── */
/* Always-visible strip: Log + the three Library sections. Underline marks active;
   scrolls horizontally on narrow phones rather than wrapping. */
.nut-tabstrip {
  display: flex; gap: var(--space-4); align-items: center;
  border-bottom: 1px solid var(--line); margin-bottom: var(--space-4);
  overflow-x: auto; -webkit-overflow-scrolling: touch;
}
.nut-tabstrip::-webkit-scrollbar { display: none; }
.nut-tab {
  flex: 0 0 auto; background: none; border: 0; cursor: pointer; font-family: inherit;
  color: var(--ink-2); font-size: 15px; padding: var(--space-3) 2px;
  border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap;
  /* WAVE-2 touch target: ≥44pt hitbox in BOTH dimensions; label stays centered. */
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; min-width: 44px;
}
.nut-tab.is-active { color: var(--ink); font-weight: 600; border-bottom-color: var(--accent); }

.nut-lib { display: flex; flex-direction: column; }
.nut-lib__search { margin-bottom: var(--space-3); }
.nut-lib__actions { display: flex; gap: var(--space-3); margin-bottom: var(--space-3); }
.nut-lib__actions:empty { display: none; }
.nut-libaction {
  flex: 1 1 0; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--accent); font-size: 14px; font-weight: 500; padding: 12px 14px; cursor: pointer; font-family: inherit;
}
.nut-libaction:hover { border-color: var(--accent); background: var(--accent-light); }
.nut-lib__list { margin-top: var(--space-2); }

/* Library list row: tappable body (open/preview) + trailing "+" (add to a day). */
.nut-libitem { display: flex; align-items: stretch; border-top: 1px solid var(--line-2); }
.nut-libitem:first-child { border-top: 0; }
.nut-libitem__main {
  flex: 1 1 auto; min-width: 0; text-align: left; background: none; border: 0;
  padding: var(--space-3) 0; cursor: pointer; font-family: inherit;
}
.nut-libitem__main:hover { background: var(--accent-light); }
.nut-libitem__name { font-size: var(--text-title); line-height: var(--lh-title); color: var(--ink); }
/* One line, ellipsised: a combo's item list can be far wider than the row. */
.nut-libitem__sub { font-size: 12px; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nut-libitem__add {
  flex: 0 0 auto; background: none; border: 0; color: var(--accent);
  font-size: 26px; line-height: 1; cursor: pointer; padding: 0 var(--space-3);
}
.nut-libitem__add:hover { color: var(--ink); }

/* v0.9.348 SHARED-RECIPES-AND-FOODS — per-record share/private lock on a
   library row. The icon doubles as the status indicator AND the toggle:
   closed lock = private (default for existing rows, post-GDPR backfill),
   open lock = shared with the Sassu pool. The icon swaps state immediately
   on tap (optimistic); the colour shift reinforces the new state. */
.nut-libitem__lock {
  flex: 0 0 auto; background: none; border: 0; cursor: pointer;
  padding: 0 var(--space-2); color: var(--muted); display: inline-flex;
  align-items: center; justify-content: center;
}
.nut-libitem__lock .nut-ic { width: 18px; height: 18px; }
.nut-libitem__lock:hover { color: var(--ink); }
.nut-libitem__lock--shared { color: var(--accent); }
.nut-libitem__lock--shared:hover { color: var(--accent-2); }

/* NUTRITION-RECIPE-COOK-MODE-PANTRY-DEDUCTION — icon-only "cook this" action on an
   owned recipe row. Frying-pan glyph; opens the cook sheet where the amounts
   actually used are deducted from stock. Sits between the share/lock and the +. */
.nut-libitem__cook {
  flex: 0 0 auto; background: none; border: 0; cursor: pointer;
  padding: 0 var(--space-2); color: var(--muted); display: inline-flex;
  align-items: center; justify-content: center;
}
.nut-libitem__cook .nut-ic { width: 18px; height: 18px; }
.nut-libitem__cook:hover { color: var(--accent); }

/* NUTRITION-RECIPES-CATEGORIES-AND-DRAG-SORT — user-defined subcategory buckets
   + drag-drop sort. Secondary "+ Create a category" action sits beside the
   accent recipe-create button (muted ink to keep the recipe action primary). */
.nut-libaction--secondary { color: var(--muted); }
.nut-libaction--secondary:hover { color: var(--accent); }

.recipes-categories-list { list-style: none; margin: var(--space-2) 0 0; padding: 0; }
.recipe-category { list-style: none; margin-bottom: var(--space-4); }
.recipe-category.dragging { opacity: 0.5; }
.recipe-category__header {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) 0; background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.recipe-category__name {
  flex: 1 1 auto; min-width: 0; margin: 0;
  font-size: 13px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.recipe-category__count {
  flex: 0 0 auto; font-size: 12px; color: var(--muted);
  background: var(--line-2); border-radius: var(--radius-pill);
  padding: 1px 8px; min-width: 20px; text-align: center;
}
.recipe-category__drag,
.recipe-category__edit {
  flex: 0 0 auto; background: none; border: 0; cursor: pointer;
  color: var(--muted); display: inline-flex; align-items: center;
  justify-content: center; min-width: 44px; min-height: 44px; padding: 0;
}
.recipe-category__drag { cursor: grab; touch-action: none; }
.recipe-category__edit:hover { color: var(--accent); }
.recipe-category__drag .nut-ic,
.recipe-category__edit .nut-ic { width: 18px; height: 18px; }

.recipe-category__recipes { list-style: none; margin: 0; padding: 0; min-height: 12px; }
.recipe-category--uncategorized .recipe-category__name { font-style: italic; }

.recipe-row { display: flex; align-items: stretch; border-top: 1px solid var(--line-2); }
.recipe-row:first-child { border-top: 0; }
.recipe-row.dragging { opacity: 0.5; }
.recipe-row .nut-libitem { flex: 1 1 auto; min-width: 0; border-top: 0; }
.recipe-row__drag {
  flex: 0 0 auto; background: none; border: 0; cursor: grab;
  color: var(--muted); display: inline-flex; align-items: center;
  justify-content: center; min-width: 44px; min-height: 44px; padding: 0;
  touch-action: none;
}
.recipe-row__drag:hover { color: var(--ink); }
.recipe-row__drag .nut-ic { width: 18px; height: 18px; }

/* COACH-RECIPE-SHARE-PUSH-TO-CLIENT — coach-only secondary "Deel" button on a
   recipe row (only rendered for a real coach, not in acting-as mode). Subtle
   so the primary "+" add action stays dominant. */
.recipe-row__share-btn {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: var(--space-1);
  background: none; border: 0; cursor: pointer; color: var(--muted);
  padding: 0 var(--space-2); font-size: 13px; min-height: 44px;
}
.recipe-row__share-btn:hover { color: var(--accent); }
.recipe-row__share-btn .nut-ic { width: 16px; height: 16px; }

/* Client-side attribution chip: shown on a recipe a coach pushed to this user. */
.recipe-row__shared-badge {
  display: inline-flex; align-items: center; gap: var(--space-1);
  margin-top: var(--space-1); padding: 2px var(--space-2);
  font-size: 12px; color: var(--muted);
  border-left: 2px solid var(--accent);
}
.recipe-row__shared-badge .nut-ic { width: 13px; height: 13px; }

/* Client-picker bottom-sheet (reuses the .mo-sheet shell via buildSheet). */
.recipe-share-search {
  width: 100%; box-sizing: border-box; margin: var(--space-2) 0;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line-2); border-radius: var(--radius-sm);
  background: var(--bg); color: var(--ink); font-size: 15px;
}
.recipe-share-client-list { list-style: none; margin: 0; padding: 0; }
.recipe-share-client { list-style: none; }
.recipe-share-client__btn {
  width: 100%; text-align: left; background: none; cursor: pointer;
  border: 0; border-bottom: 1px solid var(--line-2);
  padding: var(--space-3); display: flex; flex-direction: column;
  gap: 2px; color: var(--ink);
}
.recipe-share-client__btn:hover { background: var(--bg); }
.recipe-share-client__name { font-weight: 600; }
.recipe-share-client__email { font-size: 13px; color: var(--muted); }
.recipe-share-empty { padding: var(--space-3); color: var(--muted); text-align: center; }

/* Shared-pool search panel — mounted above My Recipes / My Foods. Folds back
   to a 1-line header when empty so it never crowds the user's own library. */
.nut-shared {
  background: var(--bg);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  margin-bottom: var(--space-4);
}
.nut-shared__head {
  display: flex; align-items: center; gap: 6px;
  color: var(--muted); font-size: 12px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .04em;
  margin-bottom: var(--space-2);
}
.nut-shared__head .nut-ic { width: 14px; height: 14px; }
.nut-shared__lbl { line-height: 1; }
.nut-shared__in { margin: 0; }
.nut-shared__drop {
  margin-top: var(--space-2);
  border-top: 1px solid var(--line-2);
  max-height: 320px; overflow-y: auto;
}
.nut-shared__row {
  display: block; width: 100%; text-align: left;
  background: none; border: 0; border-bottom: 1px solid var(--line-2);
  padding: var(--space-3) var(--space-2); cursor: pointer;
  font-family: inherit; color: var(--ink);
}
.nut-shared__row:last-child { border-bottom: 0; }
.nut-shared__row:hover { background: var(--accent-light); }
.nut-shared__row-name { font-size: 14px; color: var(--ink); }
.nut-shared__row-sub  { font-size: 12px; color: var(--muted); margin-top: 2px; }
.nut-shared__empty {
  color: var(--muted); font-size: 13px; padding: var(--space-3) var(--space-2);
}
/* Anonymity note on the preview sheet — sets the expectation explicitly. */
.nut-shared__note {
  color: var(--muted); font-size: 12px; line-height: 1.4;
  padding: var(--space-2) 0; margin-bottom: var(--space-2);
  border-bottom: 1px solid var(--line-2);
}
.nut-shared__items { margin-top: var(--space-2); }

/* First-time-share onboarding modal — fires once via localStorage. */
.nut-shared__modal {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(0,0,0,.55);
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-5);
}
.nut-shared__card {
  background: var(--bg); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: var(--space-5); max-width: 420px; width: 100%;
  box-shadow: var(--elev-3);
}
.nut-shared__card-h {
  font-size: 17px; font-weight: 600; color: var(--ink);
  margin-bottom: var(--space-3);
}
.nut-shared__card-p {
  font-size: 14px; line-height: 1.45; color: var(--ink);
  margin: 0 0 var(--space-3);
}
.nut-shared__card-p--strong { font-weight: 600; }

/* Saved-meal preview rows (opening a meal from My Meals). */
/* Per-instance recipe ingredient customizer (NUTRITION-RECIPE-ADD-EDIT-INGREDIENTS-PER-INSTANCE) */
.nut-ric__list { margin: var(--space-2) 0; }
.nut-ric__row {
  display: flex; align-items: center; gap: var(--space-3);
  border-top: 1px solid var(--line-2); padding: var(--space-3) 0;
}
.nut-ric__row:first-child { border-top: 0; }
.nut-ric__row.is-removed { opacity: 0.45; }
.nut-ric__main { flex: 1 1 auto; min-width: 0; }
.nut-ric__name { font-size: 14px; color: var(--ink); }
.nut-ric__macro { font-size: 12px; color: var(--muted); margin-top: 2px; }
.nut-ric__inwrap { flex: 0 0 auto; display: flex; align-items: center; gap: 4px; }
.nut-ric__in { width: 72px; padding: var(--space-2); text-align: right; }
.nut-ric__unit { font-size: 12px; color: var(--muted); }
.nut-ric__del {
  flex: 0 0 auto; background: none; border: 0; color: var(--muted);
  padding: 4px; cursor: pointer; line-height: 0;
}
.nut-ric__del:hover { color: var(--danger); }
.nut-ric__total { margin-top: var(--space-3); }
.nut-ric__total-lbl { font-size: 12px; color: var(--ink-2); margin-bottom: 4px; }
.nut-ric__hint { font-size: 12px; color: var(--danger); margin-top: var(--space-2); }
.nut-ric__addbtn { width: 100%; margin-top: var(--space-3); }
.nut-prev-row { border-top: 1px solid var(--line-2); padding: var(--space-3) 0; }
.nut-prev-row:first-child { border-top: 0; }
.nut-prev-row__name { font-size: 14px; color: var(--ink); }
.nut-prev-row__meta { font-size: 12px; color: var(--muted); margin-top: 2px; }

/* Destination picker — spacing above the "Meal" label. */
.nut-lib-dest__lbl { margin-top: var(--space-3); }
/* Day picker (step 1): primary "Today" at the top, ghost continue full-width,
   a left-aligned "‹ Change day" link in step 2. */
.nut-dest__today { margin-top: 0; }
.nut-dest__continue { width: 100%; margin-top: var(--space-2); }
.nut-dest__back { display: inline-block; margin: 0 0 var(--space-2); padding: 4px 0; }

/* My Recipes "coming soon" empty state (Phase 1 placeholder). */
.nut-lib-soon { text-align: center; padding: var(--space-6) var(--space-4); }
.nut-lib-soon__title { font-size: 16px; font-weight: 600; color: var(--ink); margin-bottom: var(--space-2); }
.nut-lib-soon__txt { font-size: 13px; color: var(--muted); line-height: 1.5; max-width: 32ch; margin: 0 auto; }

/* ─── Full-page recipe builder (v0.9.202) ───
   Takes over the whole Nutrition mount (no sub-tab strip). The recipe is built
   IN the page: name + servings, then a sheet of food rows each with an inline
   gram field you fine-tune while weighing — macros rescale by ratio live. */
.nut-rb { display: flex; flex-direction: column; }
.nut-rb__bar {
  display: flex; align-items: center; gap: var(--space-3);
  margin: 0 0 var(--space-4); padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--line);
}
.nut-rb__back {
  flex: 0 0 auto; background: none; border: 0; color: var(--ink-2);
  font-size: 28px; line-height: 1; cursor: pointer; padding: 0 4px 0 0; font-family: inherit;
}
.nut-rb__back:hover { color: var(--ink); }
.nut-rb__title { flex: 1 1 auto; font-size: 16px; font-weight: 600; color: var(--ink); }
.nut-rb__done {
  flex: 0 0 auto; background: var(--accent); border: 0; border-radius: var(--radius-sm);
  /* --on-accent (luminance-derived: dark on lime, white on blue), NEVER hardcoded
     #fff — white-on-lime fails contrast. Same protocol the Strength tab uses. */
  color: var(--on-accent); font-size: 14px; font-weight: 600; padding: 8px 18px; cursor: pointer; font-family: inherit;
}
.nut-rb__done:disabled { opacity: 0.6; }

/* RECIPE-INGREDIENTS-LINKED-TOGGLE — one compact line: switch · state label · '?'.
   The explanation lives behind the '?' (same idiom as the Strength 1RM helper) so
   there is exactly ONE place that explains this, never two side by side. */
.nut-rb__link {
  display: flex; align-items: center; gap: 10px;
  margin: 0 0 var(--space-2);
}
.nut-rb__link-sw { flex: 0 0 auto; }
.nut-rb__link-lbl {
  flex: 1 1 auto; min-width: 0;
  font-size: 13px; font-weight: 600; color: var(--ink-2);
}
.nut-rb__link-help {
  flex: 0 0 auto; width: 34px; height: 34px; margin-right: -8px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 0; padding: 0;
  color: var(--muted); cursor: pointer;
}
.nut-rb__link-help .nut-ic svg { width: 17px; height: 17px; }
.nut-rb__link-help.is-on { color: var(--ink); }
.nut-rb__link-note {
  display: none;
  font-size: 12.5px; line-height: 1.5; color: var(--muted);
  background: var(--surface); border: 1px dashed var(--line);
  border-radius: var(--radius-md); padding: 9px 11px;
  margin: 0 0 var(--space-2);
}
.nut-rb__link-note.is-open { display: block; }
.nut-rb__rows--unlocked {
  border-left: 3px solid var(--accent);
  padding-left: var(--space-3);
  margin-left: -3px;
}

.nut-rb__servwrap { display: flex; }
.nut-rb__servfield { width: 50%; max-width: 160px; margin-bottom: 0; }

/* NUTRITION-RECIPE-LIVE-PROPORTIONAL-SCALING — servings + reset row. */
.recipe-scale-row { display: flex; align-items: flex-end; gap: var(--space-2); }
.recipe-scale-row .nut-field { margin-bottom: 0; }
.recipe-reset-btn {
  flex: none;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}
.recipe-reset-btn:hover { color: var(--ink); border-color: var(--ink); }

.nut-rb__shead {
  display: flex; align-items: center; justify-content: space-between;
  margin: var(--space-4) 0 var(--space-2);
}
.nut-rb__slbl { font-size: 12px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--ink-2); }
.nut-rb__shead-r { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.nut-rb__count { font-size: 12px; color: var(--muted); text-align: right; }

.nut-rb__rows {
  display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: var(--radius-md); overflow: hidden; background: var(--surface);
}
.nut-rb__empty { text-align: center; padding: var(--space-5) var(--space-3); }
.nut-rb__row {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4); border-top: 1px solid var(--line-2);
}
.nut-rb__row:first-child { border-top: 0; }
.nut-rb__rmain {
  flex: 1 1 auto; min-width: 0; text-align: left; background: none; border: 0;
  color: var(--ink); font-size: 15px; cursor: pointer; font-family: inherit;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 4px 0;
}
.nut-rb__rmain:hover { color: var(--accent); }
.nut-rb__rright { flex: 0 0 auto; display: flex; align-items: center; gap: var(--space-3); }
.nut-rb__amt {
  display: inline-flex; align-items: center; gap: 4px;
  border: 1px solid var(--line-2); border-radius: var(--radius-md); padding: 4px 8px; background: var(--bg);
}
.nut-rb__amt:focus-within { border-color: var(--accent); }
.nut-rb__amtin {
  /* NUTRITION-AMOUNT-INPUT-AUTOSIZE-TO-CONTENT — width follows the value, no
     leading whitespace before small numbers; clamp keeps 1ch readable / 4-digit safe. */
  field-sizing: content; min-width: 3ch; max-width: 7ch;
  border: 0; background: none; color: var(--ink); font-size: 15px;
  text-align: right; font-variant-numeric: tabular-nums; font-family: inherit; padding: 0;
}
.nut-rb__amtin:focus { outline: none; }
.nut-rb__amtunit { font-size: 12px; color: var(--muted); }
.nut-rb__rstatic { font-size: 12px; color: var(--muted); }
.nut-rb__rkcal { min-width: 4.5em; text-align: right; font-size: 13px; color: var(--ink-2); font-variant-numeric: tabular-nums; }

.nut-rb__add {
  display: flex; align-items: center; gap: var(--space-2); justify-content: center;
  width: 100%; margin-top: var(--space-3);
  background: var(--bg); border: 1px dashed var(--line); border-radius: var(--radius-md);
  color: var(--accent); font-size: 14px; font-weight: 500; padding: 14px; cursor: pointer; font-family: inherit;
}
.nut-rb__add:hover { border-color: var(--accent); background: var(--accent-light); }
.nut-rb__addplus { font-size: 18px; line-height: 1; }

/* NUTRITION-RECIPE-AI-INGREDIENT-LIST — additive freeform-list fast-entry panel. */
.nut-rb__ai { margin-top: var(--space-3); border: 1px solid var(--line-2); border-radius: var(--radius-lg); overflow: hidden; }
.nut-rb__aitoggle {
  display: flex; align-items: center; gap: var(--space-2); width: 100%;
  background: var(--bg); border: 0; color: var(--ink); font-size: 14px; font-weight: 500;
  padding: 12px 14px; cursor: pointer; font-family: inherit; text-align: left;
}
.nut-rb__aitoggle:hover { background: var(--accent-light); }
.nut-rb__ailbl { flex: 1 1 auto; }
.nut-rb__aichev { flex: 0 0 auto; color: var(--muted); transition: transform var(--dur-fast) var(--ease-standard); }
.nut-rb__aitoggle[aria-expanded="true"] .nut-rb__aichev { transform: rotate(90deg); }
.nut-rb__aibody { padding: 12px 14px; border-top: 1px solid var(--line-2); display: flex; flex-direction: column; gap: 10px; }
.nut-rb__aitawrap { position: relative; }
.nut-rb__aita {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 76px;
  border: 1px solid var(--line-2); border-radius: var(--radius-md); background: var(--bg); color: var(--ink);
  font-size: 14px; font-family: inherit; padding: 10px; padding-right: 46px;
}
.nut-rb__aita:focus { outline: none; border-color: var(--accent); }
.nut-rb__aisubmit {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  background: var(--accent); color: var(--on-accent); border: 0; border-radius: var(--radius-md);
  padding: 11px; font-size: 14px; font-weight: 600; cursor: pointer; font-family: inherit;
}
.nut-rb__aisubmit:disabled { opacity: .6; cursor: default; }

/* NUTRITION-RECIPE-FOODS-ENTRY-FLOW-REDESIGN — entry affordances re-render off items. */
.nut-rb__entry { margin-top: var(--space-3); display: flex; flex-direction: column; gap: 10px; }
/* STATE A / reveal: the input card frame wraps textarea + mic + primary submit. */
.nut-rb__aicard {
  display: flex; flex-direction: column; gap: 10px;
  border: 1px solid var(--line-2); border-radius: var(--radius-md); padding: 12px 14px; background: var(--bg);
}
.nut-rb__aicard .nut-rb__aisubmit { padding: 12px; }
/* Secondary, quieter 'Add one food at a time'. */
.nut-rb__addquiet {
  align-self: center; background: transparent; border: 0; color: var(--muted);
  font-size: 13px; font-weight: 500; padding: 6px; cursor: pointer; font-family: inherit;
  text-decoration: underline; text-underline-offset: 2px;
}
.nut-rb__addquiet:hover { color: var(--accent); }
/* STATE B — two equal-weight side-by-side buttons. */
.nut-rb__entrybtns { display: flex; gap: 10px; }
.nut-rb__entrybtns > .nut-rb__add { flex: 1 1 0; margin-top: 0; }
.nut-rb__listbtn {
  flex: 1 1 0; display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  background: var(--bg); border: 1px dashed var(--line); border-radius: var(--radius-md);
  color: var(--accent); font-size: 14px; font-weight: 500; padding: 14px; cursor: pointer; font-family: inherit;
}
.nut-rb__listbtn:hover { border-color: var(--accent); background: var(--accent-light); }
.nut-rb__est {
  font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .03em;
  color: var(--accent); border: 1px solid var(--accent); border-radius: var(--radius-sm); padding: 0 4px; line-height: 1.5;
}

.nut-rb__totals { margin-top: var(--space-4); padding-top: var(--space-3); border-top: 1px solid var(--line); }
.nut-rb__trow { font-size: 15px; font-weight: 600; color: var(--ink); }
.nut-rb__tsub { font-size: 13px; color: var(--muted); margin-top: 2px; }

.nut-rb__day { width: 100%; margin-top: var(--space-4); }
.nut-rb__discard {
  display: block; margin: var(--space-5) auto 0; background: none; border: 0;
  color: var(--muted); font-size: 13px; cursor: pointer; font-family: inherit; padding: 6px 12px;
}
.nut-rb__discard:hover { color: var(--danger); }

/* Recipe notes / cooking instructions — appears under totals in the recipe
   builder/editor. Plain prose, so system font (no monospace). */
.fz-recipe-notes { margin-top: var(--space-4); }
.fz-recipe-notes__lbl {
  display: block;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin-bottom: var(--space-2);
}
.fz-recipe-notes__ta {
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: 10px;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: 14px;
  line-height: 1.4;
  resize: none;
  overflow: hidden;
  min-height: 80px;
  white-space: pre-wrap;
}
.fz-recipe-notes__ta:focus { outline: none; border-color: var(--accent); }
.fz-recipe-notes__count {
  margin-top: 4px;
  text-align: right;
  font-size: 11px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* v0.9.332 — COACH-LINK-EXISTING.
   Invitee banner shown at top of shell when /api/me/link-requests has pending
   rows. Mirrors .delbanner shape (border-left accent stripe) but uses the
   accent token instead of danger. Settings → Account "My clients" disclosure
   inherits .set-disclose / .set-group; the rules below add only what's new:
   email search row, three result sections, the no-Sassu-account card, the
   custom Accept/Decline sheet. NO hardcoded hex — every color goes through a
   token so dark theme inherits automatically. */
.coachlink-banner {
  margin: var(--space-3) var(--space-4);
  padding: 12px 12px 12px 14px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  display: flex; align-items: center; gap: 12px;
  cursor: pointer;
  font-family: inherit;
  color: var(--ink);
}
.coachlink-banner:hover { background: var(--surface-muted); }
.coachlink-banner:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.coachlink-banner__avatar {
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--accent); color: var(--on-accent);
  display: flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 600; flex-shrink: 0;
}
.coachlink-banner__body { flex: 1; min-width: 0; }
.coachlink-banner__title {
  font-size: 14px; color: var(--ink); line-height: 1.35; margin: 0;
}
.coachlink-banner__title strong { font-weight: 600; }
.coachlink-banner__sub {
  font-size: 12px; color: var(--muted); margin: 2px 0 0;
}
.coachlink-banner__chev { color: var(--muted); font-size: 20px; flex-shrink: 0; }

/* Accept / Decline sheet — composed atop the existing .ui-dlg-overlay +
   .ui-dlg shell, so focus trap, Esc, and aria wiring all come for free. */
.coachlink-sheet { max-width: 420px; }
.coachlink-sheet__title { padding: 0; }
.coachlink-sheet__header {
  display: flex; align-items: center; gap: 14px; margin-bottom: 12px;
}
.coachlink-sheet__avatar {
  width: 48px; height: 48px; border-radius: 50%;
  background: var(--accent); color: var(--on-accent);
  display: flex; align-items: center; justify-content: center;
  font-size: 18px; font-weight: 600; flex-shrink: 0;
}
.coachlink-sheet__namebox { min-width: 0; }
.coachlink-sheet__name { font-size: 18px; color: var(--ink); font-weight: 600; }
.coachlink-sheet__sub { font-size: 13px; color: var(--muted); margin-top: 2px; }
.coachlink-sheet__body {
  font-size: 14px; color: var(--ink-2); line-height: 1.55; margin: 0 0 16px;
}
.coachlink-sheet__permlist {
  list-style: none; padding: 0; margin: 0 0 18px;
}
.coachlink-sheet__perm {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 0; font-size: 13px; color: var(--ink-2);
}
.coachlink-sheet__perm-icon {
  width: 16px; height: 16px; flex-shrink: 0; color: var(--accent);
}
.coachlink-sheet__perm--no .coachlink-sheet__perm-icon { color: var(--muted); }
.coachlink-sheet__perm--no { color: var(--muted); }

/* COACH-PLATFORM-SPRINT-8 consent modal. */
.consent-sheet__section { margin: 4px 0 14px; }
.consent-sheet__legend {
  font-size: 12px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted); margin: 0 0 8px; font-weight: 600;
}
.consent-sheet__toggle {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 8px 0; font-size: 14px; color: var(--ink-2);
  border-bottom: 1px solid var(--line); cursor: pointer;
}
.consent-sheet__toggle:last-child { border-bottom: none; }
.consent-sheet__checkbox { width: 20px; height: 20px; accent-color: var(--accent); flex-shrink: 0; }
.consent-sheet__matrow {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 8px 0; font-size: 14px; color: var(--ink-2);
  border-bottom: 1px solid var(--line);
}
.consent-sheet__matrow:last-child { border-bottom: none; }
.consent-sheet__select {
  padding: 7px 10px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface-muted); color: var(--ink); font-family: inherit;
  font-size: 14px; outline: none; flex-shrink: 0;
}
.consent-sheet__select:focus { border-color: var(--accent); }
.consent-sheet__legalese { font-size: 13px; color: var(--muted); line-height: 1.5; margin: 4px 0 16px; }
.set-consent-log { list-style: none; padding: 0; margin: 8px 0 0; }
.set-consent-log__row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 6px 0; font-size: 13px; color: var(--ink-2);
  border-bottom: 1px solid var(--line);
}
.set-consent-log__row:last-child { border-bottom: none; }
.set-consent-log__when { color: var(--muted); flex-shrink: 0; font-variant-numeric: tabular-nums; }

/* "My clients" disclosure body — search row, hint, result sections, card. */
.clients-search {
  display: flex; gap: 8px; align-items: stretch;
  margin: var(--space-2) 0 6px;
}
.clients-search__input {
  flex: 1; padding: 10px 12px;
  background: var(--surface-muted);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 15px; color: var(--ink);
  outline: none; font-family: inherit;
}
.clients-search__input:focus {
  border-color: var(--ink-2);
}
.clients-search__btn {
  padding: 10px 16px;
  background: var(--accent); color: var(--on-accent);
  border: 0; border-radius: var(--radius-sm);
  font-size: 14px; font-weight: 600; cursor: pointer; font-family: inherit;
}
.clients-search__btn:hover { background: var(--accent-2); }
.clients-search__btn:disabled { opacity: 0.55; cursor: progress; }
.clients-search__hint {
  font-size: 12px; color: var(--muted); margin: 0 0 var(--space-3);
  line-height: 1.5;
}
.clients-nfcard {
  background: var(--surface-muted);
  border: 1px dashed var(--line);
  border-radius: var(--radius-sm);
  padding: 14px;
  margin-bottom: var(--space-3);
}
.clients-nfcard__t {
  font-size: 14px; color: var(--ink); margin: 0 0 4px;
}
.clients-nfcard__t strong { font-weight: 600; }
.clients-nfcard__sub {
  font-size: 13px; color: var(--ink-2); margin: 0 0 10px; line-height: 1.5;
}
.clients-nfcard__linkrow {
  display: flex; gap: 8px; align-items: stretch;
}
.clients-nfcard__link {
  flex: 1; padding: 8px 10px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 12px; color: var(--ink); font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  outline: none;
}
.clients-section { margin: var(--space-3) 0 0; }
.clients-section__h {
  font-size: 12px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 6px;
}
.clients-section__list { display: flex; flex-direction: column; }
.client-row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 6px; min-height: 56px;
}
.client-row + .client-row { border-top: 1px solid var(--line-2); }
.client-row__avatar {
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--surface-muted);
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 600; color: var(--ink-2); flex-shrink: 0;
}
.client-row__body { flex: 1; min-width: 0; }
.client-row__name {
  font-size: 14px; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.client-row__meta { font-size: 12px; color: var(--muted); margin-top: 2px; }
.client-row__actions {
  display: flex; gap: 6px; flex-shrink: 0; align-items: center;
}
.client-row__badge {
  font-size: 11px; color: var(--muted);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 2px 8px;
}
.client-row__badge--ok {
  color: var(--ok); border-color: var(--ok);
}

/* ──────────────────────────────────────────────────────────
   Cardio · Modality Picker (Sprint 6, v0.9.372)
   Source-of-truth spec: cardio_tab_spec.md §2.1.
   Painted inside the cardio iframe via cardio-bridge.js; tokens
   resolve from cardio.html's :root preset block (Dag / Nacht).
   Start affordances paint background:var(--accent) +
   color:var(--on-accent) — luminance-derived per brand_color,
   per rule g + feedback_accent_buttons_on_accent_token.
   ────────────────────────────────────────────────────────── */
/* CARDIO-MAP-RENDER-LAYOUT-AUDIT-AND-FIX (v0.9.465) — defensive bottom
   padding on every outermost cardio surface so any future fixed-bottom
   UI (in-iframe player, save bar, etc.) can't clip the last row.
   Matches the kaizen 2026-06-06 STRENGTH-EDIT-ARCHIVE-SCROLL-OVERFLOW-FIX
   pattern: safe-area inset + 16px buffer. */
.fz-cardio-picker { padding: 16px 16px env(safe-area-inset-bottom, 0px); }
.fz-cardio-picker__grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
}

/* DIARY-LOG-BUTTONS-RELOCATE-WATER-NUTRITION-STEPS-CARDIO (v0.9.473) — Steps
   quick-log cell, mounted at the top of the Modality Picker. Visual chrome
   mirrors the .cal-metric--empty/--filled pair used on the Diary tab so the
   two relocated buttons (Water on Nutrition, Steps here) feel like one
   surface. Tokens only — Dag + Nacht inherit correctly. */
.fz-cardio-stepslog {
  appearance: none; -webkit-appearance: none;
  display: flex; align-items: center; gap: 10px;
  width: 100%; box-sizing: border-box;
  padding: 12px 14px; margin-bottom: 12px;
  border-radius: var(--radius-lg); border: 1px solid transparent;
  font: inherit; font-size: 14px;
  min-height: 44px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  text-align: left;
  transition: background var(--dur-base) var(--ease-standard), border-color var(--dur-base) var(--ease-standard), color var(--dur-base) var(--ease-standard);
}
.fz-cardio-stepslog:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.fz-cardio-stepslog--empty {
  background: var(--accent); color: var(--on-accent);
  border-color: var(--accent);
}
.fz-cardio-stepslog--empty .fz-cardio-stepslog__lbl {
  flex: 1 1 auto; font-weight: 600; font-size: 14px; line-height: 1.2;
}
.fz-cardio-stepslog--empty .fz-cardio-stepslog__hint {
  flex: 0 0 auto; font-size: 20px; font-weight: 600; line-height: 1; opacity: 0.9;
}
.fz-cardio-stepslog--filled {
  background: var(--surface); color: var(--fg);
  border-color: var(--line);
}
.fz-cardio-stepslog--filled .fz-cardio-stepslog__lbl {
  flex: 0 0 auto; color: var(--muted);
  font-size: 12px; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.02em;
}
.fz-cardio-stepslog--filled .fz-cardio-stepslog__val {
  flex: 1 1 auto; font-weight: 600; font-size: 15px;
  font-feature-settings: 'tnum' 1;
}
.fz-cardio-stepslog--filled .fz-cardio-stepslog__hint {
  flex: 0 0 auto; color: var(--muted); font-size: 18px; line-height: 1;
}

.fz-cardio-card {
  appearance: none; -webkit-appearance: none;
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 12px; min-height: 116px;
  display: flex; flex-direction: column; gap: 8px;
  text-align: left; font: inherit; cursor: pointer;
}
.fz-cardio-card:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.fz-cardio-card__head {
  display: flex; align-items: center; gap: 8px;
}
.fz-cardio-card__icon {
  width: 24px; height: 24px;
  stroke: currentColor; fill: none; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
  flex: 0 0 auto;
}
.fz-cardio-card__label {
  font-size: 13px; font-weight: 600; letter-spacing: 0.01em;
}
.fz-cardio-card__chip {
  font-size: 11px; color: var(--muted);
  line-height: 1.3; min-height: 1.3em;
}
.fz-cardio-card__foot {
  margin-top: auto;
  display: flex; align-items: center; justify-content: space-between;
}
.fz-cardio-card__start {
  appearance: none;
  background: var(--accent); color: var(--on-accent);
  border: 0; border-radius: var(--radius-pill);
  padding: 5px 12px; font-size: 11px; font-weight: 700;
  letter-spacing: 0.04em; text-transform: uppercase;
  cursor: pointer;
}
.fz-cardio-card--saved { grid-column: 1 / -1; }
.fz-cardio-card--saved .fz-cardio-card__start {
  background: transparent; color: var(--fg);
  border: 1px solid var(--line);
}
/* ──────── CARDIO-COMMUTE-ROUTE-MANUAL-ENTRY — "Mijn routes" ──────── */
.fz-cardio-routes {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 14px;
  margin-bottom: 16px;
}
.fz-cardio-routes__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 12px;
}
.fz-cardio-routes__head-l { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.fz-cardio-routes__title { font-size: 14px; font-weight: 600; color: var(--ink); }
.fz-cardio-routes__week { font-size: 11px; color: var(--muted); white-space: nowrap; }
.fz-cardio-routes__add {
  display: inline-flex; align-items: center; gap: 4px;
  background: none; border: none; cursor: pointer;
  color: var(--accent); font: inherit; font-size: 13px; padding: 4px 2px;
}
.fz-cardio-routes__add .fz-icon { width: 14px; height: 14px; }
.fz-cardio-routes__empty { font-size: 12px; color: var(--muted); padding: 6px 0; }

.fz-cardio-routes__card {
  background: var(--bg);
  border-radius: var(--radius-md);
  padding: 12px;
}
.fz-cardio-routes__card + .fz-cardio-routes__card { margin-top: 10px; }
.fz-cardio-routes__card-head {
  display: flex; align-items: center; gap: 10px; margin-bottom: 10px;
  -webkit-user-select: none; user-select: none;
}
.fz-cardio-routes__icon {
  width: 32px; height: 32px; flex: 0 0 auto;
  background: var(--line); border-radius: var(--radius-md);
  display: flex; align-items: center; justify-content: center;
  color: var(--ink);
}
.fz-cardio-routes__mode-txt { font-size: 10px; font-weight: 600; color: var(--ink); }
.fz-cardio-routes__meta { flex: 1; min-width: 0; }
.fz-cardio-routes__name {
  font-size: 14px; font-weight: 500; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fz-cardio-routes__detail {
  font-size: 12px; color: var(--muted); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.fz-cardio-routes__week-strip { display: flex; align-items: center; gap: 6px; }
.fz-cardio-routes__nav {
  width: 24px; height: 24px; flex: 0 0 auto;
  background: none; border: none; cursor: pointer; color: var(--muted);
  display: flex; align-items: center; justify-content: center; padding: 0;
}
.fz-cardio-routes__nav .fz-icon { width: 14px; height: 14px; }
.fz-cardio-routes__days {
  display: flex; gap: 4px; flex: 1; justify-content: space-between;
}
.fz-cardio-routes__day {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
}
.fz-cardio-routes__day-lbl {
  font-size: 10px; color: var(--muted); text-transform: uppercase;
}
.fz-cardio-routes__dot {
  width: 28px; height: 28px; flex: 0 0 auto;
  border-radius: 50%; border: 2px solid var(--line);
  background: none; cursor: pointer; padding: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--muted); transition: background var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard);
}
.fz-cardio-routes__dot .fz-icon { width: 13px; height: 13px; }
.fz-cardio-routes__dot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.fz-cardio-routes__ht { font-size: 9px; font-weight: 700; }
.fz-cardio-routes__dot.is-heen {
  background: var(--accent); border-color: var(--accent); color: var(--on-accent);
}
.fz-cardio-routes__dot.is-terug {
  background: var(--line); border-color: var(--accent); color: var(--ink);
}
.fz-cardio-routes__dot.is-heen_terug {
  background: var(--ok); border-color: var(--ok); color: var(--on-accent);
}

.fz-cardio-routes__legend {
  display: flex; gap: 12px; flex-wrap: wrap;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line);
}
.fz-cardio-routes__lg {
  display: flex; align-items: center; gap: 6px;
  font-size: 11px; color: var(--muted);
}
.fz-cardio-routes__lg-dot { width: 12px; height: 12px; border-radius: 50%; }
.fz-cardio-routes__lg-dot.is-heen { background: var(--accent); }
.fz-cardio-routes__lg-dot.is-terug { background: var(--line); border: 2px solid var(--accent); }
.fz-cardio-routes__lg-dot.is-heen_terug { background: var(--ok); }

.fz-cardio-routes__form {
  margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line);
}
.fz-cardio-routes__form-title { font-size: 13px; font-weight: 600; margin-bottom: 12px; color: var(--ink); }
.fz-cardio-routes__field { margin-bottom: 12px; }
.fz-cardio-routes__field-row { display: flex; gap: 12px; }
.fz-cardio-routes__field-row .fz-cardio-routes__field { flex: 1; min-width: 0; }
.fz-cardio-routes__lbl { display: block; font-size: 12px; color: var(--muted); margin-bottom: 4px; }
.fz-cardio-routes__input {
  width: 100%; background: var(--bg);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 10px 12px; font: inherit; font-size: 14px; color: var(--ink);
}
.fz-cardio-routes__input::placeholder { color: var(--muted); }
.fz-cardio-routes__input:focus { outline: none; border-color: var(--accent); }
/* CARDIO-ROUTE-AUTO-DISTANCE-MAPBOX — auto-distance spinner + microlabel. */
.fz-cardio-routes__input-wrap { position: relative; }
.fz-cardio-routes__input-wrap.is-loading::after {
  content: ''; position: absolute; right: 10px; top: 50%; margin-top: -8px;
  width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid var(--line); border-top-color: var(--accent);
  animation: cardioSpin var(--dur-loop) linear infinite; pointer-events: none;
}
.fz-cardio-routes__hint { font-size: 11px; color: var(--muted); margin-top: 4px; }
.fz-cardio-routes__hint:empty { display: none; }
.fz-cardio-routes__mode-seg { display: flex; gap: 8px; }
.fz-cardio-routes__mode {
  flex: 1; min-width: 0; background: var(--bg);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 10px 6px; cursor: pointer; font: inherit; font-size: 12px;
  color: var(--muted);
  display: flex; flex-direction: column; align-items: center; gap: 4px;
}
.fz-cardio-routes__mode .fz-icon { width: 18px; height: 18px; }
.fz-cardio-routes__mode.is-active {
  border-color: var(--accent); color: var(--ink); background: var(--accent-subtle);
}
.fz-cardio-routes__err { font-size: 12px; color: var(--accent-orange, #FB923C); margin-bottom: 8px; min-height: 0; }
.fz-cardio-routes__err:empty { display: none; }
.fz-cardio-routes__form-actions { display: flex; gap: 8px; align-items: center; }
.fz-cardio-routes__archive {
  background: none; border: none; cursor: pointer; font: inherit; font-size: 13px;
  color: var(--muted); padding: 10px 4px; margin-right: auto;
}
.fz-cardio-routes__cancel {
  background: none; border: 1px solid var(--line); border-radius: var(--radius-md);
  cursor: pointer; font: inherit; font-size: 14px; color: var(--ink); padding: 11px 16px;
}
.fz-cardio-routes__save {
  background: var(--accent); color: var(--on-accent);
  border: none; border-radius: var(--radius-md); cursor: pointer;
  font: inherit; font-size: 14px; font-weight: 600; padding: 12px 18px;
}

/* Long-press action sheet — overlays the cardio app surface. */
.fz-cardio-routes__sheet { position: fixed; inset: 0; z-index: 60; display: flex; align-items: flex-end; }
.fz-cardio-routes__sheet-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,0.5); }
.fz-cardio-routes__sheet-card {
  position: relative; width: 100%; max-width: 420px; margin: 0 auto;
  background: var(--surface); border-top-left-radius: var(--radius-lg); border-top-right-radius: var(--radius-lg);
  padding: 12px; display: flex; flex-direction: column; gap: 6px;
  padding-bottom: max(12px, env(safe-area-inset-bottom));
}
.fz-cardio-routes__sheet-title {
  font-size: 13px; font-weight: 600; color: var(--ink);
  padding: 8px 8px 10px; text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fz-cardio-routes__sheet-btn {
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-md);
  cursor: pointer; font: inherit; font-size: 15px; color: var(--ink); padding: 14px;
}
.fz-cardio-routes__sheet-btn.is-danger { color: var(--accent-orange, #FB923C); }

.fz-cardio-picker__assign {
  margin-top: 18px; padding-top: 14px;
  border-top: 1px solid var(--line);
}
.fz-cardio-picker__assign-h {
  margin: 0 0 8px;
  font-size: 11px; color: var(--muted);
  letter-spacing: 0.1em; text-transform: uppercase; font-weight: 600;
}
.fz-cardio-picker__assign-empty {
  font-size: 12px; color: var(--muted);
}
.fz-cardio-assign {
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 12px;
  display: flex; gap: 10px; align-items: flex-start;
  cursor: pointer; width: 100%; text-align: left;
  appearance: none; font: inherit;
}
.fz-cardio-assign + .fz-cardio-assign { margin-top: 8px; }
.fz-cardio-assign:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.fz-cardio-assign__icon {
  width: 22px; height: 22px; stroke: currentColor; fill: none;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
  flex: 0 0 auto; margin-top: 2px;
}
.fz-cardio-assign__body { flex: 1; min-width: 0; }
.fz-cardio-assign__title { font-size: 13px; font-weight: 600; }
.fz-cardio-assign__meta {
  font-size: 11px; color: var(--muted); margin-top: 2px;
}
.fz-cardio-assign__when {
  font-size: 11px; color: var(--accent);
  align-self: center; white-space: nowrap;
}

/* ──────────────────────────────────────────────────────────
   Cardio — INDOOR live tracking (Sprint 7, v0.9.373).
   Top 30% is the hero-timer banner (visual reuse of the
   strength prototype's .hero-timer / .ht-time / .ht-min /
   .ht-sec / .ht-ms pattern — Inter tabular-nums display so
   the digits don't jitter). Below: manual stat inputs +
   pause-resume + end-session-confirm. Tokens-only, per
   css-hex-lint A11Y-12 / S22. --on-accent everywhere a
   control sits on --accent, per rule g + the accent-buttons
   feedback memory. No hex literals on paint properties.
   ────────────────────────────────────────────────────────── */
.fz-cardio-indoor-track {
  display: flex; flex-direction: column;
  min-height: calc(100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom));
  min-height: calc(100dvh - env(safe-area-inset-top) - env(safe-area-inset-bottom)); /* dvh wins on modern engines, 100vh above stays fallback */
}
.fz-cardio-indoor-track__hero {
  /* Override the global fixed-position .hero-timer rule for the cardio
     iframe — here we want the hero in normal flow, taking the top 30%
     of the viewport per spec §2.2. */
  position: relative;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 8px;
  min-height: 30vh;
  padding: 24px 20px;
  background: var(--accent);
  color: var(--on-accent);
  border: 0; border-bottom: 1px solid var(--line);
}
.fz-cardio-indoor-track__phase {
  font-size: 11px; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--on-accent); opacity: 0.7;
}
.fz-cardio-indoor-track__hero .ht-time {
  font-size: 56px; font-weight: 700;
  line-height: 1; letter-spacing: -1px;
  color: var(--on-accent);
  font-family: 'Inter', 'DM Sans', -apple-system, sans-serif;
  font-variant-numeric: tabular-nums;
}
.fz-cardio-indoor-track__hero .ht-min,
.fz-cardio-indoor-track__hero .ht-sec {
  display: inline-block; text-align: center;
}
.fz-cardio-indoor-track__hero .ht-min { min-width: 1.1ch; }
.fz-cardio-indoor-track__hero .ht-sec { min-width: 2.2ch; }
.fz-cardio-indoor-track__hero .ht-ms {
  font-size: 26px; font-weight: 600;
  opacity: 0.55; display: inline-block;
  min-width: 2ch; text-align: left;
  vertical-align: baseline;
}
.fz-cardio-indoor-track__pause {
  appearance: none; -webkit-appearance: none;
  background: var(--on-accent); color: var(--accent);
  border: 0; border-radius: var(--radius-pill);
  padding: 10px 28px;
  font: inherit; font-weight: 700; font-size: 14px;
  letter-spacing: 0.04em; text-transform: uppercase;
  cursor: pointer;
  min-width: 140px;
}
.fz-cardio-indoor-track__pause:focus-visible {
  outline: 2px solid var(--on-accent); outline-offset: 3px;
}
.fz-cardio-indoor-track__end {
  position: absolute; top: 12px; right: 12px;
  appearance: none; -webkit-appearance: none;
  background: transparent; color: var(--on-accent);
  border: 1px solid var(--on-accent); border-radius: var(--radius-pill);
  padding: 4px 12px;
  font: inherit; font-size: 11px; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase;
  cursor: pointer;
  opacity: 0.85;
}
.fz-cardio-indoor-track__end[data-armed="1"] {
  background: var(--on-accent); color: var(--accent);
  opacity: 1;
}
.fz-cardio-indoor-track__stats {
  flex: 1;
  display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
  padding: 16px 16px calc(env(safe-area-inset-bottom, 0px) + 16px);
}
.fz-cardio-indoor-track__stat {
  grid-column: 1 / -1;
  font-size: 22px; font-weight: 600;
  color: var(--fg);
  font-family: 'Inter', 'DM Sans', -apple-system, sans-serif;
  font-variant-numeric: tabular-nums;
  text-align: center;
  padding: 8px 0;
}
.fz-cardio-indoor-track__weight,
.fz-cardio-indoor-track__field {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 11px; color: var(--muted);
  letter-spacing: 0.04em;
}
.fz-cardio-indoor-track__weight { grid-column: 1 / -1; }
.fz-cardio-indoor-track__field--full { grid-column: 1 / -1; }
.fz-cardio-indoor-track__weight input,
.fz-cardio-indoor-track__field input,
.fz-cardio-indoor-track__field textarea {
  appearance: none; -webkit-appearance: none;
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 10px 12px;
  font: inherit; font-size: 15px;
  font-variant-numeric: tabular-nums;
}
.fz-cardio-indoor-track__field textarea {
  font-variant-numeric: normal;
  resize: vertical; min-height: 48px;
}
.fz-cardio-indoor-track__weight input:focus,
.fz-cardio-indoor-track__field input:focus,
.fz-cardio-indoor-track__field textarea:focus {
  outline: 2px solid var(--accent); outline-offset: 1px;
  border-color: var(--accent);
}

/* ────────────────────────────────────────────────────────────
   CARDIO — Summary / Save screen (Sprint 8, v0.9.374)
   cardio_tab_spec.md §2.3. Indoor variant — map snapshot
   surface is intentionally absent (lands with the outdoor
   live-track sprint when GPS / route polyline arrive).
   Tokens-only — no hex literals on paint properties per
   css-hex-lint A11Y-12. --accent / --on-accent for the
   primary CTA per accent-buttons memory.
   ──────────────────────────────────────────────────────────── */
.fz-cardio-summary {
  padding: 16px 16px calc(env(safe-area-inset-bottom, 0px) + 16px);
  display: flex; flex-direction: column; gap: 16px;
}
.fz-cardio-summary__head {
  display: flex; flex-direction: column; gap: 2px;
}
/* Sprint 12 (CARDIO-WEATHER-CAPTURE, v0.9.378) — small chip in the summary
   head per spec §9.2. Renders icon + temperature + condition label + UV
   index when the session carries a captured weather_json snapshot. Tokens-
   only paint per css-hex-lint A11Y-12 / S22 — bg/border off --surface /
   --line, icon stroke off currentColor inherited from --fg. */
.fz-cardio-weather-chip {
  display: inline-flex; align-items: center; gap: 8px;
  margin-top: 6px;
  padding: 6px 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  font-size: 12px;
  color: var(--fg);
  font-variant-numeric: tabular-nums;
  align-self: flex-start;
}
.fz-cardio-weather-chip__icon {
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--fg);
}
.fz-cardio-weather-chip__icon svg { display: block; }
.fz-cardio-weather-chip__temp {
  font-weight: 700;
}
.fz-cardio-weather-chip__cond {
  color: var(--muted);
  letter-spacing: 0.02em;
}
.fz-cardio-weather-chip__uv {
  color: var(--muted);
  border-left: 1px solid var(--line);
  padding-left: 8px;
  font-size: 11px;
  letter-spacing: 0.04em;
}
.fz-cardio-summary__title {
  font-size: 18px; font-weight: 700; color: var(--fg);
}
.fz-cardio-summary__sub {
  font-size: 11px; color: var(--muted);
  letter-spacing: 0.06em; text-transform: uppercase;
}
.fz-cardio-summary__stats {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
}
.fz-cardio-summary__stat {
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 12px;
  display: flex; flex-direction: column; gap: 2px;
}
.fz-cardio-summary__stat-v {
  font-family: 'Inter', 'DM Sans', -apple-system, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 20px; font-weight: 700; color: var(--fg);
}
.fz-cardio-summary__stat-l {
  font-size: 11px; color: var(--muted);
  letter-spacing: 0.04em;
}
.fz-cardio-summary__section {
  background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 12px;
}
.fz-cardio-summary__section-h {
  font-size: 11px; font-weight: 700; color: var(--muted);
  letter-spacing: 0.08em; text-transform: uppercase;
  margin-bottom: 8px;
}
.fz-cardio-summary__splits-wrap {
  overflow-x: auto;
}
.fz-cardio-summary__splits {
  width: 100%; border-collapse: collapse;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.fz-cardio-summary__splits th,
.fz-cardio-summary__splits td {
  padding: 6px 8px;
  text-align: left;
  border-bottom: 1px solid var(--line);
}
.fz-cardio-summary__splits th {
  font-size: 11px; font-weight: 700; color: var(--muted);
  letter-spacing: 0.04em; text-transform: uppercase;
  cursor: pointer; user-select: none;
}
.fz-cardio-summary__splits th:hover { color: var(--fg); }
.fz-cardio-summary__splits td { color: var(--fg); }
.fz-cardio-summary__splits-empty td {
  text-align: center; color: var(--muted);
  padding: 16px 8px; font-style: italic;
}
.fz-cardio-summary__field {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 11px; color: var(--muted);
  letter-spacing: 0.04em;
}
.fz-cardio-summary__field--full { width: 100%; }
.fz-cardio-summary__field select,
.fz-cardio-summary__field input,
.fz-cardio-summary__field textarea {
  appearance: none; -webkit-appearance: none;
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 10px 12px;
  font: inherit; font-size: 15px;
  font-variant-numeric: tabular-nums;
}
.fz-cardio-summary__field textarea {
  font-variant-numeric: normal;
  resize: vertical; min-height: 64px;
}
.fz-cardio-summary__field select:focus,
.fz-cardio-summary__field input:focus,
.fz-cardio-summary__field textarea:focus {
  outline: 2px solid var(--accent); outline-offset: 1px;
  border-color: var(--accent);
}
.fz-cardio-summary__rpe {
  display: flex; flex-direction: column; gap: 6px;
}
.fz-cardio-summary__rpe-head {
  display: flex; justify-content: space-between; align-items: center;
  font-size: 11px; color: var(--muted);
  letter-spacing: 0.04em;
}
.fz-cardio-summary__rpe-v {
  font-family: 'Inter', 'DM Sans', -apple-system, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 18px; font-weight: 700; color: var(--fg);
  letter-spacing: 0;
}
.fz-cardio-summary__rpe input[type="range"] {
  width: 100%;
  accent-color: var(--accent);
}
.fz-cardio-summary__rpe-scale {
  display: flex; justify-content: space-between;
  font-size: 10px; color: var(--muted);
}
.fz-cardio-summary__toggle {
  display: flex; align-items: center; gap: 10px;
  cursor: pointer;
}
.fz-cardio-summary__toggle input[type="checkbox"] {
  width: 20px; height: 20px;
  accent-color: var(--accent);
  cursor: pointer;
}
.fz-cardio-summary__toggle-label {
  font-size: 14px; color: var(--fg);
}
.fz-cardio-summary__actions {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
  margin-top: 4px;
}
.fz-cardio-summary__btn {
  appearance: none; -webkit-appearance: none;
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 14px 16px;
  font: inherit; font-size: 15px; font-weight: 700;
  cursor: pointer;
}
.fz-cardio-summary__btn--save {
  background: var(--accent); color: var(--on-accent);
  border-color: var(--accent);
}
.fz-cardio-summary__btn--ghost {
  background: var(--surface); color: var(--fg);
}
.fz-cardio-summary__btn:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.fz-cardio-summary__btn:disabled {
  opacity: 0.6; cursor: progress;
}

/* ──────────────────────────────────────────────────────────
   Cardio — MAP subsystem (Sprint 9, v0.9.375).
   Leaflet 1.9.x mount point + CSS-var theming layer (spec
   §4.2 — re-themable per tenant without code changes).
   The map root carries .fz-cardio-map; tokens default in
   :root (lime-night + blue-day presets) and may be over-
   ridden per-instance by PwaMapProvider.init({themeVars}).
   The OSM tile-pane carries `filter: var(--cardio-tile-filter)`
   so a tenant theme can hue-shift / desaturate the raster
   tiles to match its brand without re-baking tile assets.
   No hex literals on paint properties — tokens-only, per
   css-hex-lint A11Y-12 / S22.
   ────────────────────────────────────────────────────────── */
.fz-cardio-map {
  position: relative;
  width: 100%;
  min-height: 240px;
  margin: 0;
  padding: 0;
  background: var(--surface);
  /* Sprint 9 ships the abstraction; the indoor live-track surface
     keeps this hidden via display:none in cardio.html — outdoor GPS
     modes will reveal it in the live-track sprint. */
}
.fz-cardio-map .leaflet-tile-pane {
  /* Tenant brand-shift hook (spec §4.2). Default `none` is the
     vanilla OSM raster; an override can set e.g.
     `sepia(0.4) hue-rotate(220deg) saturate(0.85)` to brand-blend
     the basemap. The filter targets the tile pane only — markers,
     polylines, and the attribution control stay unfiltered so
     they keep WCAG contrast on top of the shifted basemap. */
  filter: var(--cardio-tile-filter);
}
.fz-cardio-map .leaflet-control-attribution {
  /* OSM tile-usage policy requires the attribution to remain
     visible. We re-style it to match the SPA tokens but never
     hide it — the css-hex-lint exempts :root tokens, the
     attribution copy itself is preset-aware. */
  background: var(--surface);
  color: var(--muted);
  font-size: 10px;
  padding: 2px 6px;
  border-radius: var(--radius-sm) 0 0 0;
}
.fz-cardio-map .leaflet-control-attribution a {
  color: var(--ink-2);
  border-bottom: 0;
}

/* ──────────────────────────────────────────────────────────
   Cardio — outdoor GPS live-track surface (Sprint 10,
   v0.9.376). Layout per cardio_tab_spec.md §2.2:
     top 60% live map (#fz-cardio-map host) +
     bottom 40% stat panel (.fz-cardio-gps-track__panel) +
     manual lap-button row + iOS banner above the panel +
     paused-overlay layered on top of the whole surface.
   Tokens-only — no hex literals on paint properties per
   css-hex-lint A11Y-12 / S22. --on-accent everywhere a
   control sits on --accent, per rule g + the accent-buttons
   feedback memory.
   ────────────────────────────────────────────────────────── */
.fz-cardio-gps-track {
  position: relative;
  display: flex; flex-direction: column;
  min-height: 40vh;
}
.fz-cardio-gps-track__panel {
  flex: 1;
  display: flex; flex-direction: column;
  background: var(--surface);
  color: var(--fg);
  padding: 12px 16px calc(env(safe-area-inset-bottom, 0px) + 16px);
  gap: 10px;
}
.fz-cardio-gps-track__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
}
.fz-cardio-gps-track__phase {
  font-size: 11px; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted);
}
/* CARDIO-GPS-LIFECYCLE — activity label sits LEFT of the toggle so the
   user always sees what's being measured (walk / run / cycle / …). */
.fz-cardio-gps-track__activity {
  display: flex; flex-direction: column; gap: 2px;
  min-width: 0; flex: 1 1 auto;
}
.fz-cardio-gps-track__activity-l {
  font-size: 11px; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted);
}
.fz-cardio-gps-track__activity-v {
  font-size: 18px; font-weight: 700;
  line-height: 1.1;
  color: var(--fg);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* CARDIO-GPS-LIFECYCLE — START/STOP toggle. Off (data-state="off") is
   the accent-filled primary action. On (data-state="on") flips to a
   destructive outlined treatment so STOP reads as a deliberate
   negative-space action, never confusable with a normal accent CTA.
   --on-accent is used per feedback_accent_buttons_on_accent_token. */
.fz-cardio-gps-track__toggle {
  appearance: none; -webkit-appearance: none;
  border: 0; border-radius: var(--radius-pill);
  padding: 12px 22px;
  font: inherit; font-size: 14px; font-weight: 800;
  letter-spacing: 0.1em; text-transform: uppercase;
  cursor: pointer;
  flex: 0 0 auto;
  min-width: 96px;
  text-align: center;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.fz-cardio-gps-track__toggle[data-state="off"] {
  background: var(--accent); color: var(--on-accent);
}
.fz-cardio-gps-track__toggle[data-state="on"] {
  background: transparent; color: var(--fg);
  border: 2px solid var(--fg);
}
.fz-cardio-gps-track__toggle[data-armed="1"] {
  background: var(--fg); color: var(--surface);
  border-color: var(--fg);
}
.fz-cardio-gps-track__primary {
  text-align: center;
  padding: 6px 0 2px;
}
.fz-cardio-gps-track__primary-v {
  font-size: 44px; font-weight: 700;
  letter-spacing: -1px; line-height: 1;
  color: var(--fg);
  font-family: 'Inter', 'DM Sans', -apple-system, sans-serif;
  font-variant-numeric: tabular-nums;
}
.fz-cardio-gps-track__primary-l {
  font-size: 11px; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted);
  margin-top: 4px;
}
.fz-cardio-gps-track__stats {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
}
.fz-cardio-gps-track__stat {
  background: var(--surface-muted, transparent);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 8px 10px;
  display: flex; flex-direction: column; gap: 2px;
}
.fz-cardio-gps-track__stat-v {
  font-size: 20px; font-weight: 600;
  color: var(--fg);
  font-family: 'Inter', 'DM Sans', -apple-system, sans-serif;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.fz-cardio-gps-track__stat-v .ht-ms {
  font-size: 14px; opacity: 0.6;
}
.fz-cardio-gps-track__stat-l {
  font-size: 10px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted);
}
.fz-cardio-gps-track__weight {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 11px; color: var(--muted);
  letter-spacing: 0.04em;
}
.fz-cardio-gps-track__weight input {
  appearance: none; -webkit-appearance: none;
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 10px 12px;
  font: inherit; font-size: 15px;
  font-variant-numeric: tabular-nums;
}
.fz-cardio-gps-track__actions {
  display: grid; grid-template-columns: auto 1fr; gap: 10px;
  margin-top: auto;
}
.fz-cardio-gps-track__lap {
  appearance: none; -webkit-appearance: none;
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  padding: 12px 22px;
  font: inherit; font-weight: 700; font-size: 13px;
  letter-spacing: 0.08em; text-transform: uppercase;
  cursor: pointer;
}
.fz-cardio-gps-track__pause {
  appearance: none; -webkit-appearance: none;
  background: var(--accent); color: var(--on-accent);
  border: 0; border-radius: var(--radius-pill);
  padding: 14px 28px;
  font: inherit; font-weight: 700; font-size: 14px;
  letter-spacing: 0.04em; text-transform: uppercase;
  cursor: pointer;
}
.fz-cardio-gps-track__pause[data-paused="1"] {
  background: var(--ok, var(--accent)); color: var(--on-accent);
}

/* iOS Safari "keep your screen on" banner — spec §7.7 mitigation.
   Rendered above the stat panel on iOS Safari only; on every other
   browser the bridge skips the markup entirely. */
.fz-cardio-ios-banner {
  background: var(--accent); color: var(--on-accent);
  font-size: 12px; font-weight: 600; line-height: 1.35;
  padding: 8px 14px;
  text-align: center;
}

/* GPS status strip — shown while waiting for the first GPS sample or when
   watchPosition errors out. The dot uses a subtle pulse for the acquiring
   state and a steadier hue for the error state so the user can read intent
   at a glance without reading copy. */
.fz-cardio-gps-status {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 14px;
  background: var(--surface);
  color: var(--fg);
  border-bottom: 1px solid var(--line);
  font-size: 13px; line-height: 1.35;
}
.fz-cardio-gps-status[hidden] { display: none; }
.fz-cardio-gps-status__dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--muted);
  flex: 0 0 auto;
}
.fz-cardio-gps-status[data-state="acquiring"] .fz-cardio-gps-status__dot {
  background: var(--accent);
  animation: fz-cardio-gps-pulse var(--dur-pulse) var(--ease-standard) infinite;
}
.fz-cardio-gps-status[data-state="error"] .fz-cardio-gps-status__dot {
  background: var(--danger);
}
.fz-cardio-gps-status[data-state="error"] {
  color: var(--danger); font-weight: 600;
}
/* CARDIO-GPS-OVERLAY-DISMISS-AND-MAP-FILL (v0.9.420) — paused-pill state.
   Surfaced after the user tap-dismisses the modal overlay; clicking the
   pill re-opens the modal so the full guidance is one tap away. The
   warning hue uses --warning if defined, falling back to --accent so the
   theme's day/night both have a sensible amber. */
.fz-cardio-gps-status[data-state="paused"] .fz-cardio-gps-status__dot {
  background: var(--warning, var(--accent));
}
.fz-cardio-gps-status[data-state="paused"] {
  color: var(--fg); font-weight: 600;
  cursor: pointer;
  touch-action: manipulation;
}
.fz-cardio-gps-status__msg { flex: 1; }
@keyframes fz-cardio-gps-pulse {
  0%, 100% { opacity: 0.35; transform: scale(0.85); }
  50%      { opacity: 1;    transform: scale(1.15); }
}

/* CARDIO-MAP-FILL + CARDIO-GPS-OVERLAY-DISMISS-AND-MAP-FILL (v0.9.611) —
   When the GPS map host is visible (showMapHost() sets data-fz-map-visible),
   flip the body into an exact-height flex column so #fz-cardio-map stretches
   to fill ALL remaining space below the stat panel. Using height:100dvh (not
   min-height) is critical — min-height let the container grow taller than the
   iframe, leaving a gray strip above or below the map. overflow:hidden clips
   any accidental overflow inside the iframe boundary.
   data-fz-cardio-mode="gps" is kept for GPS-specific sub-rules (gps-track
   min-height, etc.) but the flex layout now lives on data-fz-map-visible so
   future non-GPS map surfaces get the same fill automatically. */
body[data-fz-map-visible] {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  margin: 0;
}
body[data-fz-map-visible] #fz-cardio-app {
  flex: 0 0 auto;
  min-height: 0;
  padding-bottom: 0;
}
body[data-fz-map-visible] #fz-cardio-map {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
}
body[data-fz-cardio-mode="gps"] .fz-cardio-gps-track {
  min-height: 0;
}

/* CARDIO-MAP-RECENTER-BTN (v0.9.611) — FAB overlaid on the GPS map.
   Hidden until the user drags (dragstart → setFollow(false)); tap re-enables
   auto-follow and pans back to the current position. position:absolute works
   because Leaflet sets position:relative on its container during init(). */
.fz-cardio-recenter {
  position: absolute;
  bottom: 16px;
  right: 16px;
  z-index: 1000;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: none;
  background: var(--surface);
  color: var(--accent);
  box-shadow: var(--elev-1);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
}
.fz-cardio-recenter[hidden] { display: none; }

/* CARDIO-MAP-BACKGROUND-PICKER (v0.9.613) — map is the full-screen
   background layer; picker tiles float over it in a transparent overlay.
   #fz-cardio-app uses pointer-events:none so touches on the transparent
   gaps fall through to the Leaflet map (allowing map pan). The picker
   itself restores pointer-events:auto so cards + scroll work normally.
   The picker is its own scroll container (overflow-y:auto, max-height
   fits viewport) so vertical fling on the cards area scrolls the list. */
body[data-fz-cardio-mode="picker"] #fz-cardio-map {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: block;
}
body[data-fz-cardio-mode="picker"] #fz-cardio-app {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: transparent;
  min-height: 0;
  padding: 0;
  overflow: visible;
}
body[data-fz-cardio-mode="picker"] .fz-cardio-picker {
  pointer-events: auto;
  background: transparent;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  max-height: calc(100dvh - env(safe-area-inset-top, 0px));
  /* CHAT-LAUNCH-VISIBLE-ON-ALL-SURFACES-NO-OVERLAP (v0.9.701) — the parent's
     global chat-launch pill floats over the top-right of this full-screen map
     view; add the pill strip (52px) to the top padding so the first activity
     card never slides under it. (Shared app.css: this rule runs inside the
     cardio iframe, the recenter button is bottom-right so it's unaffected.) */
  padding: calc(env(safe-area-inset-top, 0px) + 16px + 52px) 16px
           calc(env(safe-area-inset-bottom, 0px) + var(--tabbar-h, 64px) + 8px);
}
/* Picker recenter button — always visible, sits above tabbar. Overrides
   the GPS recenter's bottom:16px with room for the tabbar. */
.fz-cardio-recenter--picker {
  display: flex;
  bottom: calc(var(--tabbar-h, 64px) + env(safe-area-inset-bottom, 0px) + 12px);
  pointer-events: auto;
}

/* ===== Sprint 11 — CARDIO-HR-WEB-BLUETOOTH-AND-ZONES (v0.9.377) =====
   HR widget + zone color tokens + BPM-synced pulse animation + post-
   session zone-bar chart per cardio_tab_spec §6.5 + §6.6. The zone
   colors map Karvonen-style intensity to perceptually-ordered hues
   (cool blue → warm red) and resolve against the same Dag + Nacht
   token presets the rest of the cardio surface uses, so contrast
   holds in both themes. */
:root,
html[data-theme-mode="dark"] {
  --hr-zone-1: #2e7dff;  /* Recovery — cool blue */
  --hr-zone-2: #2ec4b6;  /* Aerobic — teal */
  --hr-zone-3: #c8ff4a;  /* Tempo — accent lime */
  --hr-zone-4: #ff9f1c;  /* Threshold — amber */
  --hr-zone-5: #e84545;  /* VO2 max — red */
  --hr-zone-fg: #121212;
}
html[data-theme-mode="light"] {
  --hr-zone-1: #2a6df4;
  --hr-zone-2: #14a39a;
  --hr-zone-3: #88b800;
  --hr-zone-4: #d97706;
  --hr-zone-5: #c0392b;
  --hr-zone-fg: #ffffff;
}

/* BPM-synced pulse — animation-duration is driven by an inline
   --fz-hr-pulse-dur CSS variable set by cardio-bridge.js's hrWidgetHtml()
   to (60 / bpm)s. The keyframes scale the .fz-cardio-hr__big block by
   3 % at the systolic peak so the pulse is visible without dominating
   the layout. */
@keyframes fz-hr-pulse {
  0%   { transform: scale(1);    opacity: 1; }
  35%  { transform: scale(1.03); opacity: 0.95; }
  60%  { transform: scale(1);    opacity: 1; }
  100% { transform: scale(1);    opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  @keyframes fz-hr-pulse {
    0%, 100% { transform: none; opacity: 1; }
  }
}

.fz-cardio-hr {
  margin: 12px 0 0;
  padding: 14px;
  border-radius: var(--radius-lg);
  background: var(--surface);
  border: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 10px;
  transition: background-color var(--dur-base) var(--ease-standard), border-color var(--dur-base) var(--ease-standard), color var(--dur-base) var(--ease-standard);
}
.fz-cardio-hr[data-zone="Z1"] { background: var(--hr-zone-1); border-color: var(--hr-zone-1); color: var(--hr-zone-fg); }
.fz-cardio-hr[data-zone="Z2"] { background: var(--hr-zone-2); border-color: var(--hr-zone-2); color: var(--hr-zone-fg); }
.fz-cardio-hr[data-zone="Z3"] { background: var(--hr-zone-3); border-color: var(--hr-zone-3); color: var(--hr-zone-fg); }
.fz-cardio-hr[data-zone="Z4"] { background: var(--hr-zone-4); border-color: var(--hr-zone-4); color: var(--hr-zone-fg); }
.fz-cardio-hr[data-zone="Z5"] { background: var(--hr-zone-5); border-color: var(--hr-zone-5); color: var(--hr-zone-fg); }

.fz-cardio-hr__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px;
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em;
}
.fz-cardio-hr__zone {
  font-weight: 700;
  font-size: 13px;
}
.fz-cardio-hr__max {
  font-size: 11px; opacity: 0.8;
}
.fz-cardio-hr__connect {
  appearance: none;
  background: var(--accent); color: var(--on-accent);
  border: 0; border-radius: var(--radius-pill);
  padding: 6px 14px;
  font-size: 12px; font-weight: 700;
  cursor: pointer;
}
.fz-cardio-hr__connect:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
.fz-cardio-hr__big {
  display: flex; align-items: baseline; justify-content: center;
  gap: 6px;
  font-variant-numeric: tabular-nums;
  animation: fz-hr-pulse var(--fz-hr-pulse-dur, var(--dur-pulse)) var(--ease-standard) infinite;
  transform-origin: center;
}
.fz-cardio-hr[data-zone] .fz-cardio-hr__big {
  /* Only animate when connected (zone attribute means we have a BPM) */
}
.fz-cardio-hr:not([data-zone]) .fz-cardio-hr__big {
  animation: none;
}
.fz-cardio-hr__bpm {
  font-size: 56px; font-weight: 800; line-height: 1;
}
.fz-cardio-hr__bpm-l {
  font-size: 14px; font-weight: 600; opacity: 0.8;
}
.fz-cardio-hr__manual {
  display: flex; gap: 8px; align-items: center; justify-content: center;
}
.fz-cardio-hr__tap {
  appearance: none;
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 6px 14px;
  font-size: 13px; font-weight: 700;
  cursor: pointer;
}
.fz-cardio-hr[data-zone] .fz-cardio-hr__tap {
  background: rgba(0, 0, 0, 0.18);
  color: var(--hr-zone-fg);
  border-color: rgba(0, 0, 0, 0.25);
}
.fz-cardio-hr__manual input[type="number"] {
  appearance: none;
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 6px 10px;
  font-size: 13px;
  width: 80px; text-align: center;
}
.fz-cardio-hr[data-zone] .fz-cardio-hr__manual input[type="number"] {
  background: rgba(0, 0, 0, 0.18);
  color: var(--hr-zone-fg);
  border-color: rgba(0, 0, 0, 0.25);
}
.fz-cardio-hr__tiz {
  display: grid; grid-template-columns: repeat(5, 1fr);
  gap: 6px;
  margin-top: 4px;
}
.fz-cardio-hr__tiz-cell {
  text-align: center;
  border-radius: var(--radius-md);
  padding: 6px 4px;
  background: rgba(255, 255, 255, 0.06);
  font-variant-numeric: tabular-nums;
}
.fz-cardio-hr[data-zone] .fz-cardio-hr__tiz-cell {
  background: rgba(0, 0, 0, 0.18);
}
.fz-cardio-hr__tiz-l {
  font-size: 11px; font-weight: 700; opacity: 0.75;
}
.fz-cardio-hr__tiz-v {
  font-size: 13px; font-weight: 700; line-height: 1.2;
}
.fz-cardio-hr__tiz-cell[data-zone="Z1"] .fz-cardio-hr__tiz-l { color: var(--hr-zone-1); }
.fz-cardio-hr__tiz-cell[data-zone="Z2"] .fz-cardio-hr__tiz-l { color: var(--hr-zone-2); }
.fz-cardio-hr__tiz-cell[data-zone="Z3"] .fz-cardio-hr__tiz-l { color: var(--hr-zone-3); }
.fz-cardio-hr__tiz-cell[data-zone="Z4"] .fz-cardio-hr__tiz-l { color: var(--hr-zone-4); }
.fz-cardio-hr__tiz-cell[data-zone="Z5"] .fz-cardio-hr__tiz-l { color: var(--hr-zone-5); }
.fz-cardio-hr[data-zone] .fz-cardio-hr__tiz-cell .fz-cardio-hr__tiz-l {
  color: var(--hr-zone-fg);
}

/* Indoor + GPS surfaces both render the widget via a [data-fz-hr-slot]
   wrapper. The wrappers below give the widget the right side-padding
   so it lines up with the surrounding stat / action rows. */
.fz-cardio-indoor-track__hr,
.fz-cardio-gps-track__hr {
  padding: 0 14px;
}

/* Post-session zone-bar chart (§6.6) — 5 horizontal bars, color-coded,
   length proportional to minutes-in-zone. The .fz-cardio-zonebar__rng
   pill carries the BPM range for that zone (e.g. "114–133 bpm") so the
   user can read max_hr / zones at a glance. */
.fz-cardio-zonebar {
  display: flex; flex-direction: column; gap: 8px;
  padding: 4px 0 2px;
}
.fz-cardio-zonebar--empty {
  color: var(--muted);
  font-size: 13px;
  padding: 12px 0;
  text-align: center;
}
.fz-cardio-zonebar__row {
  display: grid;
  grid-template-columns: 28px 1fr auto;
  align-items: center;
  gap: 10px;
  font-variant-numeric: tabular-nums;
}
.fz-cardio-zonebar__l {
  font-weight: 700; font-size: 13px;
  text-align: center;
}
.fz-cardio-zonebar__row[data-zone="Z1"] .fz-cardio-zonebar__l { color: var(--hr-zone-1); }
.fz-cardio-zonebar__row[data-zone="Z2"] .fz-cardio-zonebar__l { color: var(--hr-zone-2); }
.fz-cardio-zonebar__row[data-zone="Z3"] .fz-cardio-zonebar__l { color: var(--hr-zone-3); }
.fz-cardio-zonebar__row[data-zone="Z4"] .fz-cardio-zonebar__l { color: var(--hr-zone-4); }
.fz-cardio-zonebar__row[data-zone="Z5"] .fz-cardio-zonebar__l { color: var(--hr-zone-5); }
.fz-cardio-zonebar__track {
  height: 14px; border-radius: var(--radius-md);
  background: var(--line);
  overflow: hidden;
}
.fz-cardio-zonebar__fill {
  height: 100%;
  border-radius: var(--radius-md);
  transition: width var(--dur-base) var(--ease-standard);
}
.fz-cardio-zonebar__row[data-zone="Z1"] .fz-cardio-zonebar__fill { background: var(--hr-zone-1); }
.fz-cardio-zonebar__row[data-zone="Z2"] .fz-cardio-zonebar__fill { background: var(--hr-zone-2); }
.fz-cardio-zonebar__row[data-zone="Z3"] .fz-cardio-zonebar__fill { background: var(--hr-zone-3); }
.fz-cardio-zonebar__row[data-zone="Z4"] .fz-cardio-zonebar__fill { background: var(--hr-zone-4); }
.fz-cardio-zonebar__row[data-zone="Z5"] .fz-cardio-zonebar__fill { background: var(--hr-zone-5); }
.fz-cardio-zonebar__v {
  font-size: 13px; font-weight: 700;
  display: flex; align-items: baseline; gap: 6px;
}
.fz-cardio-zonebar__rng {
  font-size: 10px; font-weight: 600;
  opacity: 0.6;
}

/* ───────────────────────────────────────────────────────────────
   Sprint 13 (CARDIO-SAVED-ROUTES-AND-GHOST-RERUN, v0.9.379)
   cardio_tab_spec §11 — saved-routes library + ghost re-run +
   post-finish side-by-side comparison. Three blocks below:
     .fz-cardio-route-library  — list + filter chips + sort + range
     .fz-cardio-ghost-stat-cell — live ghost-time stat (ahead/behind)
     .fz-cardio-comparison      — summary side-by-side new vs prev
   ───────────────────────────────────────────────────────────── */

/* --cardio-ghost is the reference-route polyline token; resolved by
   PwaMapProvider.addGhostPolyline. Themed defaults per preset so a
   tenant skin can override via CSS-only. */
:root,
html[data-theme-mode="dark"] {
  --cardio-ghost: #5b8def;
  --cardio-ghost-stat-ahead:  #6ee85a;
  --cardio-ghost-stat-behind: #ff7e7e;
}
html[data-theme-mode="light"] {
  --cardio-ghost: #ffb300;
  --cardio-ghost-stat-ahead:  #1f9a4b;
  --cardio-ghost-stat-behind: #d23434;
}

.fz-cardio-route-library { padding: 16px 16px calc(env(safe-area-inset-bottom, 0px) + 16px); }
.fz-cardio-route-library__head {
  display: flex; align-items: center; gap: 10px; margin-bottom: 12px;
}
.fz-cardio-route-library__back {
  appearance: none; background: transparent; color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  padding: 6px 12px; font-size: 12px; font-weight: 600; cursor: pointer;
}
.fz-cardio-route-library__title {
  font-size: 15px; font-weight: 700; letter-spacing: 0.01em;
}
.fz-cardio-route-library__count {
  margin-left: auto; font-size: 11px; color: var(--muted);
}
.fz-cardio-route-library__filters {
  display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px;
}
.fz-cardio-route-library__chip {
  appearance: none; background: var(--surface); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  padding: 5px 10px; font-size: 11px; font-weight: 600; cursor: pointer;
  letter-spacing: 0.02em;
}
.fz-cardio-route-library__chip[aria-pressed="true"] {
  background: var(--accent); color: var(--on-accent); border-color: var(--accent);
}
.fz-cardio-route-library__sort-row {
  display: flex; align-items: center; gap: 8px; margin-bottom: 14px;
  flex-wrap: wrap;
}
.fz-cardio-route-library__sort-row label {
  font-size: 11px; color: var(--muted); text-transform: uppercase;
  letter-spacing: 0.08em; font-weight: 600;
}
.fz-cardio-route-library__sort {
  appearance: none; background: var(--surface); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 6px 10px; font-size: 12px;
}
.fz-cardio-route-library__distance-range {
  display: flex; align-items: center; gap: 6px; flex: 1; min-width: 0;
}
.fz-cardio-route-library__distance-range input {
  appearance: none; -webkit-appearance: none;
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 6px 8px; font-size: 12px; width: 100%; min-width: 0;
  font-variant-numeric: tabular-nums;
}
.fz-cardio-route-library__distance-range span { font-size: 11px; color: var(--muted); }
.fz-cardio-route-library__list {
  display: flex; flex-direction: column; gap: 10px;
}
.fz-cardio-route-library__row {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 12px;
  display: grid; grid-template-columns: 64px 1fr auto; gap: 12px;
  align-items: center; cursor: pointer;
}
.fz-cardio-route-library__row:focus-within {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.fz-cardio-route-library__snap {
  width: 64px; height: 64px; border-radius: var(--radius-md);
  background-color: var(--surface);
  background-size: cover; background-position: center;
  border: 1px solid var(--line); overflow: hidden;
}
.fz-cardio-route-library__snap--placeholder {
  background-image:
    linear-gradient(45deg, transparent 48%, var(--cardio-polyline, var(--accent)) 48%, var(--cardio-polyline, var(--accent)) 52%, transparent 52%),
    linear-gradient(-30deg, transparent 60%, var(--cardio-ghost) 60%, var(--cardio-ghost) 64%, transparent 64%);
  opacity: 0.55;
}
.fz-cardio-route-library__name {
  font-size: 14px; font-weight: 600; margin-bottom: 4px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fz-cardio-route-library__meta {
  font-size: 11px; color: var(--muted);
  display: flex; gap: 8px; flex-wrap: wrap;
}
.fz-cardio-route-library__meta b { color: var(--fg); font-weight: 600; }
.fz-cardio-route-library__star {
  appearance: none; background: transparent; border: 0; cursor: pointer;
  padding: 6px; color: var(--muted);
}
.fz-cardio-route-library__star[aria-pressed="true"] { color: var(--accent); }
.fz-cardio-route-library__star svg {
  width: 22px; height: 22px;
  stroke: currentColor; fill: none; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}
.fz-cardio-route-library__star[aria-pressed="true"] svg { fill: currentColor; }
.fz-cardio-route-library__empty {
  text-align: center; padding: 40px 20px; color: var(--muted); font-size: 12px;
}
.fz-cardio-route-library__detail {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 14px; margin-top: 14px; display: none;
}
.fz-cardio-route-library__detail.is-open { display: block; }
.fz-cardio-route-library__detail-h {
  font-size: 13px; font-weight: 600; margin-bottom: 6px;
}
.fz-cardio-route-library__detail-meta {
  font-size: 11px; color: var(--muted); margin-bottom: 12px;
}
.fz-cardio-route-library__detail-actions {
  display: flex; gap: 8px; flex-wrap: wrap;
}
.fz-cardio-route-library__btn {
  appearance: none; flex: 1 1 0; padding: 9px 14px; border-radius: var(--radius-md);
  font-size: 12px; font-weight: 700; cursor: pointer;
  letter-spacing: 0.04em; text-transform: uppercase;
}
.fz-cardio-route-library__btn--rerun {
  background: var(--accent); color: var(--on-accent); border: 0;
}
.fz-cardio-route-library__btn--rename {
  background: transparent; color: var(--fg); border: 1px solid var(--line);
}
.fz-cardio-route-library__btn--delete {
  background: transparent; color: var(--cardio-ghost-stat-behind);
  border: 1px solid var(--cardio-ghost-stat-behind);
}

/* Ghost-time stat — overrides the calorie stat cell during a re-run.
   Bridge writes data-fz-ghost-state="ahead|behind" so we recolour
   without churning DOM. */
.fz-cardio-ghost-stat-cell {
  font-variant-numeric: tabular-nums;
}
.fz-cardio-ghost-stat-cell[data-fz-ghost-state="ahead"]  { color: var(--cardio-ghost-stat-ahead); }
.fz-cardio-ghost-stat-cell[data-fz-ghost-state="behind"] { color: var(--cardio-ghost-stat-behind); }

/* Post-finish side-by-side comparison. Renders inside a
   .fz-cardio-summary__section block, so margins are owned by the
   parent — our root just paints the card chrome. */
.fz-cardio-comparison {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 14px;
}
.fz-cardio-comparison__h {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--muted); font-weight: 600; margin-bottom: 10px;
}
.fz-cardio-comparison__cols {
  display: grid; grid-template-columns: 1fr 12px 1fr; gap: 8px;
  align-items: stretch;
}
.fz-cardio-comparison__col {
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 10px;
}
.fz-cardio-comparison__col-h {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--muted); font-weight: 600; margin-bottom: 8px;
}
.fz-cardio-comparison__col--new  .fz-cardio-comparison__col-h { color: var(--accent); }
.fz-cardio-comparison__col--prev .fz-cardio-comparison__col-h { color: var(--cardio-ghost); }
.fz-cardio-comparison__row {
  display: flex; justify-content: space-between; padding: 4px 0;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.fz-cardio-comparison__row b { font-weight: 600; }
.fz-cardio-comparison__divider {
  align-self: center; color: var(--muted); font-weight: 700;
  text-align: center; font-size: 12px;
}
.fz-cardio-comparison__delta {
  margin-top: 12px; padding: 10px; border-radius: var(--radius-md);
  display: flex; justify-content: space-between; align-items: center;
}
.fz-cardio-comparison__delta--ahead {
  background: color-mix(in srgb, var(--cardio-ghost-stat-ahead) 12%, transparent);
  border: 1px solid var(--cardio-ghost-stat-ahead);
}
.fz-cardio-comparison__delta--behind {
  background: color-mix(in srgb, var(--cardio-ghost-stat-behind) 12%, transparent);
  border: 1px solid var(--cardio-ghost-stat-behind);
}
.fz-cardio-comparison__delta-l {
  font-size: 11px; color: var(--muted); text-transform: uppercase;
  letter-spacing: 0.08em; font-weight: 600;
}
.fz-cardio-comparison__delta-v {
  font-size: 16px; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.fz-cardio-comparison__delta--ahead  .fz-cardio-comparison__delta-v { color: var(--cardio-ghost-stat-ahead); }
.fz-cardio-comparison__delta--behind .fz-cardio-comparison__delta-v { color: var(--cardio-ghost-stat-behind); }

/* v0.9.380 CARDIO-MANUAL-ROUTE-DRAWING-AND-ADHERENCE (cardio_tab_spec §10)
   Pre-planned route DRAWING surface + LIVE-session adherence overlay.
   The planner panel floats over the full-bleed map; the adherence overlay
   stacks above the GPS stat panel so the dashed planned-route polyline
   stays visible behind it. Stray-toast pins to the bottom of the surface
   with a single Sassu-branded action. Waypoint markers use a DivIcon so
   theming flows through CSS variables (no sprite swaps). */
/* CARDIO-MAP-RENDER-LAYOUT-AUDIT-AND-FIX (v0.9.465) — planner switches from
   floating absolute-overlay to a normal-flow flex column. The dedicated map
   host (#fz-cardio-planner-map) sits between the stats row and the form as
   a flex:1 1 0 slot so the layout reads header → hint → stats → big map →
   form → actions (Jayy screenshot 2 / _MOCK_cardio_map_themed_v1.html ratio
   ~50–60% viewport). Pre-fix used position:absolute + margin-top:auto on the
   form which left an empty band above the form and the persistent #fz-cardio-map
   sibling extending past the iframe viewport. min-height:100dvh keeps the
   surface stable across iOS Safari URL-bar collapse; padding-bottom carries
   the kaizen 2026-06-06 STRENGTH-EDIT-ARCHIVE-SCROLL-OVERFLOW-FIX defensive
   pattern (safe-area + 16px buffer) so any future fixed-bottom UI can't clip
   the Save row. */
.fz-cardio-route-planner {
  display: flex; flex-direction: column; gap: 8px;
  padding: 12px 12px calc(env(safe-area-inset-bottom, 0px) + 16px);
  min-height: 100vh;
  min-height: 100dvh;
}
.fz-cardio-route-planner__map {
  flex: 1 1 0;
  min-height: 240px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--line);
}
.fz-cardio-route-planner__head {
  display: flex; align-items: center; gap: 8px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 8px 10px;
  color: var(--fg);
}
.fz-cardio-route-planner__back,
.fz-cardio-route-planner__undo {
  background: transparent; color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 6px 10px; font-size: 13px; cursor: pointer;
}
.fz-cardio-route-planner__undo[disabled] { opacity: 0.4; cursor: default; }
.fz-cardio-route-planner__title {
  flex: 1; text-align: center;
  font-size: 14px; font-weight: 600;
}
.fz-cardio-route-planner__hint {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 8px 12px;
  font-size: 12px; color: var(--muted);
}
.fz-cardio-route-planner__stats {
  display: flex; justify-content: space-around; gap: 8px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 8px 12px;
  font-size: 13px; color: var(--fg);
  font-variant-numeric: tabular-nums;
}
.fz-cardio-route-planner__form {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 12px;
}
.fz-cardio-route-planner__field { display: grid; gap: 4px; font-size: 12px; color: var(--muted); }
.fz-cardio-route-planner__field--full { grid-column: 1 / -1; }
.fz-cardio-route-planner__field input,
.fz-cardio-route-planner__field select,
.fz-cardio-route-planner__field textarea {
  background: var(--bg); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 8px 10px; font-size: 14px;
  font-family: inherit;
}
.fz-cardio-route-planner__field textarea { resize: vertical; min-height: 56px; }
.fz-cardio-route-planner__form-foot {
  grid-column: 1 / -1;
  display: flex; gap: 8px; justify-content: flex-end;
}
.fz-cardio-route-planner__btn {
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: 10px 14px; font-size: 14px; font-weight: 600; cursor: pointer;
}
.fz-cardio-route-planner__btn--secondary {
  background: var(--surface); color: var(--fg);
}
.fz-cardio-route-planner__btn--primary {
  background: var(--accent); color: var(--on-accent);
  border-color: var(--accent);
}
.fz-cardio-route-planner__btn[disabled] { opacity: 0.5; cursor: default; }

.fz-cardio-route-library__draw {
  background: var(--accent); color: var(--on-accent);
  border: 1px solid var(--accent); border-radius: var(--radius-md);
  padding: 6px 12px; font-size: 13px; font-weight: 600; cursor: pointer;
}

/* Waypoint markers — Leaflet DivIcon. The numbered chip uses the planned-
   route color so the planner reads as a single visual system. */
.fz-cardio-waypoint {
  background: transparent;
  pointer-events: auto;
}
.fz-cardio-waypoint__num {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--cardio-planned);
  color: var(--on-planned);
  border: 2px solid var(--surface);
  font-size: 12px; font-weight: 700;
  font-family: inherit;
  box-shadow: var(--elev-1);
}

/* Adherence overlay — slots inside the .fz-cardio-gps-track__panel. The
   percentage + deviation cells mirror the .fz-cardio-gps-track__stat
   shape so the surface reads as a cohesive stack. data-fz-adh-strayed
   tints the overlay red when the user is past the configured threshold. */
.fz-cardio-adherence-overlay {
  display: flex; flex-direction: column; gap: 6px;
  margin: 4px 0;
  padding: 8px 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
}
.fz-cardio-adherence-overlay__head {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; color: var(--muted);
}
.fz-cardio-adherence-overlay__name {
  flex: 1;
  font-size: 12px; font-weight: 600; color: var(--fg);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fz-cardio-adherence-overlay__close {
  background: transparent; border: 0; color: var(--muted);
  font-size: 18px; line-height: 1; cursor: pointer; padding: 2px 6px;
}
.fz-cardio-adherence-overlay__row {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
}
.fz-cardio-adherence-overlay__cell {
  text-align: center;
  padding: 4px 0;
}
.fz-cardio-adherence-overlay__v {
  font-size: 18px; font-weight: 700; color: var(--fg);
  font-variant-numeric: tabular-nums;
}
.fz-cardio-adherence-overlay__l {
  font-size: 11px; color: var(--muted);
}
.fz-cardio-adherence-overlay[data-fz-adh-strayed="1"] {
  border-color: var(--danger);
}
.fz-cardio-adherence-overlay[data-fz-adh-strayed="1"] .fz-cardio-adherence-overlay__v {
  color: var(--danger);
}

.fz-cardio-route-library__btn--attach {
  background: var(--cardio-planned); color: var(--on-planned);
  border-color: var(--cardio-planned);
}

/* Stray-from-route toast — single-line notification with a Sassu-styled
   "Uit" disable action. Pinned bottom-center; fades after ~5.5 s in JS. */
.fz-cardio-stray-toast {
  position: absolute;
  left: 50%; transform: translateX(-50%);
  bottom: calc(env(safe-area-inset-bottom, 0) + 76px);
  z-index: 12;
  display: flex; align-items: center; gap: 10px;
  background: var(--surface);
  color: var(--fg);
  border: 1px solid var(--danger);
  border-radius: var(--radius-pill);
  padding: 8px 12px 8px 14px;
  font-size: 13px;
  box-shadow: var(--elev-2);
  max-width: calc(100% - 24px);
}
.fz-cardio-stray-toast__msg { color: var(--fg); }
.fz-cardio-stray-toast__action {
  background: transparent; color: var(--muted);
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  padding: 4px 10px; font-size: 12px; cursor: pointer;
}

/* CARDIO-CONSENT-AND-PRIVACY-UI (cardio_tab_spec §13) — first-time GPS
   consent dialog rendered into #fz-cardio-app, gating the Modality Picker
   from starting an outdoor session until users.cardio_consent_at is
   stamped via POST /api/me/cardio-consent. Anatomie spiegelt de overige
   cardio-surfaces (rounded card, --surface fill, accent CTA); copy zelf
   blijft sober en plain-language Nederlands. */
.fz-cardio-consent {
  padding: 16px;
  display: flex; flex-direction: column; gap: 14px;
  max-width: 520px; margin: 0 auto;
  /* Own scroll container so the CTA stays reachable inside the cardio iframe,
     independent of the parent SPA scroll + bottom-nav chrome. */
  max-height: 100vh;
  max-height: 100dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  /* Reserve room for the iOS home indicator + the parent app's bottom-nav
     (~72px) so the OK button never hides behind the chrome. */
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 88px);
}
.fz-cardio-consent__h {
  margin: 0;
  font-size: 18px; font-weight: 700; line-height: 1.25;
  color: var(--fg);
}
.fz-cardio-consent__lede {
  margin: 0; font-size: 14px; color: var(--fg); line-height: 1.45;
}
.fz-cardio-consent__card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 14px;
  display: flex; flex-direction: column; gap: 10px;
}
.fz-cardio-consent__card-h {
  margin: 0; font-size: 12px; color: var(--muted);
  letter-spacing: 0.1em; text-transform: uppercase; font-weight: 600;
}
.fz-cardio-consent__list {
  margin: 0; padding-left: 18px;
  font-size: 13px; line-height: 1.5; color: var(--fg);
}
.fz-cardio-consent__list li { margin-bottom: 4px; }
.fz-cardio-consent__list li:last-child { margin-bottom: 0; }
.fz-cardio-consent__note {
  margin: 0; font-size: 12px; color: var(--muted); line-height: 1.45;
}
.fz-cardio-consent__actions {
  display: flex; flex-direction: column; gap: 8px;
  margin-top: 4px;
}
.fz-cardio-consent__btn {
  appearance: none; -webkit-appearance: none;
  border: 0; border-radius: var(--radius-pill);
  padding: 12px 16px; font: inherit;
  font-size: 14px; font-weight: 700; letter-spacing: 0.01em;
  cursor: pointer;
}
.fz-cardio-consent__btn--ok {
  background: var(--accent); color: var(--on-accent);
}
.fz-cardio-consent__btn--ok[disabled] {
  opacity: 0.6; cursor: progress;
}
.fz-cardio-consent__btn--no {
  background: transparent; color: var(--fg);
  border: 1px solid var(--line);
}
.fz-cardio-consent__error {
  margin: 0; padding: 8px 10px;
  background: var(--surface); border: 1px solid var(--danger);
  border-radius: var(--radius-md); color: var(--danger);
  font-size: 12px; line-height: 1.4;
}

/* CARDIO-CONSENT-SCREEN-SCROLLABLE-AND-COMPACT-ON-SMALL-IPHONE — compact the
   surface on short viewports (iPhone 13/14 mini, SE 3) so the OK button fits
   above the parent SPA bottom-nav. CTA padding stays ≥44px (Apple HIG). */
@media (max-height: 740px) {
  .fz-cardio-consent {
    gap: 10px; padding: 12px 14px;
    padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 88px);
  }
  .fz-cardio-consent__lede { font-size: 13px; }
  .fz-cardio-consent__card { gap: 8px; padding: 12px; }
  .fz-cardio-consent__card-h { font-size: 11px; }
  .fz-cardio-consent__list { line-height: 1.4; }
  .fz-cardio-consent__actions { margin-top: 0; }
}

/* CARDIO-TILES-OUTDOOR-FOCUSED-REWORK — inline 'What was it?' prompt for the
   Other tile. Modal overlay over the picker; the card mirrors the consent
   dialog token palette (--surface / --line / --accent / --on-accent). */
.fz-cardio-other-prompt {
  position: fixed; inset: 0; z-index: 50;
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
  background: rgba(0, 0, 0, 0.5);
}
.fz-cardio-other-prompt__card {
  width: 100%; max-width: 360px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 16px;
  display: flex; flex-direction: column; gap: 12px;
  box-shadow: var(--elev-3);
}
.fz-cardio-other-prompt__h {
  margin: 0; font-size: 16px; color: var(--fg); font-weight: 700;
}
.fz-cardio-other-prompt__input {
  appearance: none; -webkit-appearance: none;
  width: 100%; box-sizing: border-box;
  padding: 10px 12px; font: inherit; font-size: 15px;
  background: var(--bg); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius-md);
}
.fz-cardio-other-prompt__input:focus {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
.fz-cardio-other-prompt__hint {
  margin: 0; font-size: 12px; color: var(--danger); line-height: 1.4;
}
.fz-cardio-other-prompt__actions {
  display: flex; gap: 8px; justify-content: flex-end;
}
.fz-cardio-other-prompt__btn {
  appearance: none; -webkit-appearance: none;
  border: 0; border-radius: var(--radius-pill);
  padding: 10px 16px; font: inherit;
  font-size: 14px; font-weight: 700;
  cursor: pointer;
}
.fz-cardio-other-prompt__btn--primary {
  background: var(--accent); color: var(--on-accent);
}
.fz-cardio-other-prompt__btn--secondary {
  background: transparent; color: var(--fg);
  border: 1px solid var(--line);
}

/* Mijn data — Settings sectie (spec §13.4 + §13.5). Reuses the existing
   .set-* card / row idioms in renderSettings(); only the in-progress
   spinner + 30-day deletion banner need their own classes. */
.set-mijn-data__hint {
  margin: 0 0 10px; font-size: 12px; color: var(--muted); line-height: 1.45;
}
.set-mijn-data__busy {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--muted);
}
.set-mijn-data__deleted {
  margin-top: 10px; padding: 10px 12px;
  background: var(--surface); border: 1px solid var(--accent);
  border-radius: var(--radius-md); font-size: 13px; color: var(--fg);
  line-height: 1.45;
}

/* CARDIO-SETTINGS-AND-WHITELABEL-THEMING (v0.9.383) — Cardio settings card
   rows. Reuses the .set-* base patterns (set-group / set-card / set-row);
   only the segmented-control bar + the labelled fields need their own
   classes. Tokens come from the Dag + Nacht preset vars so both themes get
   correct contrast without per-rule overrides. Accent-on-accent rule g:
   every accent-background tile takes its text colour from --on-accent,
   never a literal #fff/white. */
.set-cardio-field {
  display: flex; flex-direction: column; gap: 6px;
  padding: 12px 14px;
}
.set-cardio-field__label {
  font-size: 12px; color: var(--muted); line-height: 1.3;
  letter-spacing: 0.01em;
}
.set-cardio-seg {
  display: flex; flex-wrap: wrap; gap: 6px;
}
.set-cardio-seg__tile {
  flex: 1 1 0; min-width: 0;
  padding: 9px 10px;
  background: var(--surface-muted);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font: inherit; font-size: 13px; font-weight: 500;
  cursor: pointer;
  text-align: center;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard);
}
.set-cardio-seg__tile:hover { border-color: var(--line-2); }
.set-cardio-seg__tile.is-on {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.set-cardio-seg__tile:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* CHECKIN-LOGS-TITLE-METRIC-SINGLE-WORD-LABELS-AND-GLOBAL-UNITS — Units
   segmented control on the main Settings page. Same shape + tokens as the
   cardio segmented control (.set-cardio-seg) so the chrome reads as one
   shared pattern; lives in the main Settings card layout so the inner card
   padding wraps it correctly without the per-field column the cardio page
   uses. Two-tile (Metric / Imperial) so we let flex distribute equally
   without min-width tweaking. */
.set-units-segmented {
  display: flex; flex-wrap: wrap; gap: 6px;
  /* v0.9.508 SETTINGS-PADDING-AND-PROFILE-KERNING-V2 — segmented control
     previously bled to the card edges. Mirror .set-themes inset so the
     [Metric][Imperial] tiles align with the section's standard 16px gutter. */
  padding: var(--space-2) var(--space-4) var(--space-4);
  margin-top: 0;
}
.set-units-segmented__tile {
  flex: 1 1 0; min-width: 0;
  padding: 10px 12px;
  background: var(--surface-muted);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font: inherit; font-size: 13px; font-weight: 600;
  cursor: pointer;
  text-align: center;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard);
}
.set-units-segmented__tile:hover { border-color: var(--line-2); }
.set-units-segmented__tile.is-on {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
  box-shadow: var(--glow-soft);
}
.set-units-segmented__tile:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.set-cardio-input,
.set-cardio-select {
  width: 100%;
  padding: 10px 12px;
  background: var(--surface-muted);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font: inherit; font-size: 14px;
  -webkit-appearance: none; appearance: none;
}
.set-cardio-select {
  /* native chevron via padding + bg-svg so it tracks the theme tokens */
  padding-right: 36px;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.set-cardio-input:focus-visible,
.set-cardio-select:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}

/* CARDIO-SETTINGS-AND-WHITELABEL-THEMING — Super-admin tenant theme panel
   (public/admin/cardio-tenant-theme.html). Standalone page so layout uses
   max-width + page padding here rather than the SPA shell. */
.adm-cardio-theme {
  max-width: 720px; margin: 0 auto;
  padding: max(16px, env(safe-area-inset-top)) 16px 48px;
  background: var(--bg); color: var(--ink); min-height: 100vh;
  min-height: 100dvh; /* dvh wins on modern engines, 100vh above stays fallback */
}
.adm-cardio-theme__head {
  display: flex; flex-direction: column; gap: 4px;
  padding: 8px 0 16px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 16px;
}
.adm-cardio-theme__back {
  align-self: flex-start;
  font-size: 13px; color: var(--accent);
  text-decoration: none;
}
.adm-cardio-theme__title {
  font-size: 22px; font-weight: 700; color: var(--ink);
  margin-top: 4px;
}
.adm-cardio-theme__sub {
  font-size: 12px; color: var(--muted);
}
.adm-cardio-theme__gate {
  padding: 24px; text-align: center; color: var(--muted);
}
.adm-cardio-theme__field {
  display: flex; flex-direction: column; gap: 6px;
  padding: 12px 14px;
}
.adm-cardio-theme__label {
  font-size: 13px; color: var(--ink); font-weight: 500;
}
.adm-cardio-theme__color-wrap {
  display: flex; align-items: center; gap: 10px;
}
.adm-cardio-theme__color {
  flex: 0 0 44px;
  width: 44px; height: 44px;
  padding: 0;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  cursor: pointer;
}
.adm-cardio-theme__hex {
  flex: 1 1 auto; min-width: 0;
  padding: 9px 11px;
  background: var(--surface-muted);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font: inherit; font-size: 13px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  text-transform: uppercase;
}
.adm-cardio-theme__textinput {
  width: 100%;
  padding: 10px 12px;
  background: var(--surface-muted);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font: inherit; font-size: 13px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.adm-cardio-theme__textinput:focus-visible,
.adm-cardio-theme__hex:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}
.adm-cardio-theme__preview {
  position: relative;
  margin: 12px 14px;
  height: 180px;
  border-radius: var(--radius-md);
  overflow: hidden;
  border: 1px solid var(--line);
  /* Defaults that the JS overrides at runtime to apply the live theme. */
  --cardio-polyline: var(--accent);
  --cardio-marker:   #1A1A2E;
  --cardio-planned:  #5B8DEF;
}
.adm-cardio-theme__preview-tile {
  position: absolute; inset: 0;
  background-size: cover; background-position: center;
}
.adm-cardio-theme__preview-svg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
}
.adm-cardio-theme__preview-polyline {
  stroke: var(--cardio-polyline);
}
.adm-cardio-theme__preview-planned {
  stroke: var(--cardio-planned);
}
.adm-cardio-theme__preview-marker {
  fill: var(--cardio-marker);
}
.adm-cardio-theme__modalities {
  display: flex; flex-direction: column;
}
.adm-cardio-theme__mod-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px;
  font-size: 14px; color: var(--ink);
  border-bottom: 1px solid var(--line-2);
  cursor: pointer;
}
.adm-cardio-theme__mod-row:last-child { border-bottom: 0; }
.adm-cardio-theme__mod-row input[type=checkbox] {
  width: 18px; height: 18px; accent-color: var(--accent);
}
.adm-cardio-theme__radios {
  display: flex; flex-direction: column;
}
.adm-cardio-theme__radio {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px;
  font-size: 14px; color: var(--ink);
  border-bottom: 1px solid var(--line-2);
  cursor: pointer;
}
.adm-cardio-theme__radio:last-child { border-bottom: 0; }
.adm-cardio-theme__radio input[type=radio] {
  width: 18px; height: 18px; accent-color: var(--accent);
}
.adm-cardio-theme__save-bar {
  display: flex; justify-content: space-between; gap: 10px;
  margin: 16px 0 8px;
}
.adm-cardio-theme__reset,
.adm-cardio-theme__save {
  padding: 11px 16px;
  font: inherit; font-size: 14px; font-weight: 600;
  border-radius: var(--radius-md);
  cursor: pointer;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
}
.adm-cardio-theme__save {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.adm-cardio-theme__save[disabled],
.adm-cardio-theme__reset[disabled] {
  opacity: 0.6; cursor: not-allowed;
}
.adm-cardio-theme__status {
  margin-top: 8px;
  padding: 0 4px;
  font-size: 12px; color: var(--muted);
  min-height: 16px;
}
.adm-cardio-theme__status.is-error { color: var(--danger); }

/* EXERCISE-POOL-N-WAY (v0.9.428) — bottom-sheet picker rows for 3+-member
   pools. Rendered into the existing fz-mc-overlay + fz-mc-sheet container.
   Token-driven; spec tokens (--surface-2, --text-1) mapped to this repo's
   real tokens (--surface-card, --ink) so the css-hex-lint stays clean and
   the rows track Dag/Nacht presets automatically. */
.fz-pool-list {
  display: flex; flex-direction: column;
  gap: 8px; margin-bottom: 12px;
}
.fz-pool-row {
  width: 100%; padding: 14px;
  background: var(--surface-card); color: var(--ink);
  border: none; border-radius: var(--radius-md);
  font-size: 15px; font-weight: 600; font-family: 'DM Sans', sans-serif;
  text-align: left; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.fz-pool-row:active { opacity: 0.7; }
.fz-pool-add-btn {
  width: 100%; padding: 13px;
  background: transparent; color: var(--accent);
  border: 1.5px solid var(--accent); border-radius: var(--radius-md);
  font-size: 14px; font-weight: 700; font-family: 'DM Sans', sans-serif;
  cursor: pointer; margin-bottom: 8px;
  -webkit-tap-highlight-color: transparent;
}

/* ──────────────────────────────────────────────────────────
   Goals — DIARY-7-GOALS (v0.9.431). Behavioral lead-indicator
   targets (kcal, protein, training frequency, sleep, steps, mood)
   surfaced on Diary. Token-driven; no hex literals — css-hex-lint
   stays clean. Day/Week goal cards reuse .cal-card frame.
   ────────────────────────────────────────────────────────── */

/* /goals editor page */
.goals-page {
  display: flex; flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-4);
  max-width: 560px; margin: 0 auto;
}
.goals-header {
  display: flex; align-items: center; gap: var(--space-3);
}
.goals-back {
  width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--radius-pill);
  background: transparent; color: var(--ink);
  cursor: pointer;
}
.goals-back svg { width: 22px; height: 22px; }
.goals-back:active { background: var(--surface-card); }
.goals-header__titles { display: flex; flex-direction: column; gap: 2px; }
.goals-header__title {
  margin: 0; font-size: 22px; font-weight: 700;
  color: var(--ink);
}
.goals-header__sub {
  margin: 0; font-size: 13px; color: var(--section-label);
}
.goals-loading {
  padding: var(--space-4); text-align: center;
  color: var(--muted); font-size: 14px;
}
.goals-list {
  display: flex; flex-direction: column;
  gap: var(--space-3);
}
.goals-item {
  background: var(--surface-card);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-4);
  display: flex; flex-direction: column;
  gap: 10px;
}
.goals-item__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3);
}
.goals-item__name {
  font-size: 15px; font-weight: 600; color: var(--ink);
}
.goals-item__unit {
  font-size: 13px; color: var(--section-label);
}
.goals-item__input-wrap {
  display: flex; align-items: center; gap: 8px;
}
.goals-item__input {
  flex: 1 1 auto;
  background: var(--surface-bg);
  color: var(--ink);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-md);
  padding: 10px 12px;
  font-size: 16px;
  font-variant-numeric: tabular-nums;
  min-width: 0;
}
.goals-item__input:focus {
  outline: 2px solid var(--accent);
  border-color: var(--accent);
}
.goals-item__add {
  align-self: flex-start;
  background: var(--accent);
  color: var(--on-accent);
  border: 0; border-radius: var(--radius-md);
  padding: 8px 14px;
  font-size: 14px; font-weight: 600;
  cursor: pointer;
}
.goals-item__add:active { opacity: 0.85; }
.goals-item__remove {
  width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--section-label);
  border: 0; border-radius: var(--radius-pill);
  cursor: pointer;
}
.goals-item__remove svg { width: 18px; height: 18px; }
.goals-item__remove:active { color: var(--ink); }

/* Calendar — Day Goals card */
.cal-goals-card { display: flex; flex-direction: column; }
.cal-goals-won {
  font-variant-numeric: tabular-nums;
  color: var(--section-label);
}
.cal-goals-won.is-won { color: var(--accent); font-weight: 700; }
.cal-goal-row {
  display: grid;
  grid-template-columns: 22px 1fr auto auto;
  align-items: baseline;
  gap: 8px;
  padding: 6px 0;
  border-top: 1px solid var(--line-2);
  font-size: 13px;
}
.cal-goal-row:first-of-type { border-top: 0; padding-top: 4px; }
.cal-goal-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px;
}
.cal-goal-icon svg { width: 16px; height: 16px; }
.cal-goal-icon--met    { color: var(--accent); }
.cal-goal-icon--missed { color: var(--section-label); }
.cal-goal-icon--nodata {
  color: var(--section-label);
  font-size: 14px; font-weight: 600;
}
.cal-goal-row__label  { color: var(--ink); }
.cal-goal-row__actual {
  color: var(--ink); font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.cal-goal-row__target {
  color: var(--section-label);
  font-variant-numeric: tabular-nums;
}
.cal-goal-empty {
  color: var(--muted); font-size: 13px;
  padding: 6px 0;
}

/* Calendar — Week Goals band */
.cal-week-goals {
  display: flex; flex-direction: column;
  gap: 8px;
  background: var(--surface-card);
  border-radius: var(--radius-lg);
  padding: 12px var(--space-4);
  margin-bottom: 8px;
}
.cal-week-goals--loading { opacity: 0.6; }
.cal-week-goals__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 8px;
}
.cal-week-goals__lbl {
  font-size: 12px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--section-label);
}
.cal-week-goals__days {
  font-size: 13px; font-weight: 600;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.cal-week-goal-row {
  display: grid;
  grid-template-columns: 1fr 2fr auto;
  align-items: center;
  gap: 10px;
  padding: 4px 0;
  font-size: 13px;
}
.cal-wg-label {
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cal-wg-bar {
  height: 6px;
  background: var(--line-2);
  border-radius: var(--radius-pill);
  overflow: hidden;
  position: relative;
}
.cal-wg-bar__fill {
  height: 100%;
  background: var(--section-label);
  border-radius: var(--radius-pill);
  transition: width var(--dur-base) var(--ease-standard);
}
.cal-wg-bar__fill--met { background: var(--accent); }
.cal-wg-val {
  font-variant-numeric: tabular-nums;
  color: var(--section-label);
  font-size: 12px;
  white-space: nowrap;
}

/* DIARY-ANALYTICS-MERGE (v0.9.496) — collapsible 'This week' summary with
   WEEK GOALS expand-on-tap, and the merged TODAY card that consolidates
   Day Goals + Today nutrition + Activity into one container with sub-block
   eyebrows (NUTRIENTS / ACTIVITY / GYM). Tokens-only, both dag/nacht. */
.cal-week-collapsible {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 12px var(--space-4);
  margin: var(--space-2) 0;
}
.cal-week-collapsible__summary {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
  font: inherit;
  color: var(--ink);
  cursor: pointer;
  text-align: left;
  -webkit-tap-highlight-color: transparent;
}
.cal-week-collapsible__hd {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.cal-week-collapsible__title {
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
}
.cal-week-collapsible__chev {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  color: var(--muted);
  transition: transform var(--dur-base) var(--ease-standard);
}
.cal-week-collapsible__chev svg {
  width: 18px;
  height: 18px;
}
.cal-week-collapsible--expanded .cal-week-collapsible__chev {
  transform: rotate(180deg);
}
.cal-week-collapsible__stats {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  font-feature-settings: 'tnum' 1;
  line-height: 1.5;
}
.cal-week-collapsible__details {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--line-2);
}
.cal-week-collapsible__details .cal-week-goals {
  background: transparent;
  padding: 0;
  border-radius: 0;
  margin-bottom: 0;
}

/* DIARY-WEEK-SPARKLINE-STRIP (v0.9.544) — small-multiples trend strip under
   the 'This week' header. Three side-by-side mini sparklines (Protein avg /
   Weight / Mood avg). Each metric owns its own Y-axis (Tufte: same-units-
   only is the overlay rule — these three units differ, so small-multiples,
   never a shared axis). Token-driven, dag + nacht parity. */
.spark-strip {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--line-2);
  touch-action: pan-y;
}
.spark-mini {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.spark-mini__hd {
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.spark-mini__svg {
  display: block;
  width: 100%;
  height: 36px;
  cursor: ew-resize;
}
.spark-line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.spark-line--dot {
  fill: var(--accent);
  stroke: none;
}
.spark-cursor {
  stroke: var(--ink);
  stroke-width: 1;
  opacity: 0;
  pointer-events: none;
  vector-effect: non-scaling-stroke;
}
.spark-cursor__dot {
  fill: var(--ink);
  stroke: var(--surface);
  stroke-width: 0.6;
  opacity: 0;
  pointer-events: none;
  vector-effect: non-scaling-stroke;
}
.spark-strip--scrub .spark-cursor { opacity: 0.55; }
.spark-strip--scrub .spark-cursor__dot { opacity: 1; }
.spark-tip {
  position: absolute;
  left: var(--spark-tip-x, 50%);
  bottom: calc(100% + 2px);
  transform: translateX(-50%);
  font-size: 11px;
  font-weight: 600;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 2px 6px;
  white-space: nowrap;
  font-feature-settings: 'tnum' 1;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-standard);
}
.spark-mini--scrub .spark-tip { opacity: 1; }

/* DIARY-METRIC-TREND-SPARKLINES — long-term progress section on the Diary tab.
   One scrub-able sparkline card per tracked metric. Reuses the .spark-line /
   .spark-cursor / .spark-tip visual vocabulary; token-driven, Dag + Nacht
   parity. */
.diary-trends {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px dashed var(--line-2);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.diary-trends__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.diary-trends__title {
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.diary-trends__toggle {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  background: var(--surface-2, var(--surface));
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
}
.diary-trends__period {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: var(--radius-md);
  cursor: pointer;
}
.diary-trends__period--on {
  background: var(--accent);
  color: var(--on-accent);
}
.diary-trends__note {
  font-size: 12px;
  color: var(--muted);
  margin: 0;
  padding: 4px 0;
}
.trend-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
}
.trend-card__hd {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.trend-card__label {
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.trend-card__last {
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  font-feature-settings: 'tnum' 1;
  white-space: nowrap;
}
.trend-card__body {
  display: flex;
  gap: 6px;
}
.trend-card__ygutter {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-end;
  min-width: 26px;
  padding: 1px 0;
  font-size: 9px;
  font-weight: 600;
  color: var(--muted);
  font-feature-settings: 'tnum' 1;
  white-space: nowrap;
}
.trend-card__plotcol {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.trend-card__plot {
  position: relative;
}
.trend-card__svg {
  height: 56px;
  width: 100%;
  display: block;
}
.trend-card__endpt {
  fill: var(--accent);
  stroke: none;
}
.trend-card__pt {
  position: absolute;
  font-size: 10px;
  font-weight: 700;
  color: var(--ink);
  font-feature-settings: 'tnum' 1;
  white-space: nowrap;
  pointer-events: none;
  transform: translateY(-135%);
}
.trend-card__pt--start { left: 0; }
.trend-card__pt--end { right: 0; }
.trend-card__pt.trend-card__pt--below { transform: translateY(35%); }
.trend-card__foot {
  display: flex;
  justify-content: space-between;
  font-size: 10px;
  font-weight: 600;
  color: var(--muted);
  font-feature-settings: 'tnum' 1;
}
.trend-card--scrub .spark-cursor { opacity: 0.55; }
.trend-card--scrub .spark-cursor__dot { opacity: 1; }
.trend-card--scrub .trend-card__tip { opacity: 1; }

/* DIARY-TODAY-MINIMALIST-AUDIT (v0.9.543) — Today card is 6 rows max. Each
   row is label + progress-bar-or-blank + value. No icon column, no inner
   eyebrow sub-blocks (Nutrients/Activity/Gym): the row IS the unit. Reuses
   .cal-card chrome for outer surface + radius. */
.cal-today-merged { display: flex; flex-direction: column; }
.cal-today-merged__hd {
  align-items: baseline;
  gap: 8px;
}
.cal-today-merged__sub {
  font-size: 13px;
  color: var(--muted);
  font-weight: 500;
  margin-left: -2px;
}
.cal-tm-goals {
  display: flex;
  flex-direction: column;
  margin-top: 4px;
}
.cal-tm-row {
  display: grid;
  grid-template-columns: 90px minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  font-size: 13px;
}
.cal-tm-row + .cal-tm-row {
  border-top: 1px solid var(--line-2);
}
.cal-tm-row__label {
  color: var(--ink-2);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cal-tm-row__bar {
  height: 6px;
  background: var(--surface-muted);
  border-radius: var(--radius-pill);
  overflow: hidden;
  position: relative;
}
.cal-tm-row__fill {
  height: 100%;
  background: var(--section-label);
  border-radius: var(--radius-pill);
  transition: width var(--dur-base) var(--ease-standard);
}
.cal-tm-row__fill--met { background: var(--accent); }
.cal-tm-row__val {
  color: var(--ink);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  white-space: nowrap;
}
.cal-tm-row__val--empty {
  color: var(--muted);
  font-weight: 500;
}
.cal-tm-row__val em {
  color: var(--muted);
  font-style: normal;
  font-weight: 500;
  margin-left: 4px;
}
.cal-tm-row--link {
  cursor: pointer;
}
.cal-tm-row--link .cal-tm-row__val { color: var(--accent); }
.cal-tm-row--link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }
.cal-tm-row__chev { color: var(--accent); margin-left: 4px; }

.fz-diary-checkin-button:active { filter: brightness(0.95); }
/* DIARY-CHECKIN-BAND-BEHIND-OPEN-CHECKIN-BUTTON (v0.9.494) — the Diary tab's
   Check-in CTA sits between the week-pills and the ANALYTICS band; give it
   a touch of breathing room so it reads as a distinct hop in the scan
   rhythm rather than crowding either neighbor. */
.fz-diary-checkin-button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  min-height: 56px;
  margin-top: 16px;
  margin-bottom: 16px;
  padding: 16px 18px;
  border: 1px solid var(--ink);
  border-radius: 20px;
  background: var(--ink);
  color: var(--bg);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.25;
  text-align: left;
  cursor: pointer;
  box-shadow: var(--elev-2);
}
.fz-diary-checkin-button:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.fz-diary-checkin-button__chev { flex: none; width: 20px; height: 20px; opacity: .75; }
.fz-cal-subscribe { margin: 20px 0 8px; }
.fz-cal-subscribe__btn {
  display: block;
  width: 100%;
  min-height: 48px;
  padding: 13px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.fz-cal-subscribe__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.fz-cal-subscribe__btn[disabled] { opacity: .6; cursor: default; }
.fz-cal-subscribe__note {
  margin: 8px 2px 0;
  font-size: 12px;
  line-height: 1.45;
  color: var(--muted);
}
.fz-day-cycle-cal-sheet {
  position: fixed;
  z-index: 130;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 220px;
  max-width: 320px;
  padding: 12px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-3);
  font-family: inherit;
}
.fz-day-cycle-cal-sheet__opt {
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 10px 14px;
  min-height: 44px;
  text-align: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard);
}
.fz-day-cycle-cal-sheet__opt:active {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}

/* Full-tool page (mounted at /day-cycle). DAY-CYCLE-FULL-TOOL-AND-WIDGET-
   VISUAL-POLISH (v0.9.484) — stronger phase segmentation (per-phase cards
   with a thin colored left bar), calmer hierarchy, balanced padding,
   symmetric columns, decluttered time-range cell. All functional
   invariants (cascade math, slippage, padlock toggle, H+M auto-roll,
   cross-midnight, edit-mode sheet) untouched. */
.fz-day-cycle-page {
  padding: var(--space-2) 0 var(--space-6);
  color: var(--ink);
}
.fz-day-cycle-page__head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 4px 14px;
}
.fz-day-cycle-page__back,
.fz-day-cycle-page__edit {
  background: transparent;
  border: 0;
  padding: 6px;
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  min-width: 24px;
  min-height: 24px;
  border-radius: var(--radius-md);
  transition: background var(--dur-fast) var(--ease-standard);
}
.fz-day-cycle-page__back:hover,
.fz-day-cycle-page__edit:hover {
  background: var(--line-2);
}
.fz-day-cycle-page__back svg,
.fz-day-cycle-page__edit svg {
  width: 20px;
  height: 20px;
}
.fz-day-cycle-page__title {
  flex: 1 1 auto;
  margin: 0;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.005em;
  line-height: 1.2;
}
.fz-day-cycle-page__body { display: flex; flex-direction: column; gap: 16px; }
.fz-day-cycle-page__loading,
.fz-day-cycle-page__error,
.fz-day-cycle-page__empty {
  padding: 16px;
  font-size: 13px;
  color: var(--muted);
  text-align: center;
}

/* DAY-CYCLE-EMPTY-START-AI-INTAKE — fresh-user hero + AI intake/preview UI.
   Tokens-only (Dag/Nacht safe); accent button uses --on-accent (never white,
   per feedback_accent_buttons_on_accent_token). */
.fz-day-cycle-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: 40px 20px;
  text-align: center;
}
/* DIARY-DAYCYCLE-EMPTY-STATE-CREATE-CTA — compact variant inside the check-in card. */
.fz-day-cycle-empty--widget { gap: 12px; padding: 8px 4px 4px; }
.fz-day-cycle-empty--widget .fz-day-cycle-empty__lead { font-size: 13px; }
.fz-day-cycle-empty__lead {
  margin: 0;
  max-width: 320px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
}
.fz-day-cycle-empty__cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  padding: 12px 20px;
  border: 0;
  border-radius: var(--radius-lg);
  background: var(--accent);
  color: var(--on-accent);
  cursor: pointer;
}
.fz-day-cycle-empty__cta svg { width: 18px; height: 18px; }
.fz-day-cycle-empty__self {
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  background: transparent;
  border: 0;
  color: var(--muted);
  text-decoration: underline;
  cursor: pointer;
}

.fz-day-cycle-intake__dots {
  display: flex;
  gap: 8px;
  justify-content: center;
  padding: 4px 0 16px;
}
.fz-day-cycle-intake__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--line-2);
}
.fz-day-cycle-intake__dot.is-done { background: var(--muted); }
.fz-day-cycle-intake__dot.is-active { background: var(--accent); }
.fz-day-cycle-intake__q {
  margin: 0 0 12px;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
}
.fz-day-cycle-intake__ta {
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  font-size: 14px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--ink);
  resize: vertical;
}
.fz-day-cycle-intake__foot { justify-content: space-between; }
/* DAYCYCLE-AI-IMPROVE-ROUND — what the user said in an earlier round, shown as
   a quiet reminder ABOVE the (empty) box, never poured into it. */
.fz-day-cycle-intake__recall {
  margin: -6px 0 8px;
  font-size: 12px;
  line-height: 1.4;
  color: var(--muted);
}
.fz-day-cycle-intake__err {
  margin-top: 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
  text-align: center;
}
.fz-day-cycle-intake__err-msg {
  margin: 0;
  font-size: 13px;
  color: var(--danger);
}

.fz-day-cycle-preview__group { margin-bottom: 16px; }
.fz-day-cycle-preview__lbl {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--muted);
  margin-bottom: 6px;
}
.fz-day-cycle-preview__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-md);
  margin-bottom: 6px;
  background: var(--surface);
}
.fz-day-cycle-preview__name {
  font-size: 14px;
  color: var(--ink);
}
.fz-day-cycle-preview__dur {
  font-size: 12px;
  color: var(--muted);
  flex: 0 0 auto;
}

/* DAYCYCLE-AI-IMPROVE-ROUND — the proposal as a decision per step. The
   word-mark carries the action (text, never an icon alone), the reason sits
   under it in a quieter voice, and the switch turns that ONE line off. */
.fz-day-cycle-improve__scope {
  margin: 0 0 14px;
  font-size: 13px;
  color: var(--ink-2);
}
.fz-day-cycle-improve__row {
  padding: 10px 12px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-md);
  margin-bottom: 6px;
  background: var(--surface);
}
.fz-day-cycle-improve__row.is-off { opacity: 0.5; }
.fz-day-cycle-improve__top {
  display: flex;
  align-items: center;
  gap: 10px;
}
.fz-day-cycle-improve__tag {
  flex: 0 0 auto;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: 999px;
  background: var(--line-2);
  color: var(--ink-2);
}
.fz-day-cycle-improve__tag--change { background: var(--accent); color: var(--on-accent); }
.fz-day-cycle-improve__tag--add    { background: var(--accent); color: var(--on-accent); }
.fz-day-cycle-improve__tag--remove { background: var(--danger); color: #fff; }
.fz-day-cycle-improve__names {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
}
.fz-day-cycle-improve__name { font-size: 14px; color: var(--ink); }
.fz-day-cycle-improve__was {
  font-size: 12px;
  color: var(--muted);
  text-decoration: line-through;
}
.fz-day-cycle-improve__why {
  margin: 6px 0 0;
  font-size: 12px;
  line-height: 1.4;
  color: var(--muted);
}
.fz-day-cycle-improve__sw {
  flex: 0 0 auto;
  position: relative;
  width: 40px;
  height: 24px;
  cursor: pointer;
}
.fz-day-cycle-improve__sw input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
.fz-day-cycle-improve__sw-track {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: var(--line-2);
  transition: background 0.15s ease;
  pointer-events: none;
}
.fz-day-cycle-improve__sw-track::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--surface);
  transition: transform 0.15s ease;
}
.fz-day-cycle-improve__sw input:checked + .fz-day-cycle-improve__sw-track {
  background: var(--accent);
}
.fz-day-cycle-improve__sw input:checked + .fz-day-cycle-improve__sw-track::after {
  transform: translateX(16px);
}
.fz-day-cycle-improve__sw input:focus-visible + .fz-day-cycle-improve__sw-track {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* /check-in — DIARY-CHECKIN-BAND-BEHIND-OPEN-CHECKIN-BUTTON (v0.9.494). Thin
   shell hosting the Day Cycle widget + Mood entry card + Log Sleep + Log
   Weight surfaces. Chrome mirrors /day-cycle: back chevron + page title in
   the header, gap-16 stack in the body, 16px outer-edge padding. Routed-view
   padding follows the v0.9.489 mood-page pattern (12px top + 16px sides +
   16px bottom, then .app-main supplies nav-clearance). */
.checkin-page {
  padding: var(--space-3) var(--space-4) var(--space-4);
  color: var(--ink);
}
.checkin-page__head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 0 0 12px;
}
.checkin-page__back {
  background: transparent;
  border: 0;
  padding: 6px;
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  min-width: 24px;
  min-height: 24px;
  border-radius: var(--radius-md);
  transition: background var(--dur-fast) var(--ease-standard);
}
.checkin-page__back:hover { background: var(--line-2); }
.checkin-page__back svg {
  width: 20px;
  height: 20px;
}
.checkin-page__title {
  flex: 1 1 auto;
  margin: 0;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.005em;
  line-height: 1.2;
}
.checkin-page__body {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* CHECKIN-HCD-REDESIGN-BUILD — small-caps date eyebrow at the top of the
   check-in page. It labels the DAY, not the card below it: since the HCD
   redesign the first element underneath is the .ck-prog progress card, so the
   old negative margin-bottom (which glued the eyebrow to a quick-log button
   stack that no longer exists) made the date collide with "Check-in vandaag".
   No left inset either — the eyebrow must align with the body padding. */
.checkin-page__eyebrow {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 var(--space-3) 0;
}

/* CHECKIN-PAGE-REORDER-PLUS-MOOD-ANALYTICS-ICON-BUTTON (v0.9.506) — 3-per-row
   grid for the quick-log buttons. grid-template-columns is set inline by
   public/assets/js/checkin/index.js based on the count of enabled metrics
   (1 → 1 col / 2 → 2 cols / 3+ → 3 cols, rows wrap). Disabled-metric cells
   stay empty and are hidden via `:empty` so they don't reserve grid slots. */
.checkin-page__logs-grid {
  display: grid;
  gap: 12px;
}
.checkin-page__logs-grid__cell:empty { display: none; }
/* Tighten button chrome inside the narrower grid cell so single-word labels
   (Sleep / Weight / Waist / …) still read on a 360px viewport (~105px per
   cell at 3-per-row after the 12px gaps). Targets only the quick-log cells
   to avoid bleeding into other .cal-metric usages elsewhere. */
.checkin-page__logs-grid__cell .cal-metric {
  padding-left: 12px;
  padding-right: 12px;
}
.checkin-page__logs-grid__cell .cal-metric__lbl {
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* CHECKIN-LAYOUT-METRICS-TOP-DAYCYCLE-MOOD-BOTTOM (v0.9.979) — the hoofdmetric
   (order=0 enabled metric) gets a slightly larger/bolder label. Size-token bump
   only; the cell stays the same .cal-metric component. */
.checkin-page__logs-grid__cell.metric-row--primary .cal-metric__lbl {
  font-size: 15px;
  font-weight: 700;
}
/* CHECKIN-BUTTON-MINIMALIST-DEMOTION (v0.9.553) — demote the empty-state
   quick-log tiles on /check-in only. Diary's identical .cal-metric--empty
   cells stay accent-filled (still acting as a primary "log this" affordance
   inline with the calendar). Scoped under .checkin-page__logs-grid__cell
   so the global rule at L2923 is untouched. Border uses --line not --accent
   to read subtler than Save entry, matching the spec's
   tile-quieter-than-Save-entry hierarchy. */
.checkin-page__logs-grid__cell .cal-metric--empty {
  background: transparent;
  color: var(--ink);
  border-color: var(--line);
}
.checkin-page__logs-grid__cell .cal-metric--empty .cal-metric__hint {
  opacity: 0.7;
}

/* ── CHECKIN-HCD-REDESIGN-BUILD ──────────────────────────────────────────
   Human-centred check-in: one progress card on top that names the next
   unopened group, then four grouped surfaces (Metingen disclosure,
   Reflectie, Dagritme strip, Voortgangsfoto's chip). Class vocabulary is
   ported 1:1 from the approved mock (_MOCK_checkin_hcd_v1.html) but every
   colour goes through a theme token so Dag and Nacht both hold up. */
.checkin-page__eyebrow--past { color: var(--accent); }

.ck-card {
  background: var(--surface-card, var(--surface));
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 16px;
}

/* Progress card */
.ck-prog { display: flex; flex-direction: column; gap: 14px; }
.ck-prog__top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.ck-prog__h { margin: 0; font-size: 17px; font-weight: 700; letter-spacing: -0.005em; }
.ck-prog__n { font-size: 13px; font-weight: 600; color: var(--muted); white-space: nowrap; }

/* NUTRITION-CHECKIN-MEANINGFUL-MOMENTS-BUTTON — icon-only bookmark, muted and
   sized well below any primary CTA. It lives in the Reflection card header
   (.ck-refl__h), not in the progress header: marking a moment is reflection,
   not progress. */
.ck-moment {
  align-self: center;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: color .15s ease, border-color .15s ease, background .15s ease;
}
.ck-moment svg { width: 17px; height: 17px; }
.ck-moment:hover { color: var(--ink); border-color: var(--ink); }
.ck-moment:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ck-moment--on { color: var(--accent); border-color: var(--accent); }
.ck-moment--on svg { fill: currentColor; }

/* Mini-sheet: reuses .premium-overlay/.premium-sheet chrome; only the chip grid
   is new. Two columns on narrow phones so eight tags stay one screen, no scroll. */
.fz-mm-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
@media (min-width: 420px) { .fz-mm-grid { grid-template-columns: repeat(4, 1fr); } }
.fz-mm-chip {
  padding: 12px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.fz-mm-chip:hover { border-color: var(--accent); }
.fz-mm-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.fz-mm-chip--on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.fz-mm-chip[disabled] { cursor: default; opacity: .6; }
.fz-mm-chip--on[disabled] { opacity: 1; }

/* NUTRITION-EMA-PING-FIRST-14-DAYS — the nightly one-tap probe. Same
   .premium-overlay/.premium-sheet chrome as the moments sheet; the three smileys
   are the only new furniture, and the optional tag layer reuses .fz-mm-grid
   verbatim so both surfaces stay one visual language. */
.fz-ema-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.fz-ema-smiley {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 16px 6px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background .12s ease, border-color .12s ease, color .12s ease, opacity .12s ease;
}
.fz-ema-smiley svg { width: 34px; height: 34px; }
.fz-ema-smiley__label { line-height: 1.2; text-align: center; }
.fz-ema-smiley:hover { border-color: var(--accent); }
.fz-ema-smiley:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.fz-ema-smiley--on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.fz-ema-smiley[disabled] { cursor: default; }
.fz-ema-smiley--dim { opacity: .45; }
.fz-ema-tags { margin-top: 18px; }
.fz-ema-tags__lede { margin: 0 0 10px; font-size: 13px; color: var(--muted); }
@media (prefers-reduced-motion: reduce) {
  .fz-ema-smiley { transition: none; }
}

.ck-dots { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.ck-dot { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.ck-dot__b {
  height: 4px;
  border-radius: 999px;
  background: var(--line-2, var(--line));
  transition: background var(--dur-base) var(--ease-standard);
}
.ck-dot__l {
  font-size: 11px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ck-dot--on .ck-dot__b { background: var(--accent); }
.ck-dot--on .ck-dot__l { color: var(--ink-2, var(--ink)); }
.ck-cta {
  width: 100%;
  border: 0;
  border-radius: var(--radius-md);
  padding: 13px 16px;
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
  background: var(--accent);
  color: var(--on-accent);
}
.ck-prog--done { border-color: var(--accent); }
.ck-done-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 15px;
  font-weight: 700;
  color: var(--accent);
}
.ck-done-row__tick {
  width: 22px;
  height: 22px;
  flex: 0 0 auto;
  border-radius: 999px;
  background: var(--accent);
  color: var(--on-accent);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.ck-done-row__tick svg { width: 14px; height: 14px; }

/* Peak-end celebration on a fully completed day. Deliberately NOT confetti —
   this happens daily, and a burst that big turns into noise within a week. */
.ck-celebrate {
  font-size: 14px;
  line-height: 1.45;
  color: var(--ink-2, var(--ink));
}
@keyframes ck-dot-shimmer {
  0%   { transform: scaleY(1);   filter: brightness(1); }
  40%  { transform: scaleY(2.2); filter: brightness(1.45); }
  100% { transform: scaleY(1);   filter: brightness(1); }
}
.ck-dots--shimmer .ck-dot__b {
  animation: ck-dot-shimmer 900ms var(--ease-standard, cubic-bezier(0.2, 0, 0, 1)) both;
}
.ck-dots--shimmer .ck-dot:nth-child(2) .ck-dot__b { animation-delay: 110ms; }
.ck-dots--shimmer .ck-dot:nth-child(3) .ck-dot__b { animation-delay: 220ms; }
.ck-dots--shimmer .ck-dot:nth-child(4) .ck-dot__b { animation-delay: 330ms; }
@media (prefers-reduced-motion: reduce) {
  .ck-dots--shimmer .ck-dot__b { animation: none; }
}

/* Metingen disclosure */
.ck-disc {
  width: 100%;
  background: transparent;
  border: 0;
  padding: 0;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  text-align: left;
}
.ck-disc__t { font-size: 15px; font-weight: 700; flex: 0 0 auto; }
.ck-disc__s {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ck-disc__c {
  flex: 0 0 auto;
  display: inline-flex;
  color: var(--muted);
  transition: transform var(--dur-base) var(--ease-standard);
}
.ck-disc__c svg { width: 18px; height: 18px; }
.ck-metrics--open .ck-disc__c { transform: rotate(180deg); }
.ck-mtx {
  height: 0;
  overflow: hidden;
  transition: height var(--dur-base) var(--ease-standard);
}
.ck-mtx__inner { padding-top: 14px; }

/* Metric rows inside the disclosure. The tile stays .cal-metric (one
   painter — mountDiaryQuickLogCell owns it); we only re-lay it as a row
   and hand the trend affordance to a sibling chevron. */
.ck-metrics .checkin-page__logs-grid {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.ck-mrow { display: flex; align-items: center; gap: 8px; }
.ck-mrow .cal-metric {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 11px 12px;
  text-align: left;
}
.ck-mrow .cal-metric__lbl { font-size: 14px; font-weight: 600; }
.ck-mrow .cal-metric__val {
  flex: 0 0 auto;
  font-size: 14px;
  font-weight: 700;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--surface-muted, var(--line-2, var(--line)));
}
.ck-mrow .cal-metric__hint { display: none; }
.ck-mrow .cal-metric--empty .cal-metric__lbl { color: var(--muted); font-weight: 600; }
/* CHECKIN-TIME-AWARE-ORDER — an already-logged row sinks to the bottom of the
   check-in group and dims a touch. Scoped to .ck-mrow so the same cell in the
   diary/calendar is untouched. No "done" button, no extra glyph: the value
   itself is the signal. */
.ck-mrow .cal-metric--filled { opacity: .78; }
.ck-mrow .cal-metric--filled .cal-metric__lbl { font-weight: 500; }
/* METRICS-DERIVED-HIP-WAIST-RATIO — computed read-out, no input and no history
   button: dashed edge marks it as derived rather than something to fill in. */
.ck-mrow.metric-row--derived .cal-metric {
  border: 1px dashed var(--line);
  cursor: default;
}
.ck-mrow.metric-row--derived .cal-metric__lbl { font-weight: 500; color: var(--muted); }
.ck-mrow__h {
  flex: 0 0 auto;
  background: transparent;
  border: 0;
  padding: 8px;
  cursor: pointer;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
}
.ck-mrow__h svg { width: 18px; height: 18px; }
.ck-mrow__h:hover { background: var(--line-2, var(--line)); }

/* CHECKIN-ADAPTIVE-METRIC-PRIORITY — the rows the user actually fills stay on
   surface; the long tail collapses behind one ghost button. Same disclosure
   idiom as .ck-disc, one level down. */
.ck-mmore {
  margin-top: 10px;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: transparent;
  border: 0;
  padding: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
}
.ck-mmore__c { display: inline-flex; transition: transform var(--dur-base) var(--ease-standard); }
.ck-mmore__c svg { width: 16px; height: 16px; }
.ck-mmore[aria-expanded="true"] .ck-mmore__c { transform: rotate(180deg); }
.ck-metrics .checkin-page__logs-grid--more { margin-top: 10px; }
.ck-metrics .checkin-page__logs-grid--more[hidden] { display: none; }
.ck-madd {
  margin-top: 12px;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: transparent;
  border: 1px dashed var(--line);
  border-radius: var(--radius-md);
  padding: 10px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
}
.ck-madd svg { flex: 0 0 auto; width: 18px; height: 18px; }
/* The scan is an ACT, not a "configure which rows you see" affordance like its
   dashed sibling — solid border and full-strength ink keep the two apart. */
.ck-madd--scan {
  border-style: solid;
  color: var(--ink);
}

/* Reflectie */
.ck-refl { display: flex; flex-direction: column; gap: 12px; }
.ck-refl__h {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

/* CHECKIN-REFLECTIE-COLLAPSED-TO-ONE-PRIMARY-PATH — exactly three controls,
   ranked: accent-filled primary, bordered secondary, ghost link. */
.ck-refl__compact { display: flex; flex-direction: column; gap: 8px; }
.ck-refl__compact .mood-ai-chat-btn {
  margin: 0;
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.ck-refl__compact .mood-ai-chat-btn__icon svg { color: var(--on-accent); }
.ck-refl__compact .mood-ai-chat-btn__hint { color: var(--on-accent); opacity: 0.8; }
.ck-refl__compact .mood-ai-chat-btn--locked {
  background: var(--surface-muted);
  color: var(--muted);
  border-color: var(--line);
}
.ck-refl__compact .mood-ai-chat-btn--locked .mood-ai-chat-btn__icon svg { color: var(--muted); }
.ck-refl__compact .mood-ai-chat-btn--locked .mood-ai-chat-btn__hint { color: var(--muted); opacity: 1; }
.ck-refl__trigger {
  width: 100%;
  padding: 12px 14px;
  background: var(--surface-muted);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 15px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.ck-refl__trigger:active { opacity: 0.85; }
.ck-refl__compact .checkin-mood-link { margin-top: 0; padding-top: 0; }
.ck-refl__compact .checkin-mood-link__btn { color: var(--muted); }

/* Dagritme strip */
.ck-dr { display: flex; flex-direction: column; gap: 12px; }
.ck-dr__top { display: flex; align-items: center; gap: 8px; }
.ck-dr__h { margin: 0; font-size: 15px; font-weight: 700; flex: 1 1 auto; }
.ck-help {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}
.ck-strip { display: flex; align-items: stretch; gap: 10px; }
.ck-tchip {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  cursor: pointer;
}
.ck-tchip__k {
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ck-tchip__v {
  font-size: 18px;
  font-weight: 700;
  color: var(--ink);
  background: transparent;
  border: 0;
  padding: 0;
  font-family: inherit;
  width: 100%;
}
.ck-tchip--ask { border-style: dashed; border-color: var(--accent); }
.ck-tchip--ask .ck-tchip__k { color: var(--accent); }
.ck-tchip--ask .ck-tchip__v { text-decoration: underline dotted; text-underline-offset: 3px; }
/* The mid column sits BETWEEN the two time chips, so its rules run
   vertically — a horizontal rule here would cut the strip in half. */
.ck-mid {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  color: var(--muted);
  font-size: 11px;
  padding: 2px 0;
}
.ck-mid__rule { flex: 1 1 auto; width: 1px; min-height: 6px; background: var(--line); }
.ck-mid__d { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.ck-mid__lock { display: inline-flex; }
.ck-mid__lock svg { width: 13px; height: 13px; }
.ck-mid__dur { font-weight: 600; }
.ck-dr__foot { display: flex; gap: 8px; flex-wrap: wrap; }
.ck-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 7px 13px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
}
.ck-pill svg { width: 14px; height: 14px; }
.ck-note { font-size: 12px; color: var(--accent); margin: 0; }

/* Voortgangsfoto's */
.ck-photorow { display: flex; }
.ck-photo {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 9px 15px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
}
.ck-photo svg { width: 15px; height: 15px; color: var(--muted); }
.ck-photo__n { color: var(--muted); font-weight: 600; }
.ck-photo--on { border-color: var(--accent); }
.ck-photo--on svg,
.ck-photo--on .ck-photo__n { color: var(--accent); }

/* NUTRITION-CHECKIN-SUNDAY-RECAP-CARD — the week's closing sentence, above the
   progress card on Sundays. Deliberately the QUIETEST card on the page: no
   accent fill, no border emphasis, muted icons. It sits FIRST for recency, not
   for weight — the progress card must stay the only primary surface, so this
   one reads at body scale and takes its colour entirely from --muted/--ink.
   Every colour is a token, so Dag and Nacht both hold. */
.ck-recap { display: flex; flex-direction: column; gap: 10px; }
.ck-recap__title {
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--muted);
}
.ck-recap__list { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 8px; }
.ck-recap__row { display: flex; align-items: center; gap: 10px; }
.ck-recap__icon { width: 17px; height: 17px; flex: 0 0 auto; color: var(--muted); }
.ck-recap__text { font-size: 14px; font-weight: 600; color: var(--ink); }
.ck-recap__empty { margin: 0; font-size: 14px; color: var(--muted); }

/* 320px viewport — the four dot labels and the two time chips are the two
   places that overflow first, so tighten those rather than the whole page. */
@media (max-width: 359px) {
  .ck-card { padding: 14px; }
  .ck-recap__text, .ck-recap__empty { font-size: 13px; }
  .ck-dots { gap: 6px; }
  .ck-dot__l { font-size: 10px; }
  .ck-tchip { padding: 9px 10px; }
  .ck-tchip__v { font-size: 16px; }
  .ck-mrow .cal-metric { padding: 10px; }
  .ck-pill { padding: 7px 11px; font-size: 12px; }
}

/* Top time-range card (Start | lock + duration | End). Equal 1fr columns
   on Start and End so the two HH:MM inputs are visually rhymed; the lock
   column auto-sizes around its icon + duration label. */
.fz-day-cycle-page__controls {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: end;
  column-gap: 16px;
  padding: 18px 16px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.fz-day-cycle-controls__field {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.fz-day-cycle-controls__lbl {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
}
.fz-day-cycle-controls__inp {
  font: inherit;
  font-size: 18px;
  font-variant-numeric: tabular-nums;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--ink);
  width: 100%;
  max-width: 140px;
  text-align: center;
  transition: border-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard);
}
.fz-day-cycle-controls__inp:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring-accent);
}
.fz-day-cycle-controls__field.is-locked .fz-day-cycle-controls__inp {
  border-color: var(--accent);
}
.fz-day-cycle-controls__lock-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-width: 44px;
}
.fz-day-cycle-controls__lock {
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  padding: 7px;
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex: 0 0 auto;
  min-width: 32px;
  min-height: 32px;
}
.fz-day-cycle-controls__lock:hover { background: var(--line-2); }
.fz-day-cycle-controls__lock svg {
  width: 18px;
  height: 18px;
}
.fz-day-cycle-controls__duration {
  font-size: 11px;
  line-height: 1.2;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  white-space: nowrap;
  min-height: 13px;
}

/* Phase cards — one card per phase (Evening / Sleep / Morning). Thin
   colored left-bar accent extends the full height of the card so the
   eye picks up the phase at a glance without parsing the small-caps
   header text. SLEEP card additionally takes the existing
   --day-cycle-sleep-bg darker surface from v0.9.466 so the night band
   stays a deliberate visual separator. */
.fz-day-cycle-page__list {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.fz-dc-now-line {
  position: absolute;
  left: 0;
  right: 0;
  height: 1.5px;
  /* semi-transparent line so underlying text stays readable; dot uses full accent */
  background: color-mix(in srgb, var(--accent) 40%, transparent);
  pointer-events: none;
  z-index: 10;
}
.fz-dc-now-line::before {
  content: '';
  position: absolute;
  left: 4px;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--accent);
}
.fz-day-cycle-page__remaining { font-size: 11px; font-weight: 600; color: var(--accent); background: var(--accent-light); border-radius: var(--radius-pill); padding: 2px 8px; white-space: nowrap; }
.fz-day-cycle-page__remaining--done { color: var(--muted); background: transparent; font-weight: 400; }
.fz-day-cycle-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  padding-left: 3px;
}
.fz-day-cycle-card::before {
  content: '';
  position: absolute;
  top: 0; bottom: 0; left: 0;
  width: 3px;
  background: var(--accent);
}
.fz-day-cycle-card--evening::before { background: var(--day-cycle-evening-accent); }
.fz-day-cycle-card--sleep::before   { background: var(--day-cycle-sleep-accent); }
.fz-day-cycle-card--morning::before { background: var(--day-cycle-morning-accent); }
.fz-day-cycle-card--sleep {
  background: var(--day-cycle-sleep-bg);
}
.fz-day-cycle-card__head {
  display: flex;
  align-items: center;
  min-height: 36px;
  padding: 10px 16px 6px;
}
.fz-day-cycle-card__lbl {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
}
.fz-day-cycle-card__rows {
  display: flex;
  flex-direction: column;
}
.fz-day-cycle__row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  min-height: 56px;
  padding: 12px 16px;
  border-top: 1px solid var(--line-2);
}
.fz-day-cycle__content {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.fz-day-cycle__actions {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
}
.fz-day-cycle__time-info {
  display: flex;
  align-items: baseline;
  gap: 3px;
  flex-wrap: wrap;
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  line-height: 1.3;
}
.fz-day-cycle__ti-start { color: var(--muted); }
.fz-day-cycle__ti-dash  { color: var(--muted); margin: 0 1px; }
.fz-day-cycle__ti-end   { color: var(--ink); font-weight: 500; }
.fz-day-cycle__ti-actual { color: var(--ink); font-weight: 600; }
.fz-day-cycle__ti-was   { color: var(--muted); font-size: 11px; font-style: italic; }
.fz-day-cycle__ti-dur   { color: var(--muted); font-size: 11px; }
.fz-day-cycle__notes-wrap { min-width: 0; }
.fz-day-cycle__notes {
  display: block;
  cursor: pointer;
  border-radius: var(--radius-sm);
  padding: 2px 0;
  min-height: 18px;
}
.fz-day-cycle__notes-add {
  font-size: 12px;
  color: var(--muted);
}
.fz-day-cycle__notes-text {
  font-size: 12px;
  color: var(--muted);
  word-break: break-word;
  line-height: 1.4;
}
.fz-day-cycle__notes-input {
  font: inherit;
  font-size: 13px;
  width: 100%;
  padding: 4px 6px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  resize: none;
  min-height: 56px;
}
.fz-day-cycle__notes-input:focus {
  outline: none;
  border-color: var(--accent);
}
.fz-day-cycle-card__rows .fz-day-cycle__row:first-child { border-top: 0; }
.fz-day-cycle__row--done {
  opacity: 0.62;
}
.fz-day-cycle__row--done .fz-day-cycle__name {
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--muted);
}
.fz-day-cycle__cb {
  accent-color: var(--accent);
  width: 20px;
  height: 20px;
  cursor: pointer;
}
.fz-day-cycle__name {
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
  min-width: 0;
  line-height: 1.3;
  /* Allow up to 2 lines, then ellipsis. -webkit-line-clamp + display:-webkit-box
     is supported across iOS Safari + Chrome + FF (with the prefixed property). */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
  overflow-wrap: anywhere;
}
/* DAY-CYCLE-REFINEMENTS (v0.9.466) — reusable H+M duration input.
   Named .fz-time-duration-input so the same pattern can be lifted into
   other surfaces that take minute-precision durations (set timers,
   habit reminders) without re-deriving the layout. Internal storage
   stays minutes; the two inputs are pure UI. v0.9.484 polish:
   symmetric 40px-wide inputs, centered text, --radius-md, calm focus. */
.fz-time-duration-input {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.fz-time-duration-input__h,
.fz-time-duration-input__m {
  font: inherit;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  width: 40px;
  min-height: 36px;
  padding: 6px 4px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--ink);
  text-align: center;
  transition: border-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard);
  /* Hide spinner buttons (Chrome/Safari + Firefox). */
  -moz-appearance: textfield;
}
.fz-time-duration-input__h:focus,
.fz-time-duration-input__m:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring-accent);
}
.fz-time-duration-input__h::-webkit-outer-spin-button,
.fz-time-duration-input__h::-webkit-inner-spin-button,
.fz-time-duration-input__m::-webkit-outer-spin-button,
.fz-time-duration-input__m::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.fz-time-duration-input__suf {
  font-size: 12px;
  color: var(--muted);
  margin-right: 6px;
}
.fz-time-duration-input__suf:last-child { margin-right: 0; }
/* DAYCYCLE-EDIT-INPUT-FIXES — the edit sheet's h/m fields must clear the 44px
   Apple HIG / WCAG 2.5.5 tap-target floor and stay >=16px font so iOS never
   tries (and, under user-scalable=no, fails) to zoom the field on focus. Scoped
   to the sheet so the denser main-page duration cell keeps its compact size. */
.fz-day-cycle-sheet__dur .fz-time-duration-input__h,
.fz-day-cycle-sheet__dur .fz-time-duration-input__m {
  min-width: 44px;
  min-height: 44px;
  font-size: 16px;
  padding: 6px;
}

/* Time-range cell — stacks vertically. Line 1 (primary): start → deadline
   in primary ink. Line 2 (secondary, only when the deadline shifted):
   "was XX:XX" in muted tone. v0.9.484 — replaces the wrapping-flex layout
   that crammed up to four pieces of data onto one line. */
.fz-day-cycle__dl {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  min-width: 0;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  text-align: right;
}
.fz-day-cycle__dl-primary {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.2;
}
.fz-day-cycle__dl-was {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  font-size: 12px;
  line-height: 1.2;
  color: var(--muted);
  opacity: 0.7;
}
.fz-day-cycle__dl-was-lbl {
  font-size: 10px;
  text-transform: lowercase;
  letter-spacing: 0.04em;
}
.fz-day-cycle__dl-time {
  color: var(--ink);
  font-weight: 500;
}
.fz-day-cycle__dl-start {
  color: var(--muted);
  font-weight: 500;
}
.fz-day-cycle__dl-arrow {
  color: var(--muted);
  font-weight: 400;
}
.fz-day-cycle__dl-orig {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.fz-day-cycle__dl-actual {
  font-weight: 600;
  color: var(--ink);
}

/* Slippage badge — calm pill in its own grid column so it aligns across
   rows in the phase. Late = soft indigo bg + indigo fg; early = soft
   green bg + green fg. Pill stays monotone (no gradient). v0.9.484. */
.fz-day-cycle__slip-col {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  min-height: 18px;
}
.fz-day-cycle__slip {
  font-size: 11px;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  font-variant-numeric: tabular-nums;
  background: var(--slip-late-bg);
  color: var(--slip-late-fg);
  white-space: nowrap;
}
.fz-day-cycle__slip--late {
  background: var(--slip-late-bg);
  color: var(--slip-late-fg);
}
.fz-day-cycle__slip--early {
  background: var(--slip-early-bg);
  color: var(--slip-early-fg);
}

/* Narrow phones (iPhone SE / older Android): tighten gap. */
@media (max-width: 380px) {
  .fz-day-cycle__row { gap: 8px; padding: 10px 12px; }
  /* DAY-CYCLE-SUBTASK-VISUAL-NESTING — base gutter shrinks to 12px here, so
     +20px indent on small screens (vs +24px elsewhere). */
  .fz-day-cycle__row--child { padding-left: 32px; }
}

/* ═══════════════ DAYCYCLE-EDIT-FULLSCREEN-REBUILD (v0.9.1273) ═══════════════
   Full-screen /day-cycle/edit editor. One scrolled list of rows w/ persistent
   grab handles + immediate-drag reorder, inline-editable names, a duration pill
   that opens the single-task bottom sheet, sticky footer (primary add + running
   total), an undo snackbar on delete + a retry banner on failed save. Tokens
   only (Dag/Nacht safe); accent surfaces use --on-accent; ≥44px tap targets;
   respects env(safe-area-inset-bottom). */
.fz-dce {
  display: flex;
  flex-direction: column;
  min-height: calc(100dvh - env(safe-area-inset-top) - env(safe-area-inset-bottom));
  color: var(--ink);
}
.fz-dce__head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 4px 12px;
  position: sticky;
  top: 0;
  z-index: 4;
  background: var(--bg, var(--surface));
}
.fz-dce__back,
.fz-dce__more-btn {
  flex: 0 0 auto;
  background: transparent;
  border: 0;
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard);
}
.fz-dce__back:hover,
.fz-dce__more-btn:hover { background: var(--line-2); }
.fz-dce__back svg,
.fz-dce__more-btn svg { width: 20px; height: 20px; }
.fz-dce__titlewrap { flex: 1 1 auto; min-width: 0; }
.fz-dce__title {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.2;
}
.fz-dce__saved {
  font-size: 12px;
  color: var(--muted);
  min-height: 15px;
  line-height: 15px;
}

.fz-dce__body {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 4px 4px 16px;
}
.fz-dce__phase { display: flex; flex-direction: column; gap: 6px; }
.fz-dce__phase-lbl {
  margin: 0 0 2px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
}
.fz-dce__list { display: flex; flex-direction: column; gap: 6px; }
.fz-dce__phase-empty {
  padding: 12px;
  font-size: 13px;
  color: var(--muted);
  text-align: center;
}
.fz-dce__phase-add { display: flex; gap: 8px; margin-top: 4px; }
.fz-dce__phase-btn {
  flex: 1 1 auto;
  min-height: 40px;
  background: transparent;
  border: 1px dashed var(--line-2);
  border-radius: var(--radius-md);
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.fz-dce__phase-btn:hover { background: var(--line-2); color: var(--ink); }

/* Row: swipe-wrapper (opaque row over parked Duplicate/Delete actions). */
.fz-dce__rowwrap {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-lg);
  transition: transform var(--dur-base) var(--ease-standard);
}
.fz-dce__rowwrap.is-dragging {
  overflow: visible;
  transition: none;
  z-index: 5;
}
.fz-dce__swipe-actions {
  position: absolute;
  inset: 0 0 0 auto;
  display: flex;
  align-items: stretch;
}
.fz-dce__swipe-btn {
  width: 66px;
  border: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--on-accent);
  cursor: pointer;
  pointer-events: none;
}
.fz-dce__swipe-btn svg { width: 20px; height: 20px; }
.fz-dce__swipe-btn--dup { background: var(--accent); color: var(--on-accent); }
.fz-dce__swipe-btn--del { background: var(--danger); color: var(--on-danger); }
.fz-dce__rowwrap.is-revealed .fz-dce__swipe-btn { pointer-events: auto; }

.fz-dce__row {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 52px;
  padding: 4px 6px;
  position: relative;
  z-index: 1;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-lg);
  transition: transform var(--dur-base) var(--ease-standard);
}
.fz-dce__rowwrap.is-dragging .fz-dce__row {
  transform: scale(1.02);
  box-shadow: var(--elev-2);
}
.fz-dce__rowwrap--child { margin-left: 24px; }
@media (max-width: 380px) { .fz-dce__rowwrap--child { margin-left: 16px; } }

.fz-dce__handle {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  background: transparent;
  border: 0;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: grab;
  touch-action: none;
  border-radius: var(--radius-md);
}
.fz-dce__handle:active { cursor: grabbing; }
.fz-dce__collapse {
  flex: 0 0 auto;
  width: 32px;
  height: 44px;
  background: transparent;
  border: 0;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.fz-dce__collapse svg { width: 16px; height: 16px; transition: transform var(--dur-base) var(--ease-standard); }
.fz-dce__collapse.is-collapsed svg { transform: rotate(-90deg); }

.fz-dce__main { flex: 1 1 auto; min-width: 0; }
.fz-dce__name {
  width: 100%;
  min-height: 40px;
  background: transparent;
  border: 0;
  color: var(--ink);
  font: inherit;
  font-size: 15px;
  padding: 4px 2px;
}
.fz-dce__name:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-radius: var(--radius-md);
}
.fz-dce__metric-chip {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  background: var(--line-2);
  color: var(--ink);
  font-size: 14px;
  font-weight: 600;
}
.fz-dce__field-err {
  font-size: 12px;
  color: var(--danger);
  padding: 2px 2px 0;
}

.fz-dce__dur-pill {
  flex: 0 0 auto;
  min-height: 36px;
  min-width: 56px;
  padding: 6px 12px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line-2);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}
.fz-dce__dur-pill:hover { background: var(--line-2); }
.fz-dce__dur-derived {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
  color: var(--muted);
}
.fz-dce__derived-tag {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 2px 6px;
  border-radius: var(--radius-pill);
  background: var(--line-2);
  color: var(--muted);
}
.fz-dce__more {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  background: transparent;
  border: 0;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border-radius: var(--radius-md);
}
.fz-dce__more:hover { background: var(--line-2); color: var(--ink); }
.fz-dce__more svg { width: 18px; height: 18px; }

/* Sticky footer: primary add + running total. */
.fz-dce__foot {
  position: sticky;
  bottom: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 4px calc(10px + env(safe-area-inset-bottom, 0px));
  background: var(--bg, var(--surface));
  border-top: 1px solid var(--line-2);
}
.fz-dce__add {
  flex: 1 1 auto;
  min-height: 48px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  color: var(--on-accent);
}
.fz-dce__add svg { width: 18px; height: 18px; }
.fz-dce__total {
  flex: 0 0 auto;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
}
/* DAYCYCLE-DELETE-AND-REINTERVIEW — quiet bottom danger zone. Not a prominent
   red fill: a muted outline button following the .set-mini--danger idiom. */
.fz-dce__danger {
  display: flex;
  justify-content: center;
  margin: 28px 4px 12px;
  padding-top: 16px;
  border-top: 1px solid var(--line-2);
}
.fz-dce__delete {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  padding: 8px 16px;
  font-size: 14px;
  font-weight: 600;
  background: transparent;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-md);
  color: var(--muted);
  cursor: pointer;
}
.fz-dce__delete svg { width: 16px; height: 16px; }

/* An author `display:flex` beats the UA `[hidden]` rule, so guard both the
   retry banner and the snackbar explicitly or they'd paint while hidden. */
.fz-dce__retry[hidden],
.fz-dce__snackbar[hidden] { display: none; }

/* Retry banner (failed PUT). */
.fz-dce__retry {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(84px + env(safe-area-inset-bottom, 0px));
  z-index: 120;
  width: min(92%, 520px);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-radius: var(--radius-lg);
  background: var(--danger);
  color: var(--on-danger);
  box-shadow: var(--elev-2);
}
.fz-dce__retry-msg { flex: 1 1 auto; font-size: 14px; }
.fz-dce__retry-btn {
  flex: 0 0 auto;
  min-height: 36px;
  padding: 6px 14px;
  border: 0;
  border-radius: var(--radius-md);
  background: var(--on-danger);
  color: var(--danger);
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}

/* Undo snackbar (every delete). */
.fz-dce__snackbar {
  position: fixed;
  left: 50%;
  transform: translateX(-50%) translateY(8px);
  bottom: calc(84px + env(safe-area-inset-bottom, 0px));
  z-index: 130;
  width: min(92%, 520px);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-radius: var(--radius-lg);
  background: var(--ink);
  color: var(--surface);
  box-shadow: var(--elev-2);
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-standard), transform var(--dur-base) var(--ease-standard);
}
.fz-dce__snackbar.is-open { opacity: 1; transform: translateX(-50%) translateY(0); }
.fz-dce__snackbar-msg { flex: 1 1 auto; font-size: 14px; }
.fz-dce__snackbar-btn {
  flex: 0 0 auto;
  min-height: 36px;
  padding: 6px 14px;
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--accent);
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}

/* Popover menus (metric picker + page overflow). */
.fz-dce__metric-picker,
.fz-dce__page-menu {
  position: fixed;
  /* boven .fz-dce-tasksheet (150): beide menus hangen aan body, niet in de sheet */
  z-index: 160;
  min-width: 200px;
  max-width: 280px;
  padding: 6px;
  border-radius: var(--radius-lg);
  background: var(--surface);
  border: 1px solid var(--line-2);
  box-shadow: var(--elev-3);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.fz-dce__metric-picker-hint {
  padding: 6px 10px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}
.fz-dce__metric-picker-empty { padding: 10px; font-size: 13px; color: var(--muted); }
.fz-dce__metric-picker-opt,
.fz-dce__page-menu-opt {
  min-height: 44px;
  padding: 10px 12px;
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  text-align: left;
  cursor: pointer;
}
.fz-dce__metric-picker-opt:hover,
.fz-dce__page-menu-opt:hover { background: var(--line-2); }

/* ── Single-task bottom sheet (the one legitimate sheet use) ── */
/* In-place edit mode: the editor surface is hosted inside the day-cycle player
   body, so it must not claim its own viewport height or its own scroller —
   the controls above it have to scroll away with the list. */
.fz-dce--embedded { min-height: 0; }
.fz-dce--embedded .fz-dce__body {
  overflow-y: visible;
  flex: 0 0 auto;
  padding-left: 0;
  padding-right: 0;
}
.fz-dce--embedded .fz-dce__foot { padding-left: 0; padding-right: 0; }
.fz-day-cycle-page__head-slot {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.fz-day-cycle-page__head-slot:empty { display: none; }
.fz-day-cycle-page__edit.is-active {
  padding: 6px 10px;
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}
.fz-day-cycle-page__edit-label { font-size: 14px; line-height: 1; }

.fz-dce-tasksheet {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  z-index: 150;
}
.fz-dce-tasksheet__card {
  background: var(--surface);
  color: var(--ink);
  width: 100%;
  max-width: 560px;
  max-height: 92vh;
  border-top-left-radius: var(--radius-lg);
  border-top-right-radius: var(--radius-lg);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 8px 16px calc(16px + env(safe-area-inset-bottom, 0px));
  gap: 8px;
}
.fz-dce-tasksheet__grabber {
  align-self: center;
  width: 40px;
  height: 4px;
  border-radius: var(--radius-sm);
  background: var(--line-2);
  margin: 4px 0 8px;
}
.fz-dce-tasksheet__head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.fz-dce-tasksheet__title { margin: 0; font-size: 16px; font-weight: 700; flex: 1 1 auto; }
.fz-dce-tasksheet__label {
  flex: 1 1 auto;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
}
.fz-dce-tasksheet__close {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  background: transparent;
  border: 0;
  font-size: 24px;
  line-height: 1;
  color: var(--ink);
  cursor: pointer;
}
.fz-dce-tasksheet__name,
.fz-dce-tasksheet__note {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-md);
  background: var(--bg, var(--surface));
  color: var(--ink);
  font: inherit;
  font-size: 15px;
}
.fz-dce-tasksheet__note { resize: vertical; min-height: 72px; }
.fz-dce-tasksheet__name:focus-visible,
.fz-dce-tasksheet__note:focus-visible,
.fz-dce-tasksheet__dur-field:focus-visible,
.fz-dce-tasksheet__subname:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.fz-dce-tasksheet__dur { display: flex; flex-direction: column; gap: 6px; }
.fz-dce-tasksheet__chips { display: flex; gap: 8px; flex-wrap: wrap; }
.fz-dce-tasksheet__chip {
  min-width: 48px;
  min-height: 40px;
  padding: 6px 14px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.fz-dce-tasksheet__chip.is-active {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.fz-dce-tasksheet__dur-field {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-md);
  background: var(--bg, var(--surface));
  color: var(--ink);
  font: inherit;
  font-size: 15px;
}
.fz-dce-tasksheet__dur-read { font-size: 13px; color: var(--muted); }
.fz-dce-tasksheet__subs { display: flex; flex-direction: column; gap: 6px; }
.fz-dce-tasksheet__sublist { display: flex; flex-direction: column; gap: 6px; }
.fz-dce-tasksheet__subrow { display: flex; align-items: center; gap: 8px; }
.fz-dce-tasksheet__subname {
  flex: 1 1 auto;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-md);
  background: var(--bg, var(--surface));
  color: var(--ink);
  font: inherit;
  font-size: 14px;
}
.fz-dce-tasksheet__subdel {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  background: transparent;
  border: 0;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.fz-dce-tasksheet__subdel svg { width: 18px; height: 18px; }
.fz-dce-tasksheet__submetric {
  flex: 1 1 auto;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-md);
  background: var(--surface-2, var(--surface));
  color: var(--ink);
  font-size: 14px;
  font-weight: 600;
  display: flex;
  align-items: center;
}
.fz-dce-tasksheet__addsubrow { display: flex; flex-wrap: wrap; gap: 8px; }
.fz-dce-tasksheet__addsub {
  align-self: flex-start;
  min-height: 40px;
  padding: 8px 14px;
  border: 1px dashed var(--line-2);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.fz-dce-tasksheet__acts {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
}
.fz-dce-tasksheet__act {
  flex: 1 1 auto;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.fz-dce-tasksheet__act:hover { background: var(--line-2); }
.fz-dce-tasksheet__act--danger {
  color: var(--danger);
  border-color: var(--danger);
}

/* Edit-mode sheet — full-dimmed copy + history pattern
   (feedback_reuse_sheet_ui_not_minimal_widgets). */
.fz-day-cycle-sheet {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  z-index: 100;
}
.fz-day-cycle-sheet__card {
  background: var(--surface);
  color: var(--ink);
  width: 100%;
  max-width: 560px;
  max-height: 88vh;
  border-top-left-radius: var(--radius-lg);
  border-top-right-radius: var(--radius-lg);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  position: relative; /* anchors the in-sheet discard confirm overlay */
}
.fz-day-cycle-sheet__head {
  display: flex;
  align-items: center;
  padding: 12px 16px;
  border-bottom: 1px solid var(--line-2);
}
.fz-day-cycle-sheet__title {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  flex: 1 1 auto;
}
.fz-day-cycle-sheet__close {
  background: transparent;
  border: 0;
  font-size: 22px;
  line-height: 1;
  color: var(--ink);
  cursor: pointer;
  padding: 4px 8px;
}
.fz-day-cycle-sheet__body {
  overflow-y: auto;
  padding: 12px 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.fz-day-cycle-sheet__phase {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.fz-day-cycle-sheet__phase-lbl {
  margin: 0;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
}
.fz-day-cycle-sheet__list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* DAY-CYCLE-TASK-EDITOR-DRAG-AND-SWIPE — each row sits in a positioned
   wrapper so a swipe-left can slide the (opaque) row over a Delete action
   parked at the right edge. The ≡ handle owns the long-press drag-reorder. */
.fz-day-cycle-sheet__rowwrap {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-md);
  transition: transform var(--dur-base) var(--ease-standard); /* sibling shift while another row drags */
}
.fz-day-cycle-sheet__rowwrap.is-dragging {
  overflow: visible; /* let the lifted row + its shadow escape the clip */
  transition: none;  /* the grabbed row tracks the finger, no easing */
  z-index: 5;
}
.fz-day-cycle-sheet__del {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: 88px;
  border: 0;
  border-radius: var(--radius-md);
  background: var(--danger);
  color: var(--on-danger);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  pointer-events: none; /* only tappable once the row is swiped open */
}
.fz-day-cycle-sheet__rowwrap.is-revealed .fz-day-cycle-sheet__del { pointer-events: auto; }
.fz-day-cycle-sheet__row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-areas:
    "handle name ctl"
    "handle notes dur";
  gap: 6px 8px;
  align-items: start;
  position: relative;
  z-index: 1;
  background: var(--surface); /* opaque so it hides the Delete action beneath */
  transition: transform var(--dur-base) var(--ease-standard); /* swipe snap-back */
}
.fz-day-cycle-sheet__rowwrap.is-dragging .fz-day-cycle-sheet__row {
  transform: scale(1.02);
  box-shadow: var(--elev-2);
}
.fz-day-cycle-sheet__handle {
  grid-area: handle;
  align-self: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  min-height: 30px;
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--ink-2);
  cursor: grab;
  touch-action: none; /* the handle handles its own gesture */
}
.fz-day-cycle-sheet__handle:active { cursor: grabbing; }
.fz-day-cycle-sheet__handle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-md);
}
@media (prefers-reduced-motion: reduce) {
  /* Drag + swipe still work functionally; drop the lift-scale and the
     snap-back spring so motion-sensitive users get instant state changes. */
  .fz-day-cycle-sheet__rowwrap,
  .fz-day-cycle-sheet__row { transition: none; }
  .fz-day-cycle-sheet__rowwrap.is-dragging .fz-day-cycle-sheet__row {
    transform: none;
    box-shadow: var(--ring-accent);
  }
}
.fz-day-cycle-sheet__name {
  grid-area: name;
  font: inherit;
  font-size: 13px;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--ink);
  min-width: 0;
}
.fz-day-cycle-sheet__notes {
  grid-area: notes;
  font: inherit;
  font-size: 13px;
  padding: 5px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--ink);
  resize: none;
  width: 100%;
  min-height: 52px;
}
.fz-day-cycle-sheet__notes:focus { outline: none; border-color: var(--accent); }
.fz-day-cycle-sheet__dur { grid-area: dur; align-self: center; }
.fz-day-cycle-sheet__ctl { grid-area: ctl; }
.fz-day-cycle-sheet__ctl {
  display: inline-flex;
  gap: 4px;
}
.fz-day-cycle-sheet__btn {
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 4px 8px;
  font-size: 13px;
  color: var(--ink);
  cursor: pointer;
  min-width: 28px;
}
.fz-day-cycle-sheet__btn:disabled { opacity: 0.4; cursor: default; }
.fz-day-cycle-sheet__add {
  align-self: flex-start;
  background: transparent;
  border: 1px dashed var(--line);
  border-radius: var(--radius-md);
  padding: 6px 12px;
  font-size: 12px;
  color: var(--ink-2);
  cursor: pointer;
}
.fz-day-cycle-sheet__foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 16px;
  /* DAYCYCLE-EDIT-INPUT-FIXES — keep the footer above the home-indicator /
     keyboard inset so Save/Cancel stay reachable. */
  padding-bottom: calc(12px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line-2);
}
.fz-day-cycle-sheet__cancel,
.fz-day-cycle-sheet__save {
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 8px 14px;
  border-radius: var(--radius-md);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.fz-day-cycle-sheet__save {
  background: var(--accent);
  color: var(--on-accent);
  border-color: transparent;
}
/* DAYCYCLE-EDIT-INPUT-FIXES — per-row visible delete signifier (swipe stays as
   the power-user shortcut). Danger-tinted icon button in the ctl area. */
.fz-day-cycle-sheet__btn--del {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  min-height: 34px;
  padding: 4px;
  color: var(--danger);
  border-color: var(--line-2);
}
.fz-day-cycle-sheet__btn--del:active { background: var(--line-2); }
/* DAYCYCLE-EDIT-INPUT-FIXES — themed discard confirm (replaces window.confirm)
   overlaying the card when Cancel/close is hit with unsaved edits. */
.fz-day-cycle-sheet__confirm {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  z-index: 20;
}
.fz-day-cycle-sheet__confirm-box {
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-lg);
  padding: 18px;
  width: 100%;
  max-width: 320px;
}
.fz-day-cycle-sheet__confirm-msg {
  margin: 0 0 16px;
  font-size: 14px;
  font-weight: 600;
}
.fz-day-cycle-sheet__confirm-btns {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
.fz-day-cycle-sheet__confirm-discard {
  background: var(--danger);
  color: var(--on-danger);
  border-color: transparent;
}

/* DAYCYCLE-METRIC-TASKS-AND-SUBTASKS — editor extras + per-row metric/
   subtask chrome. Tokens-only (no hex) so Dag/Nacht both stay legible. */
.fz-day-cycle-sheet__row--child { padding-left: 20px; border-left: 2px solid var(--line-2); margin-left: 4px; }
.fz-day-cycle-sheet__metric-chip {
  grid-area: name;
  display: inline-flex;
  align-items: center;
  padding: 6px 10px;
  border: 1px dashed var(--accent);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--accent);
  font-size: 13px;
  font-weight: 600;
}
.fz-day-cycle-sheet__add-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.fz-day-cycle-sheet__add--metric {
  color: var(--accent);
  border-color: var(--accent);
  border-style: dashed;
}
.fz-day-cycle-sheet__btn--sub {
  color: var(--accent);
  border-color: var(--accent);
  font-size: 11px;
  padding: 2px 6px;
}
.fz-day-cycle-sheet__metric-picker {
  position: fixed;
  z-index: 1000;
  min-width: 200px;
  max-height: 60vh;
  overflow: auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  box-shadow: var(--elev-2);
}
.fz-day-cycle-sheet__metric-picker-hint,
.fz-day-cycle-sheet__metric-picker-empty {
  font-size: 11px;
  color: var(--muted);
  padding: 6px 8px;
}
.fz-day-cycle-sheet__metric-picker-opt {
  font: inherit;
  font-size: 13px;
  text-align: left;
  background: transparent;
  border: none;
  padding: 8px 10px;
  border-radius: var(--radius-md);
  cursor: pointer;
  color: var(--ink);
}
.fz-day-cycle-sheet__metric-picker-opt:hover { background: var(--accent-light); color: var(--accent); }

/* Page-side row chrome */
/* DAY-CYCLE-SUBTASK-VISUAL-NESTING — subtasks read as subordinate: indented
   +24px past the parent's 16px gutter, a 1px accent rail grouping them to the
   parent, smaller title + meta, and smaller h/m inputs. Parents untouched. */
.fz-day-cycle__row--child {
  padding-left: 40px;
  border-left: 1px solid var(--line);
  margin-left: 4px;
}
.fz-day-cycle__row--child .fz-day-cycle__name {
  font-size: 13px;
  line-height: 1.35;
}
.fz-day-cycle__row--child .fz-day-cycle__time-info { font-size: 11px; }
.fz-day-cycle__row--child .fz-day-cycle__ti-dur,
.fz-day-cycle__row--child .fz-day-cycle__ti-was { font-size: 10px; }
.fz-day-cycle__row--child .fz-time-duration-input__h,
.fz-day-cycle__row--child .fz-time-duration-input__m,
.fz-day-cycle__row--child .fz-time-duration-input__suf { font-size: 11px; }
.fz-day-cycle__notes--child-readonly { cursor: default; }
.fz-day-cycle__notes--child-readonly .fz-day-cycle__notes-text {
  font-size: 11px;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.fz-day-cycle__name--metric { display: inline-flex; align-items: center; gap: 6px; }
.fz-day-cycle__metric-btn {
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 6px 12px;
  border-radius: var(--radius-md);
  border: none;
  background: var(--accent);
  color: var(--on-accent);
  cursor: pointer;
}
.fz-day-cycle__metric-btn--filled {
  background: var(--accent-light);
  color: var(--accent);
}
/* DIARY-SLEEP-ROW-SKIPONLY-AND-WEARABLE-AUTOFILL */
.fz-day-cycle__metric-btn--nosleep {
  background: transparent;
  color: var(--muted);
  text-decoration: line-through;
}
/* DAYCYCLE-RECIPE-TILE-LOGS-TO-NUTRITION — recipe tile in the player's name
   slot. Deliberately NOT .fz-day-cycle__metric-btn: a recipe has no
   filled/nosleep status states, and a long recipe name must be able to
   ellipsise inside a narrow (possibly indented) row. */
.fz-day-cycle__name--recipe { display: inline-flex; align-items: center; min-width: 0; max-width: 100%; }
.fz-day-cycle__recipe-btn {
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 6px 12px;
  border-radius: var(--radius-md);
  border: 1px solid var(--accent);
  background: transparent;
  color: var(--accent);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  max-width: 100%;
}
.fz-day-cycle__recipe-btn-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fz-day-cycle__recipe-btn--logged {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.fz-day-cycle__recipe-check { width: 14px; height: 14px; flex: 0 0 auto; }

.fz-day-cycle__recipesheet {
  position: fixed; inset: 0; z-index: 1200;
  background: rgba(0, 0, 0, 0.45);
  display: flex; align-items: flex-end; justify-content: center;
}
.fz-day-cycle__recipesheet-card {
  width: 100%; max-width: 560px;
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  padding: 8px 16px calc(16px + env(safe-area-inset-bottom));
  max-height: 88vh; overflow-y: auto;
}
.fz-day-cycle__recipesheet-grab {
  width: 40px; height: 4px; border-radius: 2px;
  background: var(--line); margin: 4px auto 12px;
}
.fz-day-cycle__recipesheet-head { display: flex; align-items: center; gap: 8px; }
.fz-day-cycle__recipesheet-title { flex: 1; margin: 0; font-size: 17px; font-weight: 700; }
.fz-day-cycle__recipesheet-close {
  font: inherit; font-size: 22px; line-height: 1;
  min-width: 44px; min-height: 44px;
  background: none; border: none; color: var(--muted); cursor: pointer;
}
.fz-day-cycle__recipesheet-notes { margin: 10px 0; font-size: 13px; color: var(--ink-2); white-space: pre-wrap; }
.fz-day-cycle__recipesheet-items { margin: 10px 0; padding-left: 18px; }
.fz-day-cycle__recipesheet-item { font-size: 13px; margin-bottom: 4px; }
.fz-day-cycle__recipesheet-loading,
.fz-day-cycle__recipesheet-empty { padding: 16px 0; font-size: 13px; color: var(--muted); }
.fz-day-cycle__recipesheet-servings {
  display: flex; align-items: center; gap: 10px; margin: 12px 0;
}
.fz-day-cycle__recipesheet-servlabel { flex: 1; font-size: 13px; font-weight: 600; }
.fz-day-cycle__recipesheet-step {
  font: inherit; font-size: 18px; line-height: 1;
  min-width: 44px; min-height: 44px;
  border: 1px solid var(--line); border-radius: var(--radius-md);
  background: transparent; color: var(--ink); cursor: pointer;
}
.fz-day-cycle__recipesheet-servval { min-width: 44px; text-align: center; font-weight: 700; }
.fz-day-cycle__recipesheet-log {
  width: 100%; min-height: 48px;
  font: inherit; font-size: 15px; font-weight: 700;
  border: none; border-radius: var(--radius-md);
  background: var(--accent); color: var(--on-accent); cursor: pointer;
}
.fz-day-cycle__recipesheet-log:disabled { opacity: 0.5; cursor: default; }

/* undo bar — the player has no snackbar of its own (the editor's is scoped
   to .fz-dce__). */
.fz-day-cycle__undobar {
  position: fixed; z-index: 1300;
  left: 16px; right: 16px; bottom: calc(80px + env(safe-area-inset-bottom));
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px;
  border-radius: var(--radius-md);
  background: var(--ink); color: var(--bg);
  font-size: 13px;
}
.fz-day-cycle__undobar-msg { flex: 1; }
.fz-day-cycle__undobar-btn {
  font: inherit; font-weight: 700;
  background: none; border: none; color: inherit;
  text-decoration: underline; cursor: pointer;
  min-height: 44px;
}

/* recipe attachment row + picker in the editor task sheet */
.fz-dce-tasksheet__recipe { display: flex; align-items: center; gap: 8px; position: relative; }
.fz-dce-tasksheet__recipe-btn {
  flex: 1; min-width: 0; text-align: left;
  font: inherit; font-size: 14px;
  min-height: 44px; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: var(--radius-md);
  background: transparent; color: var(--muted); cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fz-dce-tasksheet__recipe-btn.is-set { color: var(--ink); font-weight: 600; }
.fz-dce-tasksheet__recipe-clear {
  min-width: 44px; min-height: 44px;
  background: none; border: none; color: var(--muted); cursor: pointer;
}
.fz-dce__recipe-picker {
  position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 20;
  max-height: 260px; overflow-y: auto;
  border: 1px solid var(--line); border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--elev-2);
}

/* DAYCYCLE-WORKOUT-TILE-OPENS-GYM — workout attachment row + picker, and the
   player tile. The tile borrows the recipe tile's shape (same name slot, same
   ellipsis behaviour) but stays a plain outlined chip: opening the Gym tab is a
   navigation, so it has no logged/filled state to paint. */
.fz-day-cycle__name--workout { display: inline-flex; align-items: center; min-width: 0; max-width: 100%; }
.fz-day-cycle__workout-btn {
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 6px 12px;
  border-radius: var(--radius-md);
  border: 1px solid var(--accent);
  background: transparent;
  color: var(--accent);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  max-width: 100%;
}
.fz-day-cycle__workout-btn-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fz-day-cycle__workout-icon { width: 14px; height: 14px; flex: 0 0 auto; }

.fz-dce-tasksheet__workout { display: flex; align-items: center; gap: 8px; position: relative; }
.fz-dce-tasksheet__workout[hidden] { display: none; }
.fz-dce-tasksheet__workout-btn {
  flex: 1; min-width: 0; text-align: left;
  font: inherit; font-size: 14px;
  min-height: 44px; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: var(--radius-md);
  background: transparent; color: var(--muted); cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fz-dce-tasksheet__workout-btn.is-set { color: var(--ink); font-weight: 600; }
.fz-dce-tasksheet__workout-clear {
  min-width: 44px; min-height: 44px;
  background: none; border: none; color: var(--muted); cursor: pointer;
}
.fz-dce__workout-picker {
  position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 20;
  /* The options are plain buttons — without an explicit column they flow
     inline and two schema names end up sharing a line. */
  display: flex; flex-direction: column; gap: 2px; padding: 6px;
  max-height: 260px; overflow-y: auto;
  border: 1px solid var(--line); border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--elev-2);
}

.fz-day-cycle__metric-src {
  margin-left: 8px;
  font-size: 11px;
  font-weight: 500;
  color: var(--muted);
}
.fz-day-cycle__nosleep-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  flex: none;
}
.fz-day-cycle__nosleep-toggle svg {
  width: 20px;
  height: 20px;
}
.fz-day-cycle__nosleep-toggle--active {
  color: var(--accent);
}
.fz-day-cycle__metric-input {
  font: inherit;
  font-size: 14px;
  padding: 6px 8px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-md);
  width: 88px;
  background: var(--surface);
  color: var(--ink);
}
.fz-day-cycle__metric-unit {
  font-size: 12px;
  color: var(--muted);
  margin-left: 4px;
}

/* ─────────────────────────────────────────────────────────────────────
   SETTINGS-PAGE-COMPREHENSIVE-OVERHAUL (v0.9.476)
   IA tiering — Trainer / Personal / Technical / Dev — with drill-in
   sub-pages at /settings/account + /settings/cardio (mirror /goals).
   Tokens only — both Dag (light + blue) + Nacht (dark + lime) presets.
───────────────────────────────────────────────────────────────────── */

/* Drill-in sub-page chrome (Goals/Account/Cardio share this pattern).
   v0.9.490 SETTINGS-PADDING-BLEED — top reduced 16→12px to match the main
   Settings page (.app-main provides the nav-bottom clearance). */
.set-page {
  padding: 12px 16px 16px;
  max-width: 720px;
  margin: 0 auto;
}
.set-page__header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}
.set-page__back {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-md);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.set-page__back svg { width: 18px; height: 18px; }
.set-page__titles { flex: 1; }
.set-page__title {
  font-size: 18px;
  font-weight: 700;
  margin: 0;
  color: var(--ink);
}
.set-page__hint {
  font-size: 12px;
  color: var(--muted);
  margin: 6px 0 12px;
  line-height: 1.4;
}
.set-page__hint--inline { margin: 0 0 8px; }

/* Profile header — tappable drill-in to /settings/account. */
.set-profile--tappable {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 14px;
  text-align: left;
  cursor: pointer;
  color: var(--ink);
  font: inherit;
}
.set-profile--tappable:hover { background: var(--surface-2, var(--surface)); }
.set-profile__chev {
  margin-left: auto;
  color: var(--muted);
  font-size: 22px;
  line-height: 1;
}

/* Drill-in row pattern — used by Account, Cardio settings, Goals,
   Edit goals, etc. The chevron sits at the right; whole row taps. */
.set-row--nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  min-height: 44px;
  padding: 12px 14px;
  background: transparent;
  border: 0;
  text-align: left;
  font: inherit;
  color: var(--ink);
  cursor: pointer;
  text-decoration: none;
}
.set-row--nav:hover { background: var(--surface-2, var(--surface)); }
.set-row__chev {
  color: var(--muted);
  font-size: 20px;
  line-height: 1;
  margin-left: 12px;
}

/* Loading row placeholder for async sections (Diary metrics). */
.set-row--loading .set-row__label {
  color: var(--muted);
  font-style: italic;
}

/* Per-section Restore-defaults button — calm inline link tone. */
.set-reset-row {
  display: block;
  width: 100%;
  background: transparent;
  border: 0;
  border-top: 1px solid var(--line);
  padding: 12px 14px;
  text-align: left;
  font: inherit;
  font-size: 13px;
  color: var(--muted);
  cursor: pointer;
}
.set-reset-row:hover { color: var(--ink); }

/* Cardio drill-in page — Restore defaults large button at bottom. */
.set-reset-section {
  margin: 16px 0 24px;
  display: flex;
  justify-content: center;
}
.set-reset-section__btn {
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 10px 18px;
  border-radius: var(--radius-md);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}

/* Global Restore all defaults — danger row at the very bottom. */
.set-group--danger-soft .set-card {
  border-color: var(--danger, var(--line));
}
.set-row--danger-action {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  min-height: 44px;
  padding: 12px 14px;
  background: transparent;
  border: 0;
  text-align: left;
  font: inherit;
  cursor: pointer;
}
.set-row__label--danger { color: var(--danger, var(--ink)); font-weight: 600; }

/* Trainer section consolidated card. */
.set-group--trainer .set-card { border-color: var(--accent); }

/* Note-style hint underneath toggles (Diary metrics Mood line). */
.set-group__hint--note {
  margin-top: 12px;
  border-top: 1px solid var(--line);
  padding-top: 10px;
}

/* App-version value tappable (easter egg trigger). */
.set-row__value--tappable {
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

/* Developer section — hidden by default; the easter egg sets the body
   class which scopes the reveal (CSS-only fallback if JS misses). */
.set-group--dev .set-card { border-style: dashed; }

/* Account drill-in page — disclosure value preview + inline form bits. */
.set-disclose__value {
  margin-left: auto;
  color: var(--muted);
  font-size: 13px;
  padding-right: 8px;
}
.set-disclose__note {
  margin: 8px 0 0;
  font-size: 12px;
  color: var(--muted);
}
.set-account-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 10px 0;
}
.set-account-field__label {
  font-size: 12px;
  color: var(--muted);
  font-weight: 600;
}
.set-account-input {
  font: inherit;
  font-size: 14px;
  padding: 10px 12px;
  border-radius: var(--radius-md);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  min-height: 44px;
}
.set-account-input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Inline confirmation states (display name save, email/password save). */
.set-inline-status {
  font-size: 12px;
  color: var(--muted);
  margin-left: 8px;
  align-self: center;
}
.set-inline-status.is-ok { color: var(--ok, var(--accent)); }
.set-inline-status.is-err { color: var(--danger, var(--ink)); }
.set-inline-error {
  font-size: 13px;
  color: var(--danger, var(--ink));
  padding: 8px 10px;
  border-radius: var(--radius-md);
  background: var(--surface-2, var(--surface));
  margin-bottom: 8px;
}
.set-inline-confirm {
  font-size: 13px;
  color: var(--ok, var(--accent));
  padding: 0 2px;
  min-height: 0;
  transition: min-height var(--dur-base) var(--ease-standard);
}
.set-inline-confirm.is-on {
  min-height: 24px;
  padding: 6px 10px;
  background: var(--surface-2, var(--surface));
  border-radius: var(--radius-md);
  margin: 6px 0;
}

/* Page-level destructive action buttons on /settings/account. */
.set-page-action {
  width: 100%;
  min-height: 48px;
  font: inherit;
  font-weight: 600;
  border-radius: var(--radius-md);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.set-page-action--danger {
  color: var(--danger, var(--ink));
  border-color: var(--danger, var(--line));
}

/* TESTIMONIAL-1-SCHEMA-KLANTFLOW — the client's recommendation panel on
   /settings/account. The quote block is deliberately styled as the finished
   article rather than as a form preview: it is what the coach's public page
   will show, so it must not read like a draft. Tokens only — both presets
   inherit, no hardcoded colors. */
.set-ct-area {
  width: 100%;
  min-height: 120px;
  resize: vertical;
  line-height: 1.45;
}
.set-ct-name {
  font: inherit;
  padding: 8px 14px;
  min-height: 40px;
  border-radius: var(--radius-md);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.set-ct-name[aria-pressed="true"] {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.set-ct-quote {
  margin: 0;
  padding: var(--space-4) var(--space-5);
  border-left: 3px solid var(--accent);
  background: var(--surface-2, var(--surface));
  border-radius: var(--radius-md);
}
.set-ct-quote__body {
  margin: 0;
  color: var(--ink);
  line-height: 1.5;
  white-space: pre-wrap;
}
.set-ct-quote__name {
  margin-top: 6px;
  color: var(--section-label);
  font-size: 0.86rem;
}

/* TDEE-CALCULATOR-SETTINGS-AND-ONBOARDING — Settings 'Body & energy' card.
   The five inputs render as full-width labelled rows; the live TDEE preview
   line + the muted tracker-override note pin to the bottom of the card.
   Both Dag + Nacht presets inherit via tokens; no hardcoded colors. */
.set-be-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: var(--space-4) var(--space-5);
}
.set-be-field__label {
  font-size: 14px;
  color: var(--ink-2);
  font-weight: 500;
}
.set-be-field__input {
  width: 100%;
  min-height: 44px;
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 0 12px;
  box-sizing: border-box;
}
.set-be-field__input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.set-be-field__select {
  padding-right: 32px;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-2) 50%),
                    linear-gradient(135deg, var(--ink-2) 50%, transparent 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  -webkit-appearance: none;
  appearance: none;
}
.set-be-field--radio {
  align-items: flex-start;
}
.set-be-radio-group {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
.set-be-radio {
  font: inherit;
  font-weight: 500;
  min-height: 40px;
  padding: 0 18px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.set-be-radio.is-on {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.set-be-radio:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.set-be__preview {
  margin: 0;
  padding: var(--space-4) var(--space-5) 6px;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}
.set-be__tracker-note {
  margin: 0;
  padding: 0 var(--space-5) var(--space-4);
  font-size: 13px;
  color: var(--muted);
}

/* TDEE-CALCULATOR-SETTINGS-AND-ONBOARDING — full-screen new-profile takeover.
   Mounts inside the existing .auth shell so it inherits the notch-aware
   ladder + max-width frame. Five field grid stacks 1-column on mobile; the
   primary "Save & continue" + ghost "Skip for now" buttons sit at the foot. */
.onboarding__grid {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}
.onboarding__field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.onboarding__label {
  font-size: 14px;
  color: var(--ink-2);
  font-weight: 500;
}
.onboarding__input {
  width: 100%;
  min-height: 44px;
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 0 12px;
  box-sizing: border-box;
}
.onboarding__input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.onboarding__select {
  padding-right: 32px;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-2) 50%),
                    linear-gradient(135deg, var(--ink-2) 50%, transparent 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  -webkit-appearance: none;
  appearance: none;
}
.onboarding__radio-group {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
.intake-goal__explainer {
  margin-top: var(--space-2);
  font-size: 0.9em;
  color: var(--muted);
  line-height: 1.4;
  min-height: 1.4em;
}
.onboarding__radio {
  font: inherit;
  font-weight: 500;
  min-height: 40px;
  padding: 0 18px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.onboarding__radio.is-on {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.onboarding__radio:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* ONBOARDING-THREE-STEP-PROFILE — three panels, one visible at a time. */
.onboarding__dots {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin-bottom: var(--space-2);
}
.onboarding__dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--line);
}
.onboarding__dot.is-on {
  background: var(--accent);
}
.onboarding__step-label {
  text-align: center;
  font-size: 13px;
  color: var(--muted);
  margin: 0 0 var(--space-4);
}
.onboarding__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 12px;
}
.onboarding__summary-text {
  color: var(--ink);
}
.onboarding__link {
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  color: var(--accent);
  background: none;
  border: 0;
  padding: 0;
  text-decoration: underline;
  cursor: pointer;
}
.onboarding__override {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: var(--space-3);
}
/* INTAKE-DIETARY-PREFERENCES-AND-USER-AI-CONTEXT — Voorkeuren block. Generic,
   shared by the onboarding form and the Settings Body & energy card. */
.diet-subhead {
  margin: var(--space-3) 0 var(--space-1);
  font-size: 0.95em;
  font-weight: 600;
  color: var(--ink);
}
.diet-help {
  margin: 0 0 var(--space-1);
  font-size: 0.85em;
  color: var(--muted);
}
.diet-chips {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.diet-chip {
  font: inherit;
  font-weight: 500;
  min-height: 36px;
  padding: 0 14px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.diet-chip.is-on {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.diet-chip:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.diet-dislike-chips:empty {
  display: none;
}
.diet-dislike-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--ink);
  font-size: 0.9em;
}
.diet-dislike-chip__rm {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--muted);
  font-size: 1.15em;
  line-height: 1;
  padding: 0;
}
.onboarding__actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.onboarding--stepped .onboarding__actions {
  flex-direction: row;
}
.onboarding--stepped > .onboarding__btn--muted {
  margin-top: 10px;
}
.onboarding__btn {
  width: 100%;
  min-height: 48px;
}
.onboarding__btn--muted {
  background: transparent;
  color: var(--muted);
}

/* ──────────────────────────────────────────────────────────
   /gym/sequencer route — Gym-tab global periodisation editor.
   Header (back / Loop master-toggle / Save / Library) at the
   top, selected-cycle card body in the middle, horizontal
   Cycle-strip docked at the bottom of the .app-main area.
   ────────────────────────────────────────────────────────── */
.fz-seq-page {
  display: flex;
  flex-direction: column;
  /* SEQUENCER-SCROLL-FIX (v0.9.521): use a fixed viewport-bounded height so
     the body scrolls horizontally within the page rather than the whole page
     scrolling. 56px = topbar content+padding (excluding safe-top). */
  height: calc(100dvh - var(--safe-top) - 56px - var(--tabbar-h) - var(--safe-bottom) - var(--space-5));
  overflow: hidden;
  color: var(--ink);
}
.fz-seq-page__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
}
.fz-seq-page__title {
  flex: 1 1 auto;
  margin: 0;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.005em;
  line-height: 1.2;
  color: var(--ink);
}
.fz-seq-page__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.fz-seq-page__icon-btn {
  background: transparent;
  border: 0;
  padding: 6px;
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 36px;
  min-height: 36px;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.fz-seq-page__icon-btn:hover { background: var(--line-2); }
.fz-seq-page__icon-btn svg {
  width: 22px;
  height: 22px;
}
.fz-seq-page__loop.is-on {
  background: var(--accent);
  color: var(--on-accent);
}
.fz-seq-page__loop.is-on:hover {
  background: var(--accent-2);
  color: var(--on-accent);
}
.fz-seq-page__save {
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--ink);
  padding: 8px 14px;
  font-size: 14px;
  font-weight: 600;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard);
}
.fz-seq-page__save:hover {
  background: var(--surface-muted);
  border-color: var(--ink-2);
}
/* Master-loop ON visual — gentle accent-ring pulse so "repeat enabled"
   reads at a glance. Respects reduced-motion. */
@keyframes fz-seq-loop-pulse {
  0%   { box-shadow: 0 0 0 0 var(--accent); }
  70%  { box-shadow: 0 0 0 8px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
.fz-seq-page__loop.is-on {
  animation: fz-seq-loop-pulse var(--dur-pulse) var(--ease-standard) infinite;
}
@media (prefers-reduced-motion: reduce) {
  .fz-seq-page__loop.is-on { animation: none; }
}

/* ──────────────────────────────────────────────────────────
   SEQUENCER-V2-DAW-OVERHAUL (v0.9.526) — DAW-style canvas for
   /gym/sequencer. Three stacked tracks (Rep Max, Volume/Sets,
   Nutrition) with a sticky timeline ruler at the top carrying a
   draggable loop-brace. Sticky 140px left-rail with per-track
   instrument panels (color tape + label + axis). Drag-to-scroll
   on the canvas; Pointer Events for touch+mouse+pen.
   ────────────────────────────────────────────────────────── */
.fz-daw-canvas {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  position: relative;
  cursor: grab;
  background: var(--bg);
}
.fz-daw-canvas.is-grabbing { cursor: grabbing; }

.fz-daw-inner {
  min-width: max-content;
  display: flex;
  flex-direction: column;
  position: relative;
}

.fz-daw-loading,
.fz-daw-error {
  padding: var(--space-5);
  text-align: center;
  color: var(--muted);
  font-size: 14px;
}

/* ── Ruler ───────────────────────────────────────────────── */
.fz-daw-ruler {
  display: flex;
  height: 32px;
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.fz-daw-corner {
  width: 140px;
  flex: 0 0 auto;
  position: sticky;
  left: 0;
  z-index: 50;
  background: var(--surface);
  border-right: 2px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 0 8px;
}
.fz-daw-corner__lbl {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}
.fz-daw-ruler-body {
  position: relative;
  display: flex;
  flex-grow: 1;
}
.fz-daw-day-head {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 4px;
  border-right: 1px solid rgba(255, 255, 255, 0.06);
  color: var(--muted);
}
.fz-daw-day-head__num {
  font-size: 11px;
  font-weight: 700;
  color: var(--ink-2);
}
.fz-daw-add-day {
  flex: 0 0 auto;
  background: transparent;
  border: 0;
  border-right: 1px dashed var(--line);
  color: var(--ink-2);
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
  padding-bottom: 4px;
}
.fz-daw-add-day:hover { color: var(--ink); }

/* ── Loop brace + handles ───────────────────────────────── */
.fz-daw-loop-brace {
  position: absolute;
  bottom: 0;
  height: 14px;
  z-index: 20;
  background: rgba(45, 212, 191, 0.20); /* allow-hex — --loop teal at 20% */
  border-left: 3px solid var(--loop);
  border-right: 3px solid var(--loop);
  border-top: 2px solid var(--loop);
  border-top-left-radius: var(--radius-sm);
  border-top-right-radius: var(--radius-sm);
  cursor: grab;
  touch-action: none;
}
.fz-daw-loop-handle {
  position: absolute;
  top: 0;
  width: 44px;
  height: 100%;
  cursor: col-resize;
  z-index: 10;
  touch-action: none;
  display: flex;
  align-items: center;
  justify-content: center;
}
.fz-daw-loop-handle--left  { left: -22px; }
.fz-daw-loop-handle--right { right: -22px; }
.fz-daw-loop-handle::after {
  content: '';
  width: 6px;
  height: 8px;
  border-left: 2px solid var(--loop);
  border-right: 2px solid var(--loop);
  pointer-events: none;
}

/* ── Loop highlight overlay across all tracks ───────────── */
.fz-daw-loop-highlight {
  position: absolute;
  background: rgba(45, 212, 191, 0.05); /* allow-hex — --loop teal at 5% */
  border-left: 1px dashed rgba(45, 212, 191, 0.30); /* allow-hex */
  border-right: 1px dashed rgba(45, 212, 191, 0.30); /* allow-hex */
  z-index: 5;
  pointer-events: none;
}

/* ── Track row ───────────────────────────────────────────── */
.fz-daw-track {
  display: flex;
  align-items: stretch;
  border-bottom: 1px solid var(--line);
  background: var(--bg);
}
.fz-daw-panel {
  width: 140px;
  flex: 0 0 auto;
  position: sticky;
  left: 0;
  z-index: 30;
  display: flex;
  background: var(--surface);
  border-right: 1px solid var(--line);
}
.fz-daw-panel__controls {
  width: 84px;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px;
  position: relative;
  background: var(--surface);
  border-right: 1px solid var(--line);
}
.fz-daw-panel__tape {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 4px;
}
.fz-daw-panel__tape--rm   { background: var(--accent-orange); }
.fz-daw-panel__tape--vol  { background: var(--accent); }
.fz-daw-panel__tape--nutr { background: var(--protein-2); }
.fz-daw-panel__title {
  margin: 0;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
  text-align: center;
}
.fz-daw-panel__badge {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--ink-2);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 2px 6px;
}
.fz-daw-panel__axis {
  width: 56px;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  background: var(--surface-muted);
}
.fz-daw-axis-row {
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 9px;
  color: var(--ink-2);
}
.fz-daw-axis-row.is-zebra {
  background: rgba(255, 255, 255, 0.03); /* allow-hex — subtle zebra */
}

/* ── Grid cells ─────────────────────────────────────────── */
.fz-daw-grid {
  display: grid;
}
.fz-daw-cell {
  border-right: 1px solid rgba(255, 255, 255, 0.05); /* allow-hex */
  border-bottom: 1px solid rgba(255, 255, 255, 0.05); /* allow-hex */
  position: relative;
  cursor: crosshair;
}
.fz-daw-cell.is-zebra {
  background: rgba(255, 255, 255, 0.015); /* allow-hex */
}
.fz-daw-note {
  position: absolute;
  inset: 1px;
  border-radius: var(--radius-sm);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25); /* allow-hex */
  pointer-events: none;
}
.fz-daw-note--rm  { background: var(--accent-orange); }
.fz-daw-note--vol { background: var(--accent); }

/* ── Nutrition track ────────────────────────────────────── */
.fz-daw-panel__nutr-labels {
  width: 56px;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  background: var(--surface-muted);
}
.fz-daw-nutr-label {
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 6px;
  gap: 4px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 9px;
  color: var(--ink-2);
  border-bottom: 1px solid var(--line);
}
.fz-daw-nutr-label:last-child { border-bottom: 0; }
.fz-daw-nutr-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex: 0 0 auto;
}
.fz-daw-nutr-dot--kcal { background: var(--danger); }
.fz-daw-nutr-dot--p    { background: var(--accent); }
.fz-daw-nutr-dot--c    { background: var(--mac-c); }
.fz-daw-nutr-dot--f    { background: var(--mac-f); }
.fz-daw-nutr-cards {
  display: flex;
  background: var(--bg);
}
.fz-daw-nutr-card {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  border-right: 1px solid rgba(255, 255, 255, 0.05); /* allow-hex */
}
.fz-daw-nutr-cell {
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-bottom: 1px solid var(--line);
}
.fz-daw-nutr-cell:last-child { border-bottom: 0; }
.fz-daw-nutr-input {
  width: calc(100% - 8px);
  margin: 0 4px;
  padding: 4px 0;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-size: 12px;
  text-align: center;
}
.fz-daw-nutr-input:focus {
  outline: none;
  border-color: var(--accent);
}

/* ── Rep Max 1RM%/REPS segmented toggle ─────────────────── */
.fz-daw-seg {
  display: flex;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg);
}
.fz-daw-seg__btn {
  background: transparent;
  border: 0;
  color: var(--muted);
  padding: 3px 6px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
}
.fz-daw-seg__btn.is-on {
  background: var(--ink-2);
  color: var(--surface);
}

.fz-daw-spacer { background: transparent; }

/* SEQUENCER-V2-AUTOREGULATION-SIGNAL (v0.9.503) — bell + pop-up +
   alertstaat panel. Mounted at the parent-app level above the Strength
   iframe. Token-driven; the bell colour follows --accent-orange (Dag +
   Nacht both define this token). The pulse uses a pseudo-element with
   opacity + scale transitions so no rgba hex literals are needed. */
.fz-autoreg-bellrow {
  display: flex; justify-content: flex-end; align-items: center;
  padding: var(--space-2) var(--space-3) 0;
}
.fz-autoreg-bell {
  position: relative;
  background: transparent; border: 0;
  padding: 6px; color: var(--ink);
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 36px; min-height: 36px;
  border-radius: var(--radius-md); cursor: pointer;
}
.fz-autoreg-bell svg { width: 22px; height: 22px; }
.fz-autoreg-bell.is-active { color: var(--accent-orange); }
.fz-autoreg-bell.is-active::after {
  content: ''; position: absolute; top: 8px; right: 8px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent-orange);
  opacity: 1;
  animation: fzAutoregPulse var(--dur-pulse) var(--ease-standard) infinite;
}
@keyframes fzAutoregPulse {
  0%   { transform: scale(1);   opacity: 0.85; }
  70%  { transform: scale(2.6); opacity: 0;    }
  100% { transform: scale(2.6); opacity: 0;    }
}
@media (prefers-reduced-motion: reduce) {
  .fz-autoreg-bell.is-active::after { animation: none; }
}

.fz-autoreg-overlay {
  position: fixed; inset: 0; z-index: 9300;
  background: var(--surface-muted);
  display: flex; align-items: flex-end; justify-content: center;
  opacity: 0; transition: opacity var(--dur-base) var(--ease-standard);
}
.fz-autoreg-overlay.is-open { opacity: 1; }
.fz-autoreg-overlay > * {
  background: var(--surface);
  width: 100%; max-width: 520px;
  border-top-left-radius: var(--radius-lg); border-top-right-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-4) var(--space-5);
  box-shadow: var(--elev-up);
  transform: translateY(20px);
  transition: transform var(--dur-base) var(--ease-standard);
  max-height: 90vh; overflow-y: auto;
}
.fz-autoreg-overlay.is-open > * { transform: translateY(0); }

.fz-autoreg-sheet__title,
.fz-alertstaat__title {
  margin: 0 0 var(--space-1);
  font-size: 18px; font-weight: 700; color: var(--ink);
}
.fz-autoreg-sheet__lede,
.fz-alertstaat__lede {
  margin: 0 0 var(--space-4); color: var(--muted); font-size: 13px;
}
.fz-autoreg-row { margin-bottom: var(--space-4); }
.fz-autoreg-row__label {
  display: flex; justify-content: space-between; align-items: center;
  font-size: 13px; font-weight: 600; color: var(--ink-2);
  margin-bottom: var(--space-1);
}
.fz-autoreg-row__value { color: var(--ink); font-variant-numeric: tabular-nums; }
.fz-autoreg-row__slider {
  width: 100%; -webkit-appearance: none; appearance: none;
  height: 6px; border-radius: var(--radius-pill); background: var(--line);
  outline: none;
}
.fz-autoreg-row__slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--surface);
  cursor: pointer;
}
.fz-autoreg-row__slider::-moz-range-thumb {
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--surface);
  cursor: pointer;
}
.fz-autoreg-row__textarea {
  width: 100%; min-height: 64px; resize: vertical;
  background: var(--surface-bg); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3); font: inherit;
}
.fz-autoreg-actions {
  display: flex; gap: var(--space-2); margin-top: var(--space-3);
}
.fz-autoreg-btn {
  flex: 1 1 0; padding: 12px 14px; font-size: 15px; font-weight: 600;
  border-radius: var(--radius-md); cursor: pointer;
  border: 1px solid var(--line);
  background: var(--surface); color: var(--ink);
}
.fz-autoreg-btn--primary {
  background: var(--accent); color: var(--on-accent); border-color: var(--accent);
}
.fz-autoreg-btn--close {
  margin-top: var(--space-3);
  width: 100%;
}

.fz-alertstaat__section-h {
  font-size: 11px; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--muted);
  margin: var(--space-4) 0 var(--space-2);
}
.fz-alertstaat__rows {
  border: 1px solid var(--line); border-radius: var(--radius-md);
  overflow: hidden; background: var(--surface);
}
.fz-alertstaat__row {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--line);
}
.fz-alertstaat__row:last-child { border-bottom: 0; }
.fz-alertstaat__row-name { font-weight: 600; font-size: 14px; color: var(--ink); }
.fz-alertstaat__row-count {
  font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums;
}
.fz-alertstaat__row.is-hot .fz-alertstaat__row-count {
  color: var(--accent-orange); font-weight: 600;
}
.fz-alertstaat__row.is-force .fz-alertstaat__row-count {
  color: var(--danger); font-weight: 700;
}
.fz-alertstaat__trend {
  display: flex; align-items: center; gap: var(--space-2);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-md); padding: var(--space-3) var(--space-4);
  font-size: 14px; color: var(--ink-2);
}
.fz-alertstaat__trend strong { color: var(--ink); font-weight: 700; }
.fz-alertstaat__cta {
  margin-top: var(--space-4); width: 100%;
  padding: 14px; font-size: 15px; font-weight: 700; border-radius: var(--radius-md);
  background: var(--accent-orange); color: var(--on-accent); border: 0; cursor: pointer;
}
.fz-alertstaat__cta--stub {
  background: transparent; color: var(--accent-orange);
  border: 1px dashed var(--accent-orange);
}
.fz-alertstaat__cta-sub {
  display: block; font-size: 11px; font-weight: 600; margin-top: 2px;
  letter-spacing: 0.04em; text-transform: uppercase;
}
.fz-alertstaat__cta.is-pressed { opacity: 0.6; }

/* SEQUENCER-V3-DELOAD-WEEK-OVERLAY (v0.9.504) — chrome rendered above
   the Strength iframe (and on the sequencer Plan-paneel) while an
   active deload-week covers today. Token-driven; the orange tokens
   (--accent-orange / --accent-orange-light) were added with the Periodisering paneel and are
   present in both the Nacht :root and the [data-theme-preset=
   "blue-day"] Dag override so nothing here needs a theme-conditional
   rule. Hex literals are absent on purpose — css-hex-lint forbids
   them outside :root. */
.fz-deload-eyebrow {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 8px 12px;
  background: var(--accent-orange-light);
  color: var(--accent-orange);
  font-size: 12px; font-weight: 700;
  letter-spacing: 0.04em; text-transform: uppercase;
}
.fz-deload-eyebrow svg { width: 14px; height: 14px; }

.fz-deload-cancel-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 12px;
  background: var(--accent-orange);
  color: var(--on-accent);
  border: 0; border-radius: var(--radius-pill);
  font-size: 12px; font-weight: 700;
  letter-spacing: 0.02em; cursor: pointer;
}
.fz-deload-cancel-btn:disabled { opacity: 0.6; cursor: default; }
.fz-deload-cancel-btn svg { width: 12px; height: 12px; }

.fz-deload-chip {
  display: inline-flex; align-items: center;
  padding: 2px 7px;
  background: var(--accent-orange-light);
  color: var(--accent-orange);
  border-radius: var(--radius-pill);
  font-size: 10px; font-weight: 700;
  letter-spacing: 0.04em; text-transform: uppercase;
  margin-left: 4px;
}

/* Forced-deload modal — non-dismissable, blocks the Strength surface
   until the user starts the deload. Higher z-index than the bell-
   panel overlay (9300) so a stray bell tap can't surface a panel
   above it. */
.fz-forced-deload-overlay {
  z-index: 9400;
}
.fz-forced-deload-sheet {
  border-top-left-radius: var(--radius-lg); border-top-right-radius: var(--radius-lg);
}

/* NAV-TAB-ORDER-EDITABLE-V1 (v0.9.510) — Settings → Navigatie card.
   Vertical list of every known tab; drag-handle on the right OR a
   350ms long-press anywhere on the row picks up the drag. Visual
   layout mirrors a .set-row so the per-tab affordance feels native
   to the rest of the Settings surface, but the row itself is
   absolutely-positioned-friendly (we translate during drag) so the
   .set-card > * + *::before divider rule still draws between rows. */
.set-navorder {
  display: flex;
  flex-direction: column;
  padding: 4px 0;
  touch-action: pan-y;
}
.set-navorder__row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 56px;
  padding: 12px var(--space-4);
  background: var(--surface);
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  transition: transform var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard);
  will-change: transform;
}
.set-navorder__row.is-dragging {
  cursor: grabbing;
  z-index: 2;
  box-shadow: var(--elev-2);
  transition: none;
}
body.navorder-dragging { cursor: grabbing; }
.set-navorder__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px; height: 32px;
  color: var(--ink);
  flex: 0 0 32px;
}
.set-navorder__icon svg { width: 22px; height: 22px; }
/* SETTINGS-TAB-ORDER-LIST-POLISH (v0.9.853) — de photographic nav-icons
   (`.tabbar__btn__photo`) worden in de bottombar 64px + scale + grayscale-
   inactive gerenderd; in deze Settings-list-context is er geen active/inactive
   state, dus context-specifieke override: 32px, vol kleur, geen transform. */
.set-navorder__icon .tabbar__btn__photo {
  width: 32px;
  height: 32px;
  object-fit: contain;
  filter: none;
  transform: none;
}
/* SETTINGS-TAB-ORDER-I18N-AND-RESTORE-DEDUPE — the Hub row uses the emblem
   avatar (`.tabbar__btn__avatar`), which is 64px + grayscale in the bottombar.
   Normalise it to the same 32px, full-colour box as the four photo-icons so all
   five list rows align on one baseline. */
.set-navorder__icon .tabbar__btn__avatar {
  width: 32px;
  height: 32px;
  object-fit: contain;
  filter: none;
  transform: none;
  background: transparent;
  padding: 0;
}
.set-navorder__label {
  color: var(--ink);
  font-size: 15px;
  flex: 1 1 auto;
}
.set-navorder__handle {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  align-items: stretch;
  gap: 4px;
  width: 28px; height: 28px;
  padding: 6px 4px;
  border-radius: var(--radius-md);
  cursor: grab;
  flex: 0 0 auto;
  touch-action: none;
}
.set-navorder__handle:active { cursor: grabbing; background: var(--surface-muted); }
.set-navorder__bar {
  display: block;
  height: 2px;
  border-radius: var(--radius-sm);
  background: var(--section-label);
}
.set-navorder__reset {
  display: block;
  width: 100%;
  background: none;
  border: 0;
  padding: 12px var(--space-4);
  text-align: left;
  color: var(--accent);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}
.set-navorder__reset:hover { color: var(--accent); text-decoration: underline; }

/* ──────── NUTRITION-MICRONUTRIENTS (migration 0098) ────────
   Token-only paint so Dag (light/blauw) and Nacht (dark/lijmgroen) inherit
   without overrides. No emoji glyphs; the disclosure indicator is a CSS
   triangle replacing the default summary marker. Approved mocks:
   _MOCK_nutrition_micronutrients_edit_v1_dag.html (collapsible edit panel)
   _MOCK_nutrition_micronutrients_display_v1_dag.html (read-only display block) */

/* Collapsible edit panel on the food-edit / manual-entry sheets. */
.fz-micro {
  margin: var(--space-2) 0 var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg);
  overflow: hidden;
}
.fz-micro > summary::-webkit-details-marker { display: none; }
.fz-micro > summary { list-style: none; }
.fz-micro__head {
  display: flex; align-items: center; gap: var(--space-2);
  padding: 12px 14px;
  cursor: pointer;
  color: var(--ink);
  font-family: inherit;
}
.fz-micro__head:hover { background: var(--surface-muted); }
.fz-micro__caret {
  width: 10px; height: 10px;
  border-right: 2px solid var(--ink-2);
  border-bottom: 2px solid var(--ink-2);
  transform: rotate(-45deg); transition: transform var(--dur-base) var(--ease-standard);
  margin-right: 2px;
}
.fz-micro[open] .fz-micro__caret { transform: rotate(45deg); }
.fz-micro__title { flex: 1 1 auto; font-size: 14px; font-weight: 600; }
.fz-micro__sub {
  font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--muted);
}
.fz-micro__body {
  padding: 4px 14px 14px;
  background: var(--surface);
  border-top: 1px solid var(--line);
}
.fz-micro__group-h {
  font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted);
  margin: 14px 0 8px;
}
.fz-micro__group-h:first-child { margin-top: 8px; }
.fz-micro__grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
}
.fz-micro__fld { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.fz-micro__fld label {
  font-size: 11px; font-weight: 600; color: var(--ink-2);
  display: flex; justify-content: space-between; align-items: baseline;
}
.fz-micro__fld label .u {
  font-size: 10px; font-weight: 500; color: var(--muted);
}
.fz-micro__fld input {
  border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface);
  padding: 8px 10px; font-size: 14px; color: var(--ink); font-family: inherit;
  min-width: 0; box-sizing: border-box;
}
.fz-micro__fld input:focus { outline: none; border-color: var(--accent); }

/* Read-only display block on the DB-sourced food scaler sheet. */
.fz-micro-disp { margin-top: var(--space-3); }
.fz-micro-disp__h {
  font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted);
  margin: var(--space-3) 0 6px;
}
.fz-micro-disp__h:first-child { margin-top: 0; }
.fz-micro-disp__rows {
  display: grid; grid-template-columns: 1fr 1fr; gap: 6px 18px;
}
.fz-micro-disp__row {
  display: flex; justify-content: space-between; align-items: baseline;
  padding: 6px 0;
  border-bottom: 1px dashed var(--line-2);
  font-size: 13px;
  min-width: 0;
}
.fz-micro-disp__row .k { color: var(--ink-2); }
.fz-micro-disp__row .v { font-weight: 600; color: var(--ink); white-space: nowrap; }
.fz-micro-disp__row .v .u {
  font-weight: 500; color: var(--muted); margin-left: 2px; font-size: 11px;
}
.fz-micro-disp__row.is-null .v { color: var(--muted); font-weight: 500; }
.fz-micro-disp__foot {
  margin-top: 10px; font-size: 11px; color: var(--muted); font-style: italic;
}

/* GOAL-DIRECTION-CONFIG (v0.9.546) — /settings/goals sub-page. Mirrors the
   /settings/cardio chrome (set-page__header + set-group + set-card) and
   builds two new patterns on top: a 4-pill mode picker and per-metric rows
   with a 3-state direction toggle + 1 or 2 value inputs. Token-driven, no
   hex literals (css-hex-lint clean) and accent buttons pair --accent with
   --on-accent so dark + bright accents stay legible (lint rule g). */
.gd-mode-pills {
  display: flex; flex-wrap: wrap; gap: 6px;
  padding: var(--space-2) var(--space-4) var(--space-4);
}
.gd-mode-pill {
  flex: 1 1 calc(50% - 6px); min-width: 0;
  padding: 10px 12px;
  background: var(--surface-muted);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  font: inherit; font-size: 13px; font-weight: 600;
  cursor: pointer;
  text-align: center;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard);
}
.gd-mode-pill:hover { border-color: var(--line-2); }
.gd-mode-pill.is-on {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.gd-mode-pill.is-disabled {
  opacity: 0.55; cursor: not-allowed;
}
.gd-mode-pill:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.gd-coach-lock-note {
  margin: var(--space-2) var(--space-4) var(--space-3);
  font-size: 12px; color: var(--muted); line-height: 1.4;
}

.gd-metric-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
  padding: 12px var(--space-4);
}
.gd-metric-row.is-locked { opacity: 0.55; }
.gd-metric-row__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 8px;
}
.gd-metric-row__label {
  font-size: 14px; font-weight: 600; color: var(--ink);
}
.gd-metric-row__unit {
  font-size: 11px; color: var(--muted); letter-spacing: 0.02em;
  margin-left: auto;
}

/* INTAKE-AND-SETTINGS-MACROS auto-calculate — per-metric reset-to-auto icon
   (recomputes one macro from the profile) + the page-level "calculated" hint. */
.gd-metric-reset {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0; margin-left: 8px;
  border: 1px solid var(--line, var(--border)); border-radius: 50%;
  background: var(--surface); color: var(--muted); cursor: pointer;
  flex: 0 0 auto;
}
.gd-metric-reset svg { width: 14px; height: 14px; }
.gd-metric-reset:hover { color: var(--accent); border-color: var(--accent); }
.gd-metric-reset:active { transform: scale(0.94); }
.gd-autocalc-hint {
  margin: var(--space-2) var(--space-4) var(--space-3);
  font-size: 12px; color: var(--accent); line-height: 1.4;
}

/* GOAL-METRIC-PER-ELEMENT-TOGGLE — direction pill + on/off switch on one row,
   switch sits to the RIGHT of the pill. When the metric is OFF the row gets
   .is-metric-off, which dims the pill + value inputs (but keeps them editable
   — off only means "don't count toward the day score"). Token-only. */
.gd-dir-row {
  display: flex; align-items: center; gap: 10px;
}
.gd-dir-row .gd-dir-toggle { flex: 1 1 auto; min-width: 0; }
.gd-metric-onoff {
  flex: 0 0 auto;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  max-width: 78px;
}
.gd-metric-onoff__cap {
  font-size: 10px; line-height: 1.2; color: var(--muted);
  text-align: center;
}
.gd-metric-row.is-metric-off .gd-dir-toggle,
.gd-metric-row.is-metric-off .gd-value-input { opacity: 0.45; }
.gd-metric-toggle-hint {
  margin: 0 var(--space-4) var(--space-2);
  font-size: 12px; color: var(--muted); line-height: 1.4;
}

.gd-dir-toggle {
  display: flex; gap: 6px;
}
.gd-dir-toggle__tile {
  flex: 1 1 0; min-width: 0;
  padding: 8px 10px;
  background: var(--surface-muted);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font: inherit; font-size: 12px; font-weight: 500;
  cursor: pointer;
  text-align: center;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard);
}
.gd-dir-toggle__tile:hover { border-color: var(--line-2); }
.gd-dir-toggle__tile.is-on {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.gd-dir-toggle__tile[disabled] { cursor: not-allowed; opacity: 0.6; }
.gd-dir-toggle__tile:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.gd-value-input {
  display: flex; align-items: center; gap: 8px;
}
.gd-value-input__field {
  flex: 1 1 0; min-width: 0;
  padding: 10px 12px;
  background: var(--surface-muted);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font: inherit; font-size: 14px;
  -webkit-appearance: none; appearance: none;
}
.gd-value-input__field:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}
.gd-value-input__field[disabled] { cursor: not-allowed; opacity: 0.6; }
.gd-value-input__sep {
  color: var(--muted); font-size: 14px; padding: 0 2px;
}

/* ─── INTAKE-SOURCE-OF-TRUTH-AND-SIGNUP-MIN (v0.9.559) ─────────────────────
   Single canonical Intake card on the Settings landing. 4 collapsible
   sub-sections (A: Body & energy, B: Nutrition targets, C: Behavioral
   targets, D: Fitness tracker) under a "Profile X% complete" progress
   header. All token-driven; no hard-coded hex (css-hex-lint enforced).
   Lives at the bottom of app.css so it overrides any earlier .set-card
   rule that would otherwise repaint a nested .set-card inside a sub-section
   body (sub-A + sub-D mount full standalone sections that carry their own
   .set-card child). */
.set-group--intake { /* spacing inherits from .set-group */ }
.intake-card {
  /* base inherits from .set-card (background, padding, divider rule) */
}
.intake-card__head {
  padding: var(--space-3) var(--space-4) var(--space-2) var(--space-4);
}
.intake-card__head-title {
  margin: 0 0 var(--space-1) 0;
  color: var(--ink);
  font-weight: 600;
  font-size: 15px;
}
.intake-card__progress {
  margin: var(--space-2) 0 var(--space-1) 0;
  color: var(--accent);
  font-weight: 600;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}
.intake-card__pips {
  margin: 0;
  color: var(--muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
}
/* Collapsible sub-section — uses <details>/<summary>. Visual divider is
   supplied by the generic .set-card > * + *::before rule which already
   inset-strokes between cards children. */
.intake-card__section {
  background: transparent;
}
.intake-card__section[open] {
  background: var(--surface-muted);
}
.intake-card__section-sum {
  list-style: none;
  cursor: pointer;
  padding: var(--space-3) var(--space-4);
  display: flex; align-items: center; gap: var(--space-3);
  user-select: none;
}
.intake-card__section-sum::-webkit-details-marker { display: none; }
.intake-card__section-sum::after {
  content: '›';
  color: var(--muted);
  font-size: 16px;
  transform: rotate(90deg);
  transition: transform var(--dur-fast) var(--ease-standard);
  margin-left: var(--space-2);
}
.intake-card__section[open] > .intake-card__section-sum::after {
  transform: rotate(270deg);
}
.intake-card__section-title {
  flex: 1 1 auto;
  color: var(--ink);
  font-weight: 600;
  font-size: 14px;
}
.intake-card__section-pip {
  color: var(--muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--surface);
  border: 1px solid var(--line);
}
.intake-card__section-body {
  padding: 0 0 var(--space-2) 0;
}
/* Suppress the nested <section>'s own h2 + outer margin when it's hosted
   inside a sub-section body — the summary already carries the title. */
.intake-card__section-body > .set-group {
  margin-top: 0;
}
.intake-card__section-body > .set-group > .set-group__title {
  display: none;
}
.intake-card__section-body > .set-group > .set-card {
  border-radius: 0;
  margin: 0;
}
.intake-card__section-edit {
  padding: var(--space-2) var(--space-4) var(--space-3) var(--space-4);
}
.intake-card__section-edit .set-row {
  border-radius: var(--radius-md);
  background: var(--surface);
  border: 1px solid var(--line);
  padding: var(--space-3) var(--space-4);
  display: flex; align-items: center; gap: var(--space-3);
  cursor: pointer;
}
.intake-card__section-edit .set-row__label {
  color: var(--accent);
  font-weight: 600;
}
.intake-card__section-edit .set-row__chev {
  color: var(--muted);
  margin-left: auto;
}
/* GOALS-SINGLE-PLACE-WITH-REDIRECTS — the full goals editors expand in place
   under "Edit details" instead of taking over a route of their own. */
.intake-card__section-editor {
  padding: 0 var(--space-4) var(--space-3) var(--space-4);
}
.intake-card__section-edit .set-row[aria-expanded="true"] .set-row__chev {
  transform: rotate(90deg);
}
/* COACH-GOAL-STRUCTURE-V2 — the goal your coach agreed with you, read-only,
   above sub-section A. Empty means no element at all, not an empty state. */
.intake-goal {
  padding: 0 var(--space-4);
}
.intake-goal:empty { display: none; }
.intake-goal .intake-row + .intake-row {
  border-top: 1px solid var(--line);
}
.intake-goal__quote {
  margin: 0 0 var(--space-2);
  padding-left: var(--space-3);
  border-left: 2px solid var(--accent);
  color: var(--ink);
  font-size: 14px; line-height: 1.5;
}

/* Sub-section B + C list rows — read-only label-value pair. */
.intake-list {
  padding: 0 var(--space-4);
}
.intake-list .intake-row + .intake-row {
  border-top: 1px solid var(--line);
}
.intake-row {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) 0;
}
.intake-row__label {
  flex: 1 1 auto;
  color: var(--ink);
  font-size: 14px;
}
.intake-row__value {
  color: var(--section-label);
  font-variant-numeric: tabular-nums;
  font-size: 14px;
}
.intake-row--mode {
  padding: var(--space-3) var(--space-4) var(--space-1) var(--space-4);
  margin: 0;
  color: var(--accent);
  font-weight: 600;
  font-size: 13px;
}

/* ─── NUTRITION-GROCERIES-TAB (kanban-flow) ─── */
/* 5th Nutrition sub-tab. Three lanes (To Sort / Shopping / Pantry) over a
   single grocery list. Sub-tab strip mirrors .nut-tab anatomy, scoped to the
   groceries container so the parent strip stays in its own namespace. */
.nut-groc {
  display: flex;
  flex-direction: column;
}
.nut-groc__add {
  display: flex;
  gap: 8px;
  margin-bottom: var(--space-3);
}
.nut-groc__add-input {
  flex: 1;
  font: inherit;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  background: var(--surface);
  color: var(--ink);
}
.nut-groc__add-btn {
  background: var(--accent);
  color: var(--on-accent);
  border: 0;
  border-radius: var(--radius-sm);
  padding: 8px 14px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
/* PANTRY-RESTOCK-SUGGESTIONS-WITH-TAB-BADGE (Ship 4/4) — collapsible Suggesties */
.restock-suggestions {
  margin-bottom: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  overflow: hidden;
}
.restock-suggestions__header {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-3);
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  user-select: none;
  transition: background var(--dur-fast) var(--ease-standard);
}
.restock-suggestions__header:hover {
  background: var(--surface-2);
}
.restock-suggestions__title {
  flex: 1;
  margin: 0;
  font-size: 1em;
  font-weight: 600;
}
.restock-suggestions__title .restock-suggestions__count {
  color: var(--muted);
  font-weight: 400;
}
.restock-suggestions__collapse {
  color: var(--ink);
  font-size: 1em;
  font-weight: 700;
}
.restock-suggestions__body {
  padding: 0 var(--space-3) var(--space-3);
}
.restock-suggestions--collapsed .restock-suggestions__body {
  display: none;
}
.restock-suggestions__bulk {
  display: block;
  width: auto;
  margin-left: auto;
  margin-bottom: var(--space-2);
  padding: var(--space-2) var(--space-3);
}
.restock-suggestion-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--line);
  min-height: 52px;
}
.restock-suggestion-row:last-child {
  border-bottom: 0;
}
.restock-suggestion-row--removing {
  opacity: 0;
  transform: translateX(10px);
  min-height: 0;
  max-height: 0;
  padding-top: 0;
  padding-bottom: 0;
  border-bottom: 0;
  overflow: hidden;
  transition: opacity var(--dur-base) var(--ease-standard), transform var(--dur-base) var(--ease-standard), max-height var(--dur-base) var(--ease-standard), padding var(--dur-base) var(--ease-standard);
}
.restock-suggestion-row__name {
  flex: 1 1 auto;
  min-width: 0; /* CRITICAL: laat flex-item krimpen onder content-breedte */
  font-weight: 500;
  font-size: 0.95em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.restock-suggestion-row__amount {
  flex: 0 0 auto;
  font-size: 0.85em;
  white-space: nowrap; /* CRITICAL: voorkomt char-per-line break */
  font-variant-numeric: tabular-nums;
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);
}
.restock-suggestion-row__shortfall {
  color: var(--ink);
  font-weight: 600;
}
.restock-suggestion-row__pack {
  color: var(--muted);
  font-weight: 400;
}
.restock-suggestion-row__add {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--accent);
  cursor: pointer;
}
.restock-suggestion-row__add:hover {
  background: var(--surface-2);
}
.restock-suggestion-row__add svg {
  width: 18px;
  height: 18px;
}
.restock-suggestion-row__dismiss {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--muted);
  opacity: 0.6;
  cursor: pointer;
}
.restock-suggestion-row__dismiss:hover {
  opacity: 1;
  background: var(--surface-2);
}
.restock-suggestion-row__dismiss svg {
  width: 16px;
  height: 16px;
}

.nut-groc__subtabs {
  display: flex;
  gap: var(--space-4);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--space-3);
  position: relative;
  padding-right: 40px; /* ruimte voor sort-btn */
}
.nut-groc__sortbtn {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  padding: 0;
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden; /* safety: cap content even als child mis-sized */
}
.nut-groc__sortbtn:hover { color: var(--ink); }
.nut-groc__sortbtn .nut-ic { width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center; }
.nut-groc__sortbtn .nut-ic svg { display: block; width: 18px; height: 18px; }
.nut-groc__row-recipe {
  font-size: 11px;
  color: var(--muted);
  margin-top: 2px;
}
.nut-groc__row-recipe--manual { font-style: italic; opacity: 0.85; }
.nut-groc__subtab {
  flex: 0 0 auto;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: inherit;
  color: var(--muted);
  font-size: 15px;
  padding: var(--space-3) 2px var(--space-2);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
.nut-groc__subtab.is-active {
  color: var(--ink);
  font-weight: 600;
  border-bottom-color: var(--accent);
}
.nut-groc__badge {
  display: inline-block;
  background: var(--accent);
  color: var(--on-accent);
  border-radius: var(--radius-pill);
  font-size: 10px;
  font-weight: 600;
  padding: 1px 6px;
}
.nut-groc__list {
  display: flex;
  flex-direction: column;
}
.nut-groc__recipe-group {
  margin-bottom: var(--space-4);
  background: var(--surface-2, var(--line));
  border-radius: var(--radius-sm);
  padding: var(--space-2);
}
.nut-groc__recipe-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  border-top: 1px solid var(--line);
  padding-top: var(--space-3);
  padding-bottom: var(--space-2);
  text-transform: none;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--ink);
}
.nut-groc__recipe-group:first-child .nut-groc__recipe-head {
  border-top: 0;
  padding-top: var(--space-1);
}
.nut-groc__recipe-head-name {
  flex: 1;
}
.nut-groc__recipe-head--collapsible { cursor: pointer; }
.nut-groc__recipe-head--collapsible:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-md); }
.nut-groc__recipe-chev { color: var(--muted); transition: transform var(--dur-fast) var(--ease-standard); }
.nut-groc__recipe-group.is-collapsed .nut-groc__recipe-chev { transform: rotate(-90deg); }
.nut-groc__recipe-del {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
  padding: var(--space-2);
  margin-right: calc(var(--space-2) * -1);
  min-width: 40px;
  min-height: 40px;
  color: var(--muted);
  opacity: .6;
}
.nut-groc__recipe-del:hover {
  opacity: 1;
  color: var(--danger);
}
.nut-groc__recipe-del .nut-ic { width: 16px; height: 16px; }
.nut-groc__row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) 0;
  border-top: 1px solid var(--line-2);
}
.nut-groc__row:first-child { border-top: 0; }
.nut-groc__name {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 15px;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.nut-groc__name--done {
  text-decoration: line-through;
  color: var(--muted);
}
.nut-groc__sub {
  font-size: 12px;
  color: var(--ok);
  margin-top: 2px;
}
.nut-groc__amount {
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
}
/* NUTRITION-GROCERIES-INLINE-AMOUNT-UNIT-EDIT-LIKE-RECIPES — inline-edit row */
.nut-groc__amt {
  /* NUTRITION-AMOUNT-INPUT-AUTOSIZE-TO-CONTENT — auto-size to the typed value
     so small numbers don't leave a whitespace gap before the digits. */
  flex: 0 0 auto;
  field-sizing: content;
  min-width: 3ch;
  max-width: 7ch;
  min-height: 44px;
  padding: 4px 6px;
  font-size: 0.95em;
  text-align: right;
  font-variant-numeric: tabular-nums;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--ink);
  transition: border-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard);
}
.nut-groc__amt:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring-focus);
}

/* NUTRITION-PANTRY-INVENTORY-MODE — toggle bar, hint strip, per-row qty input. */
.pantry-inv-bar {
  display: flex;
  justify-content: flex-end;
  margin: 0 0 10px;
}
.pantry-inv-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  font-size: 0.9em;
  font-weight: 500;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.pantry-inv-toggle .nut-ic svg { width: 16px; height: 16px; display: block; }
.pantry-inv-toggle.is-active {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.pantry-inv-hint {
  margin: 0 0 12px;
  padding: 8px 12px;
  font-size: 0.82em;
  color: var(--muted);
  background: var(--surface-2);
  border-radius: var(--radius-sm);
}
.pantry-row__qty {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
}
.pantry-row__qty-input {
  field-sizing: content;
  min-width: 3ch;
  max-width: 7ch;
  padding: 4px 6px;
  font-size: 0.95em;
  text-align: right;
  font-variant-numeric: tabular-nums;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  transition: border-color var(--dur-base) var(--ease-standard);
}
.pantry-row__qty-input:focus {
  outline: none;
  border-color: var(--accent);
}
.pantry-row__qty-input--ok { border-color: var(--accent); }
.pantry-row__qty-input--err { border-color: var(--danger); }
.pantry-row__qty-unit {
  flex: 0 0 auto;
  font-size: 0.85em;
  color: var(--muted);
}
.nut-groc__unit {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 4px 8px;
  font-size: 0.85em;
  font-weight: 500;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--ink);
  cursor: pointer;
  min-height: 32px;
}
.nut-groc__unit-caret {
  font-size: 0.7em;
  color: var(--muted);
}
.unit-dropdown {
  position: fixed;
  z-index: 1200;
  min-width: 80px;
  padding: 4px 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--elev-2);
  display: flex;
  flex-direction: column;
}
.unit-dropdown__opt {
  padding: 8px 16px;
  text-align: left;
  font-size: 0.95em;
  background: none;
  border: 0;
  cursor: pointer;
  min-height: 36px;
  color: var(--ink);
}
.unit-dropdown__opt:hover { background: var(--surface-2); }
.unit-dropdown__opt.is-current {
  font-weight: 600;
  background: var(--surface-2);
}
.nut-groc__icon-btn {
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--muted);
  padding: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.nut-groc__icon-btn:hover { color: var(--ink); }
.nut-groc__edit {
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--muted);
  padding: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.nut-groc__edit:hover { color: var(--ink); }
.nut-groc__pack-size {
  display: inline-block;
  margin-left: var(--space-2);
  padding: 2px 6px;
  font-size: 11px;
  color: var(--muted);
  background: var(--surface-muted);
  border-radius: var(--radius-sm);
  white-space: nowrap;
}
.grocery-edit__name { margin-bottom: var(--space-3); }
.grocery-edit__name h4,
.grocery-edit__purchase h4 {
  margin: 0 0 var(--space-1);
  font-size: 14px;
  font-weight: 600;
}
.grocery-edit__name-input {
  width: 100%;
  padding: var(--space-2);
  font-size: 15px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
}
.grocery-edit__purchase {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}
.grocery-edit__purchase p.muted {
  margin: 0 0 var(--space-2);
  font-size: 13px;
  color: var(--muted);
}
.grocery-edit__purchase-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
.grocery-edit__purchase-amt,
.grocery-edit__purchase-pack {
  flex: 0 0 72px;
  padding: var(--space-2);
  font-size: 15px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
}
.grocery-edit__purchase-unit {
  flex: 0 0 84px;
  padding: var(--space-2);
  font-size: 15px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
}
.grocery-edit__times,
.grocery-edit__pkg {
  font-size: 14px;
  color: var(--muted);
}
.nut-groc__rm {
  color: var(--muted);
  background: none;
  border: 0;
  cursor: pointer;
  font-size: 18px;
  padding: 4px 8px;
  line-height: 1;
}
.nut-groc__cta {
  background: var(--accent);
  color: var(--on-accent);
  border: 0;
  border-radius: var(--radius-sm);
  padding: 14px 20px;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  width: 100%;
  text-align: center;
  margin-top: var(--space-3);
}
.nut-groc__empty {
  text-align: center;
  color: var(--muted);
  padding: 32px 16px;
  font-size: 14px;
}
/* Pantry pill toggle — native checkbox + sibling span styled as a pill. */
.nut-groc__toggle {
  position: relative;
  display: inline-block;
  width: 44px;
  height: 26px;
  cursor: pointer;
  flex: 0 0 auto;
}
.nut-groc__toggle input {
  position: absolute;
  opacity: 0;
  /* WAVE-2 touch target: extend the (invisible) input 9px above+below the 26px
     pill → 44pt-tall hitbox with zero visual/layout change (input is absolute). */
  inset: -9px 0;
  margin: 0;
  cursor: pointer;
}
.nut-groc__toggle-pill {
  position: absolute;
  inset: 0;
  background: var(--line);
  border-radius: var(--radius-pill);
  transition: background var(--dur-base) var(--ease-standard);
}
.nut-groc__toggle-pill::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--surface);
  transition: left var(--dur-base) var(--ease-standard), background var(--dur-base) var(--ease-standard);
}
.nut-groc__toggle input:checked + .nut-groc__toggle-pill {
  background: var(--accent);
}
.nut-groc__toggle input:checked + .nut-groc__toggle-pill::after {
  left: 21px;
  background: var(--on-accent);
}

/* PANTRY-UI-QUANTITY-FEEDBACK-AND-ONBOARDING (Ship 3/4) — quantity meter + inline
   edit + Refilled on linked pantry rows; deduction sheet + action toast. Token-only
   (no hex); the low/empty fill colours derive from --danger via color-mix so all
   Dag/Nacht presets stay consistent without new theme tokens. */
.pantry-meter-wrap {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.pantry-meter {
  display: inline-block;
  height: 6px;
  width: 80px;
  background: var(--line-2);
  border-radius: var(--radius-sm);
  overflow: hidden;
  flex: 0 0 auto;
}
.pantry-meter__fill {
  height: 100%;
  background: var(--accent);
  transition: width var(--dur-base) var(--ease-standard);
}
.pantry-meter__fill--low { background: color-mix(in srgb, var(--danger) 55%, var(--accent)); }
.pantry-meter__fill--empty { background: var(--danger); }
/* v0.9.911 — over-target: full accent bar with an inset hairline marker so the
   "above your goal" state reads distinct from a plain full (100%) meter. */
.pantry-meter__fill--overflow {
  background: var(--accent);
  box-shadow: inset 0 0 0 1px var(--surface);
}
/* v0.9.911 — negative current_amount clipped to 0%: flag the row's meter. */
.pantry-meter--warning { box-shadow: 0 0 0 1px var(--warning); }
.pantry-meter__label {
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
}
.pantry-meter__label--warning { color: var(--warning); }
.pantry-meter__shortfall {
  margin-left: 4px;
  color: var(--warning);
  font-weight: 600;
}
.pantry-meter__edit {
  background: none;
  border: 0;
  padding: var(--space-1);
  cursor: pointer;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font: inherit;
  font-size: 12px;
}
.pantry-meter__edit:hover { color: var(--ink); }
/* v0.9.911 — restock is a utility action: pill-shaped button (NOT accent,
   reserve accent for primary actions) so it reads as tappable, not a status. */
.pantry-meter__restock {
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
  min-height: 28px;
  font: inherit;
  font-size: 12px;
}
.pantry-meter__restock:hover { background: var(--surface-2); }
.pantry-meter__edit:disabled,
.pantry-meter__restock:disabled { opacity: 0.5; cursor: default; }

/* Inline amount edit sheet */
.pantry-edit-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}
.pantry-edit-input {
  flex: 1;
  font: inherit;
  font-size: 16px;
  padding: 10px 12px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
}
.pantry-edit-unit { color: var(--muted); font-size: 14px; }
.pantry-edit-save { width: 100%; }

/* Deduction details sheet */
.pantry-deduction-list { list-style: none; padding: 0; margin: 0 0 var(--space-3); }
.pantry-deduction-list li {
  display: flex;
  justify-content: space-between;
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--line-2);
}
.pantry-deduction__name { color: var(--ink); font-size: 14px; }
.pantry-deduction__delta { color: var(--muted); font-size: 14px; white-space: nowrap; }
.pantry-undo-all { width: 100%; }
.pantry-onb__p { color: var(--ink); font-size: 14px; margin: 0 0 var(--space-2); }

/* Action toast — transient, with a tap-through to the deduction sheet */
.pantry-toast {
  position: fixed;
  left: 50%;
  bottom: 88px;
  transform: translate(-50%, 16px);
  z-index: 9999;
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: 92vw;
  padding: 12px 16px;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-sm);
  box-shadow: var(--elev-2);
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-standard), transform var(--dur-base) var(--ease-standard);
}
.pantry-toast--in { opacity: 1; transform: translate(-50%, 0); }
.pantry-toast__msg { font-size: 14px; }
.pantry-toast__action {
  background: var(--accent);
  color: var(--on-accent);
  border: 0;
  border-radius: var(--radius-sm);
  padding: 6px 14px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  flex: 0 0 auto;
}

/* INVITE-ONLY-SYSTEM (v0.9.602) — Settings Uitnodigingen + admin panel.
   Token-based; no hex literals so Dag + Nacht presets stay consistent. */
.fz-invite-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px 0;
}
.fz-invite-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  font-size: 13px;
  color: var(--ink);
  background: var(--surface);
}
.fz-invite-chip__code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  flex: 1 1 auto;
  color: var(--ink);
}
.fz-invite-chip__copy,
.fz-invite-chip__revoke {
  background: transparent;
  border: 0;
  padding: 4px 8px;
  font-size: 13px;
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.fz-invite-chip__copy {
  background: var(--accent);
  color: var(--on-accent);
}
.fz-invite-chip__revoke {
  color: var(--ink-2);
}
.fz-invite-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 12px;
  font-weight: 600;
}
.fz-invite-bonus {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px 0 0 0;
}
.fz-invite-bonus__cta {
  align-self: flex-start;
}
.fz-invite-progress {
  height: 4px;
  border-radius: var(--radius-sm);
  background: var(--line-2);
  overflow: hidden;
}
.fz-invite-progress__fill {
  height: 100%;
  background: var(--accent);
  transition: width var(--dur-base) var(--ease-standard);
}
.fz-admin-invites__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  color: var(--ink);
}
.fz-admin-invites__table th,
.fz-admin-invites__table td {
  text-align: left;
  padding: 6px 8px;
  border-bottom: 1px solid var(--line);
}
.fz-admin-invites__table th {
  color: var(--ink-2);
  font-weight: 600;
}
.fz-admin-user-row {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 8px 0;
}
.fz-admin-user-row .form-input {
  flex: 0 0 140px;
}
.fz-invite-status {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-2);
  background: var(--line-2);
  text-transform: lowercase;
}
.fz-invite-status--used {
  background: var(--accent);
  color: var(--on-accent);
}
.fz-invite-status--revoked,
.fz-invite-status--expired {
  background: var(--line);
  color: var(--ink-2);
}

/* CHAT-1-BRICKS-TO-SETTINGS (v0.9.625) — Chat tab upsell panel for
   non-premium accounts. Premium-pitch surface; copy is the design.
   Premium accounts see .placeholder (the binnenkort state); only the
   upsell needs its own treatment. Token-only colours per the css-hex
   lint (no hardcoded literals outside :root). */
.chat-upsell {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-5);
  background: var(--surface);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.chat-upsell__eyebrow {
  display: inline-block;
  align-self: flex-start;
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.chat-upsell__title {
  margin: 0;
  font-size: 24px;
  font-weight: 300;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.chat-upsell__lede {
  margin: 0;
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink-2);
}
.chat-upsell__bullets {
  list-style: none;
  padding: 0;
  margin: var(--space-2) 0 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.chat-upsell__bullet {
  position: relative;
  padding-left: 18px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-2);
}
.chat-upsell__bullet::before {
  content: "";
  position: absolute;
  left: 0;
  top: 9px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
}
.chat-upsell__note {
  margin: var(--space-3) 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}

/* ═══════════════════════════════════════════════════════════════════════════
   CHAT-LOOK-FINISH-AWARE-AND-NAV-CHAT-BUTTON (v0.9.668) — premium chat surface,
   finish-AWARE. The V1 (v0.9.662) shell SHAPE is unchanged — only the colour
   vocabulary swaps from finish-invariant brand-moment hex to the theme tokens,
   so each finish renders the same premium chat in its OWN personality: Sassu
   Dag = light premium, Nacht unchanged, gauge/linen/volt/pulse each their own.
   Every paint reads a --chat-* alias that resolves to a theme token, so a
   Settings finish-switch recolours the chat end-to-end. No raw hex remains —
   the V1 css-hex-lint brand-moment exemption is gone.
   ═══════════════════════════════════════════════════════════════════════════ */
.chat {
  --chat-bg: var(--bg);
  --chat-surface: var(--surface);
  --chat-surface-2: var(--surface-2, var(--surface));
  --chat-ink: var(--ink);
  --chat-dim: var(--muted);
  --chat-accent: var(--accent);
  --chat-on-accent: var(--on-accent);
  --chat-line: var(--line);
  --chat-status-on: var(--accent);
  --chat-status-off: var(--muted);
  /* Per-finish SPREEK glow + status halo — accent diffused to ~40% alpha. */
  --chat-glow: color-mix(in srgb, var(--accent) 40%, transparent);

  /* CHAT-TAB-STARTS-NEW-AND-KEEPS-BOTTOM-NAV-VISIBLE — the shell ends where the
     bottom nav begins instead of covering it, so chat is a tab like any other
     rather than a trap you can only leave via the topbar ×. The inset is a
     variable because body.kb-open slides the tabbar off-screen (see the A11Y-8
     S23 rule): when the keyboard is up the strip is not there to be avoided,
     and reclaiming it is what keeps the sticky input row reachable on iOS. */
  --chat-nav-inset: calc(var(--tabbar-h) + var(--safe-bottom));
  /* The nav strip below the shell already clears the home indicator, so every
     bottom padding INSIDE chat must collapse to zero or the safe area is paid
     for twice. It comes back only in the kb-open case, where the nav is gone
     and the shell is flush with the viewport edge again. */
  --chat-safe-bottom: 0px;

  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: var(--chat-nav-inset);
  z-index: 50;
  display: flex;
  flex-direction: column;
  background: var(--chat-bg);
  color: var(--chat-ink);
}
body.kb-open .chat {
  --chat-nav-inset: 0px;
  --chat-safe-bottom: var(--safe-bottom);
}

/* ── Topbar: hamburger · brand (emblem + wordmark + status dot) · spacer ──── */
.chat__topbar {
  display: grid;
  grid-template-columns: 40px 1fr 40px;
  align-items: center;
  gap: 8px;
  padding: calc(8px + var(--safe-top)) 12px 8px;
  background: var(--chat-surface);
  border-bottom: 1px solid var(--chat-line);
}
.chat__menu {
  appearance: none;
  background: transparent;
  border: 0;
  margin: 0;
  padding: 8px;
  width: 40px;
  height: 40px;
  color: var(--chat-dim);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
}
/* Author display:inline-flex beats the [hidden] attribute — the back arrow is
   hidden on the list view, so it needs the explicit guard. */
.chat__menu[hidden] { display: none; }
.chat__menu:hover { color: var(--chat-ink); }
.chat__menu svg { width: 22px; height: 22px; }
/* ── brand-horse-head — S3 right-profile head, fixed ice-blue silhouette on a
   constant navy plate, clipped to a true circle. The ::before paints
   --horse-glyph (finish-invariant ice-blue) masked to the horse alpha; the
   wrapper supplies the navy plate. Both tokens are brand-moment fixed palette —
   no finish recolouring, matching the auth-lockup brand treatment. A no-mask
   fallback swaps to the full-colour PNG. Used in chat topbar (sm) + Settings
   profile avatar (md). */
.brand-horse-head {
  display: inline-block;
  position: relative;
  background-color: var(--horse-plate);
  border-radius: 50%;
  overflow: hidden;
  flex: 0 0 auto;
}
.brand-horse-head--sm { width: 28px; height: 28px; }
.brand-horse-head--md { width: 48px; height: 48px; }
.brand-horse-head::before {
  content: '';
  position: absolute;
  inset: 0;
  background-color: var(--horse-glyph);
  -webkit-mask: url('/assets/img/brand/horse-head-alpha-512.png') center / cover no-repeat;
          mask: url('/assets/img/brand/horse-head-alpha-512.png') center / cover no-repeat;
}
@supports not ((-webkit-mask: url('x')) or (mask: url('x'))) {
  .brand-horse-head::before {
    background-image: url('/assets/img/brand/horse-head-512.png');
    background-size: cover;
    background-position: center;
    background-color: transparent;
    -webkit-mask: none;
            mask: none;
  }
}
.chat__brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
/* Wordmark via alpha-mask (same recolor trick as .auth__brand): paints
   --chat-ink clipped to the letterform alpha. The 'sassu' text stays in the
   DOM (transparent) so AT still announces the brand. 80×11 ≈ the 7.5:1 ratio. */
.chat__wordmark {
  width: 80px;
  height: 11px;
  overflow: hidden;
  color: transparent;
  user-select: none;
  background: var(--chat-ink);
  -webkit-mask: url('/assets/img/brand/wordmark-alpha.png') center / contain no-repeat;
  mask: url('/assets/img/brand/wordmark-alpha.png') center / contain no-repeat;
}
.chat__status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--chat-status-on);
  flex: 0 0 auto;
}
.chat__status-dot--off { background: var(--chat-status-off); }
.chat__topbar-spacer { width: 40px; }
/* Notification bell (CHAT-SPLIT-DIALOGUE-DRAWER-FROM-NOTIFICATION-BELL) — holds
   the right edge the close × used to, so it stays the mirror of .chat__menu and
   the brand block keeps its optical centre. Subtle hit-target hint on
   hover/active, same as the hamburger. `position:relative` anchors the bubble. */
.chat__notif-bell {
  appearance: none;
  background: transparent;
  border: 0;
  margin: 0;
  padding: 8px;
  width: 40px;
  height: 40px;
  color: var(--chat-ink);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  justify-self: end;
  position: relative;
}
.chat__notif-bell[hidden] { display: none; }
.chat__notif-bell:hover,
.chat__notif-bell:active { background: var(--chat-line); }
.chat__notif-bell svg { width: 24px; height: 24px; }
/* Count bubble — reuses the SAME --badge-bg / --on-badge pair as .tabbar__badge
   so the two counts read as one family per finish, and tabular-nums + line-height
   1 for the geometric centring the tabbar badge learned the hard way (v0.9.318). */
.chat__notif-badge {
  position: absolute;
  top: 2px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  background: var(--badge-bg);
  color: var(--on-badge);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}
.chat__notif-badge[hidden] { display: none; }

/* ── Notification sheet (bell) ─────────────────────────────────────────────── */
/* Bottom-sheet, matching the mood-chat / add-food idiom rather than inventing a
   bell-anchored dropdown that would be cramped at 320px. Fixed like .chat__drawer
   and therefore re-applies the same nav inset. */
.chat__notifsheet {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: var(--chat-nav-inset, 0px);
  z-index: 61;
}
.chat__notifsheet[hidden] { display: none; }
.chat__notifsheet__backdrop {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--chat-bg) 70%, transparent);
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-standard);
}
.chat__notifsheet--open .chat__notifsheet__backdrop { opacity: 1; }
.chat__notifsheet__panel {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  max-height: 70%;
  display: flex;
  flex-direction: column;
  background: var(--chat-surface);
  border-top: 1px solid var(--chat-line);
  border-radius: var(--radius-lg, 16px) var(--radius-lg, 16px) 0 0;
  transform: translateY(100%);
  transition: transform var(--dur-base) var(--ease-standard);
  box-shadow: 0 -2px 18px color-mix(in srgb, var(--chat-bg) 60%, transparent);
}
.chat__notifsheet--open .chat__notifsheet__panel { transform: translateY(0); }
.chat__notifsheet__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--chat-line);
}
.chat__notifsheet__title {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--chat-ink);
}
.chat__notifsheet__close {
  appearance: none;
  background: transparent;
  border: 0;
  margin: 0;
  padding: 8px;
  width: 40px;
  height: 40px;
  color: var(--chat-ink);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
}
.chat__notifsheet__close:hover,
.chat__notifsheet__close:active { background: var(--chat-line); }
.chat__notifsheet__close svg { width: 22px; height: 22px; }
.chat__notifsheet__list {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: var(--safe-bottom, 0px);
}

/* ── Conversation list (CHAT-LIST-FIRST-CONTACTS-THEN-CHAT) ────────────────── */
/* Was a slide-in drawer; it is now the tab's LANDING VIEW — full-screen, no
   backdrop, no transform. Fixed (viewport-relative) despite sitting inside the
   fixed .chat shell, so it needs the same nav inset applied again; the variable
   is inherited from .chat. */
/* In flow, NOT fixed: it fills the tab body under the topbar, so the notification
   bell and the status dot stay reachable while the list is up. `display: flex`
   would beat the [hidden] attribute, hence the explicit guard. */
.chat__drawer {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  z-index: 1;
}
.chat__drawer[hidden] { display: none; }
.chat__drawer__panel {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: var(--chat-surface);
}
/* On the list view the chat surface itself is put away — only the topbar (which
   owns the notification bell + status dot) and the list stay. */
.chat--list .chat__messages,
.chat--list .chat__attach-strip,
.chat--list .chat__actions,
.chat--list .chat__input { display: none; }
/* CHAT-LIST-FIRST-FIX-NAV-ENTRY-AND-RESPONSIVE-ROWS — the list is now the
   full-width landing view, not a ~320px overlay panel, and the inherited row
   styling does not survive that width change: a row's background is the SAME
   token as the panel it sits on, which reads as a card inside a narrow overlay
   but as loose, oversized raw text once it spans the whole screen with nothing
   marking where one row ends and the next begins. At full width the rows
   therefore get an explicit hairline separator and a proper gutter instead of a
   card. Typography is deliberately NOT touched — the compact drawer sizes are
   the intended look. Scoped to .chat--list so the overlay-drawer geometry is
   untouched if any path still opens it. */
.chat--list .chat__drawer__panel { width: 100%; max-width: 100%; }
.chat--list .chat__drawer__list {
  overflow-x: hidden;
  padding-left: 0;
  padding-right: 0;
  /* The separator IS the row boundary; a gap on top of it doubles it up. */
  gap: 0;
}
.chat--list .chat__drawer__row {
  max-width: 100%;
  border-radius: 0;
  border-bottom: 1px solid color-mix(in srgb, var(--chat-line) 70%, transparent);
}
.chat--list .chat__drawer__row:last-child { border-bottom: 0; }
.chat--list .chat__drawer__row-main {
  max-width: 100%;
  /* Aligns the text column with the header title and the New-chat buttons. */
  padding: 12px 14px;
  /* NOTE: the opaque background is load-bearing — it is what covers the
     absolutely-positioned swipe action layer (rename / delete) until the row is
     actually swiped. Making it transparent leaves those buttons on screen. */
}
/* Truncation contract. The base rules already satisfy this today (a flex item
   with overflow:hidden resolves min-width:auto to 0), which is precisely why it
   is worth pinning at full width: what must hold is the GUARANTEE, not the
   particular rule that currently provides it. */
.chat--list .chat__drawer__row-body,
.chat--list .chat__drawer__row-titlerow,
.chat--list .chat__drawer__row-metarow { min-width: 0; max-width: 100%; }
.chat--list .chat__drawer__row-title,
.chat--list .chat__drawer__row-snippet {
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The trailing time column must never be the thing that squeezes the name. */
.chat--list .chat__drawer__row-time { flex: 0 0 auto; white-space: nowrap; }
.chat__drawer__empty-action--primary {
  background: var(--accent);
  color: var(--on-accent);
  border: 0;
  border-radius: 999px;
  padding: 10px 18px;
  font-weight: 600;
  cursor: pointer;
}
.chat__drawer__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  /* No --safe-top: the list sits under the chat topbar, which already clears it. */
  padding: 10px 14px;
  border-bottom: 1px solid var(--chat-line);
}
/* CHAT-DRAWER-SINGLE-HEADER-STRIP-AND-AI-CHAT-IDENTITY — in the LIST view the
   topbar and this header were two stacked bars over the same 1px line. The list
   header becomes the ONE strip (status dot · Chats … kebab · Select · bell) and
   the now-empty topbar goes, so the reclaimed height goes to the list. The chat
   view is untouched. */
.chat--list .chat__topbar { display: none; }
.chat--list .chat__drawer__header {
  padding-top: calc(10px + var(--safe-top));
}
.chat--list .chat__drawer__title { flex: 1 1 auto; min-width: 0; }
.chat--list .chat__drawer__header > .chat__status-dot { margin-right: 2px; }
.chat--list .chat__drawer__header-actions .chat__notif-bell {
  width: 34px;
  height: 34px;
  padding: 6px;
}
.chat--list .chat__drawer__header-actions .chat__notif-bell svg { width: 21px; height: 21px; }
.chat__drawer__title {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--chat-ink);
}
.chat__drawer__new {
  appearance: none;
  margin: 12px 14px;
  padding: 11px 14px;
  min-height: 44px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid var(--chat-line);
  border-radius: var(--radius-lg);
  background: var(--chat-accent);
  color: var(--chat-on-accent);
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
}
/* The author `display:flex` above beats the UA `[hidden]` rule, so the
   trainer-only client button needs an explicit guard or every member sees it. */
.chat__drawer__new[hidden] { display: none; }
/* The accent fill marks "+ New chat" as THE primary action, so the human entry
   point directly beneath it is a quieter surface at the same size. Tinting
   --chat-ink (not --surface-2, which this repo never defines) keeps the fill
   visible against the panel in every theme preset. */
.chat__drawer__new--client {
  margin-top: 0;
  background: color-mix(in srgb, var(--chat-ink) 8%, transparent);
  color: var(--chat-ink);
}
.chat__drawer__new-icon { display: inline-flex; }
.chat__drawer__new-icon svg { width: 18px; height: 18px; }
/* FRIENDSHIP-CHAT-SEARCH-AND-LISTS-UI — find-a-user search + the named groups
   (Clients / Friends / Search results) the list is now divided into. */
.chat__drawer__search {
  appearance: none;
  margin: 0 14px 8px;
  padding: 9px 12px;
  min-height: 40px;
  flex-shrink: 0;
  border: 1px solid var(--chat-line);
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--chat-ink) 6%, transparent);
  color: var(--chat-ink);
  font-size: 0.9rem;
}
.chat__drawer__search::placeholder { color: var(--chat-dim, #8a8a8a); }
.chat__drawer__group {
  padding: 12px 8px 4px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--chat-dim, #8a8a8a);
}
/* Results sit UNDER the list rather than inside it, so they get their own
   scroll budget instead of pushing the friends list out of view. */
.chat__drawer__results {
  flex: 0 1 auto;
  min-height: 0;
  max-height: 45%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 0 8px;
  border-top: 1px solid var(--chat-line);
}
.chat__drawer__results[hidden] { display: none; }
/* A search hit is NOT a chat you can open — only its button is interactive. */
.chat__drawer__row--search {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 8px;
}
/* NOT .chat__drawer__row-action — that name is taken by the swipe-revealed
   rename/delete layer (position:absolute, further down this file). */
.chat__drawer__row-cta {
  appearance: none;
  flex: 0 0 auto;
  margin-left: auto;
  padding: 6px 12px;
  min-height: 32px;
  border: 1px solid var(--chat-line);
  border-radius: var(--radius-lg);
  background: var(--chat-accent);
  color: var(--chat-on-accent);
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
}
/* "Verstuur verzoek" is the ONE thing a search row can do, so it keeps the
   accent alone. A pointer elsewhere (accept in the bell) is a quieter button —
   two filled buttons in one list would read as two equal offers. */
.chat__drawer__row-cta--quiet {
  background: color-mix(in srgb, var(--chat-ink) 8%, transparent);
  color: var(--chat-ink);
}
.chat__drawer__row-cta:disabled {
  background: color-mix(in srgb, var(--chat-ink) 8%, transparent);
  color: var(--chat-dim, #8a8a8a);
  cursor: default;
}
.chat__drawer__list {
  flex: 1;
  /* min-height:0 lets this flex child actually shrink inside the column panel
     so overflow-y:auto engages instead of the list pushing past the panel. */
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 0 8px calc(10px + var(--chat-safe-bottom));
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.chat__drawer__empty {
  padding: 18px 14px;
  /* CHAT-DRAWER-RESILIENT-INIT — defensive: if --chat-dim is ever undefined or
     too low-contrast, fall back to a readable muted grey so the empty/loading
     text is never invisible against the dark drawer. min-height reserves space
     so the loader dot is always a visible proof-of-life. */
  color: var(--chat-dim, #8a8a8a);
  opacity: 0.9;
  min-height: 2em;
  font-size: 0.9rem;
  text-align: center;
}
.chat__drawer__empty--error {
  color: var(--danger, #c4453d);
  opacity: 1;
  font-style: italic;
}
.chat__drawer__row {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-md);
  /* flex-shrink:0 — rows are flex children of the column list; without this the
     default flex-shrink:1 squeezes every row toward zero height once there are
     more than a few chats, collapsing titles while the absolutely-positioned
     swipe-action layer survives (the reported "blank drawer, swipe still works"). */
  flex: 0 0 auto;
}
.chat__drawer__row--active { background: color-mix(in srgb, var(--chat-accent) 16%, transparent); }
.chat__drawer__row-action {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 128px;
  display: flex;
  align-items: stretch;
  justify-content: flex-end;
}
.chat__drawer__rename-btn {
  appearance: none;
  border: 0;
  width: 64px;
  background: color-mix(in srgb, var(--chat-accent) 12%, var(--chat-surface));
  color: var(--chat-ink);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.chat__drawer__rename-btn svg { width: 19px; height: 19px; }
.chat__drawer__rename-input {
  position: absolute;
  z-index: 2;
  inset: 0;
  width: 100%;
  border: 1px solid var(--chat-accent);
  border-radius: var(--radius-md);
  background: var(--chat-surface);
  color: var(--chat-ink);
  padding: 11px 12px;
  font-size: 0.92rem;
  font-family: inherit;
}
.chat__drawer__rename-input:focus { outline: none; }
.chat__drawer__paywall {
  margin: 8px 6px;
  padding: 18px 16px;
  border: 1px solid var(--chat-line);
  border-radius: var(--radius-lg);
  background: var(--chat-surface);
  text-align: center;
}
.chat__drawer__paywall-title {
  margin: 0 0 6px;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--chat-ink);
}
.chat__drawer__paywall-lede {
  margin: 0 0 14px;
  font-size: 0.85rem;
  /* Full contrast (--ink), NOT --muted: the lede was invisible in Nacht when
     it inherited the intentionally low-contrast dim token. */
  color: var(--chat-ink);
}
.chat__drawer__paywall-cta {
  appearance: none;
  border: 0;
  border-radius: var(--radius-md);
  padding: 10px 22px;
  background: var(--chat-accent);
  color: var(--chat-on-accent);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
}
.chat__drawer__archive {
  appearance: none;
  border: 0;
  width: 64px;
  height: 100%;
  background: color-mix(in srgb, var(--chat-accent) 22%, var(--chat-surface));
  color: var(--chat-ink);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.chat__drawer__archive svg { width: 20px; height: 20px; }
.chat__drawer__archive--confirm { width: 124px; padding: 0 6px; }
.chat__drawer__archive-confirm { font-size: 0.72rem; font-weight: 600; line-height: 1.1; }
.chat__drawer__row-main {
  position: relative;
  z-index: 1;
  appearance: none;
  border: 0;
  width: 100%;
  text-align: left;
  background: var(--chat-surface);
  color: var(--chat-ink);
  padding: 11px 12px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  cursor: pointer;
  transition: transform var(--dur-base) var(--ease-standard);
}
.chat__drawer__row-main:hover { background: var(--chat-surface-2); }
.chat__drawer__row-title {
  font-size: 0.92rem;
  color: var(--chat-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chat__drawer__row-time {
  font-size: 0.74rem;
  color: var(--chat-dim);
}

/* ── Unified client message threads (COACH-CHAT-CLIENT-THREADS-UNIFIED) ────── */
.chat__drawer__row-main--client,
.chat__drawer__row-main--ai {
  flex-direction: row;
  align-items: center;
  gap: 10px;
}
/* AI rows carry the sparkle in the very slot the person icon holds on human
   rows, so "who am I talking to" is answerable without reading the title. */
.chat__drawer__row-main--ai .chat__drawer__row-time { align-self: flex-start; }
.chat__drawer__row-icon {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--chat-accent) 20%, var(--chat-surface));
  color: var(--chat-accent);
}
.chat__drawer__row-icon svg { width: 17px; height: 17px; }
.chat__drawer__row-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.chat__drawer__row-titlerow {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.chat__drawer__row-titlerow .chat__drawer__row-title { flex: 0 1 auto; }
.chat__drawer__row-tag {
  flex: 0 0 auto;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--chat-accent);
  background: color-mix(in srgb, var(--chat-accent) 15%, transparent);
  padding: 1px 6px;
  border-radius: var(--radius-md);
}
.chat__drawer__row-unread {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--chat-accent);
}
/* CHAT-CLIENT-COACH-THREAD-AND-PUSH — the member's pinned Coach thread sits at the
   top of the drawer; a subtle accent rail marks it apart from AI chats. */
.chat__drawer__row--coach {
  box-shadow: inset 3px 0 0 var(--chat-accent);
}
.chat__drawer__row-snippet {
  font-size: 0.76rem;
  color: var(--chat-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chat__drawer__row-main--client .chat__drawer__row-time { align-self: flex-start; }

/* ── Notification centre (CHAT-SCREEN-BECOMES-THE-NOTIFICATION-CENTRE) ─────── */
/* The drawer is ONE time-ordered list: AI chats, client threads and inbox
   notifications. Everything below only ADDS the notification row + the category
   filter — every row keeps the .chat__drawer__row-* geometry defined above. */
.chat__drawer__header-actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 2px;
}
.chat__drawer__kebab {
  appearance: none;
  position: relative;
  background: transparent;
  border: 0;
  padding: 6px;
  width: 34px;
  height: 34px;
  color: var(--chat-dim);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
}
/* Same trap as .chat__drawer__new[hidden]: the author display:inline-flex above
   beats the UA [hidden] rule, so the trainer-only kebab needs this guard or
   every member sees a filter for notifications they can never receive. */
.chat__drawer__kebab[hidden] { display: none; }
.chat__drawer__kebab:hover { color: var(--chat-ink); }
.chat__drawer__kebab svg { width: 20px; height: 20px; }
/* Accent pip = "a filter is on". The filter bar under the header carries the
   words; this is the at-a-glance marker on the control itself. */
.chat__drawer__kebab--on { color: var(--chat-accent); }
.chat__drawer__kebab--on::after {
  content: '';
  position: absolute;
  top: 4px;
  right: 4px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--chat-accent);
}
.chat__drawer__menu {
  position: absolute;
  z-index: 4;
  top: calc(48px + var(--safe-top));
  right: 8px;
  width: min(208px, calc(100% - 16px));
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--chat-surface-2);
  border: 1px solid var(--chat-line);
  border-radius: var(--radius-lg);
  box-shadow: 0 8px 24px color-mix(in srgb, var(--chat-bg) 70%, transparent);
}
/* Third instance of the .chat__drawer__new[hidden] trap in this file: the author
   display:flex above beats the UA [hidden] rule. The menu is create/remove'd, so
   nothing relies on this today — it is here so the next builder who reaches for
   `hidden` on the popover doesn't ship an un-closable filter menu. */
.chat__drawer__menu[hidden] { display: none; }
.chat__drawer__menu-title {
  padding: 6px 8px 2px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--chat-dim);
}
.chat__drawer__menu-row {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--chat-ink);
  font-family: inherit;
  font-size: 0.88rem;
  text-align: left;
  padding: 8px;
  min-height: 40px;
  display: flex;
  align-items: center;
  gap: 10px;
  border-radius: var(--radius-md);
  cursor: pointer;
}
.chat__drawer__menu-row:hover { background: color-mix(in srgb, var(--chat-ink) 8%, transparent); }
.chat__drawer__menu-box {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  border-radius: 5px;
  border: 1.5px solid var(--chat-line);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--chat-on-accent);
}
.chat__drawer__menu-box svg { width: 13px; height: 13px; }
.chat__drawer__menu-box--on { border-color: var(--chat-accent); background: var(--chat-accent); }
/* A hidden category must never read as "nothing happened". This bar states what
   is being held back and offers the way out, in the header, above the list. */
.chat__drawer__filterbar {
  flex-shrink: 0;
  margin: 0 14px 8px;
  padding: 7px 10px;
  display: flex;
  align-items: center;
  gap: 8px;
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--chat-accent) 12%, transparent);
  font-size: 0.74rem;
  color: var(--chat-ink);
}
.chat__drawer__filterbar[hidden] { display: none; }
.chat__drawer__filterbar-text { flex: 1 1 auto; min-width: 0; }
.chat__drawer__filterbar-reset {
  appearance: none;
  flex: 0 0 auto;
  border: 0;
  background: transparent;
  color: var(--chat-accent);
  font-family: inherit;
  font-size: 0.74rem;
  font-weight: 700;
  padding: 2px;
  cursor: pointer;
}
/* A notification is not a person: the round icon drops the accent tint so a
   client thread and a booking request stay tellable apart at a glance. */
.chat__drawer__row-icon--notif {
  background: color-mix(in srgb, var(--chat-ink) 10%, transparent);
  color: var(--chat-dim);
}
.chat__drawer__row-tag--notif {
  color: var(--chat-dim);
  background: color-mix(in srgb, var(--chat-ink) 10%, transparent);
}
/* A notification carries four facts (who / what / detail / when) where a thread
   carries three, and at 320px a right-hand time COLUMN wraps "3 min geleden"
   onto two lines and squeezes the name down to "Mari…". So the time rides the
   end of the name line and the type chip drops to the detail line: two full-width
   lines instead of three cramped columns. */
.chat__drawer__row-time--inline {
  margin-left: auto;
  padding-left: 6px;
  white-space: nowrap;
}
.chat__drawer__row-metarow {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.chat__drawer__empty-action {
  appearance: none;
  display: block;
  margin: 10px auto 0;
  min-height: 40px;
  padding: 0 14px;
  border: 1px solid var(--chat-line);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--chat-accent);
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
}
/* Detail view for a notification with no destination of its own. Reuses the
   .chat__picker full-panel overlay shell; only the content block is new. */
.chat__detail {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 14px 14px calc(14px + var(--chat-safe-bottom));
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* .chat__detail is a flex column, so a chip would stretch edge-to-edge without
   this — it must hug its own text. */
.chat__detail__chip { align-self: flex-start; }
.chat__detail__meta { font-size: 0.74rem; color: var(--chat-dim); }
.chat__detail__title { font-size: 1rem; font-weight: 600; color: var(--chat-ink); }
.chat__detail__body {
  margin: 0;
  font-size: 0.88rem;
  line-height: 1.5;
  color: var(--chat-ink);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.chat__detail__cta {
  appearance: none;
  margin-top: 4px;
  min-height: 44px;
  padding: 0 14px;
  border: 0;
  border-radius: var(--radius-lg);
  background: var(--chat-accent);
  color: var(--chat-on-accent);
  font-family: inherit;
  font-size: 0.92rem;
  font-weight: 600;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.chat__detail__cta svg { width: 17px; height: 17px; }
.chat__detail__link { font-size: 0.85rem; color: var(--chat-accent); text-decoration: none; }

/* NOTIF-REPLY-BY-CHAT-EVERYWHERE-REPLY-BY-EMAIL-IS — de twee antwoordkanalen op
   één regel. .chat__detail is een flex-kolom, dus zonder deze wrapper zouden ze
   stapelen. Geen nieuw knopidioom: beide kinderen blijven .chat__detail__link. */
.chat__detail__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 18px;
  margin-top: 2px;
  min-height: 44px;
}
/* De chat-actie is een in-page <button>, geen mailto-<a>. De UA-knopchrome moet
   eraf, anders leest hij niet als de link ernaast. */
button.chat__detail__link {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  padding: 0;
  background: none;
  font-family: inherit;
  font-weight: inherit;
  line-height: inherit;
  cursor: pointer;
  text-align: left;
}

/* ── New-message client picker (COACH-CHAT-NEW-THREAD-CLIENT-PICKER) ───────── */
/* Full-panel overlay that slides over the drawer list: header + optional search
   + the alphabetical roster. Reuses the .chat__drawer__row-* row idiom inside. */
.chat__picker {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  background: var(--chat-surface);
  transform: translateX(8%);
  opacity: 0;
  transition: transform var(--dur-base) var(--ease-standard), opacity var(--dur-base) var(--ease-standard);
}
.chat__picker--open { transform: translateX(0); opacity: 1; }
.chat__picker__header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: calc(10px + var(--safe-top)) 12px 10px;
  border-bottom: 1px solid var(--chat-line);
}
.chat__picker__back {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 6px;
  width: 34px;
  height: 34px;
  color: var(--chat-dim);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
}
.chat__picker__back:hover { color: var(--chat-ink); }
.chat__picker__back svg { width: 20px; height: 20px; }
.chat__picker__title {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--chat-ink);
}
.chat__picker__search {
  appearance: none;
  margin: 10px 12px 4px;
  padding: 9px 12px;
  border: 1px solid var(--chat-line);
  border-radius: var(--radius-md);
  background: var(--chat-surface-2);
  color: var(--chat-ink);
  font-size: 0.9rem;
}
.chat__picker__search::placeholder { color: var(--chat-dim); }
.chat__picker__list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 6px 8px calc(10px + var(--chat-safe-bottom));
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.chat__picker__empty {
  padding: 18px 14px;
  color: var(--chat-dim);
  font-size: 0.9rem;
  text-align: center;
}

.chat__thread-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 4px 0 12px;
  padding: 9px 12px;
  border-radius: var(--radius-lg);
  background: var(--chat-surface-2);
  color: var(--chat-ink);
}
.chat__thread-banner-icon {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--chat-accent) 20%, var(--chat-surface));
  color: var(--chat-accent);
}
.chat__thread-banner-icon svg { width: 17px; height: 17px; }
.chat__thread-banner-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.chat__thread-banner-name { font-size: 0.9rem; font-weight: 600; }
.chat__thread-banner-note { font-size: 0.74rem; color: var(--chat-dim); }
.chat__thread-banner.is-tappable {
  width: 100%;
  border: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.chat__thread-banner.is-tappable:active { filter: brightness(0.96); }
.chat__thread-banner-chev {
  margin-left: auto;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  color: var(--chat-dim);
}
.chat__thread-banner-chev svg { width: 18px; height: 18px; }

/* ── Bulk-select mode (CHAT-CONVERSATIONS-BULK-DELETE) ────────────────────── */
.chat__drawer__select-toggle {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--chat-accent);
  font-size: 0.82rem;
  font-weight: 600;
  font-family: inherit;
  padding: 6px 8px;
  margin-left: auto;
  border-radius: var(--radius-md);
  cursor: pointer;
}
.chat__drawer__select-toggle--on { color: var(--chat-ink); }
/* Same trap as .chat__drawer__new[hidden]: the `display:flex` below beats the UA
   `[hidden]` rule, so bar.hidden=true left the bulk-delete bar parked under the
   history at all times. Nothing renders below the one scroll of chats. */
.chat__drawer__select-bar[hidden] { display: none; }
.chat__drawer__select-bar {
  flex: 0 0 auto;
  padding: 10px 14px calc(12px + var(--chat-safe-bottom));
  border-top: 1px solid var(--chat-line);
  background: var(--chat-surface);
  display: flex;
  gap: 8px;
}
/* Neutral/secondary counterpart to the destructive delete button — outline fill
   from design tokens so it reads as non-destructive in both Dag and Nacht. */
.chat__drawer__select-all {
  appearance: none;
  flex: 1 1 0;
  min-width: 0;
  border: 1px solid var(--chat-line);
  border-radius: var(--radius-lg);
  padding: 12px 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: var(--chat-surface-2);
  color: var(--chat-ink);
  font-size: 0.92rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
}
.chat__drawer__select-all-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat__drawer__select-all:disabled { opacity: 0.5; cursor: default; }
.chat__drawer__select-del {
  appearance: none;
  border: 0;
  flex: 1 1 0;
  min-width: 0;
  border-radius: var(--radius-lg);
  padding: 12px 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: var(--chat-accent);
  /* Contrast protocol: text/icon on an accent fill uses the on-accent token,
     never a hardcoded white (fails on the lime Nacht accent). */
  color: var(--chat-on-accent);
  font-size: 0.92rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
}
.chat__drawer__select-del svg { width: 18px; height: 18px; }
.chat__drawer__select-del:disabled { opacity: 0.5; cursor: default; }
.chat__drawer__select-del--confirm { background: var(--danger, #c4453d); }
.chat__drawer__row--selectable { display: flex; align-items: center; gap: 10px; padding: 0 4px; cursor: pointer; }
.chat__drawer__row--checked { background: color-mix(in srgb, var(--chat-accent) 14%, transparent); }
.chat__drawer__check {
  appearance: none;
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  margin-left: 8px;
  border: 2px solid var(--chat-dim);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--chat-on-accent);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.chat__drawer__check svg { width: 14px; height: 14px; }
.chat__drawer__check--on { border-color: var(--chat-accent); background: var(--chat-accent); }
.chat__drawer__row-main--select { background: transparent; cursor: pointer; }
.chat__drawer__row-main--select:hover { background: transparent; }

/* ── Message list ─────────────────────────────────────────────────────────── */
.chat__messages {
  flex: 1;
  overflow-y: auto;
  overflow-anchor: none;
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  padding: 14px 14px 8px;
}
/* CHAT-PERSONAL-GREETING-AND-DUAL-HORSE-BACKGROUND — the Aligi Sassu two-horses
   engraving sits behind the conversation as a faint watermark. Two decisions
   worth knowing:
   (1) It is painted as an ALPHA MASK over --chat-ink, NOT as a background-image.
       logo-emblem-480.webp is opaque (no alpha channel), so using it directly
       would paint a solid navy square that breaks every light finish. A mask
       inherits the ink colour, so it is neutral by construction on dark AND
       light with no per-theme filter tuning.
   (2) It hangs off .chat (the fixed shell), not .chat__messages (the scroller),
       so the watermark stays put while the thread scrolls over it. .chat is
       already position:fixed, so it is the containing block already — do NOT
       add position:relative here, it would override the fixed shell layout.
   Sibling children are left alone on purpose: the drawer is position:fixed with
   z-index 60, and a blanket `.chat > *` rule would flatten it. At 0.05 alpha the
   layer reads as a watermark whether it composites over or under the text. */
.chat::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: 0.05;
  background-color: var(--chat-ink);
  -webkit-mask: url('/assets/img/brand/logo-emblem-alpha-480.png') center / 60% auto no-repeat;
          mask: url('/assets/img/brand/logo-emblem-alpha-480.png') center / 60% auto no-repeat;
}
@supports not ((-webkit-mask-image: none) or (mask-image: none)) {
  /* No mask support: paint nothing rather than an opaque plate. */
  .chat::before { background-color: transparent; }
}
.chat__empty {
  margin: auto;
  padding: 24px 16px;
  text-align: center;
  max-width: 320px;
}
.chat__empty-title {
  margin: 0 0 8px;
  font-size: 20px;
  font-weight: 300;
  color: var(--chat-ink);
}
.chat__empty-lede {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--chat-dim);
}

/* ── Bubbles ──────────────────────────────────────────────────────────────── */
.chat__msg {
  margin-top: 4px;
  padding: 10px 14px;
  border-radius: var(--radius-lg);
  color: var(--chat-ink);
  word-wrap: break-word;
  overflow-wrap: anywhere;
}
.chat__msg:first-child { margin-top: 0; }
.chat__msg--user {
  align-self: flex-end;
  max-width: 78%;
  background: var(--chat-surface-2);
}
.chat__msg--assistant {
  align-self: flex-start;
  max-width: 84%;
  background: var(--chat-surface);
}
/* Sequential bubbles from the same role tighten the vertical gap. */
.chat__msg--user + .chat__msg--user,
.chat__msg--assistant + .chat__msg--assistant { margin-top: 2px; }
.chat__msg-text {
  font-size: 14.5px;
  line-height: 1.4;
  white-space: pre-wrap;
}
.chat__msg-text strong { font-weight: 700; }
.chat__msg-text em { font-style: italic; }
.chat__msg-text code {
  font-family: var(--mono, monospace);
  font-size: 0.92em;
  background: var(--surface-2, rgba(127, 127, 127, 0.16));
  padding: 0.05em 0.3em;
  border-radius: var(--radius-sm);
}
.chat__msg-text ul {
  margin: 0.25rem 0;
  padding-left: 1em;
  white-space: normal;
}
.chat__msg-text li {
  margin: 0.25rem 0;
}
/* GFM tables in assistant bubbles only. Block + overflow-x so a wide macro
   table scrolls horizontally on a phone instead of overflowing the bubble. */
.chat__msg--assistant .chat__msg-text .chat__md-table {
  width: 100%;
  border-collapse: collapse;
  margin: 6px 0;
  font-size: 0.86em;
  display: block;
  overflow-x: auto;
}
.chat__msg--assistant .chat__msg-text .chat__md-table th,
.chat__msg--assistant .chat__msg-text .chat__md-table td {
  padding: 4px 8px;
  border: 1px solid var(--chat-line);
  text-align: left;
  white-space: nowrap;
}
.chat__msg--assistant .chat__msg-text .chat__md-table th {
  font-weight: 700;
  background: var(--chat-surface-2);
}
.chat__msg--assistant .chat__msg-text .chat__md-table tbody tr:nth-child(even) {
  background: color-mix(in srgb, var(--chat-line) 30%, transparent);
}
/* CHAT-AI-ANSWER-FEEDBACK-THUMBS — chrome under an AI answer: dim at rest, full
   on hover/focus, so the thumbs never compete with the reply itself. */
.chat__fb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 4px;
  opacity: .6;
  transition: opacity .15s ease;
}
.chat__msg:hover .chat__fb,
.chat__fb:focus-within { opacity: 1; }
.chat__fb-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 2px;
  background: transparent;
  border: 0;
  border-radius: 6px;
  color: var(--chat-dim);
  cursor: pointer;
}
.chat__fb-btn svg { width: 16px; height: 16px; }
.chat__fb-btn.is-on  { color: var(--chat-accent); }
.chat__fb-btn.is-off { opacity: .4; }
/* Author display beats the hidden attribute, so restore it explicitly. */
.chat__fb-strip[hidden] { display: none; }
.chat__fb-strip {
  display: flex;
  flex: 1 1 100%;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.chat__fb-lbl {
  font-size: 12px;
  color: var(--chat-dim);
}
.chat__fb-note {
  flex: 1 1 140px;
  min-width: 0;
  padding: 4px 6px;
  font: inherit;
  font-size: 12px;
  color: inherit;
  background: var(--chat-surface-2);
  border: 1px solid var(--chat-line);
  border-radius: 6px;
}
.chat__fb-send,
.chat__fb-skip {
  padding: 4px 8px;
  font-size: 12px;
  background: transparent;
  border: 1px solid var(--chat-line);
  border-radius: 6px;
  color: var(--chat-accent);
  cursor: pointer;
}
.chat__fb-skip { color: var(--chat-dim); }
.chat__fb-thanks {
  font-size: 12px;
  color: var(--chat-dim);
}
.chat__msg-meta {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  margin-top: 4px;
}
.chat__msg-time {
  font-size: 11px;
  color: var(--chat-dim);
  font-variant-numeric: tabular-nums;
}
.chat__msg-tick {
  display: inline-flex;
  color: var(--chat-accent);
}
.chat__msg-tick svg { width: 15px; height: 12px; }
.chat__thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 6px;
}
.chat__thumb {
  max-width: 160px;
  max-height: 160px;
  border-radius: var(--radius-md);
  display: block;
  object-fit: cover;
}
.chat__file {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  border-radius: var(--radius-pill);
  background: var(--chat-surface-2);
  border: 1px solid var(--chat-line);
  color: var(--chat-dim);
  font-size: 12px;
}
.chat__file svg { width: 14px; height: 14px; }

/* Action proposal card — two-step confirm for chat-proposed mutations
   (CHAT-TOOL-USE-MUTATIONS, v0.9.670). Tokens only, finish-invariant. */
.chat__action-card {
  margin-top: 8px;
  padding: 10px 12px;
  border-radius: var(--radius-lg);
  background: var(--chat-surface-2);
  border: 1px solid var(--chat-line);
}
.chat__action-summary {
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--chat-ink);
  margin-bottom: 10px;
}
.chat__action-btns {
  display: flex;
  gap: 8px;
}
.chat__action-btn {
  flex: 1 1 0;
  padding: 9px 12px;
  border-radius: var(--radius-pill);
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid var(--chat-line);
  transition: opacity var(--dur-fast) var(--ease-standard);
}
.chat__action-btn:active { opacity: 0.85; }
.chat__action-btn--yes {
  background: var(--chat-accent);
  color: var(--chat-on-accent);
  border-color: var(--chat-accent);
}
.chat__action-btn--no {
  background: transparent;
  color: var(--chat-dim);
}
.chat__action-note {
  font-size: 12.5px;
  color: var(--chat-dim);
  font-style: italic;
}

/* Typing indicator (assistant) */
.chat__typing {
  display: flex;
  gap: 8px;
  align-items: center;
}
.chat__typing-dots {
  display: flex;
  gap: 4px;
  align-items: center;
}
/* CHAT-CANCEL-AND-TRANSPARENT-STATUS — honest generic status line. */
.chat__typing-status {
  font-size: 0.8rem;
  color: var(--chat-dim);
  animation: chat-status-pulse var(--dur-pulse) var(--ease-standard) infinite;
}
@keyframes chat-status-pulse {
  0%, 100% { opacity: 0.55; }
  50%      { opacity: 1; }
}
.chat__msg-stopped {
  font-size: 0.8rem;
  font-style: italic;
  color: var(--chat-dim);
  margin-top: 2px;
}
/* CHAT-SEND-RESILIENCE-RETRY-AND-FAILED-STATE — a send that exhausted its
   retries. The failure lives on the user's own bubble (tinted + labelled) with
   the Resend control inside it, so the typed text is never thrown away. */
.chat__msg--failed {
  outline: 1px solid var(--danger, #d9534f);
  outline-offset: 0;
}
.chat__msg-failed {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 4px;
}
.chat__msg-failed-label {
  font-size: 0.8rem;
  color: var(--danger, #d9534f);
}
.chat__msg-resend {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 48px;
  padding: 0 14px;
  border: 1px solid var(--accent);
  border-radius: 999px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}
.chat__msg-resend svg { width: 18px; height: 18px; }
/* CHAT-RESEND-VISIBLE-FEEDBACK-AND-CLASSIFIED-REASON — the tap has to show itself
   BEFORE the outcome is known, and a second tap must be impossible. */
.chat__msg-resend--pending {
  opacity: 0.7;
  cursor: default;
}
.chat__msg-resend--pending svg {
  animation: chat-resend-spin 900ms linear infinite;
}
@keyframes chat-resend-spin {
  to { transform: rotate(360deg); }
}
/* Escalation chip — only after 3 identical failures, so it is absent in the golden
   path. Monochrome + no icon (no-emoji / icon policy). */
.chat__msg-escalate {
  display: inline-flex;
  align-items: center;
  min-height: 48px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--ink-2);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}
.chat__typing-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--chat-dim);
  display: inline-block;
  animation: chat-typing var(--dur-pulse) var(--ease-standard) infinite;
}
.chat__typing-dot:nth-child(2) { animation-delay: 0.15s; }
.chat__typing-dot:nth-child(3) { animation-delay: 0.3s; }
@keyframes chat-typing {
  0%, 80%, 100% { transform: translateY(0); opacity: 0.45; }
  40%           { transform: translateY(-4px); opacity: 1; }
}

/* ── Pending-attachment strip (above the action row) ──────────────────────── */
.chat__attach-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 6px 14px 0;
}
.chat__attach-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 4px 4px 6px;
  border-radius: var(--radius-pill);
  background: var(--chat-surface);
  border: 1px solid var(--chat-line);
  font-size: 12px;
  color: var(--chat-dim);
  max-width: 200px;
}
.chat__attach-chip-thumb {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  object-fit: cover;
}
.chat__attach-chip-icon {
  display: inline-flex;
  width: 28px;
  height: 28px;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--chat-surface-2);
}
.chat__attach-chip-icon svg { width: 16px; height: 16px; }
.chat__attach-chip-name {
  max-width: 120px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chat__attach-chip-rm {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 4px;
  margin: 0;
  min-width: 24px;
  min-height: 24px;
  color: var(--chat-dim);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.chat__attach-chip-rm svg { width: 16px; height: 16px; }

/* ── Action row: BIJLAGE · SPREEK (glow PTT) · FOTO ───────────────────────── */
.chat__actions {
  display: flex;
  align-items: center;
  justify-content: space-around;
  gap: 8px;
  padding: 10px 18px 6px;
}
.chat__action {
  appearance: none;
  background: transparent;
  border: 0;
  margin: 0;
  padding: 0;
  cursor: pointer;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  color: var(--chat-dim);
}
.chat__action-icon {
  display: inline-flex;
  width: 48px;
  height: 48px;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--chat-surface-2);
  border: 1px solid var(--chat-line);
  color: var(--chat-ink);
}
.chat__action-icon svg { width: 22px; height: 22px; }
.chat__action:hover .chat__action-icon { background: var(--chat-surface); }
.chat__action-label,
.chat__mic-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--chat-dim);
}

/* Push-to-talk circle — the glowing centrepiece */
.chat__mic {
  appearance: none;
  background: transparent;
  border: 0;
  margin: 0;
  padding: 0;
  cursor: pointer;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}
.chat__mic-icon {
  position: relative;
  display: inline-flex;
  width: 72px;
  height: 72px;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--chat-surface-2);
  color: var(--chat-accent);
  box-shadow: 0 0 18px var(--chat-glow), 0 0 0 1px var(--chat-glow);
}
.chat__mic-icon svg { width: 30px; height: 30px; }
.chat__mic-sub {
  font-size: 10.5px;
  color: var(--chat-dim);
}
.chat__mic:active .chat__mic-icon,
.chat__mic--holding .chat__mic-icon,
.chat__mic--recording .chat__mic-icon {
  background: var(--chat-accent);
  color: var(--chat-on-accent);
}
/* Telemetry-only marker — the button stays fully interactive and soft-degrades
   at first press (toast after a real failure), never a silent dead button. */
/* Pulse ring while recording */
.chat__mic-icon::before {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 2px solid var(--chat-accent);
  opacity: 0;
  pointer-events: none;
}
/* Pulse the moment the press lands — independent of capture starting, so the
   user always sees "I'm holding" even on slow/denied devices. */
.chat__mic--holding .chat__mic-icon::before,
.chat__mic--recording .chat__mic-icon::before {
  animation: chat-mic-pulse var(--dur-pulse) var(--ease-standard) infinite;
}
@keyframes chat-mic-pulse {
  0%   { opacity: 0.7; transform: scale(0.9); }
  100% { opacity: 0;   transform: scale(1.4); }
}

/* ── Text input row (pinned below) ────────────────────────────────────────── */
.chat__input {
  position: relative;
  display: flex;
  align-items: flex-end;
  gap: 8px;
  padding: 8px 14px calc(10px + var(--chat-safe-bottom));
  background: var(--chat-surface);
  border-top: 1px solid var(--chat-line);
}
.chat__input-field {
  flex: 1 1 auto;
  appearance: none;
  resize: none;
  background: var(--chat-surface-2);
  border: 1px solid var(--chat-line);
  border-radius: var(--radius-lg);
  color: var(--chat-ink);
  font: inherit;
  font-size: 15px;
  line-height: 1.4;
  padding: 9px 14px;
  max-height: 120px;
  min-height: 40px;
  outline: 0;
}
.chat__input-field::placeholder { color: var(--chat-dim); }
.chat__input-field:focus { border-color: var(--chat-accent); }
.chat__send {
  appearance: none;
  border: 0;
  margin: 0;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--chat-accent);
  color: var(--chat-on-accent);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
}
.chat__send svg { width: 18px; height: 18px; }
.chat__send[disabled] { opacity: 0.5; cursor: not-allowed; }
/* Stop state of the send control (CHAT-CANCEL-AND-TRANSPARENT-STATUS). */
.chat__send--stop { background: var(--danger, #d9534f); }
.chat__send--stop svg { width: 16px; height: 16px; }

@media (prefers-reduced-motion: reduce) {
  .chat__typing-dot { animation: none; opacity: 0.7; }
  .chat__typing-status { animation: none; opacity: 0.8; }
  .chat__mic--holding .chat__mic-icon::before,
  .chat__mic--recording .chat__mic-icon::before { animation: none; opacity: 0; }
}

/* ── SLEEP-DURATION-TIMEPICKER — two-column wheel picker overlay ─────────── */
.fz-wheel-backdrop {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, 0.45);
  display: flex; align-items: flex-end; justify-content: center;
  z-index: 1000;
}
.fz-wheel-sheet {
  background: var(--surface);
  color: var(--ink);
  width: 100%; max-width: 480px;
  border-top-left-radius: var(--radius-lg);
  border-top-right-radius: var(--radius-lg);
  padding: 8px 12px 16px;
  box-shadow: var(--elev-up);
}
.fz-wheel-sheet__header {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px;
  padding: 8px 4px 12px;
}
.fz-wheel-sheet__title {
  flex: 1; text-align: center;
  font-weight: 600; font-size: 16px;
}
.fz-wheel-sheet__btn {
  background: transparent;
  border: none;
  font: inherit; font-weight: 600;
  padding: 8px 12px;
  border-radius: var(--radius-md);
  cursor: pointer;
}
.fz-wheel-sheet__btn--ghost { color: var(--muted); }
.fz-wheel-sheet__btn--accent {
  background: var(--accent);
  color: var(--on-accent);
}
.fz-wheel-sheet__body {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  gap: 8px;
  padding: 4px 0 8px;
}
.fz-wheel-sheet__indicator {
  position: absolute; left: 12px; right: 12px;
  top: 50%; height: 40px;
  transform: translateY(-50%);
  border-top: 1px solid var(--surface-2, rgba(127, 127, 127, 0.2));
  border-bottom: 1px solid var(--surface-2, rgba(127, 127, 127, 0.2));
  pointer-events: none;
}
.fz-wheel-col {
  --fz-wheel-item-h: 40px;
  height: 200px;
  width: 80px;
  overflow-y: auto;
  scroll-snap-type: y mandatory;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  text-align: center;
}
.fz-wheel-col::-webkit-scrollbar { display: none; }
.fz-wheel-col__pad { height: 80px; }
.fz-wheel-col__item {
  height: 40px;
  line-height: 40px;
  scroll-snap-align: center;
  font-size: 20px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  transition: color var(--dur-fast) var(--ease-standard), font-weight var(--dur-fast) var(--ease-standard);
}
.fz-wheel-col__item.is-selected {
  color: var(--ink);
  font-weight: 700;
}
.fz-wheel-col__suffix {
  font-size: 18px;
  color: var(--muted);
  font-weight: 600;
  align-self: center;
}

/* ───────── RECIPE-SHARE-VIA-COMPACT-URL-V1 — /r/:code landing ─────────
   Standalone (no tabbar) share view: recipe card + either a sticky Ja/Nee add
   bar (logged-in) or a create-account upsell (logged-out). Tokens only. */
.fz-share__sec {
  /* leave room for the sticky add bar so it never covers the last ingredient */
  padding-bottom: var(--space-7);
}
.fz-share__body {
  width: 100%;
}
.fz-share__from {
  margin: 0 0 var(--space-1);
  color: var(--ink-2);
  font-size: 13px;
  font-weight: 600;
}
.fz-share__title {
  margin: 0 0 var(--space-4);
  color: var(--ink);
  font-size: 24px;
  font-weight: 700;
  line-height: 1.2;
}
.fz-share__items {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  overflow: hidden;
}
.fz-share__item {
  padding: var(--space-3) var(--space-4);
  color: var(--ink);
  font-size: 15px;
  border-bottom: 1px solid var(--line-2);
}
.fz-share__item:last-child {
  border-bottom: 0;
}
/* Sticky bottom add bar — logged-in receiver. */
.fz-share__bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  padding-bottom: calc(var(--space-4) + var(--safe-bottom));
  background: var(--surface);
  border-top: 1px solid var(--line);
}
.fz-share__barq {
  color: var(--ink);
  font-size: 15px;
  font-weight: 600;
  text-align: center;
}
.fz-share__baracts {
  display: flex;
  gap: var(--space-3);
}
.fz-share__baracts .btn {
  flex: 1;
}
/* Upsell card — logged-out visitor. */
.fz-share__upsell {
  width: 100%;
  margin-top: var(--space-5);
  padding: var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  text-align: center;
}
.fz-share__upsellcopy {
  margin: 0 0 var(--space-4);
  color: var(--ink);
  font-size: 15px;
  line-height: 1.4;
}
.fz-share__cta {
  display: inline-flex;
  width: 100%;
  justify-content: center;
}
/* "Deel recept" button on the recipe builder — icon + label inline. */
.nut-rb__share {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
}

/* ADMIN-3-WEEKLY-RATING-PROMPT — bottom-sheet star rating overlay. */
.rate-overlay {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(0, 0, 0, 0.45);
}
.rate-sheet {
  width: 100%;
  max-width: 520px;
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  box-shadow: var(--elev-up);
}
.rate-sheet__title {
  margin: 0;
  font-size: 18px;
  line-height: 1.3;
  text-align: center;
}
/* FUNNEL-D-POST-ENTRY-ACTIVATION — supporting copy line in the activation sheet. */
.rate-sheet__sub {
  margin: 0;
  font-size: 14px;
  line-height: 1.45;
  color: var(--muted);
  text-align: center;
}
.rate-sheet__stars {
  display: flex;
  justify-content: center;
  gap: var(--space-2);
}
.rate-star {
  background: transparent;
  border: 0;
  padding: var(--space-2);
  cursor: pointer;
  color: var(--muted);
  line-height: 0;
}
.rate-star svg { fill: currentColor; }
.rate-star.on { color: var(--accent); }
.rate-sheet__comment {
  width: 100%;
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  color: var(--ink);
  font: inherit;
  resize: vertical;
}
.rate-sheet__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.rate-sheet__submit {
  flex: 1;
  padding: var(--space-3) var(--space-4);
  background: var(--accent);
  color: var(--on-accent);
  border: 0;
  border-radius: var(--radius-sm);
  font-weight: 600;
  cursor: pointer;
}
.rate-sheet__submit:disabled { opacity: 0.5; cursor: not-allowed; }
.rate-sheet__skip {
  background: transparent;
  border: 0;
  color: var(--muted);
  padding: var(--space-3);
  cursor: pointer;
  text-decoration: underline;
}

/* FEEDBACK-SETTINGS-ENTRY-AND-SHEET — settings row lead icon + sheet chips. */
.set-row__lead {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  margin-right: var(--space-3);
  flex: 0 0 auto;
}
.set-row__lead svg { width: 20px; height: 20px; }
.fb-sheet__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.fb-chip {
  flex: 1 1 auto;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}
.fb-chip.on {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
  font-weight: 600;
}
.fb-sheet__context {
  margin: 0;
  font-size: 12px;
  color: var(--muted);
  text-align: center;
  word-break: break-word;
}

/* PREMIUM-UPGRADE-UI-AND-SELF-SERVE — single reusable subscribe sheet. */
.premium-overlay {
  position: fixed;
  inset: 0;
  z-index: 9200;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
}
.premium-sheet {
  width: 100%;
  max-width: 520px;
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  box-shadow: var(--elev-up);
}
.premium-sheet__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.premium-sheet__header h2 {
  margin: 0;
  font-size: 20px;
  line-height: 1.2;
}
.premium-sheet__close {
  background: transparent;
  border: 0;
  color: var(--muted);
  padding: var(--space-2);
  cursor: pointer;
  line-height: 0;
}
.premium-sheet__close svg { width: 22px; height: 22px; }
.premium-sheet__lede {
  margin: 0;
  color: var(--muted);
}
.premium-sheet__benefits {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.premium-sheet__benefits li {
  position: relative;
  padding-left: var(--space-5);
}
.premium-sheet__benefits li::before {
  content: "";
  position: absolute;
  left: 6px;
  top: 0.55em;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
}
.premium-sheet__skus {
  display: flex;
  gap: var(--space-3);
}
.premium-sku {
  flex: 1;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: var(--space-4) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  color: var(--ink);
  cursor: pointer;
}
.premium-sku:disabled { opacity: 0.5; cursor: not-allowed; }
.premium-sku--annual {
  border-color: var(--accent);
}
/* PAYMENTS-MOLLIE-MANDATE-FIRST-PAYMENT — the coach-subscription sheet reuses
   the premium sheet chrome but has ONE amount and ONE button, so the price is a
   read-only line and the button is a full-width primary rather than a chip in a
   SKU row. */
.pt-sub-sheet__price {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 6px;
  padding: var(--space-3) 0;
}
.pt-sub-sheet__pay {
  width: 100%;
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
  font-weight: 700;
}
.premium-sku__price {
  font-size: 22px;
  font-weight: 700;
}
.premium-sku__interval {
  font-size: 13px;
  color: var(--muted);
}
.premium-sku__badge {
  position: absolute;
  top: -10px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 11px;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: var(--radius-sm);
}
.premium-sheet__loading {
  margin: 0;
  color: var(--muted);
}
.premium-sheet__legal {
  color: var(--muted);
  font-size: 12px;
  text-align: center;
}

/* PREMIUM-UPSELL-REQUEST-FORM-INTERIM — access-request form body inside the
   reused .premium-sheet chrome. */
.premium-request__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.premium-request__textarea {
  width: 100%;
  padding: var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-weight: 400;
  min-height: 72px;
  resize: vertical;
}
.premium-request__submit { width: 100%; }
.premium-request__done {
  margin: 0;
  color: var(--muted);
}

/* SASSU-FOR-COACHES-WAITLIST — coming-soon + beta waitlist sheet. Reuses the
   .premium-overlay / .premium-sheet chrome; these rules style the form body. */
.coach-waitlist__hero {
  margin-bottom: var(--space-3);
}
.coach-waitlist__hero .lede {
  margin: 0 0 var(--space-1);
  font-weight: 600;
}
.coach-waitlist__hero .sub {
  margin: 0;
  color: var(--muted);
  font-size: 14px;
}
.coach-waitlist__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.coach-waitlist__form label {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: 14px;
  font-weight: 600;
}
.coach-waitlist__form input,
.coach-waitlist__form select,
.coach-waitlist__form textarea {
  width: 100%;
  padding: var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-weight: 400;
}
.coach-waitlist__form textarea {
  min-height: 72px;
  resize: vertical;
}
.coach-waitlist__form .honeypot {
  position: absolute;
  left: -9999px;
  opacity: 0;
  height: 0;
  width: 0;
}
.coach-waitlist__submit {
  width: 100%;
  margin-top: var(--space-1);
}
.coach-waitlist__error {
  margin: 0;
  color: var(--danger);
  font-size: 13px;
}
.coach-waitlist__success {
  text-align: center;
  padding: var(--space-4) var(--space-2);
}
.coach-waitlist__success h3 {
  margin: 0 0 var(--space-2);
}
.coach-waitlist__success p {
  margin: 0;
  color: var(--muted);
}

/* TRAINER-MARKETPLACE-BROWSABLE-PROFILES — the marketplace as a real screen
   (/marketplace + /marketplace/:slug), not a drawer. Single-column list: with
   one coach a grid would read as missing tiles, one full-width card reads as a
   complete list. All colour comes from tokens, so Dag and Nacht both hold. */
.mkt-page { padding-bottom: var(--space-6); }

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

.mkt-card {
  display: grid;
  grid-template-columns: 64px 1fr 20px;
  align-items: start;
  gap: var(--space-3);
  width: 100%;
  text-align: left;
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  transition: border-color 140ms ease, transform 140ms ease;
}
.mkt-card:hover { border-color: var(--accent); }
.mkt-card:active { transform: scale(0.99); }

.mkt-card__avatar {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  object-fit: cover;
  flex: 0 0 auto;
}
.mkt-card__avatar--placeholder,
.mkt-hero__avatar--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 700;
  font-size: 22px;
}
.mkt-card__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.mkt-card__name {
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
}
.mkt-card__meta {
  font-size: 12px;
  color: var(--muted);
}
.mkt-card__pitch {
  margin: var(--space-1) 0 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--ink-2);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.mkt-card__chev {
  display: inline-flex;
  align-self: center;
  color: var(--muted);
}
.mkt-card__chev svg { width: 20px; height: 20px; }

.mkt-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-top: var(--space-1);
}
.mkt-chip {
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--surface-muted, var(--line));
  border: 1px solid var(--line);
  color: var(--ink-2);
  font-size: 11px;
  line-height: 1.6;
}

.mkt-note {
  margin: var(--space-2) 0 0;
  font-size: 12px;
  color: var(--muted);
  text-align: center;
}

/* Loading skeleton — structure while the fetch runs, never a blank screen. */
.mkt-skel {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.mkt-skel__row {
  height: 96px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background: var(--surface);
  opacity: 0.6;
  animation: mkt-skel-pulse 1.4s ease-in-out infinite;
}
@keyframes mkt-skel-pulse {
  0%, 100% { opacity: 0.4; }
  50%      { opacity: 0.75; }
}

/* Empty / error / success — always carries the next action. */
.mkt-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  text-align: center;
  padding: var(--space-6) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.mkt-state__title {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  color: var(--ink);
}
.mkt-state__body {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
  max-width: 34ch;
}
.mkt-state__action { margin-top: var(--space-2); }

/* Profile page. */
.mkt-profile {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.mkt-hero {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.mkt-hero__avatar {
  width: 88px;
  height: 88px;
  border-radius: 50%;
  object-fit: cover;
  flex: 0 0 auto;
}
.mkt-hero__ident {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.mkt-hero__name {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  color: var(--ink);
}
.mkt-hero__city {
  font-size: 13px;
  color: var(--muted);
}

.mkt-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.mkt-section__title {
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}
.mkt-section__body {
  margin: 0;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink);
  white-space: pre-line;
}

.mkt-lead {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.mkt-lead__label {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: 13px;
  color: var(--muted);
}
.mkt-lead__msg {
  width: 100%;
  min-height: 84px;
  padding: var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface-bg, var(--surface));
  color: var(--ink);
  font: inherit;
  resize: vertical;
}
.mkt-lead__error {
  margin: 0;
  font-size: 13px;
  color: var(--danger, #c0392b);
}
.mkt-lead__book { width: 100%; }

@media (prefers-reduced-motion: reduce) {
  .mkt-card { transition: none; }
  .mkt-card:active { transform: none; }
  .mkt-skel__row { animation: none; opacity: 0.55; }
}

/* Settings Premium card — status + manage / upgrade. */
.premium-card__upgrade {
  width: 100%;
  margin-top: var(--space-2);
}
.premium-status {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.premium-status__line {
  margin: 0;
  font-weight: 600;
}
.premium-status__cancel {
  align-self: flex-start;
}

/* Return-from-Mollie verifying overlay. */
.premium-verify {
  position: fixed;
  inset: 0;
  z-index: 9300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  background: rgba(0, 0, 0, 0.55);
}
.premium-verify__card {
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--radius-sm);
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  text-align: center;
}
.premium-verify__spinner {
  width: 36px;
  height: 36px;
  border: 3px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: premium-spin var(--dur-loop) linear infinite;
}
.premium-verify__msg { margin: 0; }
@keyframes premium-spin { to { transform: rotate(360deg); } }

/* ADMIN-4-MANUAL-PROMPTS-PUSH — centered modal overlay for admin questions. */
.admin-prompt-overlay {
  position: fixed;
  inset: 0;
  z-index: 9100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  background: rgba(0, 0, 0, 0.55);
}
.admin-prompt-sheet {
  width: 100%;
  max-width: 520px;
  background: var(--surface-card);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  box-shadow: var(--elev-3);
}
.admin-prompt-sheet__question {
  margin: 0;
  font-size: 20px;
  line-height: 1.35;
  font-weight: 600;
}
.admin-prompt-sheet__answer {
  width: 100%;
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  resize: vertical;
}
.admin-prompt-sheet__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.admin-prompt-sheet__submit {
  flex: 1;
  padding: var(--space-3) var(--space-4);
  background: var(--accent);
  color: var(--on-accent);
  border: 0;
  border-radius: var(--radius-sm);
  font-weight: 600;
  cursor: pointer;
}
.admin-prompt-sheet__submit:disabled { opacity: 0.5; cursor: not-allowed; }
.admin-prompt-sheet__later {
  background: transparent;
  border: 0;
  color: var(--muted);
  padding: var(--space-3);
  cursor: pointer;
  text-decoration: underline;
}

/* ADMIN-8-PAYMENT-FLOW-POLISH-AND-FAQ — /beta/pay + /beta/confirm polish. */
.beta-pay { padding: var(--space-4); max-width: 480px; margin: 0 auto; }
.beta-pay-logo { display:block; width:96px; height:96px; margin:0 auto var(--space-3); }
.beta-pay-intro { margin: 0 0 var(--space-3); }
.beta-pay-wave { margin: 0 0 var(--space-2); }
.beta-pay-perks { padding-left: var(--space-4); margin: 0 0 var(--space-3); color: var(--ink); }
.beta-pay-perks li { margin-bottom: var(--space-1); }
.beta-pay-price { font-weight:600; margin: 0 0 var(--space-3); }
.beta-pay-testmode { background: var(--warn-bg, #fff8c4); color: var(--ink); border: 1px solid var(--warn-line, #e6c200); padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); margin-bottom: var(--space-3); }
.beta-pay-status { margin: var(--space-2) 0 0; }
.beta-pay-mailto { display:inline-block; margin-top: var(--space-2); color: var(--accent); }
.beta-pay-celebrate { text-align:center; padding-top: 25vh; }
.beta-pay-celebrate h1 { color: var(--accent); }

/* DONATE-FLOW — voluntary donation surface (/donate + /donate/confirm). */
.donate { padding: var(--space-4); max-width: 520px; margin: 0 auto; }
.donate-back-btn { background: transparent; color: var(--ink); padding: var(--space-1) var(--space-2); margin-bottom: var(--space-2); }
.donate-hero-img { width: 100%; max-height: 260px; object-fit: cover; object-position: center 30%; border-radius: var(--radius-lg); margin-bottom: 1rem; }
.donate-settings-cta { display: flex; align-items: center; gap: 0.875rem; width: 100%; padding: 1rem 1.25rem; background: var(--accent); color: var(--on-accent); border: none; border-radius: var(--radius-lg); cursor: pointer; text-align: left; transition: opacity var(--dur-fast) var(--ease-standard); }
.donate-settings-cta:active { opacity: 0.82; }
.donate-settings-cta svg { flex-shrink: 0; width: 1.75rem; height: 1.75rem; }
.donate-settings-cta__text { display: flex; flex-direction: column; gap: 0.2rem; flex: 1; }
.donate-settings-cta__label { font-weight: 700; font-size: 1rem; line-height: 1.2; }
.donate-settings-cta__sub { font-size: 0.8rem; opacity: 0.8; }
.donate-title { margin: 0 0 var(--space-2); color: var(--ink); }
.donate-subtitle { margin: 0 0 var(--space-3); }
.donate-feedback { display: flex; flex-direction: column; gap: var(--space-2); margin: var(--space-3) 0 var(--space-4); }
.donate-feedback__intro { color: var(--ink); font-size: 0.95em; line-height: 1.5; max-width: 32rem; margin: 0 0 var(--space-2); }
.donate-feedback__label { font-size: 0.85rem; color: var(--muted); font-weight: 500; }
.donate-feedback__input { width: 100%; padding: var(--space-2); border-radius: var(--radius-sm); border: 1px solid var(--line); background: var(--surface); color: var(--ink); font-size: 1rem; line-height: 1.4; resize: vertical; font-family: inherit; }
.donate-feedback__input:focus { outline: none; border-color: var(--accent); }
.donate-feedback__submit { align-self: flex-start; }
.donate-feedback__status { font-size: 0.8rem; color: var(--muted); font-style: italic; min-height: 1.2em; margin: 0; }
.donate__hero-stats { font-size: 0.875rem; color: var(--muted); margin-top: 0.75rem; line-height: 1.5; }
.donate-testmode { background: var(--warn-bg, var(--surface-card)); color: var(--ink); border: 1px solid var(--warn-line, var(--line)); padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); margin-bottom: var(--space-3); }
.donate-tier { padding: 1rem; margin-bottom: 0.75rem; display: flex; flex-direction: column; gap: 0.5rem; border-radius: var(--radius-lg); }
.donate-tier__product-img { display: block; width: 96px; height: 96px; object-fit: contain; margin: 0 auto 0.25rem; }
.donate-tier__head { display: flex; align-items: center; gap: var(--space-2); }
.donate-tier__label { font-weight: 600; color: var(--ink); }
.donate-tier__desc { font-size: 0.85rem; color: var(--muted); margin: 0; }
.donate-amount-input { width: 100%; padding: var(--space-2); border-radius: var(--radius-sm); border: 1px solid var(--line); background: var(--surface); color: var(--ink); font-size: 1rem; }
.donate-tier__cta { background: var(--accent); color: var(--on-accent); border: 0; padding: 0.6rem 1rem; border-radius: var(--radius-sm); font-weight: 600; cursor: pointer; }
.donate-tier__cta[disabled] { opacity: 0.6; }
.donate-confirm { text-align: center; padding: 1.5rem; }
.donate-confirm__img { width: 100%; border-radius: var(--radius-lg); margin-bottom: 1.5rem; }
.donate-confirm__headline { font-size: 1.4rem; font-weight: 700; margin-bottom: 0.75rem; color: var(--ink); }
.donate-confirm__body { color: var(--muted); margin-bottom: 1.5rem; }
.donate-confirm__back { background: var(--accent); color: var(--on-accent); border: 0; padding: 0.7rem 1.2rem; border-radius: var(--radius-sm); font-weight: 600; cursor: pointer; }

/* ADMIN-8 — dashboard summary cards row. */
.admin-summary-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; margin-bottom: 16px; }
.admin-summary-card { background: var(--surface-card, #fff); border: 1px solid var(--line, #e5e7eb); border-radius: var(--radius-md); padding: 14px; text-decoration: none; color: inherit; display: block; }
.admin-summary-card:hover { border-color: var(--accent, #c3e600); }
.admin-summary-card .label { font-size: 12px; color: var(--muted, #777); text-transform: uppercase; letter-spacing: 0.04em; }
.admin-summary-card .value { font-size: 28px; font-weight: 700; margin-top: 4px; }
.admin-summary-card .sub { font-size: 11px; color: var(--muted, #777); margin-top: 4px; }

/* USER-SKILLS-SYSTEM-PHASE-1 — My Skills settings list, editor modal, and the
   chat slash-trigger autocomplete menu. Tokens only (no hex literals). */
.settings-skills__list { list-style: none; margin: 0; padding: 0; }
.settings-skills__item {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
}
.settings-skills__item-main { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.settings-skills__item-name { font-size: 15px; font-weight: 600; color: var(--ink); }
.settings-skills__item-trigger { font-size: 13px; color: var(--accent); font-family: ui-monospace, monospace; }
.settings-skills__item-desc { font-size: 13px; color: var(--muted); }
.settings-skills__item-actions { flex: 0 0 auto; display: flex; gap: var(--space-2); }
.settings-skills__edit,
.settings-skills__delete {
  -webkit-appearance: none; appearance: none; background: transparent;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 6px 12px; font: inherit; font-size: 13px; font-weight: 600;
  color: var(--ink); cursor: pointer;
}
.settings-skills__delete { color: var(--danger); border-color: var(--danger); }
.settings-skills__add {
  -webkit-appearance: none; appearance: none; width: 100%;
  background: transparent; border: 0; padding: var(--space-3) var(--space-4);
  font: inherit; font-size: 15px; font-weight: 600; color: var(--accent);
  text-align: left; cursor: pointer;
}

.skills-editor__overlay {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(0, 0, 0, 0.5);
  display: flex; align-items: flex-end; justify-content: center;
}
.skills-editor {
  width: 100%; max-width: 560px; max-height: 90vh; overflow-y: auto;
  background: var(--surface-card); border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  padding: var(--space-5) var(--space-4)
    calc(var(--space-5) + var(--safe-bottom));
  display: flex; flex-direction: column; gap: var(--space-3);
}
@media (min-width: 600px) {
  .skills-editor__overlay { align-items: center; }
  .skills-editor { border-radius: var(--radius-lg); }
}
.skills-editor__title { margin: 0; font-size: 18px; font-weight: 700; color: var(--ink); }
.skills-editor__examples { display: flex; flex-direction: column; gap: var(--space-2); }
.skills-editor__examples-label { margin: 0; font-size: 12px; color: var(--section-label); text-transform: uppercase; letter-spacing: 0.05em; }
.skills-editor__examples-row { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.skills-editor__example {
  -webkit-appearance: none; appearance: none;
  background: transparent; border: 1px solid var(--line); border-radius: var(--radius-pill);
  padding: 6px 14px; font: inherit; font-size: 13px; color: var(--ink); cursor: pointer;
}
.skills-editor__field { display: flex; flex-direction: column; gap: 4px; }
.skills-editor__field-label { font-size: 13px; font-weight: 600; color: var(--ink); }
.skills-editor__field-input {
  width: 100%; box-sizing: border-box;
  background: var(--surface-app, transparent);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 10px 12px; font: inherit; font-size: 15px; color: var(--ink);
}
textarea.skills-editor__field-input { resize: vertical; min-height: 120px; }
.skills-editor__error { margin: 0; font-size: 13px; color: var(--danger); }
.skills-editor__preview {
  margin: 0; white-space: pre-wrap; word-break: break-word;
  background: var(--surface-app, transparent); border: 1px solid var(--line);
  border-radius: var(--radius-sm); padding: 12px; font-size: 13px; color: var(--muted);
  max-height: 240px; overflow-y: auto;
}
.skills-editor__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.skills-editor__preview-btn,
.skills-editor__cancel {
  -webkit-appearance: none; appearance: none; background: transparent;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 10px 18px; font: inherit; font-weight: 600; color: var(--ink); cursor: pointer;
}
.skills-editor__save {
  -webkit-appearance: none; appearance: none; margin-left: auto;
  background: var(--accent); color: var(--on-accent); border: 0;
  border-radius: var(--radius-sm); padding: 10px 18px; font: inherit; font-weight: 600; cursor: pointer;
}
.skills-editor__save:disabled { opacity: 0.6; cursor: default; }

/* Chat slash-trigger autocomplete — floats above the input row. */
.chat__skills-menu {
  position: absolute; left: var(--space-3); right: var(--space-3); bottom: 100%;
  margin-bottom: var(--space-2); z-index: 60;
  background: var(--surface-card); border: 1px solid var(--line);
  border-radius: var(--radius-lg); overflow: hidden;
  box-shadow: var(--elev-2);
}
.chat__skills-menu[hidden] { display: none; }
.chat__skill-item {
  display: flex; flex-direction: column; gap: 2px;
  padding: 10px var(--space-3); cursor: pointer;
}
.chat__skill-item + .chat__skill-item { border-top: 1px solid var(--line); }
.chat__skill-item.is-active { background: var(--accent); }
.chat__skill-item.is-active .chat__skill-name,
.chat__skill-item.is-active .chat__skill-trigger,
.chat__skill-item.is-active .chat__skill-desc { color: var(--on-accent); }
.chat__skill-name { font-size: 14px; font-weight: 600; color: var(--ink); }
.chat__skill-trigger { font-size: 12px; color: var(--accent); font-family: ui-monospace, monospace; }
.chat__skill-desc { font-size: 12px; color: var(--muted); }

/* STRENGTH-REST-TIMER-WORKOUT-DONE-BUTTON-USE-TARGET — primary action under the
   TARGET pill in the fullscreen rest-timer overlay; commits target as rest. */
.rt-full__done-btn {
  margin-top: var(--space-3, 12px);
  padding: var(--space-3, 12px) var(--space-4, 16px);
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  border-radius: var(--radius-lg);
  font-family: inherit;
  font-size: 1em;
  font-weight: 600;
  min-height: 48px;
  cursor: pointer;
}
.rt-full__done-btn:active { opacity: 0.85; }

/* ─────────────────────────────────────────────────────────────────────────
 * DIARY-MOOD-CHAT-PREMIUM-BEST-SELF-JOURNAL — premium AI mood check-in.
 * Button on the Check-in mood section + the guided-conversation sheet.
 * Tokens only, no hardcoded hex (css-hex-lint rule).
 * ───────────────────────────────────────────────────────────────────────── */
.mood-ai-chat-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  margin: 10px 0 8px;
  padding: 12px 14px;
  background: var(--accent-subtle);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 15px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.mood-ai-chat-btn:active { opacity: 0.85; }
.mood-ai-chat-btn__icon { display: inline-flex; flex: 0 0 auto; }
.mood-ai-chat-btn__icon svg { width: 20px; height: 20px; color: var(--accent); }
.mood-ai-chat-btn__label { flex: 1 1 auto; }
.mood-ai-chat-btn__hint { color: var(--muted); font-weight: 500; font-size: 12px; }
.mood-ai-chat-btn--locked {
  background: var(--surface-muted);
  color: var(--muted);
  border-style: dashed;
}
.mood-ai-chat-btn--locked .mood-ai-chat-btn__icon svg { color: var(--muted); }

/* Paywall prompt (non-premium tap) */
.mood-chat-paywall {
  position: fixed; inset: 0; z-index: 1200;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.55);
  padding: 24px;
}
.mood-chat-paywall__card {
  width: 100%; max-width: 360px;
  background: var(--surface-card);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 20px;
}
.mood-chat-paywall__title { margin: 0 0 8px; font-size: 18px; color: var(--ink); }
.mood-chat-paywall__body { margin: 0 0 18px; color: var(--ink-2); font-size: 14px; line-height: 1.45; }
.mood-chat-paywall__actions { display: flex; gap: 10px; justify-content: flex-end; }
.mood-chat-paywall__cancel,
.mood-chat-paywall__cta {
  padding: 10px 16px; border-radius: var(--radius-sm); font-weight: 600; font-size: 14px; cursor: pointer;
}
.mood-chat-paywall__cancel { background: transparent; color: var(--muted); border: 1px solid var(--line); }
.mood-chat-paywall__cta { background: var(--accent); color: var(--on-accent); border: none; }

/* Conversation sheet */
.mood-chat-overlay {
  position: fixed; inset: 0; z-index: 1200;
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(0, 0, 0, 0.55);
}
.mood-chat-sheet {
  width: 100%; max-width: 560px;
  max-height: 88vh;
  display: flex; flex-direction: column;
  background: var(--surface);
  border-top-left-radius: var(--radius-lg); border-top-right-radius: var(--radius-lg);
  border: 1px solid var(--line);
  overflow: hidden;
}
.mood-chat-sheet__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 16px;
  border-bottom: 1px solid var(--line);
}
.mood-chat-sheet__head h2 { margin: 0; font-size: 16px; color: var(--ink); }
.mood-chat-sheet__close {
  display: inline-flex; padding: 6px; background: transparent; border: none;
  color: var(--muted); cursor: pointer;
}
.mood-chat-sheet__close svg { width: 20px; height: 20px; }
.mood-chat-sheet__loading { padding: 32px 20px; text-align: center; color: var(--ink-2); font-size: 14px; }
.mood-chat-sheet__progress { padding: 10px 16px 0; color: var(--muted); font-size: 12px; font-weight: 600; }
.mood-chat-sheet__messages {
  flex: 1 1 auto; overflow-y: auto;
  padding: 12px 16px;
  display: flex; flex-direction: column; gap: 8px;
}
.mood-chat-msg {
  max-width: 82%;
  padding: 10px 12px;
  border-radius: var(--radius-lg);
  font-size: 14px; line-height: 1.45;
  white-space: pre-wrap;
}
.mood-chat-msg--ai { align-self: flex-start; background: var(--surface-card); color: var(--ink); }
.mood-chat-msg--user { align-self: flex-end; background: var(--accent); color: var(--on-accent); }
.mood-chat-sheet__emotions {
  padding: 8px 16px;
  border-top: 1px solid var(--line);
  font-size: 13px; color: var(--ink-2);
}
.mood-chat-sheet__emotions-lbl { font-weight: 600; color: var(--ink); }
.mood-chat-sheet__emotions small { display: block; margin-top: 4px; color: var(--muted); font-size: 11px; }
/* Two axes, two rows: what the user FELT vs what the answer was ABOUT. The row
   separator is what tells the user the second set came from their own words. */
.mood-chat-sheet__tagrow { margin-bottom: 4px; }
.mood-chat-sheet__tagrow + .mood-chat-sheet__tagrow {
  padding-top: 4px; border-top: 1px dashed var(--line);
}
.mood-chat-chip {
  display: inline-block;
  padding: 2px 8px; margin: 2px 2px 0 0;
  background: var(--accent-subtle); color: var(--accent);
  border-radius: var(--radius-sm); font-size: 12px; font-weight: 600;
}
.mood-chat-chip--topic {
  background: transparent; color: var(--ink-2);
  border: 1px solid var(--line);
}
.mood-chat-sheet__input {
  display: flex; gap: 8px; align-items: flex-end;
  padding: 12px 16px;
  border-top: 1px solid var(--line);
}
.mood-chat-sheet__input textarea {
  flex: 1 1 auto;
  resize: none;
  padding: 10px 12px;
  background: var(--surface-card);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: inherit; font-size: 14px;
}
.mood-chat-sheet__input button {
  flex: 0 0 auto;
  padding: 10px 16px;
  background: var(--accent); color: var(--on-accent);
  border: none; border-radius: var(--radius-sm);
  font-weight: 600; font-size: 14px; cursor: pointer;
}
.mood-chat-sheet__input button:disabled { opacity: 0.6; cursor: default; }
/* The dictation mic is a round icon control, not an accent pill — it has to beat
   the generic `.mood-chat-sheet__input button` rule above on specificity. */
.mood-chat-sheet__input .mood-chat-sheet__mic {
  width: 40px; height: 40px; padding: 0; border-radius: 50%;
  background: var(--accent-subtle); color: var(--ink);
  border: 1px solid var(--line);
  display: inline-flex; align-items: center; justify-content: center;
  -webkit-tap-highlight-color: transparent;
}
.mood-chat-sheet__input .mood-chat-sheet__mic svg { width: 20px; height: 20px; display: block; }
.mood-chat-sheet__input .mood-chat-sheet__mic.fz-dict-mic--recording {
  background: var(--danger, #e5484d); color: var(--on-accent); border-color: transparent;
}
.mood-chat-sheet__input .mood-chat-sheet__mic.fz-dict-mic--busy {
  background: var(--accent); color: var(--on-accent); border-color: transparent;
}
/* NUTRITION-ADHERENCE-EVENING-QUESTION-AND-DAYSTAMP — de ja/nee-tikrij boven de invoer.
   Tekst-only: geen emoji, geen icoon (harde projectregel, net als de maat-chips). */
.mood-chat-sheet__qr { display: flex; gap: var(--space-2); margin: 0; padding: 0 16px 12px; }
.mood-chat-qr {
  flex: 1 1 0; padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--ink); font: inherit; cursor: pointer;
}
.mood-chat-qr:hover:not(:disabled) { border-color: var(--accent); }
.mood-chat-qr:disabled { opacity: .5; cursor: default; }

.mood-chat-sheet__choice { padding: 20px 16px; }
.mood-chat-sheet__choice p { margin: 0 0 14px; color: var(--ink-2); font-size: 14px; }
.mood-chat-sheet__choice-btns { display: flex; gap: 10px; }
.mood-chat-sheet__choice-btns button {
  flex: 1 1 0; padding: 12px; cursor: pointer;
  background: var(--accent-subtle); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  font-weight: 600; font-size: 14px;
}
.mood-chat-sheet__done { padding: 14px 16px; border-top: 1px solid var(--line); }
.mood-chat-sheet__mood { display: block; margin-bottom: 14px; }
.mood-chat-sheet__mood-lbl {
  display: block; margin-bottom: 8px;
  color: var(--ink); font-size: 14px; font-weight: 600;
}
.mood-chat-sheet__mood input[type="range"] {
  width: calc(100% - 40px); vertical-align: middle; accent-color: var(--accent);
}
.mood-chat-sheet__mood-val {
  display: inline-block; width: 32px; margin-left: 8px;
  text-align: right; color: var(--ink); font-size: 15px; font-weight: 700;
}
.mood-chat-sheet__err {
  margin: 0 0 10px; color: var(--danger, #e5484d); font-size: 13px;
}
.mood-chat-sheet__done-btn {
  width: 100%; padding: 12px;
  background: var(--accent); color: var(--on-accent);
  border: none; border-radius: var(--radius-sm);
  font-weight: 600; font-size: 15px; cursor: pointer;
}
.mood-chat-sheet__done-btn:disabled { opacity: 0.6; cursor: default; }

/* COACH-CLIENT-SIGNUP-LINK-GENERATE-AND-SHARE (v0.9.869) — coach dialog + public landing.
   Accent CTAs use var(--on-accent) for contrast (lint rule g). No hex literals. */
.invite-gen { margin: var(--space-4) 0; }
.invite-gen__btn {
  width: 100%; padding: var(--space-3) var(--space-4);
  background: transparent; color: var(--accent);
  border: 1px solid var(--accent); border-radius: var(--radius-sm);
  font-weight: 600; font-size: 15px; cursor: pointer;
}
.invite-gen__btn:hover { background: var(--surface); }

.btn-accent {
  width: 100%; padding: var(--space-3) var(--space-4);
  background: var(--accent); color: var(--on-accent);
  border: 1px solid var(--accent); border-radius: var(--radius-sm);
  font-weight: 700; font-size: 15px; cursor: pointer;
}
.btn-accent:hover { opacity: 0.9; }
.btn-accent:disabled { opacity: 0.4; cursor: not-allowed; }

.invite-dialog { max-width: 360px; display: flex; flex-direction: column; gap: var(--space-3); }
.invite-dialog__h { margin: 0; font-size: 18px; color: var(--ink); }
.invite-dialog__hint { margin: 0; color: var(--muted); font-size: 14px; }
.invite-dialog__note { color: var(--muted); font-size: 12px; }
.invite-dialog__in {
  width: 100%; padding: var(--space-3); box-sizing: border-box;
  background: var(--bg); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: 15px;
}
.invite-dialog__opt {
  display: flex; align-items: center; gap: var(--space-2);
  color: var(--ink); font-size: 15px; cursor: pointer;
}
.invite-dialog__opt input { accent-color: var(--accent); width: 18px; height: 18px; }
.invite-link-box {
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: var(--space-3); overflow-wrap: anywhere;
}
.invite-link-box code { color: var(--accent); font-size: 13px; }

/* Public sign-up landing (/invite/<token>) — standalone page, loads app.css. */
.invite-landing-body {
  margin: 0; min-height: 100vh; background: var(--bg); color: var(--ink);
  min-height: 100dvh; /* dvh wins on modern engines, 100vh above stays fallback */
  display: flex; align-items: center; justify-content: center; padding: var(--space-5);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.invite-landing, .invite-landing__msg {
  width: 100%; max-width: 380px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm); padding: var(--space-5);
}
.invite-landing__head { text-align: center; margin-bottom: var(--space-4); }
.invite-landing__emblem { width: 64px; height: 64px; object-fit: contain; }
.invite-landing__head h1 { font-size: 20px; margin: var(--space-3) 0 var(--space-2); }
.invite-landing__head p { color: var(--muted); font-size: 14px; margin: 0; }
.invite-signup-form { display: flex; flex-direction: column; gap: var(--space-3); }
.invite-signup-form label { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: var(--muted); }
.invite-signup-form input {
  padding: var(--space-3); box-sizing: border-box;
  background: var(--bg); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: 16px;
}
.invite-landing__err { color: var(--danger); font-size: 14px; margin: 0; }
.invite-landing footer { margin-top: var(--space-4); text-align: center; }
.invite-landing footer small, .invite-landing__msg p { color: var(--muted); font-size: 12px; }
.invite-landing__msg { text-align: center; }
.invite-landing__msg a { color: var(--accent); }
.invite-landing__alt { margin: var(--space-2) 0 0; text-align: center; font-size: 13px; }
.invite-landing__alt a { color: var(--accent); }

/* COACH-SELF-SERVICE-INTAKE-LINK — one-question-per-step intake wizard shown
   before the account screen when the invite requires an intake. */
.invite-wizard { display: flex; flex-direction: column; gap: var(--space-3); }
.invite-wizard__progress { font-size: 12px; color: var(--muted); text-align: center; }
.invite-wizard__bar { height: 4px; border-radius: var(--radius-pill); background: var(--line); overflow: hidden; }
.invite-wizard__bar > span { display: block; height: 100%; background: var(--accent); transition: width var(--dur-base) var(--ease-standard); }
.invite-wizard__q { font-size: 16px; color: var(--ink); font-weight: 600; }
.invite-wizard__opts { display: flex; flex-direction: column; gap: var(--space-2); }
.wiz-opt {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3); box-sizing: border-box;
  background: var(--bg); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  font-size: 15px; cursor: pointer;
}
.wiz-opt input { accent-color: var(--accent); width: 18px; height: 18px; }
.wiz-opt:has(input:checked) { border-color: var(--accent); }
.invite-wizard input[type="text"], .invite-wizard input[type="number"],
.invite-wizard input[type="date"], .invite-wizard textarea {
  padding: var(--space-3); box-sizing: border-box; width: 100%;
  background: var(--bg); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: 16px;
}
.invite-wizard textarea { min-height: 96px; resize: vertical; font-family: inherit; }
.invite-wizard__other { margin-top: var(--space-2); }
.invite-wizard__nav { display: flex; gap: var(--space-3); margin-top: var(--space-2); }
.invite-wizard__nav .btn-accent { flex: 1; }
.invite-wizard .btn-ghost {
  padding: var(--space-3) var(--space-4); box-sizing: border-box;
  background: transparent; color: var(--muted);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  font-size: 15px; cursor: pointer;
}

/* NUTRITION-PHOTO-ESTIMATE-CHOOSER-SHEET-CAMERA-OR-GALLERY (2026-06-24) */
.photo-estimate-chooser__hint {
  text-align: center;
  color: var(--muted);
  margin-bottom: var(--space-3);
}
.photo-estimate-chooser__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-2);
  min-height: 48px;
}

/* DESKTOP-VIEW-FOUNDATION-RESPONSIVE-SHELL-AND-SIDEBAR — additive desktop-only layer; mobile cascade untouched. */
@media (min-width: 1024px) and (pointer: fine) and (hover: hover) {
  /* NUTD-V2-F1 — desktop app-shell: a content column (slim topbar strip + main)
     that WRAPS above the right nav rail (80px compact icon rail since F7).
     flex-wrap keeps the flat DOM (topbar, banners, main, tabbar) intact: every
     child except .app-main + .tabbar is a full-width row, so the topbar renders
     as a top strip — never a broken side column (the pre-fix bug) — and
     .app-main + .tabbar share the next line. */
  html:not([data-touch-tablet]) .app-shell {
    flex-direction: row;
    flex-wrap: wrap;
    align-content: flex-start;
  }
  html:not([data-touch-tablet]) .app-shell > * { flex-basis: 100%; }
  html:not([data-touch-tablet]) .app-shell > .app-main {
    /* basis 0 (not auto/max-content) so app-main never pushes the 80px rail
       onto its own wrapped line — it just fills the space left of the rail. */
    flex: 1 1 0;
    min-width: 0;
  }
  html:not([data-touch-tablet]) .app-shell > .tabbar { flex: 0 0 80px; }

  /* Slim full-width top strip: title left, any topbar actions right (base
     .topbar already flex/space-between/border-bottom). Drop the mobile
     safe-area top padding so it's a true ~56px strip, not a tall header. */
  html:not([data-touch-tablet]) .topbar {
    height: 56px;
    min-height: 56px;
    padding: 0 var(--space-6);
  }
  html:not([data-touch-tablet]) .topbar__title { letter-spacing: 0.08em; }

  /* NUTD-V2-F7-COMPACT-ICON-RAIL — M3-style compact icon rail: an 80px column of
     icon-only cells (was a 240px icon+label rail). Thinner rail = more content
     width + calmer surface ('rust'). Labels stay in the DOM for a11y and reappear
     as a hover/focus tooltip; the active tab keeps a tonal M3 pill. Desktop MQ
     only — the mobile + tablet bottom-bar cascade is pixel-identical. */
  html:not([data-touch-tablet]) .tabbar {
    position: sticky;
    top: 0;
    left: auto;
    right: auto;
    bottom: auto;
    height: 100dvh;
    width: 80px;
    flex: 0 0 80px;
    flex-direction: column;
    justify-content: flex-start;
    align-items: stretch;
    gap: var(--space-3);
    padding: var(--space-5) var(--space-2);
    border-top: 0;
    border-left: 1px solid var(--line);
    z-index: 50;
    align-self: flex-start;
  }

  html:not([data-touch-tablet]) .tabbar__btn {
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 0;
    flex: 0 0 auto;
    padding: var(--space-2) 0;
    border-radius: var(--radius-lg);
    text-transform: none;
    letter-spacing: 0.01em;
    /* comfortable centered hit area, well above the 48px M3 floor */
    min-height: 56px;
    /* let the hover tooltip escape the cell — the base rule clips overflow */
    overflow: visible;
  }

  /* Label kept in the DOM for screen-readers (NOT display:none, so aria stays
     intact) but visually pulled out of the icon cell to opacity:0; revealed as a
     floating tooltip pill to the LEFT of the rail on hover/focus-visible. Hover
     can't fire on touch, and this whole MQ already excludes touch (pointer:fine +
     hover:hover), so the tooltip never appears on a tablet. */
  html:not([data-touch-tablet]) .tabbar__btn span {
    position: absolute;
    right: calc(100% + var(--space-2));
    top: 50%;
    transform: translateY(-50%);
    max-width: none;
    width: max-content;
    padding: 4px var(--space-2);
    border-radius: var(--radius-sm);
    background: var(--ink);
    color: var(--surface);
    font-size: 12px;
    line-height: 1.2;
    letter-spacing: 0.01em;
    text-transform: none;
    white-space: nowrap;
    box-shadow: var(--elev-1);
    opacity: 0;
    pointer-events: none;
    z-index: 60;
    transition: opacity var(--dur-fast) var(--ease-standard);
  }
  html:not([data-touch-tablet]) .tabbar__btn:hover span,
  html:not([data-touch-tablet]) .tabbar__btn:focus-visible span { opacity: 1; }

  html:not([data-touch-tablet]) .tabbar__btn:hover { background: var(--surface-muted); }

  /* M3 active indicator — tonal pill behind the centered icon. */
  html:not([data-touch-tablet]) .tabbar__btn[aria-current="page"] { background: var(--accent-subtle); }

  html:not([data-touch-tablet]) .tabbar__btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }

  /* Re-anchor the count / unseen-dot badge onto the centered icon of the 80px
     cell (the mobile clamp math assumed a flex:1 bottom-bar button). */
  html:not([data-touch-tablet]) .tabbar__badge {
    top: 6px;
    right: 16px;
  }

  html:not([data-touch-tablet]) .app-main {
    max-width: 1680px;
    width: 100%;
    margin: 0 auto;
    padding-left: var(--space-6);
    padding-right: var(--space-6);
    padding-bottom: var(--space-6);
    min-width: 0;
    /* Fill the viewport below the 56px topbar strip so there's no dead
       letterbox at the bottom of short pages. */
    min-height: calc(100dvh - 56px);
  }

  html:not([data-touch-tablet]) .strength-iframe-wrap { height: 100dvh; }
  html:not([data-touch-tablet]) body.tab-strength .app-main { padding: 0; max-width: none; }
  html:not([data-touch-tablet]) .strength-iframe-area { overflow: hidden; }

  html:not([data-touch-tablet]) .nut-fab {
    right: var(--space-6);
    bottom: var(--space-6);
    z-index: 45;
  }

  /* NUTRITION-DESKTOP — standalone master/detail page (Log tab). */
  html:not([data-touch-tablet]) .nutd { width: 100%; min-width: 0; }
  /* NUTD-V2-F1 — keep hydration quick-add labels ('+250 ml') on one line in the
     narrower desktop summary pane; the flex:1 buttons wrapped otherwise. */
  html:not([data-touch-tablet]) .hydration-btn { white-space: nowrap; }
  html:not([data-touch-tablet]) .nutd-grid {
    display: grid;
    grid-template-columns: 220px minmax(0, 1fr);
    gap: var(--space-5);
    align-items: start;
  }
  html:not([data-touch-tablet]) .nutd-rail {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    border-right: 1px solid var(--line);
    padding-right: var(--space-4);
  }
  html:not([data-touch-tablet]) .nutd-rail__today {
    text-align: left;
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    padding: var(--space-2) var(--space-3);
    margin-bottom: var(--space-2);
    color: var(--accent);
    cursor: pointer;
    font: inherit;
    font-weight: 600;
  }
  html:not([data-touch-tablet]) .nutd-rail__today:hover { background: var(--surface-muted); }
  html:not([data-touch-tablet]) .nutd-rail__day {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-2);
    text-align: left;
    background: transparent;
    border: 0;
    border-left: 2px solid transparent;
    border-radius: var(--radius-sm);
    padding: var(--space-2) var(--space-3);
    color: var(--ink-2);
    cursor: pointer;
    font: inherit;
  }
  html:not([data-touch-tablet]) .nutd-rail__day-wd { font-weight: 600; text-transform: capitalize; }
  html:not([data-touch-tablet]) .nutd-rail__day-dt { color: var(--muted); font-size: 12px; }
  html:not([data-touch-tablet]) .nutd-rail__day.is-active .nutd-rail__day-dt { color: var(--ink-2); }
  html:not([data-touch-tablet]) .nutd-rail__day:hover { background: var(--surface-muted); color: var(--ink); }
  html:not([data-touch-tablet]) .nutd-rail__day.is-active {
    background: var(--surface-muted);
    color: var(--ink);
    border-left: 2px solid var(--accent);
  }
  html:not([data-touch-tablet]) .nutd-detail { min-width: 0; }
  html:not([data-touch-tablet]) .nutd-panes {
    display: grid;
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
    gap: var(--space-5);
    align-items: start;
  }
  html:not([data-touch-tablet]) .nutd-pane { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
  html:not([data-touch-tablet]) .nutd-pane--summary { grid-column: 2; grid-row: 1; }
  html:not([data-touch-tablet]) .nutd-pane--meals { grid-column: 1; grid-row: 1 / span 2; }

  /* NUTD-V2-F3 — meals become a comfortable, scannable timeline on the laptop.
     More breathing room between cards; entry rows read as aligned table-like
     rows with kcal locked into its own right-aligned column; the touch swipe
     tray is retired in favour of hover/keyboard-revealed compact action buttons.
     Desktop-only branch (inside the min-width/hover MQ) — mobile keeps the touch
     swipe tray pixel-identical. Tokens only. */
  html:not([data-touch-tablet]) .nutd-pane--meals .nut-meals { gap: var(--space-4); }
  html:not([data-touch-tablet]) .nutd-pane--meals .nut-meal__head { padding: var(--space-4); }
  html:not([data-touch-tablet]) .nutd-pane--meals .nut-entry { padding: var(--space-3) var(--space-4); }
  /* kcal locked to a right-aligned column so numbers line up down the card. */
  html:not([data-touch-tablet]) .nutd-pane--meals .nut-entry__kcal { min-width: 3.5em; text-align: right; }
  /* Subtle M3 state-layer over the whole row while pointer or keyboard is on it. */
  html:not([data-touch-tablet]) .nutd-pane--meals .nut-entry-wrap:hover .nut-entry,
  html:not([data-touch-tablet]) .nutd-pane--meals .nut-entry-wrap:focus-within .nut-entry {
    background: var(--surface-muted);
  }
  /* Retire the full-height touch tray: compact icon buttons pinned right,
     opacity 0 by default (kept in the tab order — NOT display:none / visibility:
     hidden), revealed on row :hover and :focus-within for keyboard a11y parity.
     pointer-events gate so the invisible cluster never blocks the kcal beneath. */
  html:not([data-touch-tablet]) .nutd-pane--meals .nut-entry-actions {
    inset: 0 var(--space-2) 0 auto;
    align-items: center;
    gap: var(--space-1);
    padding-left: var(--space-5);
    background: linear-gradient(to right, transparent, var(--surface-muted) var(--space-5));
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--dur-fast) var(--ease-standard);
  }
  html:not([data-touch-tablet]) .nutd-pane--meals .nut-entry-wrap:hover .nut-entry-actions,
  html:not([data-touch-tablet]) .nutd-pane--meals .nut-entry-wrap:focus-within .nut-entry-actions {
    opacity: 1;
    pointer-events: auto;
  }
  html:not([data-touch-tablet]) .nutd-pane--meals .nut-entry-act {
    width: auto; min-height: 32px; padding: 0 var(--space-2);
    background: none; color: var(--muted); border-radius: var(--radius-md);
  }
  html:not([data-touch-tablet]) .nutd-pane--meals .nut-entry-act:hover { background: var(--surface); color: var(--ink); }
  html:not([data-touch-tablet]) .nutd-pane--meals .nut-entry-act--del:hover { color: var(--danger); }
  html:not([data-touch-tablet]) .nutd-pane--meals .nut-entry-act__lbl { display: none; }
  /* NUTD-V2-F2 — full-width hero dashboard band: elevated tonal card with three
     zones (calorie ring · macro bars · hydration). Desktop-only; tokens only. */
  html:not([data-touch-tablet]) .nutd-hero {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) minmax(14rem, 20rem);
    gap: var(--space-5);
    align-items: center;
    padding: var(--space-5);
    margin-bottom: var(--space-5);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--elev-1);
  }
  html:not([data-touch-tablet]) .nutd-hero__ring {
    position: relative;
    width: 180px;
    height: 180px;
    justify-self: center;
  }
  html:not([data-touch-tablet]) .nutd-ring { display: block; transform: rotate(-90deg); }
  html:not([data-touch-tablet]) .nutd-ring__track { fill: none; stroke: var(--line); stroke-width: 14; }
  html:not([data-touch-tablet]) .nutd-ring__prog {
    fill: none;
    stroke: var(--accent);
    stroke-width: 14;
    stroke-linecap: round;
  }
  html:not([data-touch-tablet]) .nutd-ring.is-over .nutd-ring__prog { stroke: var(--warning); }
  html:not([data-touch-tablet]) .nutd-ring__center {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-1);
    pointer-events: none;
  }
  html:not([data-touch-tablet]) .nutd-ring__num {
    font-size: var(--text-headline);
    line-height: 1;
    font-weight: 700;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
  }
  html:not([data-touch-tablet]) .nutd-ring__lbl {
    font-size: var(--text-label);
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
  }
  html:not([data-touch-tablet]) .nutd-hero__macros {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    min-width: 0;
  }
  html:not([data-touch-tablet]) .nutd-macro { display: flex; flex-direction: column; gap: var(--space-1); }
  html:not([data-touch-tablet]) .nutd-macro__top {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-2);
  }
  html:not([data-touch-tablet]) .nutd-macro__lbl { font-size: var(--text-body); font-weight: 600; color: var(--ink); }
  html:not([data-touch-tablet]) .nutd-macro__val { font-size: var(--text-body-sm); color: var(--ink-2); font-variant-numeric: tabular-nums; }
  html:not([data-touch-tablet]) .nutd-macro__rem { font-size: var(--text-label); color: var(--muted); }
  html:not([data-touch-tablet]) .nutd-hero__hydration { min-width: 0; }

  /* NUTD-V2-F4 — week-insights card (7-day kcal bar chart + stats row) and the
     day-rail logged/achieved dots. Desktop-only; tokens only. */
  html:not([data-touch-tablet]) .nutd-insights {
    display: flex; flex-direction: column; gap: var(--space-3);
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--radius-lg); padding: var(--space-4) var(--space-5);
  }
  html:not([data-touch-tablet]) .nutd-insights__title {
    font-size: var(--text-label); font-weight: 600; color: var(--muted);
    text-transform: uppercase; letter-spacing: 0.04em;
  }
  html:not([data-touch-tablet]) .nutd-insights__empty {
    font-size: var(--text-body-sm); color: var(--muted);
    padding: var(--space-4) 0; text-align: center;
  }
  /* NUTD-V2-F8 — week-glance is now the full-width top band: title + macro
     segmented switcher on one header row, a taller detailed chart, and a stats
     row. Reuses the .set-cardio-seg tile idiom with tokens; desktop-only. */
  html:not([data-touch-tablet]) .nutd-week__head {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-4); flex-wrap: wrap;
  }
  html:not([data-touch-tablet]) .nutd-week__body {
    display: flex; flex-direction: column; gap: var(--space-3);
  }
  html:not([data-touch-tablet]) .nutd-week-seg {
    display: flex; gap: 4px; flex-wrap: wrap;
    background: var(--surface-muted); border: 1px solid var(--line);
    border-radius: var(--radius-pill); padding: 3px;
  }
  html:not([data-touch-tablet]) .nutd-week-seg__tile {
    padding: 6px 14px; min-width: 0; border: 0; background: transparent;
    color: var(--ink-2); font: inherit; font-size: var(--text-body-sm); font-weight: 600;
    border-radius: var(--radius-pill); cursor: pointer;
    transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
  }
  html:not([data-touch-tablet]) .nutd-week-seg__tile.is-on {
    background: var(--accent); color: var(--on-accent);
  }
  html:not([data-touch-tablet]) .nutd-week-seg__tile:focus-visible {
    outline: 2px solid var(--accent); outline-offset: 2px;
  }
  html:not([data-touch-tablet]) .nutd-ichart { position: relative; height: 120px; }
  /* Promoted week band gets a ~2x taller chart for the detailed labeled bars. */
  html:not([data-touch-tablet]) .nutd-week .nutd-ichart {
    height: 240px; margin-top: var(--space-4);
  }
  html:not([data-touch-tablet]) .nutd-ichart__goal {
    position: absolute; left: 0; right: 0; height: 0;
    border-top: 1px dashed var(--warning); z-index: 1; pointer-events: none;
  }
  /* Goal value labeled at the right end of the dashed line. */
  html:not([data-touch-tablet]) .nutd-ichart__goalval {
    position: absolute; right: 0; bottom: 1px;
    font-size: var(--text-label); font-weight: 600; color: var(--warning);
    background: var(--surface); padding: 0 4px; font-variant-numeric: tabular-nums;
  }
  html:not([data-touch-tablet]) .nutd-ibars {
    position: absolute; inset: 0; display: flex; align-items: flex-end; gap: var(--space-2);
  }
  html:not([data-touch-tablet]) .nutd-ibar-slot {
    flex: 1 1 0; min-width: 0; height: 100%;
    display: flex; flex-direction: column; align-items: center; gap: var(--space-1);
  }
  html:not([data-touch-tablet]) .nutd-ibar-track {
    flex: 1 1 auto; width: 60%; max-width: 26px; min-height: 0;
    display: flex; align-items: flex-end;
  }
  html:not([data-touch-tablet]) .nutd-ibar {
    position: relative; width: 100%; min-height: 2px;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    background: var(--accent);
  }
  html:not([data-touch-tablet]) .nutd-ibar.is-over { background: var(--warning); }
  /* Exact value labeled above each bar — muted, small, anchored to the bar top. */
  html:not([data-touch-tablet]) .nutd-ibar__val {
    position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%);
    margin-bottom: 3px; font-size: var(--text-label); color: var(--muted);
    font-variant-numeric: tabular-nums; white-space: nowrap; pointer-events: none;
  }
  html:not([data-touch-tablet]) .nutd-ibar__wd {
    flex: 0 0 auto; font-size: var(--text-label); color: var(--muted); text-transform: uppercase;
  }
  html:not([data-touch-tablet]) .nutd-ibar__wd.is-current { color: var(--ink); font-weight: 700; }
  html:not([data-touch-tablet]) .nutd-ibar__dom {
    flex: 0 0 auto; font-size: var(--text-label); color: var(--muted);
    font-variant-numeric: tabular-nums; line-height: 1;
  }
  html:not([data-touch-tablet]) .nutd-ibar__dom.is-current { color: var(--ink); font-weight: 700; }
  html:not([data-touch-tablet]) .nutd-istats {
    display: flex; gap: var(--space-5);
    border-top: 1px solid var(--line); padding-top: var(--space-3);
  }
  html:not([data-touch-tablet]) .nutd-istat { display: flex; flex-direction: column; gap: 2px; }
  html:not([data-touch-tablet]) .nutd-istat__val {
    font-size: var(--text-body); font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums;
  }
  html:not([data-touch-tablet]) .nutd-istat__lbl {
    font-size: var(--text-label); color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em;
  }
  /* Day-rail dots: accent outline = logged, filled accent = achieved (strict
     engine semantics, same tokens as .nut-consist__dot). Absolute so the rail
     row layout is unchanged for un-logged days. */
  html:not([data-touch-tablet]) .nutd-rail__day { position: relative; }
  html:not([data-touch-tablet]) .nutd-rail__dot {
    position: absolute; left: 4px; top: 50%; transform: translateY(-50%);
    width: 6px; height: 6px; border-radius: 50%; box-sizing: border-box;
    background: transparent; border: 1.5px solid var(--accent);
  }
  html:not([data-touch-tablet]) .nutd-rail__dot.is-achieved { background: var(--accent); }
}

/* NUTD-V2-F6 — 13" laptop (1024–1280px) intermediate tweak. Below ~1280px the
   detail column is narrow enough that the hero's three-column grid (ring · macros
   · 14rem hydration) crushes the macro zone — "124 / 150 g" wrapped letter-by-
   letter and the hydration card collided with the bars. Drop to two columns
   (ring · macros) and float hydration onto a full-width second row so every zone
   keeps a readable measure. Same MQ guards as the wave; mobile untouched. */
@media (min-width: 1024px) and (max-width: 1279.98px) and (pointer: fine) and (hover: hover) {
  html:not([data-touch-tablet]) .nutd-hero {
    grid-template-columns: auto minmax(0, 1fr);
  }
  html:not([data-touch-tablet]) .nutd-hero__hydration {
    grid-column: 1 / -1;
  }
}

/* NUTD-V2-F4 — the bar-grow reveal is gated behind prefers-reduced-motion so no
   bars animate under reduce. Top-level (not nested) but the selector still only
   matches the desktop insights chart. */
@media (prefers-reduced-motion: no-preference) {
  html:not([data-touch-tablet]) .nutd-ibar {
    transform-origin: bottom;
    animation: nutd-ibar-grow var(--dur-slow) var(--ease-standard) both;
  }
}
@keyframes nutd-ibar-grow { from { transform: scaleY(0); } to { transform: scaleY(1); } }

/* NUTD-V2-F5-LIBRARY-GRID — desktop nutrition wave 5/6: the library sub-tabs
   (My Recipes / Groceries / My Meals / My Foods) get real laptop layouts.
   Flat item lists become a responsive card grid; grouped grocery lists get a
   centered readable column instead of a stretched mobile strip. Cards reuse the
   F3 --surface-muted state-layer on hover/focus. Guarded by the same desktop MQ
   + html:not([data-touch-tablet]); mobile stays pixel-identical. Tokens only. */
@media (min-width: 1024px) and (pointer: fine) and (hover: hover) {
  /* Header row: search grows, create-actions right-aligned, one line. The
     shared-community panel (recipes/foods) stays a full-width band above. */
  html:not([data-touch-tablet]) .nutd .nut-lib {
    display: grid; grid-template-columns: 1fr auto;
    column-gap: var(--space-4); row-gap: var(--space-4);
    align-items: center; max-width: 1200px;
  }
  html:not([data-touch-tablet]) .nutd .nut-lib > * { grid-column: 1 / -1; }
  html:not([data-touch-tablet]) .nutd .nut-lib > .nut-lib__search {
    grid-column: 1; margin-bottom: 0;
  }
  html:not([data-touch-tablet]) .nutd .nut-lib > .nut-lib__actions {
    grid-column: 2; justify-self: end; margin-bottom: 0;
  }
  html:not([data-touch-tablet]) .nutd .nut-libaction { flex: 0 0 auto; }
  html:not([data-touch-tablet]) .nutd .nut-lib__list { margin-top: 0; }

  /* Meals / Foods: the flat item list becomes a card grid. Recipes keep their
     category buckets stacked (:has) and grid the rows INSIDE each bucket. */
  html:not([data-touch-tablet]) .nutd .nut-lib__list {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: var(--space-4); align-items: start;
  }
  html:not([data-touch-tablet]) .nutd .nut-lib__list:has(.recipe-category) {
    display: block;
  }
  html:not([data-touch-tablet]) .nutd .recipe-category__recipes {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: var(--space-4); align-items: start;
  }

  /* Card shell on the grid cell: .nut-libitem for meals/foods, .recipe-row for
     recipes (it wraps the drag grip + .nut-libitem). Full border replaces the
     mobile top-divider list style. */
  html:not([data-touch-tablet]) .nutd .nut-lib__list > .nut-libitem,
  html:not([data-touch-tablet]) .nutd .recipe-category__recipes > .recipe-row {
    border: 1px solid var(--line); border-radius: var(--radius-lg);
    background: var(--surface); padding-inline: var(--space-3);
    transition: background var(--dur-fast) var(--ease-standard);
  }
  /* Reuse the F3 state-layer; neutralize the mobile per-button accent tint so
     the whole card lights up as one surface. focus-within keeps keyboard focus
     visible without touching focus-visible outlines. */
  html:not([data-touch-tablet]) .nutd .nut-lib__list > .nut-libitem:hover,
  html:not([data-touch-tablet]) .nutd .nut-lib__list > .nut-libitem:focus-within,
  html:not([data-touch-tablet]) .nutd .recipe-category__recipes > .recipe-row:hover,
  html:not([data-touch-tablet]) .nutd .recipe-category__recipes > .recipe-row:focus-within {
    background: var(--surface-muted);
  }
  html:not([data-touch-tablet]) .nutd .nut-libitem__main:hover { background: transparent; }

  /* Grouped grocery list resists gridding — give it a centered readable column
     instead of a stretched full-width strip. */
  html:not([data-touch-tablet]) .nutd .nut-groc {
    max-width: 760px; margin-inline: auto;
  }

  /* Tab strip stays; active state already token-driven (accent underline).
     Add a desktop hover cue. */
  html:not([data-touch-tablet]) .nutd .nut-tab:hover { color: var(--ink); }
}

/* SASSU-CHALLENGE-MEMBER-TAB-LAUNCH — member Challenges tab.
   CHALLENGES-MEMBER-UX-OVERHAUL (iter 1): wallet demoted to a header chip +
   toggle panel; classes first; leaderboard = podium centerpiece with motion.
   All colour via theme tokens so it renders in every preset. */
.challenges .ch-section { margin-bottom: var(--space-4); }
.challenges .set-card.ch-section { padding: var(--space-4); }
/* set-card auto-draws divider lines between children; ch-* sections manage
   their own hierarchy, so suppress them here. */
.challenges .set-card.ch-section > * + *::before { display: none; }
.challenges .ch-section__title { margin: 0 0 var(--space-3); font-size: 1.05rem; color: var(--ink); }
.challenges .ch-muted { color: var(--muted); }
.challenges .ch-error { color: var(--danger); }
.challenges .ch-confirm { color: var(--ok); }
.challenges .ch-subhead { color: var(--ink); margin: var(--space-3) 0 var(--space-2); font-size: 0.95rem; }

/* ── Header + wallet chip ─────────────────────────────────────────── */
.ch-header { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.ch-header__title { margin: 0; font-size: 1.5rem; color: var(--ink); }
.ch-wallet-chip {
  position: relative;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px;
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  background: var(--surface-card); color: var(--ink);
  font-size: 0.9rem; font-weight: 600;
  cursor: pointer;
}
.ch-wallet-chip svg { color: var(--accent); }
.ch-wallet-chip[aria-expanded="true"] { border-color: var(--accent); }
.ch-wallet-chip__dot {
  position: absolute; top: -2px; right: -2px;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--bg);
}
.ch-wallet-panel .ch-wallet__balance { margin: 0 0 var(--space-2); color: var(--ink); font-weight: 600; font-size: 1.05rem; }
.ch-wallet-panel .ch-wallet__pending { margin: 0 0 var(--space-2); color: var(--accent); font-size: 0.85rem; }
.ch-wallet__earned {
  display: flex; align-items: center; gap: var(--space-2);
  margin: 0 0 var(--space-3); padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm); background: var(--accent); color: var(--on-accent);
}
.ch-wallet__earned svg { color: var(--on-accent); flex-shrink: 0; }
.ch-wallet__earned-head { font-weight: 600; font-size: 0.9rem; }
.ch-wallet__earned-sub { font-size: 0.78rem; opacity: 0.85; margin-top: 2px; }
.ch-wallet__explainer { margin: var(--space-3) 0 0; color: var(--muted); font-size: 0.8rem; line-height: 1.45; }

/* Mollie checkout return banner */
.ch-purchase-banner {
  display: flex; align-items: center; gap: var(--space-2);
  margin: 0 0 var(--space-3); padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm); font-size: 0.9rem; font-weight: 600;
}
.ch-purchase-banner svg { flex-shrink: 0; }
.ch-purchase-banner--ok { background: var(--accent); color: var(--on-accent); }
.ch-purchase-banner--ok svg { color: var(--on-accent); }
.ch-purchase-banner--wait { background: var(--line); color: var(--ink); }

/* ── Shared bits ──────────────────────────────────────────────────── */
.ch-badge { display: inline-block; padding: 2px 8px; border-radius: var(--radius-pill); background: var(--line); color: var(--ink); font-size: 0.75rem; white-space: nowrap; }
.ch-badge--ok { background: var(--ok); color: var(--bg); }
.ch-badge--accent { background: var(--accent); color: var(--on-accent); }
.ch-need { color: var(--danger); font-size: 0.85rem; }
.ch-ok-text { color: var(--ok); font-size: 0.85rem; }
.ch-bar { height: 4px; border-radius: var(--radius-pill); background: var(--line); overflow: hidden; }
.ch-bar__fill { height: 100%; border-radius: var(--radius-pill); background: color-mix(in srgb, var(--accent) 55%, var(--line)); }
/* COLOR-3 — the sRGB midpoint of the cyan accent and the near-neutral track
   line lands noticeably greyer than half-strength accent looks like it should;
   in oklch the mix keeps its chroma. Declared as an override so the sRGB line
   above stays the fallback. */
@supports (color: color-mix(in oklch, red, blue)) {
  .ch-bar__fill { background: color-mix(in oklch, var(--accent) 55%, var(--line)); }
}
.ch-bar__fill--accent { background: var(--accent); }

/* ── Wallet bundles ───────────────────────────────────────────────── */
.ch-bundles { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.ch-bundle { position: relative; flex: 1 1 140px; padding: var(--space-3); border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface); }
.ch-bundle__credits { font-weight: 600; color: var(--ink); padding-right: 64px; }
.ch-bundle__price { margin: var(--space-1) 0 var(--space-2); }
.ch-bundle__was { text-decoration: line-through; color: var(--muted); margin-right: 6px; }
.ch-bundle__now { color: var(--ink); font-weight: 600; }
.ch-bundle .ch-badge { position: absolute; top: 6px; right: 6px; }
.ch-bundle__btn { width: 100%; padding: 10px 12px; font-size: 12px; }
.ch-bundle--single { border: 1px solid var(--accent); background: var(--accent-subtle); }
.ch-bundle__sub { margin: 2px 0 0; color: var(--muted); font-size: 0.78rem; }

/* ── Classes ──────────────────────────────────────────────────────── */
.ch-class { padding: var(--space-3); border: 1px solid var(--line); border-radius: var(--radius-sm); margin-bottom: var(--space-3); background: var(--surface); }
.ch-class:last-child { margin-bottom: 0; }
.ch-class__head { display: flex; justify-content: space-between; align-items: center; gap: var(--space-2); }
.ch-class__title { font-weight: 600; color: var(--ink); }
.ch-class__meta { display: flex; flex-wrap: wrap; gap: var(--space-2); color: var(--muted); font-size: 0.85rem; margin-top: var(--space-1); }
.ch-class__cost { color: var(--ink); font-weight: 600; }
.ch-class__capacity { margin-top: var(--space-2); }
.ch-class__capacity-label { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-2); color: var(--muted); font-size: 0.8rem; margin-bottom: var(--space-1); }
.ch-class__cutoff { display: inline-flex; align-items: center; gap: 4px; color: var(--muted); font-size: 0.8rem; margin-top: var(--space-2); }
.ch-class__cutoff--urgent { color: var(--danger); }
.ch-class__actions { margin-top: var(--space-3); }
.ch-class__actions .btn { padding: 12px 16px; font-size: 13px; }

/* ── Leaderboard hero ─────────────────────────────────────────────── */
.ch-hero {
  padding: var(--space-4);
  border-radius: var(--radius-sm);
  background: linear-gradient(
    135deg,
    color-mix(in srgb, var(--accent) 16%, var(--surface)),
    var(--surface) 70%
  );
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
}
.ch-hero--banner { overflow: hidden; }
.ch-hero__banner {
  position: relative;
  margin: calc(-1 * var(--space-4)) calc(-1 * var(--space-4)) var(--space-3);
  height: 150px;
}
.ch-hero__banner-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.ch-hero__banner::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    transparent 40%,
    color-mix(in srgb, var(--surface) 78%, transparent)
  );
  pointer-events: none;
}
.ch-hero__top { display: flex; align-items: center; gap: var(--space-2); }
.ch-hero__top svg { color: var(--accent); flex-shrink: 0; }
.ch-hero__title { flex: 1; font-size: 1.15rem; font-weight: 700; color: var(--ink); }
.ch-hero__desc { margin: var(--space-2) 0 0; color: var(--muted); font-size: 0.85rem; line-height: 1.45; }
.ch-hero__discount {
  display: inline-block; margin: var(--space-2) 0 0;
  padding: 2px 8px; border-radius: var(--radius-pill);
  background: var(--accent-subtle); color: var(--accent);
  font-size: 0.72rem; font-weight: 600; white-space: nowrap;
}
.ch-hero__stats { display: flex; gap: var(--space-4); margin-top: var(--space-3); flex-wrap: wrap; }
.ch-hero__stat-label { color: var(--muted); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.08em; }
.ch-hero__stat-value { color: var(--ink); font-size: 0.9rem; margin-top: 2px; }
.ch-hero__mybest-num { font-size: 1.7rem; font-weight: 700; color: var(--ink); line-height: 1.1; }
.ch-hero__mybest-unit { color: var(--muted); font-size: 0.9rem; }
.ch-hero__pending { display: inline-flex; align-items: center; gap: 4px; margin-top: 4px; color: var(--accent); font-size: 0.75rem; }
.ch-hero__stats--tappable { cursor: pointer; align-items: center; }
.ch-hero__stats--tappable:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--radius-md); }
.ch-hero__stats-chev { margin-left: auto; color: var(--muted); flex: 0 0 auto; transform: rotate(-90deg); }
.ch-challenge__standings--flash { animation: chStandingsFlash var(--dur-slow) var(--ease-standard); border-radius: var(--radius-lg); }
@keyframes chStandingsFlash {
  0% { box-shadow: 0 0 0 0 var(--accent-subtle); }
  30% { box-shadow: 0 0 0 4px var(--accent-subtle); }
  100% { box-shadow: 0 0 0 0 transparent; }
}

/* ── Podium ───────────────────────────────────────────────────────── */
.ch-podium {
  display: grid; grid-template-columns: 1fr 1fr 1fr;
  gap: var(--space-2); align-items: end;
  margin: var(--space-4) 0 var(--space-2);
}
.ch-podium__spot { display: flex; flex-direction: column; align-items: center; gap: 4px; min-width: 0; }
.ch-podium__medal {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 50%;
  border: 2px solid var(--accent); color: var(--accent);
  background: var(--surface);
  font-weight: 700;
}
.ch-podium__spot--r1 .ch-podium__medal { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.ch-podium__rank-num { font-size: 0.9rem; }
.ch-podium__bar {
  width: 100%; border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  background: linear-gradient(
    to top,
    color-mix(in srgb, var(--accent) 12%, var(--surface)),
    color-mix(in srgb, var(--accent) 45%, var(--surface))
  );
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
  border-bottom-width: 2px;
  transform-origin: bottom;
}
.ch-podium__spot--r1 .ch-podium__bar { height: 72px; }
.ch-podium__spot--r2 .ch-podium__bar { height: 52px; }
.ch-podium__spot--r3 .ch-podium__bar { height: 38px; }
/* CHALLENGE-PODIUM-BAR-PHOTO-FILL — best-entry evidence photo fills the column
   (inline background-image set in JS). Same shape/height/animation; a neutral
   base shows if the photo 403/404s, and a bottom gradient keeps the medal/name/
   value legible over any photo in both Dag and Nacht. */
.ch-podium__bar--photo {
  background-color: color-mix(in srgb, var(--accent) 18%, var(--surface));
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  position: relative;
  overflow: hidden;
}
.ch-podium__bar--photo::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0) 55%);
  pointer-events: none;
}
.ch-podium__name { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.85rem; font-weight: 600; color: var(--ink); }
.ch-podium__val { font-size: 0.75rem; color: var(--muted); }
.ch-podium__spot.is-mine .ch-podium__name { color: var(--accent); }

/* ── Leaderboard rows 4+ ──────────────────────────────────────────── */
.ch-lb { list-style: none; margin: var(--space-2) 0 0; padding: 0; }
.ch-lb__row { padding: var(--space-2) var(--space-2); border-radius: var(--radius-sm); }
.ch-lb__row + .ch-lb__row { margin-top: 2px; }
.ch-lb__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2); margin-bottom: 5px; }
.ch-lb__left { display: flex; align-items: baseline; gap: var(--space-2); min-width: 0; }
.ch-lb__rank { color: var(--muted); font-size: 0.8rem; min-width: 2em; }
.ch-lb__name { color: var(--ink); font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ch-lb__val { color: var(--ink); font-weight: 600; font-size: 0.9rem; white-space: nowrap; }
.ch-lb__you { padding: 1px 7px; border-radius: var(--radius-pill); background: var(--accent); color: var(--on-accent); font-size: 0.7rem; font-weight: 700; }
.ch-lb__row--mine {
  background: color-mix(in srgb, var(--accent) 10%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--line));
}
.ch-lb__row--mine .ch-lb__name { font-weight: 700; }
/* Unofficial (not-yet-checked) pending row: dimmed, icon-only badge + label. */
.ch-lb__row--pending { opacity: 0.6; }
.ch-lb__unchecked {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 2em; color: var(--muted);
}
.ch-lb__unchecked svg { flex-shrink: 0; }
.ch-lb__unchecked-note {
  color: var(--muted); font-size: 0.72rem; margin: 0 0 5px;
}
.ch-bar__fill--pending {
  background: repeating-linear-gradient(
    45deg,
    color-mix(in srgb, var(--muted) 40%, var(--line)) 0 6px,
    color-mix(in srgb, var(--muted) 20%, var(--line)) 6px 12px
  );
}
.ch-goal {
  display: flex; align-items: center; gap: 6px;
  margin-top: var(--space-2); padding: var(--space-2) var(--space-2);
  color: var(--accent); font-size: 0.85rem; font-weight: 600;
}
.ch-goal svg { flex-shrink: 0; }

/* ── Entry to the full per-category leaderboard (under the top-3 podium) ── */
.ch-lb-more {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  width: 100%; margin: var(--space-2) 0 0; padding: var(--space-2) var(--space-3);
  background: var(--surface-card); border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--ink);
  font-size: 0.85rem; font-weight: 600; cursor: pointer; text-align: left;
}
.ch-lb-more:hover { border-color: var(--accent); }
.ch-lb-more__label { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
.ch-lb-more__label svg { flex-shrink: 0; color: var(--accent); }
.ch-lb-more > svg { flex-shrink: 0; color: var(--muted); }

/* ── Category leaderboard overlay (horizontal scroll-snap cards) ───── */
.ch-cat-overlay {
  position: fixed; inset: 0; z-index: 9400;
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(0, 0, 0, 0.55);
}
.ch-cat-sheet {
  display: flex; flex-direction: column;
  width: 100%; max-width: 640px; height: 88vh; max-height: 88vh;
  background: var(--bg);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: var(--elev-up);
  overflow: hidden;
}
.ch-cat-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}
.ch-cat-header__title { font-size: 1rem; font-weight: 700; color: var(--ink); }
.ch-cat-header__close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; margin: -8px -8px -8px 0;
  background: none; border: none; color: var(--muted); cursor: pointer;
}
.ch-cat-header__close:hover { color: var(--ink); }
.ch-cat-track {
  display: flex; gap: var(--space-3); flex: 1 1 auto; min-height: 0;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
  padding: var(--space-3) var(--space-4);
  scrollbar-width: none;
}
.ch-cat-track::-webkit-scrollbar { display: none; }
.ch-cat-card {
  flex: 0 0 86%; scroll-snap-align: center;
  display: flex; flex-direction: column; min-height: 0;
  background: var(--surface-card); border: 1px solid var(--line);
  border-radius: var(--radius-lg); overflow: hidden;
}
.ch-cat-card__title {
  margin: 0; padding: var(--space-3) var(--space-3) var(--space-2);
  font-size: 0.95rem; font-weight: 700; color: var(--ink);
  border-bottom: 1px solid var(--line); flex-shrink: 0;
}
.ch-cat-card__body {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  padding: var(--space-2) var(--space-3) var(--space-3);
  -webkit-overflow-scrolling: touch;
}
.ch-cat-card__empty { padding: var(--space-4) var(--space-2); text-align: center; }
.ch-cat-dots {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: var(--space-2) 0 max(var(--space-3), env(safe-area-inset-bottom));
  flex-shrink: 0;
}
.ch-cat-dot {
  width: 8px; height: 8px; padding: 0; border: none; border-radius: var(--radius-pill);
  background: var(--line); cursor: pointer; transition: background var(--dur-base) var(--ease-standard), width var(--dur-base) var(--ease-standard);
}
.ch-cat-dot.is-active { width: 22px; background: var(--accent); }

/* ── Your / Browse sections + cards ───────────────────────────────── */
.challenges .ch-sec + .ch-sec { margin-top: var(--space-4); }
.ch-challenge + .ch-challenge { margin-top: var(--space-4); }
.ch-challenge--moving { opacity: 0; transform: translateY(-8px); }
@media (prefers-reduced-motion: no-preference) {
  .ch-challenge--moving { transition: opacity var(--dur-base) var(--ease-standard), transform var(--dur-base) var(--ease-standard); }
}
.ch-join { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--line); }
.ch-join__btn { width: 100%; padding: 12px 16px; font-size: 13px; }
.ch-leave { margin-top: var(--space-3); width: auto; padding: 8px 14px; font-size: 12px; color: var(--muted); }

/* ── Compact browse rows ──────────────────────────────────────────── */
.ch-browse-row { border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface); overflow: hidden; }
.ch-browse-row + .ch-browse-row { margin-top: var(--space-2); }
.ch-browse-row__head {
  display: flex; align-items: center; gap: var(--space-2);
  width: 100%; padding: var(--space-2) var(--space-3);
  background: none; border: none; text-align: left; cursor: pointer; color: var(--ink);
}
.ch-browse-row__thumb { width: 40px; height: 40px; border-radius: var(--radius-sm); object-fit: cover; flex-shrink: 0; }
.ch-browse-row__main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.ch-browse-row__title { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ch-browse-row__meta { color: var(--muted); font-size: 0.8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ch-browse-row__head svg { color: var(--muted); flex-shrink: 0; transition: transform var(--dur-base) var(--ease-standard); }
.ch-browse-row.is-open .ch-browse-row__head svg { transform: rotate(180deg); }
.ch-browse-row__detail { padding: var(--space-3); border-top: 1px solid var(--line); }
@media (prefers-reduced-motion: reduce) {
  .ch-browse-row__head svg { transition: none; }
}

/* ── MEMBER-CLASSES-PAGE-AVAILABLE-LESSONS — entry card + /classes page ── */
/* Entry card on /challenges (same card family as ch-* surfaces). */
.cls-entry {
  display: flex; align-items: center; gap: var(--space-3); width: 100%; text-align: left;
  padding: var(--space-3); border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink); cursor: pointer;
}
.cls-entry__icon { display: inline-flex; color: var(--accent); flex-shrink: 0; }
.cls-entry__main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.cls-entry__title { font-weight: 600; color: var(--ink); }
.cls-entry__teaser { color: var(--muted); font-size: 0.85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cls-entry__chevron { display: inline-flex; color: var(--muted); flex-shrink: 0; }

/* /classes page. */
.cls-header { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-3); }
.cls-back {
  display: inline-flex; align-items: center; justify-content: center; padding: 4px;
  border: none; background: none; color: var(--ink); cursor: pointer;
}
.cls-header__title { margin: 0; font-size: 1.5rem; color: var(--ink); }
.cls-body { display: flex; flex-direction: column; }
.cls-group {
  margin: var(--space-4) 0 var(--space-2); font-size: 0.8rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted);
}
.cls-group:first-child { margin-top: 0; }
.cls-muted { color: var(--muted); }
.cls-error { color: var(--danger); }
.cls-need { color: var(--danger); font-size: 0.85rem; }
.cls-ok-text { color: var(--ok); font-size: 0.85rem; }

.cls-class {
  padding: var(--space-3); border: 1px solid var(--line); border-radius: var(--radius-sm);
  margin-bottom: var(--space-3); background: var(--surface);
}
.cls-class:last-child { margin-bottom: 0; }
.cls-class__head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-2); }
.cls-class__title { font-weight: 600; color: var(--ink); }
.cls-class__time { color: var(--ink); font-weight: 600; white-space: nowrap; }
.cls-class__meta { display: flex; flex-wrap: wrap; gap: var(--space-2); color: var(--muted); font-size: 0.85rem; margin-top: var(--space-1); }
.cls-class__cost { color: var(--ink); font-weight: 600; }
.cls-class__cost--free { color: var(--accent); }
.cls-class__loc { display: inline-flex; align-items: center; gap: 4px; color: var(--muted); text-decoration: none; }
.cls-class__loc:hover { color: var(--ink); text-decoration: underline; }
.cls-class__loc svg { flex: 0 0 auto; }
.cls-class__capacity { margin-top: var(--space-2); }
.cls-class__capacity-label { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-2); color: var(--muted); font-size: 0.8rem; margin-bottom: var(--space-1); }
.cls-bar { height: 4px; border-radius: var(--radius-pill); background: var(--line); overflow: hidden; }
.cls-bar__fill { height: 100%; border-radius: var(--radius-pill); background: color-mix(in srgb, var(--accent) 55%, var(--line)); }
/* COLOR-3 — the sRGB midpoint of the cyan accent and the near-neutral track
   line lands noticeably greyer than half-strength accent looks like it should;
   in oklch the mix keeps its chroma. Declared as an override so the sRGB line
   above stays the fallback. */
@supports (color: color-mix(in oklch, red, blue)) {
  .cls-bar__fill { background: color-mix(in oklch, var(--accent) 55%, var(--line)); }
}
.cls-class__cutoff { display: inline-flex; align-items: center; gap: 4px; color: var(--muted); font-size: 0.8rem; margin-top: var(--space-2); }
.cls-class__cutoff--urgent { color: var(--danger); }
.cls-class__actions { margin-top: var(--space-3); }
.cls-class__actions .btn { padding: 12px 16px; font-size: 13px; }

@media (prefers-reduced-motion: no-preference) {
  .cls-entry { transition: border-color var(--dur-fast) var(--ease-standard); }
  .cls-entry:hover { border-color: var(--accent); }
}

/* ── Motion (all gated on prefers-reduced-motion) ─────────────────── */
@media (prefers-reduced-motion: no-preference) {
  @keyframes ch-rise { from { transform: scaleY(0); } to { transform: scaleY(1); } }
  @keyframes ch-fade-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
  .ch-podium__bar { animation: ch-rise var(--dur-slow) var(--ease-standard) backwards; }
  .ch-podium__spot--r2 .ch-podium__bar { animation-delay: 0.1s; }
  .ch-podium__spot--r3 .ch-podium__bar { animation-delay: 0.18s; }
  .ch-lb__row { animation: ch-fade-in var(--dur-slow) var(--ease-standard) backwards; animation-delay: calc(0.25s + var(--row-i, 0) * 0.06s); }
  .ch-goal { animation: ch-fade-in var(--dur-slow) var(--ease-standard) 0.9s backwards; }
}

/* ── Submit flow ──────────────────────────────────────────────────── */
.ch-submit { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--line); }
.ch-submit__reveal { width: 100%; padding: 12px 16px; font-size: 13px; }
.ch-submit__btn { width: 100%; padding: 14px 16px; font-size: 14px; }
.ch-submit__form {
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent) 5%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 22%, var(--line));
  position: relative;
}
.ch-submit__label { display: block; color: var(--ink); font-size: 0.9rem; font-weight: 600; margin-bottom: var(--space-2); }
.ch-submit__inputwrap { display: flex; align-items: stretch; gap: var(--space-2); margin-top: var(--space-1); }
.ch-submit__inputwrap .ch-submit__input { flex: 1; min-width: 0; font-size: 1.05rem; padding: 12px 14px; }
.ch-submit__timesep {
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 1.4rem; font-weight: 700; color: var(--ink); padding: 0 2px;
}
.ch-submit__unit {
  display: inline-flex; align-items: center; padding: 0 14px;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
  color: var(--ink); font-weight: 600; font-size: 0.9rem; white-space: nowrap;
}
.ch-submit__reqnote {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: var(--space-2); padding: 4px 10px; border-radius: var(--radius-pill);
  font-size: 0.78rem; font-weight: 600;
  color: color-mix(in srgb, var(--accent) 82%, var(--ink));
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--line));
}
.ch-submit__photo { display: flex; align-items: center; gap: var(--space-2); margin: var(--space-3) 0; flex-wrap: wrap; }
.ch-submit__photobtn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  width: auto; padding: 11px 16px; font-size: 13px;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--line));
  background: var(--surface); color: var(--ink);
}
.ch-submit__photoname { color: var(--muted); font-size: 0.85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ch-submit__feedback { margin-top: var(--space-2); font-size: 0.85rem; }
.ch-submit__pending {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 16px; border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--ok) 14%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--ok) 45%, var(--line));
  color: var(--ok); font-weight: 600; font-size: 0.9rem;
}

/* ── Submit celebration ───────────────────────────────────────────── */
.ch-celebrate {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
  text-align: center; padding: var(--space-3) var(--space-2);
}
.ch-celebrate__check {
  color: var(--ok);
  border-radius: var(--radius-pill);
  padding: 8px;
  background: color-mix(in srgb, var(--ok) 14%, var(--surface));
  border: 2px solid color-mix(in srgb, var(--ok) 45%, var(--line));
}
.ch-celebrate__title { color: var(--ink); font-weight: 700; font-size: 1.05rem; }
.ch-celebrate__msg { color: var(--accent); font-weight: 600; font-size: 0.9rem; }
.ch-confetti { position: absolute; inset: 0; pointer-events: none; }
.ch-confetti__bit {
  position: absolute; top: 40%; width: 8px; height: 8px; border-radius: var(--radius-sm);
  opacity: 0;
}
@media (prefers-reduced-motion: no-preference) {
  @keyframes ch-check-pop { 0% { transform: scale(0.4); opacity: 0; } 60% { transform: scale(1.12); } 100% { transform: scale(1); opacity: 1; } }
  @keyframes ch-confetti-fly {
    0% { opacity: 1; transform: translate(0, 0) rotate(0); }
    100% { opacity: 0; transform: translate(var(--dx, 0), -70px) rotate(var(--rot, 180deg)); }
  }
  @keyframes ch-check-draw { from { stroke-dashoffset: 32; } to { stroke-dashoffset: 0; } }
  .ch-celebrate__check { animation: ch-check-pop var(--dur-slow) var(--ease-standard) backwards; }
  .ch-celebrate__check polyline { stroke-dasharray: 32; animation: ch-check-draw var(--dur-slow) var(--ease-standard) 0.2s backwards; }
  .ch-celebrate__title { animation: ch-fade-in var(--dur-slow) var(--ease-standard) 0.25s backwards; }
  .ch-celebrate__msg { animation: ch-fade-in var(--dur-slow) var(--ease-standard) 0.4s backwards; }
  .ch-confetti__bit { animation: ch-confetti-fly var(--dur-slow) var(--ease-standard) forwards; }
}

/* ════════════════════════════════════════════════════════════════════════
   NUTRITION-M3-WAVE-2 — touch feedback + focus (2026-07-16).
   Builds on the WAVE-1 shape/type/scrim tokens. Purely additive: no glyph,
   colour or layout change beyond the press-overlay and keyboard focus-ring.
   ──────────────────────────────────────────────────────────────────────── */

/* (2) M3 state-layer: a currentColor ink-overlay that fades in on press.
   ~10% opacity keeps the tint within the M3 "pressed" band. Token-free —
   currentColor tracks each surface's own ink, so it works across all themes. */
.nut-meal__head,
.nut-libitem__main,
.nut-tab,
.nut-addmenu__tile,
.nut-utile { position: relative; }

.nut-meal__head::after,
.nut-libitem__main::after,
.nut-tab::after,
.nut-addmenu__tile::after,
.nut-utile::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: currentColor;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease-standard);
}
.nut-meal__head:active::after,
.nut-libitem__main:active::after,
.nut-tab:active::after,
.nut-addmenu__tile:active::after,
.nut-utile:active::after { opacity: .10; }

@media (prefers-reduced-motion: reduce) {
  .nut-meal__head::after,
  .nut-libitem__main::after,
  .nut-tab::after,
  .nut-addmenu__tile::after,
    .nut-utile::after { transition: none; }
}

/* (3) Keyboard focus-ring — one generic rule across the interactive nut-*/mo-*
   surfaces. Pointer taps never trigger :focus-visible, so this is invisible on
   touch and only appears for keyboard/AT users. */
.nut-meal__head:focus-visible,
.nut-meal__menu:focus-visible,
.nut-meal__caret:focus-visible,
.nut-libitem__main:focus-visible,
.nut-libitem__add:focus-visible,
.nut-tab:focus-visible,
.nut-addmenu__tile:focus-visible,
.nut-utile:focus-visible,
.nut-groc__toggle:focus-visible,
.nut-entry-act:focus-visible,
.mo-close:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ═══ IN-APP-INTAKE-WOW-FLOW-AND-SETTINGS-INTAKE-CATEGORY ═══════════════════
   The in-app twin of the public /scan funnel. Everything that draws a step —
   stage, progress bar, fields, reward beat, photo tile, scan line, count-up
   result — comes from scan-core.css, which intake-wow.js links on first open.
   This block is only the two things scan-core.css deliberately does NOT carry:
   the full-screen layer that hosts it, and the dark palette it assumes.

   The palette is scoped, not global. Sassu's two presets are Dag (light) and
   Nacht (dark); the funnel is premium-dark by design, so a Dag user would
   otherwise get lime-on-white — which fails contrast and reads as a different
   product. Re-declaring the volt rungs on .scan-layer keeps the funnel looking
   like the funnel without touching the user's chosen theme anywhere else.
   Values are copied verbatim from :root[data-theme-preset="volt"] above. */
.scan-layer {
  --bg:        #121212;
  --surface:   #1E1E1E;
  --surface-2: #1C1C1E;
  --ink:       #E8E8E8;
  --ink-2:     #B0B0B0;
  --muted:     #666666;
  --line:      #333333;
  --accent:    #ADFF00;
  --on-accent: #0A0A0A;
  --accent-subtle: rgba(173, 255, 0, 0.20);
  --danger:    #FF6B6B;
  color-scheme: dark;

  position: fixed;
  inset: 0;
  /* Above the bottom nav and any open sheet: this is a takeover, and a visible
     tab bar would invite a mid-scan tab switch that loses the picked photo. */
  z-index: 3000;
  display: flex;
  justify-content: center;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--bg);
  color: var(--ink);
  animation: scan-rise var(--dur-base) var(--ease-standard) both;
}

.scan-layer__shell {
  position: relative;
  width: 100%;
  max-width: 460px;
  min-height: 100%;
  padding: calc(env(safe-area-inset-top, 0px) + var(--space-6)) var(--space-5)
           calc(env(safe-area-inset-bottom, 0px) + var(--space-5));
  display: flex;
  flex-direction: column;
  background:
    radial-gradient(120% 60% at 50% 0%, rgba(173, 255, 0, 0.07), transparent 70%),
    var(--bg);
}

/* The only way out that is always present. Absolute rather than in-flow so it
   never competes with .scan-nav for the thumb zone at the bottom. */
.scan-layer__close {
  position: absolute;
  top: calc(env(safe-area-inset-top, 0px) + var(--space-3));
  right: var(--space-3);
  width: 40px;
  height: 40px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
}

.scan-layer__close:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-subtle);
}

.scan-layer__close svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
}

@media (prefers-reduced-motion: reduce) {
  .scan-layer { animation: none !important; }
}

/* ══════════ DIARY-METRIC-DETAIL-TAP — full-screen metric detail ══════════
   Tapping a sparkline in the Diary opens this. Full-screen variant of the
   existing .mo-sheet, so the open/close transition and the reduced-motion
   guard above are inherited rather than restated. Tokens only. */

.mo-overlay--full { align-items: stretch; }
.mo-sheet--full {
  max-width: 720px;
  max-height: 100%;
  height: 100%;
  border-radius: 0;
  padding-bottom: 0;
  display: flex;
  flex-direction: column;
}

.mdx__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-2);
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}
.mdx__iconbtn {
  background: none;
  border: 0;
  color: var(--ink-2);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  border-radius: var(--radius-md);
}
.mdx__iconbtn:hover { color: var(--ink); }
.mdx__title {
  flex: 1 1 auto;
  font-size: var(--text-title);
  line-height: var(--lh-title);
  font-weight: 500;
  letter-spacing: 0.04em;
  margin: 0;
}
.mdx__headacts { display: flex; align-items: center; flex: 0 0 auto; }

.mdx__selectors {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  flex: 0 0 auto;
}
.mdx__metricwrap { position: relative; }
.mdx__metric {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  color: var(--ink);
  font-size: var(--text-body);
  line-height: var(--lh-body);
  padding: var(--space-2) var(--space-3);
  min-height: 36px;
  cursor: pointer;
}
.mdx__metriclist {
  position: absolute;
  z-index: 2;
  top: calc(100% + var(--space-1));
  left: 0;
  min-width: 180px;
  max-height: 260px;
  overflow-y: auto;
  margin: 0;
  padding: var(--space-1);
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--elev-2);
}
.mdx__metricopt {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  color: var(--ink);
  font-size: var(--text-body);
  line-height: var(--lh-body);
  padding: var(--space-2) var(--space-3);
  min-height: 40px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.mdx__metricopt:hover { background: var(--surface-2, var(--bg)); }
.mdx__metricopt--on { color: var(--accent); font-weight: 600; }

.mdx__ranges { display: inline-flex; gap: 2px; }
.mdx__range {
  background: none;
  border: 0;
  color: var(--ink-2);
  font-size: var(--text-label);
  line-height: var(--lh-label);
  letter-spacing: 0.06em;
  padding: var(--space-2);
  min-width: 38px;
  min-height: 32px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.mdx__range--on { background: var(--accent); color: var(--on-accent); font-weight: 600; }

.mdx__addrow {
  display: flex;
  gap: var(--space-2);
  padding: 0 var(--space-4) var(--space-3);
  flex: 0 0 auto;
}
.mdx__addfield {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  color: var(--ink);
  font-size: var(--text-body);
  line-height: var(--lh-body);
  padding: var(--space-2) var(--space-3);
  min-height: 44px;
  min-width: 0;
}
.mdx__addfield--date { flex: 1 1 auto; }
.mdx__addfield--val { flex: 0 1 96px; }
.mdx__addsave {
  background: var(--accent);
  color: var(--on-accent);
  border: 0;
  border-radius: var(--radius-md);
  font-size: var(--text-body);
  line-height: var(--lh-body);
  font-weight: 600;
  padding: var(--space-2) var(--space-4);
  min-height: 44px;
  cursor: pointer;
}
.mdx__addsave:disabled { opacity: 0.6; cursor: default; }

.mdx__note {
  padding: var(--space-5) var(--space-4);
  margin: 0;
  color: var(--ink-2);
  font-size: var(--text-body);
  line-height: var(--lh-body);
}
.mdx__note--err { color: var(--danger, var(--ink)); }

.mdx__kpis {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4) var(--space-4);
  flex: 0 0 auto;
}
.mdx__kpi { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.mdx__kpi-lbl {
  font-size: var(--text-label);
  line-height: var(--lh-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.mdx__kpi-val {
  font-size: var(--text-headline);
  line-height: var(--lh-headline);
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.mdx__kpi-sub {
  font-size: var(--text-label);
  line-height: var(--lh-label);
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}

.mdx__chart { padding: 0 var(--space-4); flex: 0 0 auto; }
.mdx__plotrow { display: flex; gap: var(--space-2); }
.mdx__ygutter {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  font-size: var(--text-label);
  line-height: var(--lh-label);
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
  padding: 2px 0;
}
.mdx__plot { position: relative; flex: 1 1 auto; min-width: 0; }
.mdx__svg { display: block; width: 100%; height: 168px; overflow: visible; }
.mdx__grid { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }
.mdx__line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.mdx__dot { fill: var(--accent); }
/* Scrub: reuses .spark-cursor / .spark-cursor__dot / .spark-tip from the trend
   card. pan-y keeps a vertical swipe that starts on the chart scrolling the
   page, while horizontal movement belongs to the scrubber. */
.mdx__svg { touch-action: pan-y; cursor: ew-resize; }
.mdx__chart--scrub .spark-cursor { opacity: 0.55; }
.mdx__chart--scrub .spark-cursor__dot { opacity: 1; }
.mdx__chart--scrub .mdx__tip { opacity: 1; }
/* The trend card hangs its tip ABOVE the plot; here that would sit on top of
   the KPI row, so the label lives inside the plot instead. */
.mdx__tip { bottom: auto; top: 4px; }
.mdx__xaxis {
  display: flex;
  justify-content: space-between;
  font-size: var(--text-label);
  line-height: var(--lh-label);
  color: var(--ink-2);
  padding-top: var(--space-2);
}

.mdx__entries {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--space-5) var(--space-4) var(--space-6);
}
.mdx__sec {
  font-size: var(--text-label);
  line-height: var(--lh-label);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin: 0 0 var(--space-2);
  font-weight: 500;
}
.mdx__list { list-style: none; margin: 0; padding: 0; }
.mdx__entry {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--line-2);
}
.mdx__entry-val {
  font-size: var(--text-body);
  line-height: var(--lh-body);
  color: var(--ink);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.mdx__entry-date {
  font-size: var(--text-body);
  line-height: var(--lh-body);
  color: var(--ink-2);
}

/* The tap affordance on the sparklines that lead here. */
.spark-mini--tappable, .trend-card--tappable { cursor: pointer; }
.spark-mini--tappable:focus-visible,
.trend-card--tappable:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* ═══════════════════════════════════════════════════════════════════════════
   NUTRITION-LIQUID-GLASS-PLATE (2026-07-27)

   The Log tab gets a plate photo as WALLPAPER and every card above it becomes
   frosted glass. Two things make this survivable rather than a legibility
   disaster:

   1. The plate is pinned (sticky, zero-height wrapper) so it never scrolls with
      the content. The glass slides over a stationary picture — that is the
      whole effect, and it also means the backdrop under any given card changes
      as you scroll, so contrast must hold against the WORST pixel of the photo,
      not the average.

   2. Because of (1), --glass-tint is deliberately heavier than a designer would
      pick by eye, and secondary text on glass is lifted off --muted onto
      --glass-ink-2. --muted (#5C6B80 dark / #8593A1 light) is mid-luminance and
      therefore CANNOT reach 4.5:1 against a mid-luminance blurred photo at any
      tint — the backdrop would have to be brighter than white or darker than
      black. Lifting the text is not a polish step, it is the only fix that
      exists. tests/visual/nutrition-glass-plate.spec.js samples the composited
      pixels and fails the build if any run drops under 4.5:1.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  --glass-tint:   rgba(0, 0, 0, 0.62);
  --glass-tint-2: rgba(0, 0, 0, 0.52);   /* secondary panels: less to hide */
  --glass-ink:    #EAF1F7;
  --glass-ink-2:  #BFCBD8;
  --glass-edge:   rgba(255, 255, 255, 0.14);
  --glass-hair:   rgba(255, 255, 255, 0.10);
}
html[data-theme-mode="light"] {
  --glass-tint:   rgba(255, 255, 255, 0.80);
  --glass-tint-2: rgba(255, 255, 255, 0.72);
  --glass-ink:    #0B1320;
  --glass-ink-2:  #333F4C;
  --glass-edge:   rgba(255, 255, 255, 0.70);
  --glass-hair:   rgba(11, 19, 32, 0.10);
}

/* ── The plate ───────────────────────────────────────────────────────────── */
/* Zero-height so it claims no layout: the cards sit exactly where they sat
   before this ship. Sticky (not fixed) on purpose — sticky is measured against
   whichever ancestor actually scrolls, so it survives the app shell moving its
   scroll container without a JS probe. */
.nut-plate-wrap {
  position: sticky;
  top: min(423px, 50vh);
  height: 0;
  z-index: 0;
  pointer-events: none;
  overflow: visible;
}
.nut-plate {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  /* 390x260 = 1.5, the source PNG's own 480x320 ratio. Sized against the
     VIEWPORT, not the padded mount, so the plate is the full 390 the mocks were
     drawn at rather than 390-minus-gutters. */
  width: min(390px, 100vw);
  height: 260px;
  border-radius: 28px;
  overflow: hidden;
}
.nut-plate__img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
/* Feather the edges so the plate reads as light in the page rather than as a
   pasted rectangle with corners. */
.nut-plate::after {
  content: '';
  position: absolute; inset: 0;
  background: radial-gradient(ellipse at center, transparent 38%, var(--bg) 78%);
}

/* Everything the user reads must paint ABOVE the wallpaper. */
/* .nut-fab is excluded BY NAME: this rule and `.nut-fab` carry identical
   specificity (one class each) and this one is later in the file, so `position:
   relative` silently beat `position: fixed` and dropped the "+" into the flow at
   the bottom-left of the mount. Its own z-index (40) already clears the
   wallpaper, so it needs nothing from this rule. */
.nut-mount > *:not(.nut-plate-wrap):not(.nut-fab) { position: relative; z-index: 1; }

/* ── The glass ───────────────────────────────────────────────────────────── */
/* .nut-ns and .nut-meal are the panels the eye lands on, so they get the full
   24px blur. The weekstrip/header and the hydration card drop to 16px: the
   performance guard for this ship is eight simultaneous blurred panels, and a
   day with several meals blows past that on the meal rows alone. Halving the
   radius on the two supporting panels keeps the composite cheap without the
   user being able to name what changed. */
.nut-ns,
.nut-meal {
  background: var(--glass-tint);
  -webkit-backdrop-filter: blur(24px) saturate(160%);
  backdrop-filter: blur(24px) saturate(160%);
  border: 1px solid var(--glass-edge);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.14) inset;
}
.hydration-card {
  background: var(--glass-tint-2);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
  backdrop-filter: blur(16px) saturate(150%);
  border: 1px solid var(--glass-edge);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.14) inset;
}
/* The date strip is NAVIGATION, not a card — it never had a border-radius, so
   as glass it read as a rectangular slab among rounded cards. It also sits
   ABOVE the plate's sticky top (min(423px, 50vh)), so it is over plain --bg and
   needs no over-photo tint at all: dropping the glass entirely both fixes the
   corners and keeps --ink/--ink-2 at their tuned contrast. */
.nut-head {
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border: 0;
  box-shadow: none;
}
/* The north-star card is the one panel that is on screen for the entire scroll,
   so it is the only one worth promoting. Promoting the meal rows too is what
   turns this effect into jank on a mid-range phone. */
.nut-ns { will-change: backdrop-filter; }

/* Meal rows are glass containers; the entry rows inside them must NOT stack a
   second blur on top (blur-on-blur is both expensive and muddy) — they simply
   let their parent's glass show through. */
.nut-meal .nut-entry { background: transparent; }
.nut-meal__head { background: transparent; }

/* Text on glass. Anything that was --muted is lifted; see the header note for
   why this is mandatory rather than cosmetic. These are the LEAF elements that
   actually carry a `color` — setting it on the container does nothing, because
   every one of these sets its own. */
.nut-ns__hero-label,
.nut-ns__hero-goal,
.nut-ns__hero-micro,
.nut-ns__band-cal-l,
.nut-ns__calnum,
.nut-mac__lbl,
.nut-ns__fiber-lbl,
.nut-meal__name,
.nut-entry__name,
.hydration-btn { color: var(--glass-ink); }

.nut-ns__band-cal-r,
.nut-ns__calgoal,
.nut-ns__rem,
.nut-ns__rem--met,
.nut-ns__fiber-val,
.nut-mac__val,
.nut-meal__kcal,
.nut-meal__caret,
.nut-meal__menu,
.nut-entry__meta,
.nut-entry__kcal { color: var(--glass-ink-2); }

/* Hairlines inside glass have to come off --line (a solid token tuned for a
   solid surface) or they vanish over a bright patch of the photo. */
.nut-meal .nut-entry-wrap:not(:first-child)::before { background: var(--glass-hair); }

/* The dashed add-meal button reads as a hole in the glass, which is right — but
   its border needs the glass edge token to stay visible over the photo. */
.nut-addmeal {
  border-color: var(--glass-edge);
  color: var(--glass-ink);
  background: var(--glass-tint-2);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
  backdrop-filter: blur(16px) saturate(150%);
}

/* ── Swipe tray over glass ───────────────────────────────────────────────── */
/* Before this ship .nut-entry was opaque and physically covered the action
   tray parked behind it. Glass does not cover anything, so the tray has to be
   hidden until a swipe has actually locked — otherwise the delete button is a
   permanent red ghost on the right of every row. renderEntry() adds
   .is-swiping on gesture lock and removes it on gesture end; .is-open keeps it
   up once the tray is parked open. */
.nut-entry-actions { opacity: 0; pointer-events: none; transition: opacity var(--dur-fast, 120ms) linear; }
.nut-entry-wrap.is-swiping .nut-entry-actions,
.nut-entry-wrap.is-open .nut-entry-actions { opacity: 1; pointer-events: auto; }
/* While the tray is showing, the sliding row must be opaque or the buttons read
   straight through the glass it is sliding over. */
.nut-entry-wrap.is-swiping .nut-entry,
.nut-entry-wrap.is-open .nut-entry { background: var(--surface); }

@media (prefers-reduced-transparency: reduce) {
  /* .nut-head is deliberately absent: it is transparent in BOTH modes now, and
     listing it here would hand the strip back its rectangular slab. */
  .nut-ns, .nut-meal, .hydration-card, .nut-addmeal {
    -webkit-backdrop-filter: none; backdrop-filter: none; background: var(--surface);
  }
  .nut-plate-wrap { display: none; }
}

/* ══════ DAYCYCLE-HELP-BUTTON-OPENS-EXPLAINER-SHEET ══════
   Explainer body for the "?" on the check-in day-cycle strip. Tokens only, so it
   inherits Dag/Nacht; the sheet shell itself is the shared .mo-* bottom-sheet. */
.dc-explain__p {
  margin: 0 0 var(--space-4);
  color: var(--ink-2);
  font-size: 14px;
  line-height: 1.55;
}
.dc-explain__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-5);
}
.dc-explain__actions .btn { width: 100%; }

/* ── NUTRITION-LOW-VISION-BIG-BUTTON-SURFACE ────────────────────────────────
   The Nutrition tab collapses to a few giant, vertically stacked buttons when
   users.pref_low_vision is on. Deliberately NOT a grid: a dense grid of tiles
   is the pattern senior-launcher research says fails. Every colour here is a
   theme token, so the two high-contrast presets (Dag / Nacht) are the only
   palettes this mode ever uses. Sub-text uses --ink, never --ink-2: --ink-2
   measures 5.1:1 (nacht) / 5.5:1 (dag) against --surface and misses the 7:1
   bar this mode holds itself to, while --ink clears 14.5:1 / 18.6:1. */
html[data-low-vision="1"] .nut-lv {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
html[data-low-vision="1"] .nut-lv__readout {
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--ink);
  text-align: center;
}
html[data-low-vision="1"] .nut-lv__kcal {
  font-size: 40px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: 0.01em;
}
html[data-low-vision="1"] .nut-lv__sub {
  font-size: 18px;
  line-height: 1.45;
  letter-spacing: 0.04em;
  color: var(--ink);
}
html[data-low-vision="1"] .nut-lv__title {
  margin: 6px 0 0;
  font-size: 24px;
  font-weight: 600;
  line-height: 1.45;
  letter-spacing: 0.04em;
  color: var(--ink);
}
html[data-low-vision="1"] .nut-lv__btn,
html[data-low-vision="1"] .nut-lv__back {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 14px;
  width: 100%;
  min-height: 72px;
  padding: 14px 18px;
  border-radius: var(--radius-lg);
  border: 2px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  font-family: inherit;
  font-size: 24px;
  font-weight: 600;
  line-height: 1.45;
  letter-spacing: 0.04em;
  text-align: left;
  cursor: pointer;
}
html[data-low-vision="1"] .nut-lv__btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
/* The escape hatch is outlined, never accent-filled: it must be findable
   without competing with the action the user came here to take. */
html[data-low-vision="1"] .nut-lv__btn--ghost {
  background: transparent;
  border-color: var(--ink);
  color: var(--ink);
}
html[data-low-vision="1"] .nut-lv__back {
  min-height: 56px;
  font-size: 20px;
  align-self: flex-start;
  width: auto;
  padding: 10px 18px;
}
html[data-low-vision="1"] .nut-lv__back .nut-ic svg { transform: rotate(90deg); }
html[data-low-vision="1"] .nut-lv__label { flex: 1 1 auto; }
html[data-low-vision="1"] .nut-lv__btn:active,
html[data-low-vision="1"] .nut-lv__back:active {
  outline: 3px solid var(--ink);
  outline-offset: -3px;
  transform: scale(0.985);
}
/* On the accent fill the ink outline vanishes in Dag (accent is near-black navy
   and so is --ink) — the pressed ring has to ride --on-accent there. */
html[data-low-vision="1"] .nut-lv__btn--primary:active { outline-color: var(--on-accent); }
@media (prefers-reduced-motion: reduce) {
  html[data-low-vision="1"] .nut-lv__btn:active,
  html[data-low-vision="1"] .nut-lv__back:active { transform: none; }
}
