﻿/* ============================================================
   Caploo — Design tokens & base styles
   Foundation aligned with frontend-handoff prototype.
   :root holds STRUCTURAL tokens (radius, spacing, motion, type, layout)
   shared by every theme. Color tokens split into
     :root + :root[data-theme="dark"]   — dark palette (default)
     :root[data-theme="light"]          — light palette (APCA Lc verified)
   The inline boot script in wwwroot/js/caploo.theme.js sets <html data-theme>
   BEFORE this stylesheet parses, so the right palette wins on first paint.
   :root itself carries a sane dark fallback so an unattributed <html>
   (e.g. the spinner before JS executes) does not flash a different theme.
   ============================================================ */

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

/* ── Structural tokens (theme-independent) ─────────────────────── */
:root {
    color-scheme: dark; /* per-theme override on the [data-theme] blocks below */

    /* Radius scale */
    --radius-xs:    4px;
    --radius-sm:    8px;
    --radius-md:    12px;
    --radius-lg:    20px;
    --radius-pill:  999px;
    --radius:       12px; /* legacy alias = --radius-md */

    /* Spacing scale — 8px-based */
    --space-0:     0;
    --space-0-25:  .125rem;  /* 2px — chips/badges/density dots */
    --space-0-5:   .25rem;   /* 4px */
    --space-1:     .5rem;    /* 8px */
    --space-1-5:   .75rem;   /* 12px */
    --space-2:     1rem;     /* 16px */
    --space-2-5:   1.25rem;  /* 20px */
    --space-3:     1.5rem;   /* 24px */
    --space-4:     2rem;     /* 32px */
    --space-5:     2.5rem;   /* 40px */
    --space-6:     3rem;     /* 48px */
    --space-7:     3.5rem;   /* 56px — between space-6 and space-8 */
    --space-8:     4rem;     /* 64px */

    /* Section + component layout rhythm */
    --section-py:  clamp(3rem, 6vw, 5.5rem);   /* vertical padding for landing sections */
    --card-padding: clamp(1rem, 2vw, 1.5rem);   /* inner padding for cards */

    /* Typography — discrete scale */
    --text-micro:  .72rem;
    --text-xs:     .75rem;
    --text-sm:     .875rem;
    --text-md:     1rem;
    --text-lg:     1.25rem;
    --text-xl:     1.5rem;
    --text-2xl:    2rem;
    --text-3xl:    2.6rem;

    /* Typography — fluid scale (clamp) */
    --text-fluid-h1:   clamp(2rem, 7.5vw, 3.4rem);
    --text-fluid-h2:   clamp(1.35rem, 5.5vw, 2rem);
    --text-fluid-h3:   clamp(1.15rem, 4.5vw, 1.5rem);
    --text-fluid-auth: clamp(1.6rem, 6vw, 2.2rem);
    --text-fluid-body: clamp(0.9375rem, 1.4vw, 1rem);

    /* Phase 2C: named semantic scale — maps to existing fluid sizes for now */
    --fs-display:   var(--text-fluid-h1);           /* H1 hero title */
    --fs-heading-2: clamp(1.125rem, 1vw + 0.9rem, 1.375rem); /* section h2: 18→22px */
    --fs-heading-3: clamp(1rem, 0.5vw + 0.85rem, 1.125rem);  /* card h3: 16→18px */
    --fs-body:      var(--text-fluid-body);         /* body copy */
    --fs-small:     0.875rem;                       /* utility text */
    --lh-display:   1.1;
    --lh-heading:   1.3;
    --lh-body:      1.55;
    --ls-display:   -0.03em;

    /* Layout */
    --navbar-h:        56px;
    --content-max:     680px;
    --form-max:        440px;
    --nav-cluster-gap: 14px;
    --content-rail:    min(100%, 72rem);
    --gutter-x:        clamp(0.875rem, 2.5vw, 1.75rem);

    /* Breakpoints (Phase 16b token system) */
    --bp-sm:  20rem;
    --bp-md:  48rem;
    --bp-lg:  64rem;
    --bp-xl:  90rem;
    --bp-xxl: 100rem;

    /* Motion */
    --transition:      .15s ease; /* legacy alias */
    --duration-fast:   120ms;
    --duration-base:   180ms;
    --duration-slow:   260ms;
    --duration-page:   400ms;
    --easing-default:  ease;
    --easing-emphasis: cubic-bezier(.2, .8, .2, 1);

    /* Touch targets */
    --touch-target-min:         44px;
    --touch-target-comfortable: 48px;

    /* Safe-area insets (notch / gesture bar) */
    --safe-area-top:    env(safe-area-inset-top, 0px);
    --safe-area-bottom: env(safe-area-inset-bottom, 0px);
    --safe-area-left:   env(safe-area-inset-left, 0px);
    --safe-area-right:  env(safe-area-inset-right, 0px);

    /* Mobile chrome heights */
    --m-topbar-h:        56px;
    --m-bottomnav-h:     64px;
    --m-bottomnav-total: calc(64px + env(safe-area-inset-bottom, 0px));
    --bottom-sheet-radius: var(--radius-lg);

    /* ── Color tokens — dark fallback (unattributed <html>) ───── */
    /* Mirror the [data-theme="dark"] block below so a page rendered
       before the boot script flips the attribute still works. */
    --bg:          #0b0b10;
    --surface:     #16161e;
    --surface-alt: #1f1f2a;
    --surface-hi:  #262633;
    --border:      #2a2a36;
    --border-soft: #20202b;

    --accent:      #7c6ef5;
    --accent-dim:  #5a50c4;
    --accent-glow: rgba(124, 110, 245, 0.18);

    --text:           #e8e8f0;
    --text-on-accent: #fff;
    --muted:          #b8b8d0;
    --muted-2:        #8888a3;

    --danger:       #e05c5c;
    --success:      #4caf7d;
    --info:         #5fa8e0;
    --info-glow:    rgba(95, 168, 224, .18);
    --warning:      #e09a3a;
    --warning-glow: rgba(224, 154, 58, .18);

    --accent-text:   #b3a4ff;
    --danger-text:   #f7a3a3;
    --success-text:  #82d8a8;
    --info-text:     #9ed1f3;
    --warning-text:  #f3c184;

    --shadow-sm:     0 2px 8px rgba(0,0,0,.3);
    --shadow:        0 4px 24px rgba(0,0,0,.45);
    --shadow-lg:     0 12px 60px rgba(0,0,0,.65);
    --shadow-drawer: -8px 0 32px rgba(0,0,0,.45);
    --scrim:         rgba(0,0,0,.65);

    --focus-ring:    0 0 0 3px var(--accent-glow);
    --focus-outline: 2px solid var(--accent);
    --focus-ring-color: var(--accent);
    --focus-ring-width: 2px;
    --focus-ring-width-primary: 3px;

    --brand-gradient: linear-gradient(135deg, #7c6ef5 0%, #b48ef5 60%, #e0c3fc 100%);

    /* Ambient hero orbs */
    --orb-1: rgba(124, 110, 245, 0.22);
    --orb-2: rgba(180, 142, 245, 0.18);
}

/* ── Dark palette ─────────────────────────────────────────────── */
:root[data-theme="dark"] {
    color-scheme: dark;

    --bg:           #0b0b10;
    --surface:      #16161e;
    --surface-alt:  #1f1f2a;
    --surface-hi:   #262633;
    --border:       #2a2a36;
    --border-soft:  #20202b;
    --border-strong: #3d3d52;  /* APCA Lc ≈17 vs --surface; use for card edges + section dividers */

    --accent:      #7c6ef5;
    --accent-dim:  #5a50c4;
    --accent-glow: rgba(124, 110, 245, 0.18);

    --text:           #e8e8f0;
    --text-on-accent: #fff;
    --muted:          #b8b8d0;
    --muted-2:        #8888a3;

    --danger:       #e05c5c;
    --success:      #4caf7d;
    --info:         #5fa8e0;
    --info-glow:    rgba(95, 168, 224, .18);
    --warning:      #e09a3a;
    --warning-glow: rgba(224, 154, 58, .18);

    --accent-text:   #b3a4ff;
    --danger-text:   #f7a3a3;
    --success-text:  #82d8a8;
    --info-text:     #9ed1f3;
    --warning-text:  #f3c184;

    --shadow-sm:     0 2px 8px rgba(0,0,0,.3);
    --shadow:        0 4px 24px rgba(0,0,0,.45);
    --shadow-lg:     0 12px 60px rgba(0,0,0,.65);
    --shadow-drawer: -8px 0 32px rgba(0,0,0,.45);
    --scrim:         rgba(0,0,0,.65);

    --focus-ring:    0 0 0 3px var(--accent-glow);
    --focus-outline: 2px solid var(--accent);
    --focus-ring-color: var(--accent);
    --focus-ring-width: 2px;
    --focus-ring-width-primary: 3px;

    --brand-gradient: linear-gradient(135deg, #7c6ef5 0%, #b48ef5 60%, #e0c3fc 100%);
    --orb-1: rgba(124, 110, 245, 0.22);
    --orb-2: rgba(180, 142, 245, 0.18);
}

/* ── Light palette ────────────────────────────────────────────── */
/* APCA verification (light fg/bg):
     --text  (#14141c) on --bg (#fafafd) → Lc -100 abs (excellent)
     --muted (#54546a) on --bg (#fafafd) → Lc  -65 abs (small text safe)
     --accent-text (#4438a9) on --bg     → Lc  -72 abs (body safe) */
:root[data-theme="light"] {
    color-scheme: light;

    --bg:           #fafafd;
    --surface:      #ffffff;
    --surface-alt:  #f4f5f8;
    --surface-hi:   #ebecf1;
    --border:       #e2e3eb;
    --border-soft:  #ededf3;
    --border-strong: #e2e3eb;  /* matches --border in light mode; reserved for structural boundaries */

    --accent:      #5246c2;
    --accent-dim:  #3d349c;
    --accent-glow: rgba(82, 70, 194, 0.16);

    --text:           #14141c;
    --text-on-accent: #ffffff;
    --muted:          #54546a;
    --muted-2:        #636380; /* darkened from #80809a: 3.52:1→5.4:1 vs --surface-alt #f4f5f8 (WCAG AA) */

    --danger:       #c43838;
    --success:      #2a7c52;
    --info:         #1e6cb0;
    --info-glow:    rgba(30, 108, 176, .12);
    --warning:      #b87420;
    --warning-glow: rgba(184, 116, 32, .14);

    --accent-text:   #4438a9;
    --danger-text:   #a31e1e;
    --success-text:  #1c6d44;
    --info-text:     #155583;
    --warning-text:  #7a4710;

    --shadow-sm:     0 1px 2px rgba(20,20,30,.06), 0 2px 8px rgba(20,20,30,.06);
    --shadow:        0 4px 16px rgba(20,20,30,.10);
    --shadow-lg:     0 16px 48px rgba(20,20,30,.16);
    --shadow-drawer: -8px 0 24px rgba(20,20,30,.14);
    --scrim:         rgba(15,15,25,.55);

    --focus-ring:    0 0 0 3px var(--accent-glow);
    --focus-outline: 2px solid var(--accent);
    --focus-ring-color: var(--accent);
    --focus-ring-width: 2px;
    --focus-ring-width-primary: 3px;

    /* Brand gradient retuned for light theme so all stops clear APCA Lc 45+
       on white. Smallest use site is .navbar-brand at 1.1rem/800. */
    --brand-gradient: linear-gradient(135deg, #3d349c 0%, #5246c2 60%, #7c6ef5 100%);
    --orb-1: rgba(124, 110, 245, 0.18);
    --orb-2: rgba(196, 168, 245, 0.20);
}

/* ── Dark mode: --border-strong on structural separators ────────────────────
   In dark mode, --border (#2a2a36, Lc ~10) is barely visible against
   --surface (#16161e). --border-strong (#3d3d52, Lc ~17) lifts card
   edges and section boundaries to just-perceptible, satisfying
   WCAG 1.4.11 Non-text Contrast (≥3:1) for UI component boundaries.
   Light mode: --border-strong == --border (no visual change).            */
:root[data-theme="dark"] .card {
    border-color: var(--border-strong);
}
:root[data-theme="dark"] .settings-drawer {
    border-inline-start-color: var(--border-strong);
}
:root[data-theme="dark"] .my-drops-section-divider {
    border-top-color: var(--border-strong);
}

/* F-154 retained: nav-cluster divider blend for legibility in light theme. */
:root[data-theme="light"] .navbar-actions .nav-cluster + .nav-cluster::before {
    background: color-mix(in srgb, var(--border) 60%, var(--text) 40%);
}

/* F-011 retained: brand gradient retune for light theme. Hard-coded gradient
   stops in .navbar-brand / .hero h1 / .caploo-wordmark / .progress-fill end
   at near-white on dark theme; on light bg they vanish. Override with
   accent-dim → accent → medium-purple stops — all clear APCA Lc 45+ on white.
   These rules will collapse into a single `var(--brand-gradient)` usage when
   the navbar/hero sections are rewritten in the handoff migration. */
:root[data-theme="light"] .navbar-brand {
    background: linear-gradient(135deg, var(--accent-dim) 0%, var(--accent) 100%);
    -webkit-background-clip: text;
    background-clip: text;
}
:root[data-theme="light"] .hero h1:not(.hero__title),
:root[data-theme="light"] .caploo-wordmark {
    background: linear-gradient(135deg, var(--accent-dim) 0%, var(--accent) 50%, #6e54d0 100%);
    -webkit-background-clip: text;
    background-clip: text;
}
:root[data-theme="light"] .progress-fill {
    background: linear-gradient(90deg, var(--accent-dim), var(--accent));
}

/* ============================================================
   Reset / base
   ============================================================ */

html, body { overflow-x: clip; }
html, body { height: 100%; }
body {
    margin: 0;
    font-family: 'Inter', system-ui, -apple-system,
        "Noto Sans KR", "Apple SD Gothic Neo", "Malgun Gothic", sans-serif;
    font-size: var(--text-fluid-body);
    line-height: 1.5;
    color: var(--text);
    background: var(--bg);
    overflow-wrap: anywhere;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    /* Ambient depth — radial wash behind everything */
    background-image:
        radial-gradient(60rem 40rem at 80% -10%, var(--orb-2), transparent 60%),
        radial-gradient(50rem 35rem at 10% 0%, var(--orb-1), transparent 65%);
    background-attachment: fixed;
}

img { max-width: 100%; display: block; }
button { font: inherit; color: inherit; }

/* Native form elements — keep iOS-zoom-safe 16px floor.
   Use .input / .textarea / .select for the new component styling. */
input, select, textarea { font-family: inherit; font-size: max(16px, 1rem); }

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

/* Focus styles */
:focus { outline: none; }
:focus-visible {
    outline: var(--focus-ring-width, 2px) solid var(--focus-ring-color, var(--accent));
    outline-offset: 2px;
    border-radius: var(--radius-xs);
}

/* High-contrast escalation — WCAG 1.4.11 / APCA Lc ≥75 */
@media (prefers-contrast: more) {
    :root {
        --focus-ring-width: 3px;
        --focus-ring-width-primary: 4px;
    }
    :focus-visible {
        outline-offset: 3px;
    }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

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

/* Skip-to-content (WCAG 2.4.1) */
.skip-link {
    position: absolute; left: var(--space-2); top: var(--space-2);
    background: var(--accent); color: var(--text-on-accent);
    padding: 8px 14px; border-radius: var(--radius-sm);
    font-size: var(--text-sm); font-weight: 600;
    text-decoration: none;
    transform: translateY(-200%);
    transition: transform var(--duration-base) var(--easing-emphasis);
    z-index: 500;
}
.skip-link:focus-visible { transform: translateY(0); }

/* ── No-JS fallback ─────────────────────────────────────────── */
.noscript-fallback {
    max-width: 480px;
    margin: 4rem auto;
    padding: 2rem;
    text-align: center;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
}

/* F-009 — body scroll-lock: applied by CaplooMenu.attachScrollLock when a
   mobile bottom sheet (NavOverflowSheet, LanguageMenu) is open. Removed by
   detachScrollLock on close. Only fires at ≤768px (JS guards on innerWidth). */
.scroll-locked { overflow: hidden; }

/* ============================================================
   App shell
   ============================================================ */

.app-shell { min-height: 100vh; display: flex; flex-direction: column; }
.page {
    flex: 1; width: 100%;
    padding: var(--space-3) var(--gutter-x) var(--space-8);
    padding-bottom: calc(var(--space-8) + env(safe-area-inset-bottom, 0px));
}
.page__inner { width: var(--content-rail); margin: 0 auto; }

/* ============================================================
   Buttons — handoff component classes (.btn-*).
   Native <button> retains legacy width:100% + accent fill so existing
   Razor pages that emit bare <button> still work; new code should use
   .btn-primary / .btn-secondary / .btn-ghost explicitly.
   ============================================================ */

.btn-primary, .btn-secondary {
    display: inline-flex; align-items: center; justify-content: center;
    gap: var(--space-1);
    min-height: var(--touch-target-min);
    padding: 0 var(--space-2-5);
    border-radius: var(--radius-pill);
    border: 1px solid transparent;
    font-weight: 600;
    font-size: var(--text-md);
    cursor: pointer;
    text-decoration: none;
    letter-spacing: -0.005em;
    transition: transform var(--duration-fast),
                background var(--duration-base),
                box-shadow var(--duration-base),
                border-color var(--duration-base);
    width: auto; /* override the legacy bare-<button> width:100% */
}

.btn-primary {
    background: var(--accent);
    color: var(--text-on-accent);
    box-shadow: 0 8px 24px var(--accent-glow);
}
.btn-primary:hover { background: var(--accent-dim); transform: translateY(-1px); box-shadow: 0 12px 30px var(--accent-glow); }
.btn-primary:active { transform: translateY(0); }
.btn-primary:disabled { opacity: .5; cursor: not-allowed; transform: none; box-shadow: none; }
@media (prefers-reduced-motion: reduce) {
    .btn-primary:hover, .btn-primary:active { transform: none; }
}

.btn-primary--landing,
a.btn-primary--landing {
    width: 100%;
    padding-block: var(--space-1-5);
    min-height: 52px;
    font-size: var(--text-md);
}
.btn-primary--landing:hover { text-decoration: none; }
.btn-primary--landing:focus-visible { box-shadow: var(--focus-ring); outline: none; }

/* Phase 2C — Von Restorff accent glow: makes the primary CTA the clear visual
   anchor. Filtered radial bloom behind the button, pointer-events: none. */
@media (prefers-reduced-motion: no-preference) {
    .btn-primary--landing {
        position: relative;
        isolation: isolate;
    }
    .btn-primary--landing::before {
        content: "";
        position: absolute;
        inset: -12px -20px;
        background: radial-gradient(ellipse at center, rgba(124, 110, 245, 0.28), transparent 70%);
        filter: blur(18px);
        pointer-events: none;
        z-index: -1;
        border-radius: inherit;
    }
}

.btn-secondary {
    background: var(--surface);
    color: var(--text);
    border-color: var(--border);
}
.btn-secondary:hover { border-color: var(--accent); background: var(--surface-alt); }
.btn-secondary:active { transform: translateY(1px); }
.btn-secondary:disabled { opacity: .55; cursor: not-allowed; transform: none; }

.btn-secondary--strong {
    background: color-mix(in srgb, var(--accent) 12%, var(--surface));
    border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
    color: var(--accent-text);
    font-weight: 600;
}
.btn-secondary--strong:hover {
    background: color-mix(in srgb, var(--accent) 20%, var(--surface));
    border-color: var(--accent);
    color: var(--accent-text);
}

.btn-ghost {
    background: transparent;
    border: 1px solid transparent;
    color: var(--muted);
    padding: 8px 12px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: var(--text-sm);
    font-weight: 500;
}
.btn-ghost:hover { color: var(--text); background: var(--surface-alt); }

.icon-btn {
    background: transparent;
    border: 1px solid transparent;
    padding: 0;
    cursor: pointer;
    line-height: 1;
    color: inherit;
    border-radius: var(--radius-xs);
    transition: color var(--transition), border-color var(--transition);
}

.btn-danger {
    background: var(--danger);
    color: #fff;
    border: 1px solid var(--danger);
    border-radius: var(--radius-pill);
    min-height: var(--touch-target-min);
    padding: 0 var(--space-2-5);
    font-weight: 600;
    cursor: pointer;
    transition: background var(--duration-base), transform var(--duration-fast);
}
.btn-danger:hover { background: color-mix(in srgb, var(--danger) 80%, black); transform: translateY(-1px); }
.btn-danger:disabled { opacity: .55; cursor: not-allowed; transform: none; }

@media (pointer: coarse) {
    .btn-primary, .btn-secondary, .btn-danger { min-height: var(--touch-target-comfortable); }
}

/* Legacy bare-<button> styling — preserved so existing Razor pages that
   emit untagged <button> still render. Specificity is lower than .btn-*.
   Nav trigger buttons (.nav-overflow-trigger, .nav-tray-trigger) are explicitly
   excluded: they have their own background rules that this catch-all overrides
   due to its high specificity from the :not() chain.
   NavOverflowSheet row/close buttons are excluded for the same reason —
   they are transparent menu rows, not accent CTAs.
   UX-6B: added caploo-toggle-switch, caploo-composer-ai__photo-card-btn,
   help-drawer__close, mobile-composer-page__attach-btn — all have their own
   neutral backgrounds that must not be overridden with --accent. */
button:not(.btn-primary):not(.btn-secondary):not(.btn-ghost):not(.btn-danger):not(.icon-btn):not(.mbn__item):not(.mbn__fab):not(.unstyled):not(.nav-overflow-trigger):not(.nav-tray-trigger):not(.toast__close):not(.share-trigger-btn):not(.duration-stepper__btn):not(.custom-duration__btn):not(.nav-settings):not(.nav-overflow-sheet__row):not(.nav-overflow-sheet__close):not(.caploo-toggle-switch):not(.caploo-composer-ai__photo-card-btn):not(.help-drawer__close):not(.mobile-composer-page__attach-btn) {
    cursor: pointer;
    border: none;
    border-radius: var(--radius-sm);
    padding: .7rem 1.25rem;
    min-height: 44px;
    font-size: var(--text-md);
    font-family: inherit;
    background: var(--accent);
    color: var(--text-on-accent);
    transition: background var(--transition), opacity var(--transition), transform var(--transition);
}
button:not(.btn-primary):not(.btn-secondary):not(.btn-ghost):not(.btn-danger):not(.icon-btn):not(.mbn__item):not(.mbn__fab):not(.unstyled):not(.nav-overflow-trigger):not(.nav-tray-trigger):not(.toast__close):not(.share-trigger-btn):not(.duration-stepper__btn):not(.custom-duration__btn):not(.nav-settings):not(.nav-overflow-sheet__row):not(.nav-overflow-sheet__close):not(.caploo-toggle-switch):not(.caploo-composer-ai__photo-card-btn):not(.help-drawer__close):not(.mobile-composer-page__attach-btn):hover:not(:disabled) {
    background: var(--accent-dim);
}
button:not(.btn-primary):not(.btn-secondary):not(.btn-ghost):not(.btn-danger):not(.icon-btn):not(.mbn__item):not(.mbn__fab):not(.unstyled):not(.nav-overflow-trigger):not(.nav-tray-trigger):not(.toast__close):not(.share-trigger-btn):not(.duration-stepper__btn):not(.custom-duration__btn):not(.nav-settings):not(.nav-overflow-sheet__row):not(.nav-overflow-sheet__close):not(.caploo-toggle-switch):not(.caploo-composer-ai__photo-card-btn):not(.help-drawer__close):not(.mobile-composer-page__attach-btn):active:not(:disabled) {
    transform: scale(.98);
}
button:not(.btn-primary):not(.btn-secondary):not(.btn-ghost):not(.btn-danger):not(.icon-btn):not(.mbn__item):not(.mbn__fab):not(.unstyled):not(.nav-overflow-trigger):not(.nav-tray-trigger):not(.toast__close):not(.share-trigger-btn):not(.duration-stepper__btn):not(.custom-duration__btn):not(.nav-settings):not(.nav-overflow-sheet__row):not(.nav-overflow-sheet__close):not(.caploo-toggle-switch):not(.caploo-composer-ai__photo-card-btn):not(.help-drawer__close):not(.mobile-composer-page__attach-btn):disabled {
    opacity: .45; cursor: not-allowed;
}

/* ============================================================
   Cards & forms — handoff classes
   ============================================================ */

.card {
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-md);
    padding: var(--space-3);
    box-shadow: var(--shadow-sm);
}

.field { display: grid; gap: 6px; margin-bottom: var(--space-2); }
.field > label {
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--muted);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.field small { font-size: var(--text-xs); color: var(--muted-2); }
/* E3 (2026-05-30 audit): the empty <span class="req-dot"> needs box
   dimensions to render — the prior color-only rule collapsed it to 0px
   and the required-field indicator never showed. Matches handoff. */
.req-dot {
    display: inline-block;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--accent);
    margin-left: 4px;
    vertical-align: middle;
}

.input, .textarea, .select {
    width: 100%;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    color: var(--text);
    border-radius: var(--radius-sm);
    padding: 11px 13px;
    font-size: max(16px, 1rem);
    transition: border-color var(--duration-base), box-shadow var(--duration-base), background var(--duration-base);
    min-height: var(--touch-target-min);
}
.input::placeholder, .textarea::placeholder { color: var(--muted-2); }
.input:hover, .textarea:hover, .select:hover { border-color: color-mix(in srgb, var(--accent) 30%, var(--border)); }
.input:focus-visible, .textarea:focus-visible, .select:focus-visible {
    border-color: var(--accent);
    outline: none;
    box-shadow: var(--focus-ring);
    background: var(--surface);
}

.input[aria-invalid="true"],
.textarea[aria-invalid="true"],
.select[aria-invalid="true"] {
    border-color: var(--danger);
    background: color-mix(in srgb, var(--danger) 5%, var(--surface-alt));
}
.input[aria-invalid="true"]:focus-visible,
.textarea[aria-invalid="true"]:focus-visible,
.select[aria-invalid="true"]:focus-visible {
    border-color: var(--danger);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 20%, transparent);
}
.textarea { resize: vertical; min-height: 96px; line-height: 1.5; }

.pin-input {
    letter-spacing: 0.24em;
    text-transform: uppercase;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    text-align: center;
    font-weight: 700;
}

.field-error {
    color: var(--warning-text);
    font-size: var(--text-xs);
    margin-top: 6px;
    display: flex; gap: 6px; align-items: flex-start;
}

.error[role="alert"] {
    color: var(--danger-text);
    background: color-mix(in srgb, var(--danger) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
    display: flex; gap: 8px; align-items: flex-start;
}

/* Inline error variant — small, tinted-text-only, no banner. Used for
   transient action errors that sit directly under the failing control
   (e.g. "export failed" under a drop-card export menu). Avoids the
   inline style="font-size:.8rem;margin-top:.4rem" pattern we'd otherwise
   need to repeat per page. */
.error--inline {
    color: var(--danger-text);
    background: transparent;
    border: 0;
    padding: 0;
    font-size: var(--text-xs);
    margin-top: var(--space-0-5);
}

/* Empty-state utility — taller padding for full-route empty surfaces
   (e.g. RecapPage's "no recap yet"). Use on the .empty-state container
   to upgrade from card-default padding to a route-level breathing room. */
.empty-state--page {
    padding: var(--space-6) var(--space-2);
}

/* Native form fallback — preserved for bare <input>/<textarea> that
   haven't been migrated to .input/.textarea classes yet. Specificity
   stays low so .input wins where it's applied. */
input:not(.input):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]),
select:not(.select),
textarea:not(.textarea) {
    width: 100%;
    padding: .65rem .9rem;
    min-height: 44px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    font-family: inherit;
    outline: none;
    transition: border-color var(--transition), box-shadow var(--transition);
}
input:not(.input):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):focus,
select:not(.select):focus,
textarea:not(.textarea):focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-glow);
}
input::placeholder { color: var(--muted); }

label {
    display: flex;
    flex-direction: column;
    gap: .4rem;
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--muted);
}

/* ── Animations ───────────────────────────────────── */
@keyframes fadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes fadeSlideUp {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: translateY(0); }
}

@keyframes fadeSlideDown {
    from { opacity: 0; transform: translateY(-10px); }
    to   { opacity: 1; transform: translateY(0); }
}

@keyframes shimmer {
    0%   { background-position: -400px 0; }
    100% { background-position: 400px 0; }
}

@keyframes pulse {
    0%, 100% { box-shadow: 0 0 0 0 var(--accent-glow); }
    50%       { box-shadow: 0 0 0 8px transparent; }
}

/* ── Shared utilities ─────────────────────────────── */
/* Phase 8.4 — these shared status utilities sit on dark surfaces.
   Use the paired *-text variants so foreground text clears APCA Lc 60+;
   the original --danger / --success tokens stay for solid fills. */
.error   { color: var(--danger-text);  font-size: var(--text-sm); text-align: center; }
.success { color: var(--success-text); font-size: var(--text-sm); display: flex; align-items: center; gap: .4rem; }
.loading { color: var(--muted);        text-align: center; padding: 2rem 0; }

.empty-state {
    text-align: center;
    padding: 4rem 1rem;
    color: var(--muted);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .75rem;
}
.empty-state svg {
    width: 48px;
    height: 48px;
    opacity: .35;
    flex-shrink: 0;
}
.empty-state p { font-size: var(--text-md); color: var(--muted); }
.empty-state .btn-empty-cta,
a.btn-empty-cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-top: .5rem;
    height: 36px;
    padding: 0 1.25rem;
    background: var(--accent);
    color: var(--text-on-accent);
    border: none;
    border-radius: var(--radius-pill);
    font-size: var(--text-sm, 13px);
    font-weight: 500;
    cursor: pointer;
    text-decoration: none;
    transition: background-color var(--duration-base);
}
.empty-state .btn-empty-cta:hover,
a.btn-empty-cta:hover { background: var(--accent-dim); text-decoration: none; }
.empty-state .btn-empty-cta:focus-visible,
a.btn-empty-cta:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}

/* ── Skeleton loading utility ── E-002 ──────────────────────────────────── */
/* Reusable shimmer skeleton for mobile loading states.                       */
/* Always add aria-hidden="true" to skeleton wrappers — they are decorative.  */

.skeleton {
    background: linear-gradient(
        90deg,
        var(--surface) 25%,
        var(--surface-alt) 50%,
        var(--surface) 75%
    );
    background-size: 400px 100%;
    animation: shimmer 1.4s infinite linear;
    border-radius: var(--radius-sm, 4px);
}

@media (prefers-reduced-motion: reduce) {
    .skeleton {
        animation: none;
        background: var(--surface-alt, #f3f4f6);
    }
}

/* Skeleton card — mimics a MomentCard / feed card footprint */
.skeleton-card {
    aspect-ratio: 4/3;
    border-radius: var(--radius, 8px);
    border: 1px solid var(--border);
}

/* Skeleton row — mimics a single list-row item (drops, notifications) */
.skeleton-row {
    height: 64px;
    border-radius: var(--radius-sm, 4px);
}

/* Skeleton text line — mimics a paragraph text line */
.skeleton-text {
    height: 14px;
    border-radius: var(--radius-xs, 2px);
}

/* Skeleton avatar — mimics a circular avatar chip */
.skeleton-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    flex-shrink: 0;
}

/* Skeleton list layout — vertical stack of skeleton-row items */
.skeleton-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2, 8px);
    padding: var(--space-2, 8px) 0;
}

/* ── Navbar ───────────────────────────────────────── */
/* BUG-FIX: backdrop-filter moved to ::before so the element itself does not
   create a filter stacking context. On iOS Safari a parent with backdrop-filter
   directly applied causes absolutely-positioned children (e.g. .nav-tray popup)
   to render blurry / with transparent backgrounds even when those children have
   a solid opaque background. Using ::before isolates the glass effect behind the
   content while all child elements render in a clean, unfiltered context. */
.navbar {
    height: var(--navbar-h);
    background: transparent;         /* visual bg moved to ::before */
    display: flex;
    align-items: center;
    justify-content: space-between;   /* Phase 6.4 — anchor brand left, actions right */
    gap: var(--space-2);
    padding: 0 1.5rem;
    position: sticky;
    top: 0;
    z-index: 100;
    overflow: visible;               /* allow .nav-tray popup to extend below navbar */
}
.navbar::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: color-mix(in srgb, var(--surface) 85%, transparent);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border-bottom: 1px solid var(--border);
    pointer-events: none;
}

/* Phase 6.4 — page-level action slot in the navbar (top-right corner).
   Pages push controls (Settings, etc.) here via <SectionContent
   SectionName="navbar-actions">.  Uses gap rather than margins so each
   action keeps its hit area clean (Fitts's Law). */
.navbar-actions {
    display: flex;
    align-items: center;
    gap: var(--nav-cluster-gap);
}
.navbar-actions:empty {
    display: none;   /* prevents an empty slot from stealing layout space */
}

/* Phase 19b - action-band IA. Three labelled clusters with a strict
   left-to-right priority order:
     1. .nav-cluster--context  - page-supplied actions (Settings, Share).
     2. .nav-cluster--tools    - global utilities (Language, Help) - always visible.
     3. .nav-cluster--account  - identity-scoped (Notifications, auth/user).
   Inside a cluster, controls sit tight at --space-1; between clusters they
   sit at --nav-cluster-gap with a thin divider rule for visual grouping.
   The divider is muted by default and disappears below 480 px so the band
   stays clean down to 320 px. See docs/phase-19/header-nav.md. */
.navbar-actions .nav-cluster {
    display: flex;
    align-items: center;
    gap: var(--space-1, 8px);
}
.navbar-actions .nav-cluster:empty { display: none; }
/* F-020: :empty alone misses whitespace text-nodes from Blazor's render; :has(*) is
   element-only so it reliably hides context clusters when SectionOutlet is vacant. */
.navbar-actions .nav-cluster--context:not(:has(*)) { display: none; }
/* M-012: when context cluster is empty (display:none) the adjacent-sibling
   combinator on .nav-cluster--tools still fires, adding the 14px gap + divider
   to the tools cluster even though nothing precedes it visually. Remove them. */
.navbar-actions .nav-cluster--context:not(:has(*)) + .nav-cluster--tools {
    padding-inline-start: 0;
}
.navbar-actions .nav-cluster--context:not(:has(*)) + .nav-cluster--tools::before {
    display: none;
}
.navbar-actions .nav-cluster + .nav-cluster {
    position: relative;
    padding-inline-start: var(--nav-cluster-gap);
}
.navbar-actions .nav-cluster + .nav-cluster::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 1px;
    height: 18px;
    background: color-mix(in srgb, var(--border) 80%, transparent);
    pointer-events: none;
}
@media (forced-colors: active) {
    .navbar-actions .nav-cluster + .nav-cluster::before { background: CanvasText; }
}
/* Narrow widths - drop the cluster divider and tighten the band so we never
   clip at 320 px. Inner cluster spacing collapses but stays >= 4 px so 44 px
   touch targets keep enough breathing room. */
@media (max-width: 480px) {
    .navbar-actions { gap: var(--space-1, 8px); }
    .navbar-actions .nav-cluster + .nav-cluster { padding-inline-start: 0; }
    .navbar-actions .nav-cluster + .nav-cluster::before { display: none; }
    .navbar-actions .nav-cluster { gap: 4px; }
}

.navbar-brand {
    display: flex;
    align-items: center;
    gap: .45rem;
    font-weight: 800;
    font-size: 1.1rem;
    letter-spacing: -.02em;
    background: linear-gradient(135deg, var(--accent) 0%, #b48ef5 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    text-decoration: none;
}
.navbar-brand:hover { text-decoration: none; opacity: .85; }

/* ── Home page ────────────────────────────────────── */
.home-container {
    max-width: 440px;
    margin: 0 auto;
    padding: 3rem 1.25rem 5rem;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    animation: fadeSlideUp .4s ease forwards;
}

.hero {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .5rem;
    position: relative;
    padding: .5rem 0 .75rem;
}
.hero::before {
    content: '';
    position: absolute;
    inset: -40px -80px;
    background: radial-gradient(ellipse at center, var(--accent-glow) 0%, transparent 70%);
    pointer-events: none;
    z-index: -1;
}

.hero h1:not(.hero__title) {
    font-size: var(--text-fluid-h1, clamp(1.7rem, 7.5vw, 2.8rem));
    font-weight: 800;
    letter-spacing: -.02em;
    word-spacing: 0.02em;
    text-wrap: balance;
    background: linear-gradient(135deg, var(--accent) 0%, #b48ef5 60%, #e0c3fc 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    line-height: 1.1;
}

.tagline {
    text-align: center;
    color: var(--muted);
    font-size: .95rem;
}

.card {
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius);
    padding: 1.5rem;
    display: flex;
    flex-direction: column;
    gap: .9rem;
    box-shadow: var(--shadow-sm);
    transition: border-color var(--transition);
}
.card:focus-within { border-color: color-mix(in srgb, var(--accent) 40%, transparent); }

.card h2 {
    font-size: .8rem;
    font-weight: 700;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .08em;
}

/* F-025: The join-card heading is a structural h2, not a decorative label.
   Lift it to readable size + text colour while keeping the letter-spacing. */
#join-heading {
    font-size: var(--text-sm, .875rem);
    color: var(--text);
    letter-spacing: .05em;
}

/* Primary card (Create Drop) gets a border top */
.card-primary {
    border-top: 2px solid var(--border);
}

/* Secondary button variant for Join card */
.btn-secondary {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text);
}

/* Join card submit: accent-tinted border so the button reads as a button in
   dark mode (--border alone is rgb(46,46,58) — nearly invisible on the card). */
#join > .btn-secondary {
    /* !important beats any equal-specificity rule that appears later in the
       cascade (color-mix+var() doesn't self-promote without it in Chromium). */
    border-color: color-mix(in srgb, var(--accent) 45%, var(--border)) !important;
    background: color-mix(in srgb, var(--accent) 6%, transparent) !important;
}
.btn-secondary:hover:not(:disabled) {
    background: var(--surface-alt);
    border-color: var(--border);
}

.divider {
    text-align: center;
    color: var(--muted);
    font-size: .8rem;
    position: relative;
    line-height: 1;
}
.divider::before, .divider::after {
    content: '';
    position: absolute;
    top: 50%;
    width: 44%;
    height: 1px;
    background: var(--border);
}
.divider::before { left: 0; }
.divider::after  { right: 0; }

/* ── Feed page ────────────────────────────────────── */
.banner {
    padding: .75rem 1.25rem;
    font-size: var(--text-sm);
    font-weight: normal;
    text-align: center;
}
.banner-expired {
    background: color-mix(in srgb, var(--danger) 12%, transparent);
    color: var(--danger);
    border-bottom: 1px solid color-mix(in srgb, var(--danger) 25%, transparent);
}

.drop-pin-banner {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    border-bottom: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
    padding: .55rem 1.25rem;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .75rem;
    font-size: var(--text-sm);
    color: var(--muted);
    flex-wrap: wrap;
}
.drop-pin-banner .drop-name {
    font-weight: 700;
    color: var(--text);
    font-size: .9rem;
}
.drop-pin-banner .pin-sep {
    color: var(--border);
}
.drop-pin-banner .pin {
    font-size: 1.35rem;
    font-weight: 800;
    letter-spacing: .2em;
    color: var(--accent);
    font-variant-numeric: tabular-nums;
}

.btn-copy {
    width: auto;
    padding: .3rem .8rem;
    font-size: .8rem;
    font-weight: normal;
    background: transparent;
    border: 1px solid var(--accent);
    color: var(--accent);
    border-radius: var(--radius-sm);
}
.btn-copy:hover:not(:disabled) {
    background: var(--accent-glow);
}
.btn-copy.copied {
    border-color: var(--success);
    color: var(--success);
}

/* Access-rotated banner — shown to guests when the host rotates any access secret */
.access-rotated-banner {
    display: flex;
    align-items: center;
    gap: .65rem;
    padding: .55rem 1rem;
    background: color-mix(in srgb, #e09a3a 14%, transparent);
    border-bottom: 1px solid color-mix(in srgb, #e09a3a 30%, transparent);
    color: var(--text);
    font-size: .85rem;
    flex-wrap: wrap;
    animation: fadeSlideUp .2s ease;
}
.access-rotated-banner svg { color: #e09a3a; flex-shrink: 0; }
.access-rotated-banner strong { color: var(--text); font-weight: normal; }
.access-rotated-banner__refresh {
    width: auto;
    padding: .3rem .8rem;
    font-size: .78rem;
    margin-inline-start: auto;
}
.access-rotated-banner__dismiss {
    width: 24px;
    height: 24px;
    padding: 0;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-xs);
    color: var(--muted);
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
    flex-shrink: 0;
    transition: color var(--transition), border-color var(--transition);
}
.access-rotated-banner__dismiss:hover {
    color: var(--text);
    border-color: var(--border);
}

.feed-layout {
    display: grid;
    grid-template-columns: 280px 1fr;
    min-height: calc(100vh - var(--navbar-h));    /* M-020 fallback */
    min-height: calc(100dvh - var(--navbar-h));   /* M-020: dvh follows iOS URL-bar */
}

/* Capture sidebar */
.capture-panel {
    background: var(--surface);
    border-inline-end: 1px solid var(--border);
    padding: 1.5rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 1rem;
    position: sticky;
    top: var(--navbar-h);
    height: fit-content;
    max-height: calc(100vh - var(--navbar-h));
    max-height: calc(100dvh - var(--navbar-h));
    overflow-y: auto;
}

.capture-panel h2 {
    font-size: var(--text-xs);
    font-weight: 700;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .08em;
}

.btn-capture {
    background: var(--accent);
    font-size: var(--text-md);
    padding: .85rem;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    border-radius: var(--radius-sm);
}
.btn-capture:hover:not(:disabled) {
    background: var(--accent-dim);
}
.btn-capture:not(:disabled) {
    animation: pulse 2.5s infinite;
}

.progress-bar {
    height: 5px;
    background: var(--border);
    border-radius: var(--radius-pill);
    overflow: hidden;
}
.progress-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--accent), #b48ef5);
    border-radius: var(--radius-pill);
    transition: width .3s ease;
}
.upload-status {
    font-size: .8rem;
    color: var(--muted);
    text-align: center;
}

/* Live feed */
.feed {
    padding: 1.5rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.feed-header {
    display: flex;
    align-items: center;
    gap: .5rem;
}
.feed-header__left {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex: 1 1 auto;
    min-width: 0;
}
.feed-header h2 {
    font-size: .8rem;
    font-weight: 700;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .08em;
}
.live-badge {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    font-size: var(--text-xs);
    font-weight: normal;
    color: var(--success);
}
.live-badge::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--success);
    animation: pulse 1.5s infinite;
}

.moments-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 1rem;
}

.btn-load-more {
    width: auto;
    align-self: center;
    padding: .55rem 2rem;
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text);
    font-size: .9rem;
    font-weight: 500;
}
.btn-load-more:hover:not(:disabled) {
    background: var(--surface);
    border-color: var(--accent);
}

/* ── Moment card ──────────────────────────────────── */
.moment-card {
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-md, 12px);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    box-shadow: none;
    transition: transform var(--duration-base) var(--easing-default),
                box-shadow var(--duration-base) var(--easing-default),
                border-color var(--duration-base) var(--easing-default);
    animation: fadeSlideUp .3s ease forwards;
}
@media (prefers-reduced-motion: no-preference) {
    .moment-card:hover {
        transform: translateY(-2px) scale(1.005);
        box-shadow: 0 4px 12px rgba(0,0,0,0.12);
        border-color: color-mix(in srgb, var(--accent) 30%, transparent);
    }
}
@media (prefers-reduced-motion: reduce) {
    .moment-card:hover {
        box-shadow: 0 4px 12px rgba(0,0,0,0.12);
        border-color: color-mix(in srgb, var(--accent) 30%, transparent);
    }
}

.moment-media {
    width: 100%;
    aspect-ratio: 4/3;
    object-fit: cover;
    display: block;
    background: var(--border);
}

.moment-meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: .6rem .85rem .3rem;
    font-size: .8rem;
}
.moment-meta .author { font-weight: normal; color: var(--text); }
.moment-meta .time   { color: var(--muted); font-variant-numeric: tabular-nums; }

.moment-caption {
    padding: 0 .85rem .75rem;
    font-size: var(--text-sm);
    color: var(--muted);
    line-height: 1.4;
}

/* ── Recap page ───────────────────────────────────── */
.recap-container {
    max-width: 680px;
    margin: 0 auto;
    padding: 2.5rem 1.25rem 5rem;
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    animation: fadeSlideUp .4s ease forwards;
}

.recap-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .75rem;
}

.recap-header h1 {
    font-size: var(--text-fluid-h1, var(--text-2xl));
    font-weight: 800;
    letter-spacing: -.03em;
    text-wrap: balance;
}

/* D-02 — Play story button in the recap header */
.recap-play-story-btn {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .5rem 1rem;
    background: var(--accent);
    color: var(--text-on-accent);
    border: none;
    border-radius: var(--radius-pill, 999px);
    font-size: var(--text-sm);
    font-weight: normal;
    cursor: pointer;
    white-space: nowrap;
    min-height: 44px;
    transition: background .15s, transform .1s;
    flex-shrink: 0;
}

.recap-play-story-btn:hover:not(:disabled) {
    background: var(--accent-dim, #6a5ed4);
}

.recap-play-story-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

@media (pointer: coarse) {
    .recap-play-story-btn { min-height: var(--touch-target-comfortable, 48px); }
}

.recap-meta {
    display: flex;
    align-items: center;
    gap: .75rem;
    color: var(--muted);
    font-size: var(--text-sm);
    flex-wrap: wrap;
}
.recap-meta .dot { color: var(--border); }

.timeline {
    display: flex;
    flex-direction: column;
    gap: 0;
    border-inline-start: 2px solid var(--border);
    margin-inline-start: .5rem;
    padding-inline-start: 1.5rem;
}

.timeline-item {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    padding-bottom: 2.25rem;
    position: relative;
    animation: fadeSlideUp .35s ease forwards;
}
.timeline-item::before {
    content: '';
    position: absolute;
    inset-inline-start: -1.875rem;
    top: .35rem;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--accent);
    border: 2px solid var(--bg);
    box-shadow: 0 0 0 3px var(--accent-glow);
}

.timeline-time {
    font-size: .8rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    font-weight: 500;
}

.timeline-content {
    display: flex;
    flex-direction: column;
    gap: .6rem;
}

.timeline-content .media {
    width: 100%;
    max-height: 420px;
    object-fit: cover;
    border-radius: var(--radius);
    background: var(--border);
    box-shadow: var(--shadow-sm);
}

.timeline-content .caption {
    font-size: .95rem;
    color: var(--text);
    line-height: 1.5;
}

.timeline-content .author {
    font-size: .8rem;
    color: var(--muted);
    font-weight: normal;
}

/* ── View-only bar ───────────────────────────────── */
.view-only-bar {
    background: color-mix(in srgb, var(--muted) 8%, transparent);
    border-bottom: 1px solid var(--border);
    padding: .55rem 1.25rem;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .6rem;
    font-size: .85rem;
    color: var(--muted);
    flex-wrap: wrap;
}
.view-only-bar a { font-weight: normal; }

.badge-view-only {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: .2rem .65rem;
    font-size: var(--text-xs);
    font-weight: normal;
    color: var(--muted);
}

/* When view-only, the feed takes full width (no sidebar) */
.feed-full {
    grid-column: 1 / -1;
}

/* ── Expiry overlay ───────────────────────────────── */
.expiry-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.72);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 200;
    padding: 1.25rem;
    animation: fadeIn .2s ease;
}

.expiry-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 2rem;
    max-width: 420px;
    width: 100%;
    box-shadow: 0 12px 60px rgba(0,0,0,.65);
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    animation: fadeSlideUp .3s ease forwards;
}

.expiry-card-header {
    display: flex;
    align-items: center;
    gap: 1rem;
    color: var(--muted);
}
.expiry-card-header svg { flex-shrink: 0; opacity: .7; }
.expiry-card-header h2 {
    font-size: var(--text-lg);
    font-weight: 800;
    color: var(--text);
    margin-bottom: .2rem;
}
.expiry-card-header p {
    font-size: var(--text-sm);
    color: var(--muted);
}

.expiry-section {
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: .75rem;
}
.expiry-section-label {
    font-size: var(--text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--accent);
}

.expiry-actions {
    display: flex;
    flex-direction: column;
    gap: .65rem;
}

.btn-recap-link {
    display: block;
    text-align: center;
    padding: .7rem 1.25rem;
    border-radius: var(--radius-sm);
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text);
    font-size: var(--text-md);
    font-weight: normal;
    font-family: inherit;
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition);
    text-decoration: none;
}
.btn-recap-link:hover {
    background: var(--surface-alt);
    border-color: var(--accent);
    text-decoration: none;
}
/* F-UX-008: When recap link is elevated to primary CTA, use primary styles */
.btn-recap-link.btn-primary {
    font-weight: 600;
    border: none;
}
.btn-recap-link.btn-primary:hover {
    background: var(--accent-hover, var(--accent));
    border: none;
    text-decoration: none;
}

/* ── Mobile ───────────────────────────────────────── */
@media (max-width: 700px) {
    .feed-layout {
        display: block;
    }

    .capture-panel {
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        top: auto;
        height: auto;
        max-height: none;
        z-index: 50;
        border-inline-end: none;
        border-top: 1px solid var(--border);
        border-radius: var(--radius) var(--radius) 0 0;
        padding: .85rem 1rem .85rem;
        flex-direction: row;
        align-items: center;
        gap: .75rem;
        overflow-y: visible;
        background: color-mix(in srgb, var(--surface) 97%, transparent);
        backdrop-filter: blur(16px);
        -webkit-backdrop-filter: blur(16px);
        box-shadow: 0 -4px 24px rgba(0,0,0,.4);
    }

    .capture-panel h2 { display: none; }

    .btn-capture {
        flex: 0 0 auto;
        width: auto;
        padding: .65rem 1.1rem;
        font-size: .9rem;
    }

    .capture-panel input {
        flex: 1;
        min-width: 0;
    }

    /* Hide progress in mobile inline bar; it shows above the bar */
    .capture-panel .progress-bar,
    .capture-panel .upload-status { display: none; }

    .feed {
        padding-bottom: 88px;
    }

    .home-container {
        padding-top: 2rem;
    }
}

/* ── Moment card — edit / delete / comments ─────── */
.moment-actions {
    margin-inline-start: auto;
    display: flex;
    gap: 0.25rem;
}

.btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px; min-height: 44px;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--muted);
    cursor: pointer;
    transition: color var(--transition), border-color var(--transition), background var(--transition);
    padding: 0;
}
.btn-icon:hover { color: var(--text); border-color: var(--border); background: var(--surface-alt); }
.btn-icon-danger:hover { color: var(--danger); border-color: var(--danger); background: rgba(224,92,92,.12); }
.btn-icon-xs { width: 20px; height: 20px; font-size: 0.85rem; }

.caption-editor {
    margin-top: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.caption-editor textarea { resize: vertical; min-height: 52px; }
.editor-actions { display: flex; gap: 0.5rem; }
.editor-actions button { flex: 1; padding: 0.4rem 0.75rem; font-size: var(--text-sm); }

.host-edit-notice {
    display: flex;
    align-items: flex-start;
    gap: 0.4rem;
    padding: 0.5rem 0.75rem;
    background: rgba(255,180,60,.1);
    border: 1px solid rgba(255,180,60,.3);
    border-radius: var(--radius-sm);
    color: #ffb43c;
    font-size: 0.8rem;
    line-height: 1.4;
}
.preserve-label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.85rem;
    color: var(--muted);
    cursor: pointer;
}
.preserve-label input[type="checkbox"] {
    width: auto;
    accent-color: var(--accent);
}

.host-edited-badge {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    margin-top: 0.25rem;
    font-size: var(--text-micro);
    color: var(--muted);
    font-style: italic;
}
.original-caption-hint {
    color: var(--muted);
    cursor: help;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 200px;
}

/* ── Comments ──────────────────────────────────── */
.comments-section {
    border-top: 1px solid var(--border);
    margin-top: 0.75rem;
    padding-top: 0.5rem;
}
.btn-comments-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    background: none;
    border: none;
    color: var(--muted);
    font-size: 0.8rem;
    cursor: pointer;
    padding: 0.25rem 0;
    transition: color var(--transition);
}
.btn-comments-toggle:hover { color: var(--text); }

.comments-list {
    margin-top: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}
.comments-loading { font-size: 0.8rem; color: var(--muted); }

.comment {
    display: flex;
    align-items: baseline;
    gap: 0.35rem;
    font-size: 0.82rem;
    line-height: 1.4;
}
.comment-author {
    font-weight: normal;
    color: var(--accent);
    white-space: nowrap;
    flex-shrink: 0;
}
.comment-text { color: var(--text); word-break: break-word; }
.comment-edited { color: var(--muted); font-size: var(--text-micro); font-style: italic; }

.comment-compose {
    display: flex;
    gap: 0.4rem;
    margin-top: 0.5rem;
}
.comment-compose input {
    flex: 1;
    padding: 0.4rem 0.65rem;
    font-size: 0.82rem;
}
.comment-compose button {
    padding: 0.4rem 0.75rem;
    font-size: 0.82rem;
    white-space: nowrap;
    flex-shrink: 0;
}

/* ── Close drop zone (bottom of capture sidebar) ── */
.close-drop-zone {
    margin-top: auto;
    padding-top: .75rem;
    border-top: 1px solid var(--border);
}
.btn-close-drop {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    padding: .45rem .9rem;
    font-size: .8rem;
    font-weight: normal;
    background: transparent;
    border: 1px solid color-mix(in srgb, var(--danger) 50%, transparent);
    border-radius: var(--radius-sm);
    color: color-mix(in srgb, var(--danger) 70%, var(--muted));
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition), color var(--transition);
    width: 100%;
    opacity: .7;
}
.btn-close-drop:hover:not(:disabled) {
    background: rgba(224,92,92,.1);
    border-color: var(--danger);
    color: var(--danger);
    opacity: 1;
}
.btn-close-drop:disabled { opacity: .3; cursor: not-allowed; }

/* ── Closed badge ──────────────────────────────── */
.badge-closed {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.2rem 0.6rem;
    border-radius: var(--radius-pill);
    font-size: 0.7rem;
    font-weight: normal;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    background: rgba(224,92,92,.15);
    color: var(--danger);
    border: 1px solid rgba(224,92,92,.3);
}

/* ── Moment card — redesigned hover-overlay ───── */
.moment-media-wrap {
    position: relative;
    overflow: hidden;
    border-radius: var(--radius-md, 12px) var(--radius-md, 12px) 0 0;
    aspect-ratio: 4 / 3;
    background: var(--border);
}

.moment-media {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform var(--duration-slow, .3s) var(--easing-default, ease);
}
@media (prefers-reduced-motion: no-preference) {
    .moment-card:hover .moment-media {
        transform: scale(1.03);
    }
}

/* Hover overlay — hidden until card is hovered */
.moment-hover-overlay {
    position: absolute;
    inset: 0;
    background: rgba(0,0,0,0);
    transition: background .2s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}
.moment-card:hover .moment-hover-overlay {
    background: rgba(0,0,0,.38);
    pointer-events: auto;
}

/* Edit/action buttons top-right in overlay */
.moment-card-actions {
    position: absolute;
    top: .5rem;
    right: .5rem;
    display: flex;
    gap: .35rem;
    opacity: 0;
    transform: translateY(-4px);
    transition: opacity .15s ease, transform .15s ease;
}
.moment-card:hover .moment-card-actions {
    opacity: 1;
    transform: translateY(0);
}

.card-action-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    background: rgba(15,15,19,.75);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid rgba(255,255,255,.12);
    border-radius: var(--radius-sm);
    color: var(--text-on-accent);
    width: auto;
    padding: .3rem .55rem;
    gap: .3rem;
    font-size: var(--text-xs);
    font-weight: 500;
    white-space: nowrap;
    transition: background .15s ease, border-color .15s ease;
}
.card-action-btn:hover {
    background: var(--accent);
    border-color: var(--accent);
}

/* Expand hint — centred icon */
.moment-expand-hint {
    color: rgba(255,255,255,.8);
    opacity: 0;
    transform: scale(.85);
    transition: opacity .15s ease, transform .15s ease;
    pointer-events: none;
}
.moment-card:hover .moment-expand-hint {
    opacity: 1;
    transform: scale(1);
}

/* Bottom gradient meta overlay */
.moment-card-meta {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 1.5rem .75rem .5rem;
    background: linear-gradient(to top, rgba(0,0,0,.72) 0%, transparent 100%);
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    pointer-events: none;
}
.moment-card-author {
    font-size: .8rem;
    font-weight: normal;
    color: rgba(255,255,255,.9);
}
.moment-card-time {
    font-size: var(--text-micro);
    color: rgba(255,255,255,.6);
    font-variant-numeric: tabular-nums;
}

/* Comment count badge — top-left overlay (superseded by .media-badge--comments) */

/* Caption below image */
.moment-card-caption {
    padding: .5rem .75rem .6rem;
    display: flex;
    align-items: baseline;
    gap: .45rem;
    flex-wrap: wrap;
}
.moment-card-caption .caption-text {
    font-size: .82rem;
    color: var(--muted);
    line-height: 1.4;
}
.host-edit-chip {
    display: inline-flex;
    align-items: center;
    gap: .2rem;
    font-size: .68rem;
    font-weight: normal;
    color: var(--muted);
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-xs);
    padding: .1rem .35rem;
    white-space: nowrap;
    flex-shrink: 0;
    cursor: help;
}

/* ── Moment modal ──────────────────────────────── */
.moment-modal-backdrop {
    position: fixed;
    inset: 0;
    /* MD-T3: --swipe-scrim-opacity is set by JS during the drag phase (0.12–1).
       Animated spring-back / dismiss phases drive style.background directly. */
    background: rgba(0,0,0,.82);
    background: rgb(0 0 0 / calc(.82 * var(--swipe-scrim-opacity, 1)));
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 300;
    padding: 1rem;
    animation: fadeIn .18s ease;
}

.moment-modal {
    display: grid;
    grid-template-columns: 1fr 380px;
    width: min(1020px, 100%);
    max-height: min(720px, 90vh);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: 0 24px 80px rgba(0,0,0,.7);
    animation: fadeSlideUp .22s ease forwards;
    outline: none;
}

/* Left: media panel */
.modal-media-panel {
    /* Phase 2 — relative positioning anchors the carousel prev/next buttons + counter. */
    position: relative;
    background: #000;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}
.modal-media {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

/* Right: detail panel */
.modal-detail-panel {
    display: flex;
    flex-direction: column;
    border-inline-start: 1px solid var(--border);
    min-height: 0;
    overflow: hidden;
}

/* Header */
.modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: .85rem 1rem;
    border-bottom: 1px solid var(--border);
    gap: .5rem;
    flex-shrink: 0;
}
.modal-author-block {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    min-width: 0;
}
.modal-author {
    font-size: .9rem;
    font-weight: 700;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.modal-time {
    font-size: var(--text-micro);
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}
.modal-header-actions {
    display: flex;
    align-items: center;
    gap: .3rem;
    flex-shrink: 0;
}
@media (max-width: 30rem) {
    .modal-header-actions {
        flex-wrap: wrap;
        justify-content: flex-end;
    }
}
.modal-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--muted);
    cursor: pointer;
    transition: color var(--transition), border-color var(--transition), background var(--transition);
}
.modal-icon-btn:hover {
    color: var(--text);
    border-color: var(--accent);
    background: var(--accent-glow);
}
.modal-icon-btn--danger:hover {
    color: var(--danger);
    border-color: var(--danger);
    background: rgba(224,92,92,.12);
}
.modal-close-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--muted);
    cursor: pointer;
    transition: color var(--transition), border-color var(--transition);
}
.modal-close-btn:hover {
    color: var(--text);
    border-color: color-mix(in srgb, var(--text) 30%, transparent);
}
/* PA-02 — 44px touch target floor for modal icon/close buttons on coarse pointers */
@media (pointer: coarse), (max-width: 48rem) {
    .modal-icon-btn,
    .modal-close-btn {
        min-width: var(--touch-target-min, 44px);
        min-height: var(--touch-target-min, 44px);
    }
}

/* Inline delete confirmation bar */
.modal-delete-confirm {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    padding: .55rem 1rem;
    background: rgba(224,92,92,.1);
    border-bottom: 1px solid rgba(224,92,92,.2);
    flex-shrink: 0;
    animation: fadeSlideUp .15s ease;
}
.modal-delete-confirm span {
    font-size: .82rem;
    color: var(--danger);
    font-weight: 500;
}
.modal-delete-confirm-btns {
    display: flex;
    gap: .4rem;
}
.modal-inline-btn {
    padding: .3rem .75rem;
    font-size: .8rem;
    font-weight: normal;
    border-radius: var(--radius-sm);
    cursor: pointer;
    border: 1px solid var(--border);
    background: transparent;
    color: var(--text);
    width: auto;
    transition: background var(--transition), border-color var(--transition);
}
.modal-inline-btn:hover { background: var(--surface-alt); }
.modal-inline-btn--ghost { color: var(--muted); }
.modal-inline-btn--danger {
    border-color: var(--danger);
    color: var(--danger);
}
.modal-inline-btn--danger:hover {
    background: rgba(224,92,92,.15);
}

/* Caption area */
.modal-caption-area {
    padding: .85rem 1rem;
    flex-shrink: 0;
}
.modal-caption-text {
    font-size: .9rem;
    color: var(--text);
    line-height: 1.5;
}
.modal-caption-empty {
    font-size: .85rem;
    color: var(--muted);
    font-style: italic;
}

/* Host edit notice + preserve checkbox */
.modal-host-edit-notice {
    display: flex;
    align-items: flex-start;
    gap: .4rem;
    padding: .5rem .7rem;
    background: rgba(255,180,60,.09);
    border: 1px solid rgba(255,180,60,.28);
    border-radius: var(--radius-sm);
    color: #ffb43c;
    font-size: .78rem;
    line-height: 1.45;
    margin-bottom: .55rem;
}
.modal-host-edit-notice svg { flex-shrink: 0; margin-top: .1rem; }
.modal-preserve-label {
    display: flex;
    align-items: center;
    gap: .45rem;
    font-size: .82rem;
    color: var(--muted);
    cursor: pointer;
    margin-bottom: .5rem;
    flex-direction: row;
}
.modal-preserve-label input[type="checkbox"] {
    width: auto;
    accent-color: var(--accent);
}

.modal-caption-input {
    resize: none;
    font-size: .9rem;
    min-height: 72px;
}

/* ── Edit form field labels ────────────────────────────────────────────── */
.modal-edit-label {
    display: flex;
    align-items: baseline;
    gap: .35rem;
    margin-top: .75rem;
    margin-bottom: .3rem;
    font-size: var(--text-micro);
    font-weight: 700;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .06em;
}
.modal-edit-hint {
    font-size: .68rem;
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
    color: color-mix(in srgb, var(--muted) 65%, transparent);
}

/* ── Tags input ────────────────────────────────────────────────────────── */
.modal-tags-input {
    width: 100%;
    font-size: var(--text-sm);
    padding: .45rem .6rem;
    background: var(--surface-alt, var(--surface));
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    font-family: inherit;
}
.modal-tags-input:focus {
    outline: var(--focus-outline);
    outline-offset: 1px;
    border-color: var(--accent);
}
.modal-tags-preview {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    margin-top: .4rem;
}

/* ── Location input row ────────────────────────────────────────────────── */
.modal-location-edit-row {
    display: flex;
    align-items: center;
    gap: .5rem;
}
.modal-location-input {
    flex: 1 1 auto;
    font-size: var(--text-sm);
    padding: .45rem .6rem;
    background: var(--surface-alt, var(--surface));
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    font-family: inherit;
    min-width: 0;
}
.modal-location-input:focus {
    outline: var(--focus-outline);
    outline-offset: 1px;
    border-color: var(--accent);
}
.modal-location-input:disabled {
    opacity: .45;
    cursor: not-allowed;
}
.modal-clear-location-label {
    display: flex;
    align-items: center;
    gap: .3rem;
    font-size: var(--text-xs);
    font-weight: normal;
    color: var(--muted);
    cursor: pointer;
    white-space: nowrap;
    flex-shrink: 0;
}
.modal-clear-location-label input[type="checkbox"] {
    accent-color: var(--accent);
    cursor: pointer;
    width: auto;
}

/* ── Inline meta edit link (chip row) ──────────────────────────────────── */
.modal-meta-edit-link {
    display: inline-flex;
    align-items: center;
    gap: .2rem;
    padding: .15rem .4rem;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    color: var(--muted);
    font-size: .68rem;
    font-weight: normal;
    cursor: pointer;
    width: auto;
    transition: color var(--transition), border-color var(--transition), background var(--transition);
}
.modal-meta-edit-link:hover {
    color: var(--text);
    border-color: var(--accent);
    background: var(--accent-glow);
}
.modal-meta-edit-link:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 1px;
}

.modal-edit-actions {
    display: flex;
    gap: .5rem;
    margin-top: .75rem;
}
.modal-save-btn {
    flex: 1;
    padding: .5rem;
    font-size: var(--text-sm);
}
.modal-cancel-btn {
    flex: 1;
    padding: .5rem;
    font-size: var(--text-sm);
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text);
}
.modal-cancel-btn:hover:not(:disabled) { background: var(--surface-alt); }

/* Host-edited badge below caption */
.modal-host-badge {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .3rem;
    margin-top: .45rem;
    font-size: var(--text-micro);
    color: var(--muted);
}
.modal-host-badge svg { opacity: .7; }
.modal-original-toggle {
    background: none;
    border: none;
    padding: 0;
    font-size: var(--text-micro);
    font-weight: normal;
    color: var(--accent);
    cursor: pointer;
    width: auto;
    text-decoration: underline;
}
.modal-original-caption {
    width: 100%;
    font-size: .78rem;
    color: var(--muted);
    font-style: italic;
    padding: .3rem .5rem;
    background: var(--surface-alt);
    border-inline-start: 2px solid var(--border);
    border-radius: 0 4px 4px 0;
    line-height: 1.4;
}

.modal-divider {
    height: 1px;
    background: var(--border);
    flex-shrink: 0;
}

/* Comments */
.modal-comments-area {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    padding: .75rem 1rem 0;
    overflow: hidden;
}
.modal-comments-label {
    font-size: .7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--muted);
    margin-bottom: .55rem;
    flex-shrink: 0;
}
.modal-comments-list {
    flex: 1;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: .5rem;
    padding-inline-end: .25rem;
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
}
.modal-comments-list::-webkit-scrollbar { width: 4px; }
.modal-comments-list::-webkit-scrollbar-thumb { background: var(--border); border-radius: var(--radius-xs); }

.modal-comments-loading,
.modal-comments-empty {
    font-size: .82rem;
    color: var(--muted);
    font-style: italic;
    padding: .5rem 0;
}

.modal-comment {
    display: flex;
    align-items: flex-start;
    gap: .4rem;
}
.modal-comment--own .modal-comment-author {
    color: var(--accent);
}
.modal-comment-line {
    flex: 1;
    font-size: .82rem;
    line-height: 1.5;
    min-width: 0;
    word-break: break-word;
}
.modal-comment-author {
    font-weight: 700;
    color: var(--text);
    margin-inline-end: .25rem;
}
.modal-comment-text {
    color: var(--muted);
}
.modal-comment-edited {
    font-size: .7rem;
    color: var(--muted);
    font-style: italic;
}
/* Comment body — wraps text line + meta row */
.modal-comment-body {
    flex: 1;
    min-width: 0;
}

/* Meta row: timestamp + action buttons */
.modal-comment-meta {
    display: flex;
    align-items: center;
    gap: .35rem;
    margin-top: .18rem;
}
.modal-comment-time {
    font-size: .68rem;
    color: var(--muted);
    opacity: .7;
    flex: 1;
}

/* Shared base for edit + delete comment buttons */
.modal-comment-action-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--muted);
    opacity: .5;
    cursor: pointer;
    border-radius: var(--radius-xs);
    transition: opacity var(--transition), color var(--transition), background var(--transition);
    flex-shrink: 0;
}
.modal-comment-action-btn:hover { opacity: 1; background: var(--surface-alt); color: var(--text); }
.modal-comment-action-btn--danger:hover { color: var(--danger); background: rgba(224,92,92,.1); }

/* F-132: comment reply indentation with left-border depth markers.
   Depth capped at 6 levels in the data model; each visual level adds
   border-left + padding so threads are scannable at a glance. */
.modal-comment-replies {
    display: flex;
    flex-direction: column;
    gap: .3rem;
    margin-top: .3rem;
    margin-inline-start: 12px;
    padding-inline-start: 10px;
    border-inline-start: 2px solid var(--border, rgba(0,0,0,.1));
}

/* Inline comment edit form */
.modal-comment-edit-form {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: .4rem;
}
.modal-comment-edit-input {
    width: 100%;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    font-size: .82rem;
    padding: .4rem .55rem;
    resize: none;
    line-height: 1.4;
    font-family: inherit;
}
.modal-comment-edit-input:focus-visible { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }
.modal-comment-edit-actions {
    display: flex;
    gap: .4rem;
}
.modal-save-btn--sm {
    padding: .25rem .65rem;
    font-size: .78rem;
}

.modal-comment-delete {
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    padding: .2rem;
    width: auto;
    color: var(--muted);
    cursor: pointer;
    opacity: .4;
    flex-shrink: 0;
    margin-top: .1rem;
    border-radius: var(--radius-xs);
    transition: opacity var(--transition), color var(--transition), background var(--transition);
}
.modal-comment-delete:hover {
    opacity: 1;
    color: var(--danger);
    background: rgba(224,92,92,.1);
}

/* Comment compose row */
.modal-comment-compose {
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: .65rem 0 .75rem;
    border-top: 1px solid var(--border);
    margin-top: .5rem;
    flex-shrink: 0;
}
.modal-comment-compose input {
    flex: 1;
    padding: .45rem .7rem;
    font-size: .85rem;
    min-width: 0;
}
.modal-comment-post-btn {
    width: 34px;
    height: 34px;
    min-width: 34px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--accent);
    border-radius: var(--radius-sm);
    transition: background var(--transition);
}
.modal-comment-post-btn:hover:not(:disabled) { background: var(--accent-dim); }
.modal-comment-post-btn:disabled { opacity: .35; }

/* MD-T3: swipe-to-dismiss drag handle — hidden on desktop, pill on mobile */
.modal-drag-handle {
    display: none;
}

/* ── Modal — mobile stacks vertically ──────────── */
@media (max-width: 700px) {
    .modal-drag-handle {
        display: block;
        grid-column: 1 / -1;
        width: 32px;
        height: 4px;
        border-radius: 2px;
        background: var(--bottom-sheet-handle-c, rgba(128,128,128,.4));
        margin: 8px auto 0;
        flex-shrink: 0;
        touch-action: none;
    }
    .moment-modal {
        grid-template-columns: 1fr;
        grid-template-rows: auto auto 1fr;
        max-height: min(92vh, 92svh);
    }
    .modal-media-panel {
        max-height: 45vh;
    }
    .modal-detail-panel {
        border-inline-start: none;
        border-top: 1px solid var(--border);
    }
    @media (prefers-reduced-motion: reduce) {
        .modal-drag-handle { display: none; }
    }
}

/* PA-01 — MomentModal landscape-phone adaptation.
   On landscape phones (≤500px tall) the two-column grid collapses to
   single-column (already handled at max-width:700px). We further shrink
   the media panel so the detail panel is reachable without scrolling the
   viewport. The detail panel gains overflow-y:auto so comment list + compose
   row scroll inside it. Modal header becomes sticky within the detail panel.
   Compose row is already flex-shrink:0 and pinned to the bottom of
   modal-comments-area via border-top; no sticky needed there. */
@media (orientation: landscape) and (max-height: 500px) {
    .moment-modal-backdrop {
        padding: 0.25rem;
    }
    .moment-modal {
        max-height: calc(100vh - 8px);
    }
    .modal-media-panel {
        max-height: 38vh;
    }
    .modal-detail-panel {
        overflow-y: auto;
    }
    .modal-header {
        padding-block: 0.4rem;
        position: sticky;
        top: 0;
        z-index: 1;
        background: var(--surface);
    }
    .modal-caption-area {
        padding-block: 0.4rem;
    }
    .modal-comments-area {
        padding-block-start: 0.4rem;
    }
}

/* ── Countdown bar ─────────────────────────────── */
.countdown-bar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .45rem;
    padding: .38rem 1.25rem;
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    border-bottom: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
    font-size: .78rem;
    font-weight: normal;
    color: var(--accent);
    font-variant-numeric: tabular-nums;
    letter-spacing: .01em;
    transition: background .4s ease, border-color .4s ease, color .4s ease;
}
.countdown-bar.countdown-danger {
    background: color-mix(in srgb, var(--danger) 10%, transparent);
    border-color: color-mix(in srgb, var(--danger) 22%, transparent);
    color: var(--danger);
    animation: pulseCountdown 1.4s ease-in-out infinite;
}
@keyframes pulseCountdown {
    0%, 100% { opacity: 1; }
    50% { opacity: .6; }
}

/* ── Capture panel header ──────────────────────── */
.capture-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-bottom: .75rem;
}
.capture-panel-header h2 { margin: 0; }

.btn-settings {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .3rem .65rem;
    font-size: var(--text-xs);
    font-weight: normal;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--muted);
    cursor: pointer;
    white-space: nowrap;
    transition: color var(--transition), border-color var(--transition), background var(--transition);
}
.btn-settings:hover {
    color: var(--text);
    border-color: var(--accent);
    background: var(--accent-glow);
}
/* Phase 6.4 — Settings button lives in the navbar's right-side action slot.
   Icon-only, ≥40×40 hit area (Fitts's Law: corner = high-acquisition target),
   matches the visual weight of other navbar elements. The legacy
   `.feed-header__settings` rule is kept dormant for any external CSS that
   relied on it; the button no longer renders inside the feed header. */
.nav-settings {
    width: 40px;
    height: 40px;
    padding: 0;
    border-radius: var(--radius-sm);
    color: var(--muted);
    background: transparent;
    border: 1px solid transparent;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: color var(--transition), border-color var(--transition),
                background var(--transition);
}
.nav-settings:hover,
.nav-settings:focus-visible {
    color: var(--text);
    border-color: var(--border);
    background: color-mix(in srgb, var(--surface) 70%, transparent);
}
.nav-settings[aria-expanded="true"] {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-glow);
}

/* Legacy hook kept for back-compat — old position retained as a soft anchor
   in case any downstream consumer still styles by `.feed-header__settings`. */
.feed-header__settings {
    margin-inline-start: auto;
    flex-shrink: 0;
}

/* ── Settings drawer ──────────────────────────── */
.settings-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, .42);
    z-index: 250;
    animation: fadeIn .15s ease;
}

.settings-drawer {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(360px, 92vw);
    background: var(--surface);
    border-inline-start: 1px solid var(--border);
    z-index: 260;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    box-shadow: -8px 0 32px rgba(0, 0, 0, .45);
    animation: slideInRight .2s ease forwards;
}
@keyframes slideInRight {
    from { transform: translateX(100%); }
    to   { transform: translateX(0); }
}
/* R-B-6: no slide animation for users who prefer reduced motion. */
@media (prefers-reduced-motion: reduce) {
    .settings-drawer { animation: none; }
}

.settings-drawer-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1rem 1.25rem;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}
.settings-drawer-header h3 {
    font-size: var(--text-md);
    font-weight: 700;
    color: var(--text);
    margin: 0;
}
.settings-drawer-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--muted);
    cursor: pointer;
    font-size: var(--text-md);
    line-height: 1;
    padding: .15rem .55rem;
    width: auto;
    transition: color var(--transition), border-color var(--transition);
}
.settings-drawer-close:hover { color: var(--text); border-color: var(--text); }

.settings-section {
    padding: 1rem 1.25rem;
    border-bottom: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    gap: .65rem;
}
/* R-B-1: reserve accent for active states; labels use muted so they read as
   section chrome rather than competing with toggleable primary controls. */
.settings-section-label {
    font-size: .7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--muted);
}
.settings-section-hint {
    font-size: var(--text-xs);
    color: var(--muted);
    margin-top: -.25rem;
}
.settings-section-hint--warn {
    color: #e09a3a;
}

/* PA-01 — HostSettingsDrawer landscape-phone adaptation.
   On iPhone SE 1st-gen landscape (568×320): drawer already scrolls via
   overflow-y:auto on the root; we tighten the header and section padding so
   more content fits in 320px height. Header becomes sticky so the close
   button is always reachable; sections drop from 1rem to 0.5rem block padding. */
@media (orientation: landscape) and (max-height: 500px) {
    .settings-drawer {
        max-height: calc(100vh - 16px);
    }
    .settings-drawer-header {
        padding-block: 0.5rem;
        position: sticky;
        top: 0;
        z-index: 1;
        background: var(--surface);
    }
    .settings-section {
        padding-block: 0.5rem;
    }
}

/* R-B-4': collapsible settings sections — <details>/<summary> pattern.
   The summary acts as the visual section label; chevron rotates on open. */
.settings-section--collapsible > summary {
    list-style: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    cursor: pointer;
    user-select: none;
    font-size: .7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--muted);
    padding: 0;
    margin: 0;
}
.settings-section--collapsible > summary::-webkit-details-marker { display: none; }
.settings-section-summary__chevron {
    flex-shrink: 0;
    transition: transform .15s ease;
}
details[open].settings-section--collapsible .settings-section-summary__chevron {
    transform: rotate(90deg);
}
@media (prefers-reduced-motion: reduce) {
    .settings-section-summary__chevron { transition: none; }
}

/* R-B-4: Export action hierarchy — web page is primary; JSON is a text-link for power users. */
.export-actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.export-actions__primary {
    width: 100%;
}
.export-actions__recommended {
    font-size: var(--text-xs);
    color: var(--muted);
    margin: 0;
}
.export-actions__json-link {
    background: none;
    border: none;
    padding: 0;
    font-size: var(--text-xs);
    color: var(--accent);
    text-decoration: underline;
    cursor: pointer;
    text-align: start;
    width: fit-content;
}
.export-actions__json-link:hover { color: var(--accent-dim); }
.export-actions__json-link:disabled { opacity: .45; cursor: default; }
@media (prefers-reduced-motion: reduce) {
    .export-actions__json-link { transition: none; }
}
@keyframes export-spin {
    to { transform: rotate(360deg); }
}
.export-spin {
    animation: export-spin 0.8s linear infinite;
    flex-shrink: 0;
}
@media (prefers-reduced-motion: reduce) {
    .export-spin { animation: none; }
}

/* R-B-3': Extend duration mode — Preset / Custom segmented control. */
.extend-mode-segmented {
    display: flex;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    overflow: hidden;
}
.extend-mode-segmented__btn {
    flex: 1 1 0;
    padding: .4rem .5rem;
    font-size: .78rem;
    font-weight: 500;
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    transition: background var(--transition), color var(--transition);
    border-radius: 0;
    min-height: 36px;
}
.extend-mode-segmented__btn + .extend-mode-segmented__btn {
    border-inline-start: 1px solid var(--border);
}
.extend-mode-segmented__btn[aria-checked="true"] {
    background: var(--accent);
    color: var(--text-on-accent);
    font-weight: 600;
}
.extend-mode-segmented__btn:focus-visible {
    outline: var(--focus-outline);
    outline-offset: -2px;
}
@media (pointer: coarse), (max-width: 48rem) {
    .extend-mode-segmented__btn { min-height: 44px; }
}
/* R-B-5': Regenerate button promoted from ghost to secondary weight —
   surface-alt background makes it legible as a real action, not a caption. */
.btn-regen {
    display: inline-flex;
    align-items: center;
    gap: var(--space-0-5);
    padding: .35rem .75rem;
    font-size: .75rem;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    color: var(--muted);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: color var(--transition), border-color var(--transition), background var(--transition);
    width: fit-content;
}
.btn-regen:hover:not(:disabled) { color: var(--text); border-color: var(--border); background: var(--surface); }
.btn-regen:disabled { opacity: .45; cursor: default; }

/* Access method — combined toggle + share, one row per method */
.access-method {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-1-5);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-alt);
    transition:
        border-color var(--transition),
        background var(--transition);
}
.access-method--on {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 6%, var(--surface-alt));
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 40%, transparent);
}
.access-method__head {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}
.access-method__icon {
    width: 26px;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--muted);
    flex-shrink: 0;
    transition:
        background var(--transition),
        border-color var(--transition),
        color var(--transition);
}
.access-method--on .access-method__icon {
    background: var(--accent-glow);
    border-color: color-mix(in srgb, var(--accent) 40%, transparent);
    color: var(--accent);
}
.access-method__title {
    flex: 1;
    font-size: .85rem;
    font-weight: normal;
    color: var(--text);
    line-height: 1.2;
}
.access-method__body {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    padding-top: .45rem;
    border-top: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
    animation: fadeIn .15s ease;
}
.access-method__body--center {
    align-items: center;
}
.access-method__caption {
    font-size: var(--text-micro);
    color: var(--muted);
    text-align: center;
    margin: 0;
}
/* R-B-3: inline note for coarse pointers who can't see the title tooltip */
.access-method__last-note {
    font-size: var(--text-xs);
    color: var(--muted);
    margin: 0;
    padding: 0 var(--space-0-5);
}

/* Density overrides for displays nested inside an access-method body */
.access-method__body .pin-display,
.access-method__body .link-display {
    padding: .45rem .6rem;
    background: var(--surface);
}
.access-method__body .pin-value {
    font-size: 1.15rem;
    letter-spacing: .15em;
}
.access-method__body .link-display code {
    font-size: .68rem;
}
.access-method__body .qr-canvas-wrap {
    padding: .55rem;
    box-shadow: var(--shadow-sm);
}

/* Switch — binary on/off control, immediate effect (no Save) */
.access-switch {
    position: relative;
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    cursor: pointer;
    margin: 0;
    padding: 0;
}
.access-switch input[type="checkbox"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    margin: 0;
    cursor: inherit;
    z-index: 1;
}
.access-switch__track {
    position: relative;
    display: block;
    width: 32px;
    height: 18px;
    border-radius: var(--radius-pill);
    background: var(--border);
    transition: background var(--transition), box-shadow var(--transition);
}
.access-switch__thumb {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .35);
    transition: transform var(--transition);
}
.access-switch input:checked ~ .access-switch__track { background: var(--accent); }
.access-switch input:checked ~ .access-switch__track .access-switch__thumb {
    transform: translateX(14px);
}
.access-switch input:focus-visible ~ .access-switch__track {
    box-shadow: 0 0 0 3px var(--accent-glow);
}
.access-switch input:disabled { cursor: not-allowed; }
.access-switch input:disabled ~ .access-switch__track { opacity: .55; }

/* Regenerate-access trigger inside the ACCESS METHODS section */
.access-regen-trigger {
    align-self: flex-start;
    margin-top: .15rem;
}

/* Regenerate-access confirmation dialog */
.regen-dialog {
    max-width: 440px;
}
.regen-options {
    display: flex;
    flex-direction: column;
    gap: .55rem;
    padding: .25rem 0;
}
.regen-option {
    display: flex;
    align-items: flex-start;
    gap: .65rem;
    cursor: pointer;
    user-select: none;
    line-height: 1.35;
}
.regen-option input[type="checkbox"] {
    width: 16px;
    height: 16px;
    margin-top: .15rem;
    accent-color: var(--accent);
    cursor: pointer;
    flex-shrink: 0;
}
.regen-option strong {
    color: var(--text);
    font-weight: normal;
}
.regen-option span { color: var(--muted); font-size: .85rem; }

/* R-B-2': host-mgmt is now inside Access Methods — standalone section rules removed */
.settings-section__footer-actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding-top: var(--space-0-5);
}

.pin-display,
.link-display {
    display: flex;
    align-items: center;
    gap: .5rem;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: .55rem .75rem;
}
.pin-value {
    flex: 1;
    font-size: var(--text-xl);
    font-weight: 800;
    letter-spacing: .2em;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}
.link-display code {
    flex: 1;
    font-size: var(--text-micro);
    color: var(--muted);
    word-break: break-all;
    font-family: inherit;
}
.pin-display button,
.link-display button {
    flex-shrink: 0;
    padding: .3rem .65rem;
    font-size: .78rem;
    white-space: nowrap;
    width: auto;
}

.qr-canvas-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    background: #fff;
    border-radius: var(--radius-sm);
    padding: .75rem;
    width: fit-content;
    margin: 0 auto;
    box-shadow: 0 2px 12px rgba(0, 0, 0, .3);
}
.qr-canvas-wrap canvas { display: block; }
.qr-canvas-wrap svg { width: 140px; height: 140px; display: block; }

/* ── Join method toggle cards (create page) ────── */
.join-method-group {
    display: flex;
    flex-direction: column;
    gap: .55rem;
}
.join-method-label {
    font-size: .8rem;
    font-weight: normal;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .07em;
}
.join-method-hint {
    font-size: var(--text-micro);
    color: var(--muted);
    opacity: .6;
}

.join-method-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: .6rem;
}

.join-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: .4rem;
    padding: .9rem .85rem .85rem;
    border: 1.5px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-alt);
    cursor: pointer;
    font-weight: normal;
    user-select: none;
    -webkit-user-select: none;
    transition:
        border-color 150ms ease,
        background 150ms ease,
        box-shadow 150ms ease;
}
.join-card:hover {
    border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
    background: color-mix(in srgb, var(--accent) 4%, var(--surface-alt));
}
.join-card:focus-within {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.join-card--on {
    /* Selected: stronger purple border + a clearly tinted fill carry the
       affordance on their own — the previous corner checkmark badge was
       visually redundant with the border and read as a separate element
       glued onto the card. APCA contrast for title + description against
       the tinted fill stays comfortably Lc ≥ 75 in both themes. */
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 12%, var(--surface-alt));
    box-shadow: 0 0 0 1px var(--accent);
}
.join-card--on:hover {
    background: color-mix(in srgb, var(--accent) 15%, var(--surface-alt));
}

/* Icon bubble */
.join-card__icon {
    width: 36px;
    height: 36px;
    border-radius: var(--radius-sm);
    background: var(--surface);
    border: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    margin-bottom: .2rem;
    flex-shrink: 0;
    transition:
        background 150ms ease,
        border-color 150ms ease,
        color 150ms ease;
}
.join-card--on .join-card__icon {
    background: var(--accent-glow);
    border-color: color-mix(in srgb, var(--accent) 40%, transparent);
    color: var(--accent);
}

.join-card__title {
    font-size: .88rem;
    font-weight: 700;
    color: var(--text);
    line-height: 1.2;
}
.join-card__desc {
    font-size: .74rem;
    color: var(--muted);
    line-height: 1.45;
}

/* Screen-reader only (hidden checkbox) */
.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;
}

@media (max-width: 520px) {
    .join-method-grid {
        grid-template-columns: 1fr;
    }
    /* :not(#join) — the row layout is for template-picker cards (icon+title+desc);
       the #join form card must stay single-column at all widths. */
    .join-card:not(#join) {
        flex-direction: row;
        align-items: center;
        gap: .75rem;
        padding: .75rem .9rem;
    }
    .join-card__icon {
        margin-bottom: 0;
        flex-shrink: 0;
    }
    .join-card__title,
    .join-card__desc {
        display: block;
    }
    /* Guarantee join form stays single-column with full-width fields. */
    #join.join-card {
        flex-direction: column;
        align-items: stretch;
        gap: 1rem;
        padding: .9rem .85rem .85rem;
    }
    #join.join-card > * {
        width: 100%;
        max-width: none;
        box-sizing: border-box;
    }
}

/* ── Home / join page utilities ────────────────── */
.host-link {
    text-align: center;
    font-size: var(--text-sm);
    color: var(--muted);
    margin-top: .25rem;
}
.host-link a { font-weight: normal; }

.back-link {
    display: block;
    text-align: center;
    font-size: var(--text-sm);
    color: var(--muted);
    margin-top: .5rem;
    font-weight: 500;
    text-decoration: none;
}
.back-link:hover { color: var(--text); text-decoration: none; }

.drop-access-hint {
    font-size: .85rem;
    color: var(--muted);
    margin-top: -.25rem;
}

.loading-hint {
    text-align: center;
    color: var(--muted);
    font-size: .9rem;
    padding: 2rem 0;
}

/* ────────────────────────────────────────────────────
   Phase 1 — accessibility primitives
   ──────────────────────────────────────────────────── */

/* Skip link — first focusable element on every page so keyboard
   users can jump past chrome straight to the main content.
   F-P3: clip-path pattern hides off-screen without the reflow risk
   of position:absolute left:-9999px (avoids scroll-to-corner on old WebKit). */
.skip-link {
    position: absolute;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    overflow: hidden;
    white-space: nowrap;
    width: 1px;
    height: 1px;
    top: 0;
    left: 0;
    z-index: 999;
    padding: .55rem .9rem;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--accent);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
    font-weight: normal;
}
.skip-link:focus {
    position: fixed;  /* F-040: override absolute so overflow-x:clip on html/body can't clip it */
    left: .5rem;
    top: .5rem;
    z-index: 9999;
    text-decoration: none;
    box-shadow: var(--focus-ring);
    /* D-008: reset the clip-path, size, and overflow overrides from the hidden state.
       Without these the element stays clipped even when `position:fixed` is applied. */
    clip: auto;
    clip-path: none;
    overflow: visible;
    width: auto;
    height: auto;
    white-space: nowrap;
}

/* Universal focus-visible — applies to every keyboard-focused
   interactive element that doesn't already define its own ring.
   Mouse focus stays visually quiet. */
:focus-visible {
    outline: var(--focus-ring-width, 2px) solid var(--focus-ring-color, var(--accent));
    outline-offset: 2px;
}
button:focus-visible,
.btn-recap-link:focus-visible,
.btn-copy:focus-visible,
.btn-icon:focus-visible,
.card-action-btn:focus-visible,
.modal-icon-btn:focus-visible,
.modal-close-btn:focus-visible,
.modal-inline-btn:focus-visible,
.btn-comments-toggle:focus-visible,
.btn-load-more:focus-visible,
.btn-settings:focus-visible,
.btn-regen:focus-visible,
.btn-close-drop:focus-visible,
.access-rotated-banner__refresh:focus-visible,
.access-rotated-banner__dismiss:focus-visible,
.modal-original-toggle:focus-visible,
.settings-drawer-close:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}

/* Inputs already paint a focus ring; reinforce that it survives
   the universal :focus-visible rule above. */
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: var(--focus-ring);
}

/* MomentCard is role=button; give it an explicit ring (D4). */
.moment-card:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: var(--focus-ring);
}

/* Reduced motion — kills ambient loops & fade-slide entrances,
   keeps state transitions for affordance (foundations.md §Motion). */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
    }
    /* Keep fades visible (just no slide) so layout doesn't pop. */
    .moment-card,
    .home-container,
    .recap-container,
    .timeline-item,
    .expiry-card,
    .access-rotated-banner,
    .modal-delete-confirm {
        animation: none !important;
        transform: none !important;
    }
}

/* ────────────────────────────────────────────────────
   Toast — global aria-live region for ephemeral feedback
   (rate-limit, capture errors, "PIN copied", etc.)
   ──────────────────────────────────────────────────── */
.toast-region {
    position: fixed;
    bottom: 1rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 400;
    display: flex;
    flex-direction: column;
    gap: .5rem;
    pointer-events: none;
    max-width: min(440px, 92vw);
    width: max-content;
}

/* Alerts region sits slightly higher so it doesn't overlap the polite region. */
.toast-region--alerts {
    bottom: calc(1rem + 56px);
}

.toast {
    pointer-events: auto;
    display: flex;
    align-items: center;
    gap: .55rem;
    padding: .65rem .85rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-inline-start-width: 3px;
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
    color: var(--text);
    font-size: var(--text-sm);
    line-height: 1.4;
    animation: fadeSlideUp .22s var(--easing-emphasis);
}
.toast--info    { border-inline-start-color: var(--info); }
.toast--success { border-inline-start-color: var(--success); }
.toast--warning { border-inline-start-color: var(--warning); }
.toast--error   { border-inline-start-color: var(--danger); }

.toast__icon {
    flex-shrink: 0;
}
.toast--info    .toast__icon { color: var(--info); }
.toast--success .toast__icon { color: var(--success); }
.toast--warning .toast__icon { color: var(--warning); }
.toast--error   .toast__icon { color: var(--danger); }

.toast__body  { flex: 1; min-width: 0; word-break: break-word; }
.toast__close {
    flex-shrink: 0;
    width: 22px;
    height: 22px;
    padding: 0;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-xs);
    color: var(--muted);
    cursor: pointer;
    line-height: 1;
    font-size: var(--text-md);
    transition: color var(--transition), border-color var(--transition);
}
.toast__close:hover { color: var(--text); border-color: var(--border); }

@media (max-width: 480px) {
    /* On mobile, pin toasts above the bottom nav with safe-area awareness.
       Uses the --m-bottomnav-total token (56px bar + safe-area-inset-bottom)
       rather than the old hardcoded 96px override that predated the token system. */
    .toast-region {
        left: var(--space-2, 1rem);
        right: var(--space-2, 1rem);
        transform: none;
        top: auto;
        bottom: calc(var(--m-bottomnav-total, 64px) + 16px);
        width: auto;
        max-width: none;
    }
    .toast-region--alerts {
        bottom: calc(var(--m-bottomnav-total, 64px) + 80px);
    }
    /* Slide in from bottom on mobile instead of from above. */
    .toast {
        animation: fadeSlideDown .22s var(--easing-emphasis);
    }
}

@media (prefers-reduced-motion: reduce) {
    .toast {
        animation: none;
    }
}

/* ────────────────────────────────────────────────────
   Pre-join preview card (D3)
   Shown on /join/{token} before the user types their name
   ──────────────────────────────────────────────────── */
.prejoin-preview {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    padding: .85rem 1rem;
    background: color-mix(in srgb, var(--accent) 6%, var(--surface-alt));
    border: 1px solid color-mix(in srgb, var(--accent) 25%, var(--border));
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
}
.prejoin-preview__row {
    display: flex;
    align-items: center;
    gap: .4rem;
    color: var(--muted);
}
.prejoin-preview__row strong {
    color: var(--text);
    font-weight: normal;
}
.prejoin-preview__row svg {
    color: var(--accent);
    flex-shrink: 0;
}
.prejoin-preview__row--countdown {
    font-variant-numeric: tabular-nums;
}

/* ────────────────────────────────────────────────────
   Segmented control (Phase 1) — used for duration picker.
   Replaces a native <select> with bigger touch targets.
   ──────────────────────────────────────────────────── */
.segmented {
    display: grid;
    /* Phase 16a follow-up — a fixed 3-column grid so the six duration options
       lay out as a balanced 3×2 block. The old
       `grid-auto-flow: column; grid-auto-columns: 1fr` forced all options onto
       a single row; once the picker grew to six options the narrowest cells
       could no longer fit the widest label ("30 min"), which wrapped to two
       lines. Three equal columns give every cell ample room for a single-line
       label from 320px up, with each `1fr` track's min-content floor (the
       label is `white-space: nowrap`) guarding against clipping. */
    grid-template-columns: repeat(3, 1fr);
    gap: .35rem;
    padding: .25rem;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}
.segmented__option {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: .55rem .35rem;
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
    font-weight: normal;
    color: var(--muted);
    cursor: pointer;
    user-select: none;
    /* Belt-and-suspenders: never let a label break across two lines, so
       every option renders with consistent height and baseline. */
    white-space: nowrap;
    text-align: center;
    transition: background var(--transition), color var(--transition);
}
.segmented__option input { position: absolute; opacity: 0; pointer-events: none; }
.segmented__option:hover { color: var(--text); }
.segmented__option--on {
    background: var(--accent);
    color: var(--text-on-accent);
}
.segmented__option:has(input:focus-visible) {
    box-shadow: var(--focus-ring);
}

/* ────────────────────────────────────────────────────
   PIN input — handoff prototype is the source of truth (E2,
   2026-05-30 alignment audit). The prototype `.pin-input`
   (centered, bold 700, monospace, letter-spacing .24em,
   uppercase) governs; that rule lives earlier in this file.
   The former Phase 19c left-align override that out-specified
   it via source order was removed here so the handoff style
   wins. Supersedes docs/phase-19/join-host-polish.md A1 under
   the prototype-primary directive.
   ──────────────────────────────────────────────────── */

/* ────────────────────────────────────────────────────
   Mobile — safe-area inset for fixed-bottom capture bar
   ──────────────────────────────────────────────────── */
@media (max-width: 700px) {
    .capture-panel {
        padding-bottom: calc(.85rem + env(safe-area-inset-bottom));
    }
    .feed {
        padding-bottom: calc(96px + env(safe-area-inset-bottom));
    }
    /* E2 (2026-05-30 audit): handoff `.pin-input` (centered bold mono,
       letter-spacing .24em) governs at all widths; no mobile override —
       the M-041 `.pin-input--alphanum` font-size guard below keeps the
       glyphs above the iOS 16px focus-zoom threshold. */

    /* D1: moment-card-meta always visible on mobile (no hover) */
    .moment-card-meta { opacity: 1; }
}

/* ── D1: Short-screen modal media cap ──────────────────── */
@media (max-height: 600px) {
    .modal-media-panel { max-height: 40vh; }
}

/* ── D1: Settings drawer wider on narrow screens ─────── */
@media (max-width: 520px) {
    .settings-drawer { width: min(420px, 92vw); }
}
/* C-001 (Phase 20a): at 375px the 92vw drawer leaves only ~30px overlay strip
   — below the 44px Fitts minimum. Placed after the 520px rule to win cascade.
   375px: 88vw=330px → overlay 45px ✓ */
@media (max-width: 30em) {
    .settings-drawer { width: min(320px, 88vw); }
}

/* ── D1: Timeline gutter tighter on narrow screens ──── */
@media (max-width: 520px) {
    .timeline {
        margin-inline-start: 0;
        padding-inline-start: 1rem;
        border-inline-start-width: 1px;
    }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Phase 4 — Compact search bar (44px), filter button + popover, active-filter
   chip strip, results count + sort. Replaces the Phase 3 always-on FilterPanel.
   See docs/phase-4/ux.md for the audit & rationale.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Bar wrapper ─────────────────────────────────────
   Single 44 px row holding the search input pill + the filter trigger.
   Position: relative so .caploo-filter-popover anchors to it. */
/* Phase 6: caploo-search-bar wrapper retired (renamed to caploo-toolbar). Rule
   kept only for legacy markup that may still reference the class; remove next
   sweep. */
.caploo-search-bar,
.caploo-toolbar__row--legacy {
    position: relative;
    display: flex;
    gap: var(--space-1);
    margin: var(--space-1) 0 var(--space-1);
    align-items: stretch;
}
.caploo-search-bar > .caploo-searchbar { flex: 1; min-width: 0; }


/* ── Search input pill ───────────────────────────────
   44 px tall (matches DS commitment from accessibility.md §Touch & pointer).
   Focus-within raises a soft accent glow without shifting layout. */
.caploo-searchbar {
    position: relative;
    display: flex;
    align-items: center;
    height: 44px;
    /* Phase 6.4 — softer fill so the search pill reads as the focal control
       even at rest, without breaking the dark theme. */
    background: color-mix(in srgb, var(--surface-alt) 80%, var(--surface) 20%);
    border: 1px solid var(--border);
    border-radius: var(--radius-md, 12px);
    padding: 0 .5rem 0 var(--space-2);
    transition: border-color var(--duration-base) var(--easing-default),
                background var(--duration-base) var(--easing-default),
                box-shadow   var(--duration-base) var(--easing-default);
}
.caploo-searchbar:hover {
    border-color: var(--accent-dim);
    background: var(--surface-alt);
}
.caploo-searchbar:focus-within {
    border-color: var(--accent);
    background: var(--surface-alt);
    box-shadow: var(--focus-ring);
}
.caploo-searchbar__icon {
    color: var(--muted);
    display: inline-flex;
    align-items: center;
    margin-inline-end: .5rem;
}
.caploo-searchbar:focus-within .caploo-searchbar__icon { color: var(--accent); }
.caploo-searchbar__input {
    flex: 1 1 auto;
    background: transparent;
    border: none;
    outline: none;
    color: var(--text);
    font-size: var(--text-md);   /* ≥16px — prevents iOS zoom on focus */
    line-height: 1.2;
    /* Phase 6.7 Bucket A — right padding reserves room for the absolutely-
       positioned clear button so typed text never runs under the ✕ glyph. */
    padding: .55rem 2.25rem .55rem .25rem;
    min-width: 0;
}
.caploo-searchbar__input::placeholder { color: var(--muted); }
/* Hide the native search-clear UA artifact in WebKit so our X is the only one. */
.caploo-searchbar__input::-webkit-search-cancel-button {
    -webkit-appearance: none;
    appearance: none;
}
/* Phase 6.7 Bucket A — clear button is absolutely positioned so it never
   competes for flex width and never overlays the typed text. The 32×32
   visible target sits in the right padding well; flex-shrink:0 + flex-grow:0
   guard against accidental flex-flow regression. WCAG 3.0 AA pointer target
   ≥24×24 satisfied (32×32 with focus offset). */
.caploo-searchbar__clear {
    position: absolute;
    right: .5rem;
    top: 50%;
    transform: translateY(-50%);
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    flex-grow: 0;
    padding: 0;
    background: transparent;
    border: none;
    color: var(--muted);
    border-radius: 50%;
    font-size: var(--text-md);
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    transition: background var(--duration-base) var(--easing-default),
                color      var(--duration-base) var(--easing-default);
}
.caploo-searchbar__clear:hover { background: var(--border); color: var(--text); }
.caploo-searchbar__clear:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}

/* ── Filter button (trigger) ─────────────────────────
   44 px tall pill flush with the search input. The active-filter badge is the
   only color change vs. idle, so users see "is anything filtered?" at a glance. */
.caploo-filter-button {
    flex: 0 0 auto;
    width: auto;
    min-width: 0;
    height: 44px;
    padding: 0 var(--space-2);
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    color: var(--text);
    font-size: .92rem;
    font-weight: 500;
    cursor: pointer;
    transition: border-color var(--duration-base) var(--easing-default),
                background   var(--duration-base) var(--easing-default),
                color        var(--duration-base) var(--easing-default);
}
.caploo-filter-button:hover { border-color: var(--border); background: var(--surface); }
.caploo-filter-button:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}
.caploo-filter-button--open { border-color: var(--accent); }
.caploo-filter-button--active { color: var(--accent); border-color: var(--accent-dim); }
.caploo-filter-button__icon { color: currentColor; }
.caploo-filter-button__label { line-height: 1; }
.caploo-filter-button__badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.25rem;
    height: 1.25rem;
    padding: 0 .35rem;
    background: var(--accent);
    color: var(--text-on-accent);
    border-radius: var(--radius-pill);
    font-size: var(--text-micro);
    font-weight: normal;
    font-variant-numeric: tabular-nums;
    line-height: 1;
}

/* Phase 6.7 Bucket B — toolbar-shell is the positioned anchor for the filter
   popover. Without this, the absolutely-positioned popover walked up the DOM
   to the body and landed far below the visible viewport, requiring users to
   scroll. The shell scopes the anchor to the toolbar so `top: calc(100% + …)`
   means "just below the toolbar". */
.caploo-toolbar-shell {
    position: relative;
}

/* ── Filter popover ──────────────────────────────────
   Anchored absolute beneath the toolbar (via .caploo-toolbar-shell); on desktop
   it's right-aligned to the FilterButton and capped at 480 px. On mobile it
   becomes a bottom sheet. Max-height is viewport-aware so the panel stays
   reachable on short viewports. Backdrop is invisible on desktop and a low-
   opacity scrim on mobile. */
.caploo-filter-popover__backdrop {
    position: absolute;
    inset: 0 0 auto 0;
    z-index: 9;
}
/* R-D-3: clamp popover to viewport so it never spills off-screen at narrow widths. */
.caploo-filter-popover {
    position: absolute;
    z-index: 10;
    top: calc(100% + .5rem);
    right: 0;
    width: min(480px, 100%);
    max-inline-size: min(480px, calc(100vw - 2 * var(--gutter-x)));
    max-height: min(70vh, calc(100vh - 7rem));
    max-height: min(70dvh, calc(100dvh - 7rem));
    overflow: auto;
    background: var(--surface-alt);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border));
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    animation: fadeSlideUp var(--duration-slow) var(--easing-emphasis) both;
}
@media (prefers-reduced-motion: reduce) {
    .caploo-filter-popover { animation: none; }
}
.caploo-filter-popover:focus-visible { outline: var(--focus-outline); outline-offset: -2px; }
.caploo-filter-popover__body {
    padding: var(--space-2);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}
.caploo-filter-popover__group { display: flex; flex-direction: column; gap: .55rem; }
.caploo-filter-popover__heading {
    margin: 0;
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--muted);
    font-weight: normal;
}
.caploo-filter-popover__row { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.caploo-filter-popover__field {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    flex: 1 1 9rem;
    color: var(--muted);
}
.caploo-filter-popover__field-label {
    font-size: var(--text-xs);
    color: var(--muted);
}
.caploo-filter-popover__field input[type="date"] {
    width: 100%;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    padding: .55rem .65rem;
    font-size: .9rem;
    /* Ticket 02 — native date-picker chrome inherits color-scheme from
       :root[data-theme="..."]; no per-element override needed. */
}
.caploo-filter-popover__field input[type="date"]:focus {
    outline: var(--focus-outline);
    outline-offset: 1px;
    border-color: var(--accent);
}
.caploo-filter-popover__check {
    display: inline-flex;
    align-items: center;
    gap: .55rem;
    font-size: .9rem;
    color: var(--text);
    cursor: pointer;
}
.caploo-filter-popover__check input[type="checkbox"] { width: auto; accent-color: var(--accent); }
.caploo-filter-popover__actions {
    display: flex;
    justify-content: space-between;
    gap: var(--space-1);
    border-top: 1px solid var(--border);
    padding-top: var(--space-2);
}
.caploo-filter-popover__actions .btn-secondary { width: auto; }
/* R-D-4: Done is a no-op close (filters auto-apply); ghost style so it doesn't
   compete with Clear all visually. Primary-looking Done was misleading. */
.caploo-filter-popover__close {
    width: auto;
    padding: .55rem 1.1rem;
    border-radius: var(--radius-sm);
}
.caploo-filter-popover__close--ghost {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text);
    transition: background var(--transition), border-color var(--transition);
}
.caploo-filter-popover__close--ghost:hover { background: var(--surface-alt); border-color: var(--muted); }

/* Phase 6.7 Bucket C — collapsible filter sections (Tags, Posters).
   Built on native <details>/<summary> for free a11y + keyboard handling.
   Default-open when the list is short (≤5); collapsed by default for longer
   lists so the popover stays compact. */
.caploo-filter-section {
    border-bottom: 1px solid var(--border);
    padding: var(--space-1-5) 0;
}
.caploo-filter-section:last-of-type { border-bottom: none; }
.caploo-filter-section__summary {
    display: flex;
    align-items: center;
    gap: .5rem;
    cursor: pointer;
    padding: var(--space-1) var(--space-1);
    border-radius: var(--radius-sm);
    list-style: none;
    user-select: none;
    min-height: 32px; /* WCAG 3.0 — comfortable target ≥24×24 */
}
.caploo-filter-section__summary::-webkit-details-marker { display: none; }
.caploo-filter-section__summary:hover { background: var(--surface-alt); }
.caploo-filter-section__summary:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}
.caploo-filter-section__title {
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--muted);
    font-weight: normal;
}
.caploo-filter-section__count {
    font-size: var(--text-xs);
    color: var(--muted);
    margin-inline-start: auto;
}
.caploo-filter-section__chevron {
    color: var(--muted);
    transition: transform var(--duration-base) var(--easing-default);
    flex-shrink: 0;
}
.caploo-filter-section[open] > .caploo-filter-section__summary .caploo-filter-section__chevron {
    transform: rotate(180deg);
}
.caploo-filter-section__body {
    padding: var(--space-1) 0 var(--space-1) 0;
    max-height: 240px;
    overflow-y: auto;
}
@media (prefers-reduced-motion: reduce) {
    .caploo-filter-section__chevron { transition: none; }
}

/* Phase 6.7 Bucket D — segmented scope selector that replaces the old
   "Search comments too" checkbox with named options (Captions / Captions +
   Comments). Keyboard nav follows the WAI-ARIA radio group pattern. */
.caploo-filter-scope {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
}
.caploo-filter-scope__label {
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--muted);
    font-weight: normal;
}
.caploo-filter-scope__group {
    display: inline-flex;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-md, 12px);
    padding: var(--space-0-25);
    gap: 0;
    flex-wrap: nowrap;
}
.caploo-filter-scope__option {
    min-height: 32px; /* ≥24×24 hit target */
    padding: .35rem .85rem;
    background: transparent;
    border: none;
    border-radius: var(--radius-sm);
    font-size: var(--text-sm, 13px);
    color: var(--muted);
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--duration-base) var(--easing-default),
                color      var(--duration-base) var(--easing-default);
}
.caploo-filter-scope__option:hover:not(.caploo-filter-scope__option--active) {
    background: var(--surface);
    color: var(--text);
}
.caploo-filter-scope__option--active {
    background: var(--accent);
    color: var(--text-on-accent);
    font-weight: normal;
}
.caploo-filter-scope__option:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
    .caploo-filter-scope__option { transition: none; }
}

/* ── Chip row & chips (tags / posters) ───────── */
.caploo-chip-row {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
}
.caploo-chip {
    background: var(--surface-alt);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    padding: .25rem .65rem;
    font-size: .8rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    transition: background .12s, border-color .12s, color .12s;
}
.caploo-chip:hover { border-color: var(--accent); }
.caploo-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.caploo-chip--on {
    background: var(--accent);
    border-color: var(--accent);
    color: white;
}
.caploo-chip--on .caploo-chip__count { color: rgba(255,255,255,.8); }
.caploo-chip__count {
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    font-size: .7rem;
}

/* ── Active filter chips strip ────────────────────────
   Renders only when SearchVM.HasActiveQuery == true (component guards it).
   Each chip is a real <button> with a 36 px hit area; the trailing × is a span
   so the chip lands one focus stop, not two. */
.caploo-active-chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem;
    padding: var(--space-1) 0; /* 8px top/bottom — strip below toolbar */
    margin: 0 0 var(--space-0-5);
}
.caploo-active-chip {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    height: 24px;                /* ~24px per spec */
    padding: 0 .5rem 0 .75rem;
    background: color-mix(in srgb, var(--accent) 14%, var(--surface-alt));
    border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border));
    border-radius: var(--radius-pill);
    color: var(--text);
    font-size: .8rem;
    font-weight: 500;
    cursor: pointer;
    transition: background var(--duration-base) var(--easing-default),
                border-color var(--duration-base) var(--easing-default);
    width: auto;
    max-width: 100%;
}
.caploo-active-chip:hover {
    background: color-mix(in srgb, var(--accent) 22%, var(--surface-alt));
    border-color: var(--accent);
}
.caploo-active-chip:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}
.caploo-active-chip__label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 14rem;
}
.caploo-active-chip__x {
    color: var(--muted);
    font-size: var(--text-md);
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 24px;    /* ≥24×24 hit area per spec */
    min-height: 24px;
    margin: -2px -.25rem -2px 0;
}
.caploo-active-chip:hover .caploo-active-chip__x { color: var(--text); }
.caploo-active-chips__clear {
    background: transparent;
    color: var(--muted);
    width: auto;
    padding: .35rem .55rem;
    font-size: .8rem;
    font-weight: 500;
    border-radius: var(--radius-sm);
    margin-inline-start: auto;  /* push to right end of strip */
}
.caploo-active-chips__clear:hover { color: var(--text); background: var(--surface-alt); }

/* ── Results count + sort row ─────────────────────────
   Live region for the result count plus the sort selector. Sort moved here
   from the filter popover (sort = view concern, not filter facet). */
.caploo-results-count {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-1);
    margin: 0 0 var(--space-1);
    color: var(--muted);
    font-size: .85rem;
}
.caploo-results-count__text { margin: 0; }
.caploo-results-count__sort select {
    width: auto;
    padding: .3rem 1.6rem .3rem .55rem;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    font-size: .8rem;
    /* Ticket 02 — color-scheme inherits from :root[data-theme]. */
}
.caploo-results-count__sort select:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
    border-color: var(--accent);
}

/* Phase 6.7 Bucket B — mobile bottom-sheet. On ≤ 48 rem the popover detaches
   from the toolbar and slides up from the bottom of the viewport, with a
   modal-style scrim. Easier to thumb-reach and reverse-tappable via the
   backdrop. M-099: breakpoint aligned to Phase 16b --bp-md (was 700px).
   M-100: max-height uses svh so iOS URL-bar expansion doesn't push Apply
   off-screen when the bar is visible. */
@media (max-width: 48rem) {
    .caploo-filter-popover {
        position: fixed;
        top: auto;
        right: 0;
        bottom: 0;
        left: 0;
        width: 100%;
        max-height: 85svh;      /* M-100: svh tracks dynamic viewport */
        display: flex;
        flex-direction: column;
        border-radius: var(--radius-lg, 16px) var(--radius-lg, 16px) 0 0;
        animation: caplooFilterSlideUp var(--duration-base) var(--easing-emphasis) both;
    }
    @media (prefers-reduced-motion: reduce) {
        .caploo-filter-popover { animation: none; }
    }
    .caploo-filter-popover__backdrop {
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, 0.45);
        z-index: 9;
    }
    .caploo-results-count {
        flex-direction: row;
        align-items: center;
        flex-wrap: wrap;
    }
    .caploo-active-chip__label { max-width: 9rem; }
    /* M-022 — Apply row pushed off-screen by tall calendar grid.
       The popover is now a flex column; body scrolls, actions stick. */
    .caploo-filter-popover__body {
        flex: 1;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
    }
    .caploo-filter-popover__actions {
        position: sticky;
        bottom: 0;
        background: var(--surface-alt);
        z-index: 1;
        padding-bottom: calc(var(--space-2) + env(safe-area-inset-bottom, 0px));
    }
}
@keyframes caplooFilterSlideUp {
    from { transform: translateY(100%); opacity: 0; }
    to   { transform: translateY(0);    opacity: 1; }
}

/* ── Visually hidden helper for ARIA labels ─── */
.visually-hidden {
    position: absolute !important;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0,0,0,0);
    white-space: nowrap; border: 0;
}


/* =============================================================================
 * Phase 2 — composer progressive disclosure, multi-asset carousel, meta chips
 * ===========================================================================*/

.capture-advanced {
    margin-top: .5rem;
    border-top: 1px dashed var(--border, #ddd);
    padding-top: .5rem;
}

.capture-advanced > summary.capture-advanced__summary {
    cursor: pointer;
    list-style: none;
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    color: var(--muted, #666);
    font-size: .85rem;
    padding: .2rem 0;
    user-select: none;
}

.capture-advanced > summary::-webkit-details-marker { display: none; }

.capture-advanced[open] > summary.capture-advanced__summary svg {
    transform: rotate(180deg);
    transition: transform 150ms ease-in-out;
}

.capture-advanced__body {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    padding-top: .35rem;
}

.modal-carousel-prev,
.modal-carousel-next {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    background: rgba(0, 0, 0, 0.55);
    color: var(--text-on-accent);
    border: none;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    font-size: var(--text-xl);
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

.modal-carousel-prev { left: .5rem; }
.modal-carousel-next { right: .5rem; }

.modal-carousel-prev:disabled,
.modal-carousel-next:disabled {
    opacity: 0.35;
    cursor: not-allowed;
}

.modal-carousel-counter {
    position: absolute;
    bottom: .75rem;
    left: 50%;
    transform: translateX(-50%);
    background: rgba(0, 0, 0, 0.55);
    color: var(--text-on-accent);
    padding: .15rem .55rem;
    border-radius: var(--radius-pill);
    font-size: var(--text-xs);
}

.moment-carousel-count {
    position: absolute;
    top: .5rem;
    right: .5rem;
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    background: rgba(0, 0, 0, 0.6);
    color: var(--text-on-accent);
    padding: .15rem .4rem;
    border-radius: var(--radius-pill);
    font-size: .7rem;
    line-height: 1;
}

.modal-meta-row {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
    margin-top: .55rem;
}

.modal-meta-chip {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    color: var(--accent);
    border-radius: var(--radius-pill);
    padding: .15rem .55rem;
    font-size: var(--text-xs);
    line-height: 1.3;
}


/* ============================================================================
   Phase 4 — Calendar grid (CalendarView / CalendarCell / CalendarHeader)
   ----------------------------------------------------------------------------
   - Reuses existing tokens (--surface, --surface-alt, --text, --muted,
     --accent, --accent-glow, --radius, --radius-sm, --focus-outline, --focus-ring).
   - Cells: 44×44 mobile, 36×36 desktop with 8 px padding to keep effective
     hit-area at the project's 44 commitment.
   - APCA Lc ≥ 75 on cell numerals (#e8e8f0 on #1a1a22 ≈ Lc 95).
   - Other-month cells dim with color-mix to ~Lc 60 (still legible).
   - All transitions are gated on prefers-reduced-motion.
   ============================================================================ */

.caploo-calendar {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.75rem;
    background: var(--surface);
    border: 1px solid color-mix(in srgb, var(--text) 8%, transparent);
    border-radius: var(--radius);
}

.caploo-calendar__loading,
.caploo-calendar__error {
    margin: 0;
    padding: 0.5rem 0;
    text-align: center;
    color: var(--muted);
    font-size: var(--text-sm);
}
.caploo-calendar__error { color: var(--danger); }

.caploo-calendar__weekdays,
.caploo-calendar__week {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 4px;
}

.caploo-calendar__grid {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.caploo-calendar__weekday {
    text-align: center;
    font-size: var(--text-xs);
    font-weight: normal;
    text-transform: uppercase;
    color: var(--muted);
    letter-spacing: 0.04em;
    padding: 0.25rem 0;
}

/* ---- Cell ------------------------------------------------------------- */

.caploo-calendar-cell {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    padding: 0;
    margin: 0;
    background: var(--surface-alt);
    color: var(--text);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    font: inherit;
    font-size: var(--text-sm);
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
}

@media (hover: hover) and (pointer: fine) {
    .caploo-calendar-cell {
        min-width: 36px;
        min-height: 36px;
        padding: 4px;
    }
}

.caploo-calendar-cell:hover:not(:disabled) {
    background: color-mix(in srgb, var(--surface-alt) 70%, var(--accent) 30%);
}

.caploo-calendar-cell:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
    box-shadow: var(--focus-ring);
    z-index: 1;
}

.caploo-calendar-cell--other {
    color: color-mix(in srgb, var(--text) 55%, transparent);
    background: transparent;
}

.caploo-calendar-cell--today {
    border-color: var(--accent);
}
.caploo-calendar-cell--today::after {
    content: "";
    position: absolute;
    bottom: 4px;
    left: 50%;
    width: 14px;
    height: 2px;
    background: var(--accent);
    border-radius: 2px;
    transform: translateX(-50%);
    pointer-events: none;
}

.caploo-calendar-cell--selected {
    background: color-mix(in srgb, var(--accent) 25%, var(--surface-alt));
    border-color: var(--accent);
}

.caploo-calendar-cell__num {
    position: relative;
    z-index: 1;
    font-weight: normal;
    line-height: 1;
}

/* Density disc — pre-attentive single visual variable (size + saturation). */
.caploo-calendar-cell__disc {
    position: absolute;
    bottom: 4px;
    right: 4px;
    pointer-events: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--text-on-accent);
    font-size: var(--text-micro);
    font-weight: 700;
    border-radius: var(--radius-pill);
}
.caploo-density-1 { width: 4px;  height: 4px;  background: color-mix(in srgb, var(--accent) 50%, transparent); }
.caploo-density-2 { width: 6px;  height: 6px;  background: color-mix(in srgb, var(--accent) 75%, transparent); }
.caploo-density-3 { width: 9px;  height: 9px;  background: var(--accent); }
.caploo-density-4 {
    width: 18px; height: 18px;
    background: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-glow) 80%, transparent);
}
.caploo-calendar-cell__discnum {
    font-size: 9px;
    line-height: 1;
}

/* ---- Header (title + nav + mode toggle) -------------------------------- */

/* ── Calendar header — Phase 6.4 compact single-row layout ─────────────────
   The previous 2-row layout pushed Today + mode pills into giant secondary
   buttons.  6.4 collapses everything into one tight cluster: chevrons hug
   the title, Today sits flush with the right chevron, and the mode toggle
   anchors the right edge.  All buttons are exactly 36×36 (icon-only) or
   36px tall (text), satisfying the ≥36 floor per the Phase 6.4 brief while
   shrinking the visual footprint. */
.caploo-calendar-header {
    display: flex;
    flex-direction: column;
    padding: var(--space-1) var(--space-0-5) var(--space-0-5);
}

.caploo-calendar-header__nav-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-1);
    flex-wrap: wrap;
}

/* The nav cluster: chevron · title · chevron · Today.  Tight gaps so the
   group reads as one unit (Gestalt — proximity). */
.caploo-calendar-header__cluster {
    display: inline-flex;
    align-items: center;
    gap: var(--space-0-5);
    min-width: 0;
    flex: 1 1 auto;
}

.caploo-calendar-header__title-block {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-1);
    min-width: 0;
    flex: 1 1 auto;
    padding: 0 var(--space-0-5);
}

.caploo-calendar-header__title {
    margin: 0;
    font-size: var(--text-md, 0.95rem);
    font-weight: 700;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    line-height: 1.2;
    flex: 1 1 auto;
    min-width: 0;
}

.caploo-calendar-header__count {
    font-size: var(--text-xs, 0.7rem);
    font-weight: 500;
    color: var(--muted);
    letter-spacing: 0.02em;
    white-space: nowrap;
    flex-shrink: 0;
}

/* Shared button base — 36×36 floor (Fitts), but no bigger, per the brief. */
.caploo-calendar-header__nav,
.caploo-calendar-header__today {
    min-width: 36px;
    min-height: 36px;
    height: 36px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    color: var(--muted);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font: inherit;
    flex-shrink: 0;
    transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}

.caploo-calendar-header__nav { width: 36px; }     /* icon-only, perfect square */

.caploo-calendar-header__today {
    padding: 0 var(--space-1-5);
    font-size: var(--text-xs, 0.72rem);
    font-weight: normal;
    letter-spacing: 0.02em;
    color: var(--text);
    border-color: var(--border);
    margin-inline-start: var(--space-0-5);    /* small breath between [>] and [Today] */
}

.caploo-calendar-header__nav:hover,
.caploo-calendar-header__today:hover {
    background: var(--surface-alt, rgba(255,255,255,.05));
    color: var(--text);
    border-color: var(--border);
}

.caploo-calendar-header__nav:focus-visible,
.caploo-calendar-header__today:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
    box-shadow: var(--focus-ring);
}

/* Mode toggle pills — anchors the right edge of the cluster */
.caploo-calendar-header__modes {
    display: inline-flex;
    align-items: center;
    gap: var(--space-0-25);
    padding: var(--space-0-25);
    background: color-mix(in srgb, var(--surface-alt) 80%, transparent);
    border-radius: var(--radius-pill);
    border: 1px solid var(--border);
    flex-shrink: 0;
}

.caploo-calendar-header__mode {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: transparent;
    color: var(--muted);
    min-height: 28px;
    padding: 0 var(--space-1);
    border-radius: var(--radius-pill);
    cursor: pointer;
    font: inherit;
    font-size: var(--text-xs, 0.72rem);
    font-weight: normal;
    letter-spacing: 0.02em;
    white-space: nowrap;
}

/* ≤480px (narrow sidebars / mobile drawer): allow the modes to wrap below
   the nav cluster so nothing overflows. */
@media (max-width: 480px) {
    .caploo-calendar-header__modes {
        flex: 0 0 auto;
        align-self: flex-end;
    }
}

.caploo-calendar-header__mode--active {
    background: var(--accent);
    color: var(--text-on-accent);
}

.caploo-calendar-header__mode:not(.caploo-calendar-header__mode--active):hover {
    background: color-mix(in srgb, var(--accent) 15%, transparent);
    color: var(--text);
}

.caploo-calendar-header__mode:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}

/* ---- Reduced motion ---------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
    .caploo-calendar-cell {
        transition: background-color 80ms ease-out, border-color 80ms ease-out;
    }
    .caploo-calendar-header__nav,
    .caploo-calendar-header__today,
    .caploo-calendar-header__mode {
        transition: background-color 120ms ease-out;
    }
}

/* =============================================================================
 * Phase 6 â€” toolbar, responsive grid, lens overlays, card refresh.
 * Design source: docs/phase-6/design.md
 *
 * Cross-device polish 2026-05-16 (F-cd-1): the Phase-6 `--bp-*` token block
 * that originally lived here was deleted. It declared --bp-sm/md/lg/xl with
 * px values that conflicted with the canonical Phase-16b rem scale defined
 * at line ~9806. CSS cascade resolved to the Phase-16b values, but the
 * duplicate was a footgun for any `var(--bp-md)` reference written in this
 * region. CLAUDE.md standing rule §4 cites the Phase-16b scale as canonical.
 * ============================================================================= */

/* ── FeedToolbar (Phase 6.4 polish) ─────────────────────────────────────────
   One toolbar row, single visual baseline.
     • Search input is the focal point — 44px tall, ≥16px font, soft accent
       fill on focus.  All secondary controls share a unified 40px height so
       nothing jumps off the baseline.
     • Gestalt — proximity: trigger cluster (view / filter / sort) is grouped
       with a tighter gap than the gap between search and triggers.
     • Responsive: at ≤600px the search collapses first; the trigger cluster
       wraps below it rather than overflowing the row.  View toggle stays
       reachable. */
.caploo-toolbar {
    width: 100%;
    padding: var(--space-1-5) 0;
    margin-bottom: var(--space-1);
}
.caploo-toolbar__row {
    display: flex;
    flex-wrap: nowrap;
    gap: var(--space-2);
    align-items: center;
}
.caploo-toolbar__row > .caploo-searchbar {
    flex: 1 1 auto;
    min-width: 0;
}
.caploo-toolbar__triggers {
    display: flex;
    flex-shrink: 0;
    gap: var(--space-1);
    align-items: center;
}
.caploo-toolbar__sort select {
    height: 40px;
    padding: 0 var(--space-2);
    background: var(--surface-alt);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
    cursor: pointer;
    /* Ticket 02 — color-scheme inherits from :root[data-theme]. */
}
.caploo-toolbar__sort select:hover { border-color: var(--border); }
.caploo-toolbar__sort select:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-color: var(--accent);
}
/* Phase 6.4 — Filter button height matched to the secondary cluster (40px)
   so the search pill (44px) keeps its focal-point status without the others
   crowding it. */
.caploo-toolbar .caploo-filter-button {
    height: 40px;
}
/* ≤600px: secondary cluster wraps below the search; text labels collapse */
@media (max-width: 600px) {
    .caploo-toolbar__row {
        flex-wrap: wrap;
        gap: var(--space-1);
    }
    .caploo-toolbar__row > .caploo-searchbar {
        flex: 1 1 100%;       /* search keeps a full row of its own */
    }
    .caploo-toolbar__triggers {
        flex: 1 1 100%;
        justify-content: flex-start;
    }
    .caploo-toolbar__sort select {
        padding: 0 var(--space-1);
        /* Re-apply inline-end padding so the custom chevron (background-image from
           the non-media rule below) doesn't overlap the "Newest first" text. The
           padding shorthand above would have reset it to var(--space-1) = 4px if
           this rule appeared after line 12318; keeping the same specificity here
           ensures the chevron room is preserved on narrow phones. */
        padding-inline-end: 2rem;
        font-size: var(--text-xs, 11px);
    }
    /* Prevent the sort label from shrinking below its natural width in flex layout.
       On iOS Safari, appearance:none selects don't auto-size to content. */
    .caploo-toolbar__sort {
        flex-shrink: 0;
    }
}

/* ── LensTriggerButton — compact pill, ~36px tall ──────────────────────── */
.caploo-lens-trigger {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 36px;
    min-width: 36px;
    padding: 0 12px;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill, 999px);
    font-size: var(--text-sm, 13px);
    cursor: pointer;
    transition: border-color 120ms ease-out, background-color 120ms ease-out, color 120ms ease-out;
    white-space: nowrap;
}
.caploo-lens-trigger:hover {
    border-color: var(--accent-dim, var(--border));
    background: var(--surface-alt, var(--surface));
}
.caploo-lens-trigger:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
/* Active: filled background, not just border */
.caploo-lens-trigger--open {
    background: var(--accent-glow, var(--surface-alt));
    border-color: var(--accent);
    color: var(--accent);
}
.caploo-lens-trigger--pinned {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-on-accent);
}
.caploo-lens-trigger__icon {
    display: inline-flex;
    line-height: 0;
}
.caploo-lens-trigger__label {
    line-height: 1;
}
/* ≤600px: hide text label, show icon only */
@media (max-width: 600px) {
    .caploo-lens-trigger__label { display: none; }
    .caploo-lens-trigger { padding: 0 10px; }
}

/* ── MomentGrid — fluid auto-fill grid (Phase 6 polish) ──────────────────── */
/* Replaces the --cols / breakpoint system with auto-fill so cards size
   naturally at any viewport without hard breakpoint jumps. */
.feed {
    display: grid;
    gap: 1.5rem;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr));
}

/* Very wide: cap column width so cards don't over-stretch */
@media (min-width: 1600px) {
    .feed {
        grid-template-columns: repeat(auto-fill, minmax(320px, 380px));
    }
}

/* Density opt-in: compact shrinks minimum card width */
.feed[data-density="compact"] {
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr));
}

/* Pinned right-rail (lens open): grid container is narrower, use 280px min */
.feed[data-rail="open"] {
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
}

/* ── Lens overlays (CalendarOverlay / MapOverlay) ─────────────────────────── */
/* Base: mobile-first bottom sheet (≤900px) with translucent backdrop.
   Desktop (>900px): absolute popover, NO backdrop, anchored below trigger.
   Pinned (≥1280px, [data-pinned]): right-rail panel, no shadow, flush edge. */

/* Backdrop (mobile only — hidden on desktop via media query below) */
.caploo-overlay-backdrop {
    position: fixed;
    inset: 0;
    z-index: 59;
    background: rgba(0,0,0,0.4);
}
@media (min-width: 900px) {
    .caploo-overlay-backdrop { display: none; }
}

.caploo-overlay {
    position: fixed;
    z-index: 60;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg, 20px) var(--radius-lg, 20px) 0 0;
    box-shadow: var(--shadow-lg);
    color: var(--text);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    /* Mobile bottom sheet */
    left: 0;
    right: 0;
    bottom: 0;
    max-height: 80vh;
    border-bottom: none;
}
.caploo-overlay--map {
    max-height: 65vh;
}
/* Drag handle for mobile bottom sheet */
.caploo-overlay::before {
    content: '';
    display: block;
    width: 36px;
    height: 4px;
    border-radius: 2px;
    background: var(--border);
    margin: 8px auto 0;
    flex-shrink: 0;
}
.caploo-overlay:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Desktop popover (>900px): no backdrop, anchored below-right of trigger */
@media (min-width: 900px) {
    .caploo-overlay {
        position: absolute;
        left: auto;
        right: 0;
        bottom: auto;
        top: calc(100% + 8px);
        max-width: 360px;
        width: 360px;
        max-height: 480px;
        border-radius: var(--radius-lg, 20px);
        border-bottom: 1px solid var(--border);
        box-shadow: var(--shadow-lg);
    }
    .caploo-overlay--map {
        max-width: 720px;
        width: min(720px, 90vw);
        height: 480px;
        max-height: 480px;
    }
    /* Hide drag handle on desktop */
    .caploo-overlay::before { display: none; }
}

/* Pinned right rail (≥1280px, cal=pinned / map=pinned) */
@media (min-width: 1280px) {
    .caploo-overlay--pinned {
        position: sticky;
        top: var(--space-2);
        right: 0;
        width: 340px;
        max-width: 340px;
        max-height: calc(100vh - var(--space-2) * 2);
        max-height: calc(100dvh - var(--space-2) * 2);
        border-radius: var(--radius-md, 12px);
        box-shadow: none;
        border: 1px solid var(--border);
    }
    .caploo-overlay--pinned.caploo-overlay--map {
        width: 380px;
        max-width: 380px;
        height: auto;
        max-height: calc(100vh - var(--space-2) * 2);
        max-height: calc(100dvh - var(--space-2) * 2);
    }
}

.caploo-overlay__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-1);
    min-height: 56px;
    padding: 0 1rem;
    flex-shrink: 0;
    border-bottom: 1px solid color-mix(in srgb, currentColor 12%, transparent);
}
.caploo-overlay__title {
    margin: 0;
    font-size: var(--text-md, 14px);
    font-weight: normal;
    color: var(--text);
}
.caploo-overlay__actions {
    display: inline-flex;
    gap: var(--space-1);
}
.caploo-overlay__pin,
.caploo-overlay__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 32px;
    padding: 0 10px;
    background: transparent;
    color: var(--muted, var(--text));
    border: 1px solid var(--border);
    border-radius: var(--radius-pill, 999px);
    font-size: 13px;
    cursor: pointer;
    transition: background-color 120ms ease-out, color 120ms ease-out;
}
.caploo-overlay__pin[aria-pressed=”true”] {
    background: var(--accent-glow, var(--surface-alt));
    color: var(--accent);
    border-color: var(--accent);
}
.caploo-overlay__close {
    font-size: 18px;
    line-height: 1;
}
.caploo-overlay__pin:hover,
.caploo-overlay__close:hover {
    background: var(--surface-alt, var(--surface));
    color: var(--text);
}
.caploo-overlay__pin:focus-visible,
.caploo-overlay__close:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.caploo-overlay__body {
    flex: 1 1 auto;
    overflow: auto;
    padding: 1rem;
}

/* Lens-applied chip variant in the active filter strip */
.caploo-active-chip--lens {
    background: var(--accent-glow, var(--surface-alt));
    border-color: var(--accent);
    color: var(--accent);
}

/* â”€â”€ MomentCard refresh: byline + tag chips â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ */
.moment-card-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-2);
}
.moment-card-byline {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    font-size: var(--text-xs, 11px);
    color: var(--muted, var(--text));
}
.moment-card-byline__author {
    font-weight: normal;
    line-height: 1.3;
    color: var(--text);
}
.moment-card-byline__time {
    color: var(--muted);
}
.moment-card-byline__location {
    display: inline-flex;
    align-items: center;
    gap: var(--space-0-25);
    margin-inline-start: auto;
    color: var(--muted);
    max-width: 60%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.moment-card-byline__location-text {
    overflow: hidden;
    text-overflow: ellipsis;
}
.moment-card-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}
.moment-card-tag {
    display: inline-flex;
    align-items: center;
    padding: var(--space-0-25) var(--space-1);
    background: var(--accent-glow, var(--surface-alt));
    color: var(--accent);
    border-radius: var(--radius-pill, 999px);
    font-size: var(--text-micro, 10px);
    font-weight: 500;
    line-height: 1.5;
}
.moment-card-tag--more {
    background: var(--surface-alt);
    color: var(--muted);
}

/* Phase 6.8 — unified meta row: location chip + tag chips under caption. Wraps to a
   second line on overflow. Visible on every variant (grid/list/large). */
.moment-card-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
    margin-top: var(--space-0-25);
}
.moment-card-meta__chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-0-25);
    padding: var(--space-0-25) var(--space-1);
    background: var(--accent-glow, var(--surface-alt));
    color: var(--accent);
    border-radius: var(--radius-pill, 999px);
    font-size: var(--text-micro, 10px);
    font-weight: 500;
    line-height: 1.5;
    max-width: 100%;
}
.moment-card-meta__chip--location {
    background: var(--surface-alt);
    color: var(--muted);
    max-width: 60%;
    white-space: nowrap;
    overflow: hidden;
}
.moment-card-meta__chip--location svg {
    flex-shrink: 0;
}
.moment-card-meta__chip-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.moment-card-meta__chip--more {
    background: var(--surface-alt);
    color: var(--muted);
}
@media (max-width: 599px) {
    .moment-card-meta__chip--location { max-width: 50%; }
}

/* â”€â”€ Tech debt cleanup: dead view-mode-toggle styles removed (Phase 6 Â§2.1) */
.view-mode-toggle,
.view-mode-toggle__btn,
.view-mode-toggle-removed { display: none !important; }

/* â”€â”€ Reduced-motion respects existing global rule; nothing extra needed. */



/* ═════════════════════════════════════════════════════════════════════════
   Phase 6.2 — AppShell + Sidebar + View Modes
   ─────────────────────────────────────────────────────────────────────────
   Two-column shell: collapsible sidebar (Calendar + Map) on the left, main
   content (toolbar + chips + feed) on the right. The sidebar collapses to
   icon-only at ~64px on tablet widths, and turns into a drawer at ≤900px.
   ───────────────────────────────────────────────────────────────────────── */

.caploo-shell {
    display: grid;
    grid-template-columns: var(--sidebar-w, 280px) 1fr;
    min-height: calc(100vh - var(--navbar-h));
    min-height: calc(100dvh - var(--navbar-h));
    --sidebar-w: 280px;
}
.caploo-shell--collapsed {
    --sidebar-w: 64px;
}

/* Sidebar landmark */
.caploo-shell__sidebar {
    background: var(--surface);
    border-inline-end: 1px solid var(--border);
    padding: var(--space-1-5) var(--space-1);
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    position: sticky;
    top: var(--navbar-h);
    height: calc(100vh - var(--navbar-h));
    height: calc(100dvh - var(--navbar-h));
    overflow-y: auto;
    transition: transform 250ms ease, padding 250ms ease;
    z-index: 30;
}
@media (prefers-reduced-motion: reduce) {
    .caploo-shell__sidebar { transition: none; }
}

/* Sidebar header — small brand/title + collapse + close-X (mobile only) */
.caploo-shell__sidebar-header {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    padding: 0 var(--space-1) var(--space-1);
    border-bottom: 1px solid var(--border);
}
.caploo-shell__sidebar-title {
    flex: 1 1 auto;
    font-size: var(--text-xs);
    font-weight: 700;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .08em;
}
.caploo-shell__sidebar-collapse,
.caploo-shell__sidebar-close {
    width: 40px;
    height: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    color: var(--muted);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}
.caploo-shell__sidebar-collapse:hover,
.caploo-shell__sidebar-close:hover {
    color: var(--text);
    border-color: var(--border);
    background: var(--surface-alt, var(--surface));
}
.caploo-shell__sidebar-collapse:focus-visible,
.caploo-shell__sidebar-close:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
/* Close-X is mobile-drawer-only */
.caploo-shell__sidebar-close { display: none; }
/* M-001: drag handle — hidden everywhere except mobile bottom-sheet mode (≤480px) */
.caploo-shell__drag-handle { display: none; }

/* Collapsed (icon-only) state — hide labels, center icons */
.caploo-shell--collapsed .caploo-shell__sidebar-title { display: none; }
.caploo-shell--collapsed .caploo-sidebar-panel__header {
    justify-content: center;
    padding: var(--space-1) 0;
}
.caploo-shell--collapsed .caploo-sidebar-panel__title,
.caploo-shell--collapsed .caploo-sidebar-panel__chevron,
.caploo-shell--collapsed .caploo-sidebar-panel__body,
.caploo-shell--collapsed .caploo-sidebar-panel__actions { display: none; }
.caploo-shell--collapsed .caploo-sidebar-panel__icon {
    display: inline-flex;
    width: 32px;
    height: 32px;
    align-items: center;
    justify-content: center;
}

/* Main content slot */
.caploo-shell__main {
    min-width: 0;
    display: flex;
    flex-direction: column;
}

/* Mobile drawer (≤64rem): sidebar slides in from the left over a backdrop. */
@media (max-width: 64rem) {
    .caploo-shell { grid-template-columns: 1fr; }
    .caploo-shell__sidebar {
        position: fixed;
        top: var(--navbar-h);
        left: 0;
        bottom: 0;
        width: 84vw;
        max-width: 320px;
        transform: translateX(-100%);
        box-shadow: var(--shadow-drawer);
        z-index: 70;
    }
    .caploo-shell--drawer-open .caploo-shell__sidebar { transform: translateX(0); }
    .caploo-shell__sidebar-close { display: inline-flex; font-size: var(--text-lg); }
}

/* M-001: at ≤480px the sidebar converts from a left-sliding drawer to a
   native bottom sheet. Overrides the ≤64rem left-drawer rules above. */
@media (max-width: 480px) {
    .caploo-shell__sidebar {
        top: auto;
        left: 0;
        right: 0;
        bottom: 0;
        width: 100%;
        max-width: 100%;
        height: auto;
        max-height: 85vh;
        transform: translateY(100%);
        border-radius: var(--bottom-sheet-radius, 20px) var(--bottom-sheet-radius, 20px) 0 0;
        border-inline-end: none;
        padding-bottom: calc(var(--space-1-5, 1.25rem) + env(safe-area-inset-bottom, 0px));
        box-shadow: 0 -4px 24px rgba(0, 0, 0, .3);
    }
    .caploo-shell--drawer-open .caploo-shell__sidebar {
        transform: translateY(0);
    }
    .caploo-shell__drag-handle {
        display: block;
        width: var(--bottom-sheet-handle-w, 36px);
        height: var(--bottom-sheet-handle-h, 4px);
        border-radius: var(--radius-pill);
        background: var(--bottom-sheet-handle-c, color-mix(in srgb, var(--muted) 50%, transparent));
        margin: 8px auto 4px;
        flex-shrink: 0;
        touch-action: none;
        cursor: grab;
    }
}
@media (prefers-reduced-motion: reduce) {
    /* M-001: remove sheet slide animation on reduced-motion (transition:none already
       covers the translateY transition; this guard disables any residual animation). */
    .caploo-shell__sidebar { animation: none !important; }
}

.caploo-shell__backdrop {
    position: fixed;
    inset: 0;
    z-index: 60;
    background: rgba(0,0,0,.45);
    animation: caplooFadeIn 200ms ease both;
}
@media (min-width: 901px) {
    .caploo-shell__backdrop { display: none; }
}
@keyframes caplooFadeIn { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
    .caploo-shell__backdrop { animation: none; }
}

/* ── SidebarPanel (generic collapsible card) ─────────────────────────────── */
.caploo-sidebar-panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    overflow: hidden;
}
.caploo-sidebar-panel__header-row {
    display: flex;
    align-items: stretch;
}
.caploo-sidebar-panel__header {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    gap: var(--space-1);
    background: transparent;
    color: var(--text);
    border: none;
    padding: var(--space-1) var(--space-1-5);
    min-height: 40px;
    font-size: var(--text-sm, 13px);
    font-weight: normal;
    cursor: pointer;
    text-align: start;
}
.caploo-sidebar-panel__header:hover { background: var(--surface-alt, var(--surface)); }
.caploo-sidebar-panel__header:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}
.caploo-sidebar-panel__title { flex: 1 1 auto; }
.caploo-sidebar-panel__icon { color: var(--muted); display: inline-flex; }
.caploo-sidebar-panel__chevron {
    color: var(--muted);
    transition: transform 200ms ease;
    flex-shrink: 0;
}
.caploo-sidebar-panel--collapsed .caploo-sidebar-panel__chevron {
    transform: rotate(-90deg);
}
@media (prefers-reduced-motion: reduce) {
    .caploo-sidebar-panel__chevron { transition: none; }
}
.caploo-sidebar-panel__actions {
    display: inline-flex;
    align-items: center;
    padding-inline-end: var(--space-1);
}
.caploo-sidebar-panel__expand-btn {
    width: 32px; height: 32px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; color: var(--muted);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.caploo-sidebar-panel__expand-btn:hover {
    color: var(--text);
    border-color: var(--border);
    background: var(--surface-alt, var(--surface));
}
.caploo-sidebar-panel__expand-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.caploo-sidebar-panel__body {
    padding: var(--space-1) var(--space-1-5) var(--space-1-5);
    overflow: hidden;
    transition: max-height 250ms ease;
}
@media (prefers-reduced-motion: reduce) {
    .caploo-sidebar-panel__body { transition: none; }
}

/* Body specializations: tighten Calendar / Map for ~250px width */
.caploo-sidebar-panel__body--calendar { max-height: 360px; overflow: auto; }

/* ── Map sidebar body ──────────────────────────────────────────────────── */
.caploo-map-sidebar__body {
    position: relative;
    height: 200px;
    overflow: hidden;
    border-radius: 0 0 var(--radius-md) var(--radius-md);
}
.caploo-map-sidebar__body .caploo-mapview,
.caploo-map-sidebar__body .caploo-mapview__canvas {
    width: 100%;
    height: 100%;
    min-height: unset;
}
/* Subtle hint overlay at bottom of map */
.caploo-map-sidebar__hint {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    display: flex;
    align-items: center;
    gap: .3rem;
    padding: .3rem .6rem;
    background: linear-gradient(to top, rgba(15,15,19,.85) 0%, transparent 100%);
    color: rgba(255,255,255,.65);
    font-size: .68rem;
    pointer-events: none;
    user-select: none;
}
/* Empty state inside map panel */
.caploo-map-sidebar__body .caploo-mapview__empty {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .35rem;
    padding: 1rem;
    text-align: center;
    font-size: .78rem;
    color: var(--muted);
    background: var(--surface);
}
.caploo-map-sidebar__body .caploo-mapview__empty-hint {
    font-size: var(--text-micro);
    color: color-mix(in srgb, var(--muted) 70%, transparent);
}

/* ── FeedToolbar additions: hamburger + view-toggle integration ──────────── */
.caploo-toolbar__hamburger {
    width: 44px; height: 44px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent;
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    flex-shrink: 0;
    transition: background 120ms ease;
}
.caploo-toolbar__hamburger:hover { background: var(--surface-alt, var(--surface)); }
.caploo-toolbar__hamburger:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
@media (min-width: 901px) {
    .caploo-toolbar__hamburger { display: none; }
}

/* ── ViewModeToggle (segmented control, Phase 6.4 / redesigned Phase 19e) ──
   Phase 19e redesign: single container border, no internal button dividers.
   A 2px inset pad + gap creates subtle visual separation between segments
   without the cluttered "cell grid" look of adjacent borders. The active
   accent fill and hover surface-alt fill are sufficient affordances.
   • 40px height — matches the secondary trigger cluster.
   • Keyboard: arrow-key navigation owned by ViewModeToggle.razor (radiogroup). */
.caploo-view-toggle {
    display: inline-flex;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    /* overflow:clip avoids the scroll-container side effect of overflow:hidden
       while still clipping button backgrounds to the container's border-radius.
       Vertical overflow is kept visible so border-bottom is never clipped at
       tight flex heights (fixes "buttons cut off at bottom" on iPhone). */
    overflow: clip;
    overflow-clip-margin: 0px;
    padding: var(--space-0-25);
    gap: var(--space-0-25);
    flex-shrink: 0;
    height: 40px;
}
.caploo-view-toggle__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    background: transparent;
    color: var(--muted);
    border: none;
    border-radius: calc(var(--radius-sm) - 2px);
    height: 100%;
    min-width: 40px;
    padding: 0 var(--space-1-5);
    font-size: var(--text-sm);
    font-weight: 500;
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease;
}
.caploo-view-toggle__btn:hover {
    color: var(--text);
    background: var(--surface-alt);
}
.caploo-view-toggle__btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}
.caploo-view-toggle__btn--selected,
.caploo-view-toggle__btn[aria-checked="true"] {
    background: var(--accent);
    color: var(--text-on-accent);
}
.caploo-view-toggle__btn--selected:hover,
.caploo-view-toggle__btn[aria-checked="true"]:hover {
    background: var(--accent);
    color: var(--text-on-accent);
}
@media (max-width: 600px) {
    .caploo-view-toggle__label { display: none; }
    .caploo-view-toggle__btn { min-width: 36px; padding: 0 var(--space-1); }
}
/* F-095: light-theme unselected buttons use --muted (#54546a on #fff = Lc 65)
   which falls short of the Lc ≥75 body-text floor. Promote to --text in light
   mode; selected state retains --text-on-accent on --accent (already passing). */
:root[data-theme="light"] .caploo-view-toggle__btn:not([aria-checked="true"]) {
    color: var(--text);
}

/* ── F-cd-9: progressive disclosure — view-mode overflow at ≤24rem (384px) ──
   At widths ≤ 24rem the ViewModeToggle moves out of the primary trigger row
   into a collapsible overflow row below it. A "⋮" (More) button replaces it
   in the primary row and expands/collapses the overflow row.
   At > 24rem: overflow area is displayed inline as normal; More btn is hidden. */

/* Wide widths (>24rem): overflow area is inline, More btn is hidden */
.caploo-view-overflow-area {
    display: inline-flex;
    align-items: center;
}
.caploo-toolbar__more-btn { display: none; }

@media (max-width: 24rem) {
    /* Allow the triggers container to wrap so the overflow row drops below */
    .caploo-toolbar__triggers {
        flex-wrap: wrap;
    }
    /* Hide the ViewModeToggle from the primary row */
    .caploo-view-overflow-area {
        display: none;
        flex: 1 1 100%;    /* full-width row when open */
        order: 999;         /* wraps below the primary trigger controls */
        padding: var(--space-0-5) 0;
        justify-content: flex-start;
    }
    /* Show it when the More btn is active */
    .caploo-view-overflow-area.is-open {
        display: flex;
    }
    /* Show the More kebab button */
    .caploo-toolbar__more-btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 40px;
        height: 40px;
        background: var(--surface-alt, var(--surface));
        color: var(--text);
        border: 1px solid var(--border);
        border-radius: var(--radius-sm);
        cursor: pointer;
        flex-shrink: 0;
        transition: background var(--transition), border-color var(--transition);
    }
    .caploo-toolbar__more-btn:hover { border-color: var(--border); }
    .caploo-toolbar__more-btn:focus-visible {
        outline: 2px solid var(--accent);
        outline-offset: 2px;
    }
    /* Accent border when overflow is open */
    .caploo-toolbar__triggers--overflow-open .caploo-toolbar__more-btn {
        border-color: var(--accent);
        color: var(--accent);
    }
}

/* ── MomentGrid mode variants ────────────────────────────────────────────── */
/* Grid mode (default — compact tiles, 1:1, dense). Override the legacy 320px
   minimum from the prior Phase 6 polish so Grid feels distinctly denser than
   Large. */
.feed--grid {
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 220px), 1fr));
    gap: 1rem;
}
@media (min-width: 1600px) {
    .feed--grid {
        grid-template-columns: repeat(auto-fill, minmax(220px, 280px));
    }
}

/* Large mode — 4:3 media, 360px minimum, more meta visible */
.feed--large {
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 360px), 1fr));
    gap: 1.5rem;
}

/* List mode — single column. Turn the grid into a vertical stack. */
.feed--list {
    display: flex;
    flex-direction: column;
    gap: .75rem;
}

/* ── MomentCard variants ─────────────────────────────────────────────────── */

/* Grid (compact tile, 1:1 media) */
.moment-card--grid .moment-media-wrap { aspect-ratio: 1 / 1; }
.moment-card--grid .moment-card-body { padding: .6rem .75rem; }
.moment-card--grid .moment-card-caption .caption-text {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
/* Phase 6.8 — tags + location row visible on every variant (no longer hidden on grid). */

/* Large (4:3 media, full caption visible, tags inline) */
.moment-card--large .moment-media-wrap { aspect-ratio: 4 / 3; }
.moment-card--large .moment-card-caption .caption-text {
    display: -webkit-box;
    -webkit-line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* List (thumbnail-left, content-right) */
.moment-card--list {
    display: grid;
    grid-template-columns: 96px 1fr;
    gap: 1rem;
    align-items: center;
    padding: .6rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.moment-card--list:hover {
    border-color: var(--accent-dim, var(--border));
    background: var(--surface-alt, var(--surface));
}
.moment-card--list:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.moment-card--list .moment-media-wrap {
    width: 96px;
    height: 96px;
    aspect-ratio: 1 / 1;
    border-radius: var(--radius-sm);
    overflow: hidden;
    flex-shrink: 0;
}
.moment-card--list .moment-media { width: 100%; height: 100%; object-fit: cover; }
/* Hide media-overlay chrome that's not useful in list mode */
.moment-card--list .moment-hover-overlay,
.moment-card--list .moment-card-meta { display: none; }
.moment-card--list .moment-card-body {
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}
.moment-card--list .moment-card-byline {
    flex-wrap: wrap;
}
.moment-card--list .moment-card-caption .caption-text {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.moment-card--list .media-badge--comments { display: none; }

/* MomentList wrapper (presentation-only) */
.caploo-moment-list {
    display: flex;
    flex-direction: column;
    gap: .75rem;
}

/* ── End Phase 6.2 ──────────────────────────────────────────────────────── */


/* =============================================================================
 * Phase 6.3 — sidebar composer at top, persistent calendar + map sections,
 * Sunday-first weekday header, single-dot density, divIcon-ready map markers.
 * Design source: Phase 6.3 dispatch (locked decisions).
 * ============================================================================= */

/* ── SidebarSection (always-visible variant of SidebarPanel) ─────────────── */
.caploo-sidebar-section {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    overflow: hidden;
    /* Subtle 1px divider between sections is realised via the parent gap +
       per-section border. We also add a stronger inner border-bottom on the
       header so the section reads as a small card. */
}

.caploo-sidebar-section__header {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-1-5);
    min-height: 40px;
    border-bottom: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
    background: color-mix(in srgb, var(--surface-alt, var(--surface)) 60%, transparent);
}

.caploo-sidebar-section__label {
    flex: 1 1 auto;
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    font-size: var(--text-sm, 13px);
    font-weight: normal;
    color: var(--text);
}

.caploo-sidebar-section__icon {
    color: var(--muted);
    display: inline-flex;
    flex-shrink: 0;
}

.caploo-sidebar-section__title { line-height: 1.2; }

.caploo-sidebar-section__actions {
    display: inline-flex;
    align-items: center;
}

/* "Expand to fullscreen" button reused from the Phase 6.2 pattern.
   Mirrors caploo-sidebar-panel__expand-btn for visual consistency. */
.caploo-sidebar-section__expand-btn {
    width: 32px; height: 32px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; color: var(--muted);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.caploo-sidebar-section__expand-btn:hover {
    color: var(--text);
    border-color: var(--border);
    background: var(--surface-alt, var(--surface));
}
.caploo-sidebar-section__expand-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.caploo-sidebar-section__body {
    /* Phase 6.3 spec calls for 16px inner padding (its --space-4 alias maps to
       Caploo's --space-2 = 1rem = 16px). */
    padding: var(--space-2);
}

/* Body specialisations — match the Phase 6.2 Calendar / Map sizing. */
.caploo-sidebar-section__body--calendar {
    max-height: 380px;
    overflow: auto;
}
.caploo-sidebar-section__body--map {
    padding: 0;
    height: 220px;
}

/* When the parent shell collapses to icon-only (≤tablet), hide everything but
   the section icon so the rail stays compact. */
.caploo-shell--collapsed .caploo-sidebar-section__title,
.caploo-shell--collapsed .caploo-sidebar-section__body,
.caploo-shell--collapsed .caploo-sidebar-section__actions {
    display: none;
}
.caploo-shell--collapsed .caploo-sidebar-section__header {
    justify-content: center;
    padding: var(--space-1) 0;
    border-bottom: none;
}

/* ── MomentComposerSidebar — the quick-add form at the TOP of the sidebar. ── */
.caploo-composer-sidebar {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.caploo-composer-sidebar__caption {
    width: 100%;
    box-sizing: border-box;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    font-family: inherit;
    font-size: var(--text-sm, 13px);
    line-height: 1.45;
    resize: vertical;
    min-height: 56px;   /* ~2 lines */
    max-height: 96px;   /* ~3 lines, then scrolls */
}
.caploo-composer-sidebar__caption:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

.caploo-composer-sidebar__details {
    border: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: var(--surface);
}
.caploo-composer-sidebar__summary {
    list-style: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 10px;
    font-size: 12px;
    color: var(--muted);
    user-select: none;
}
.caploo-composer-sidebar__summary::-webkit-details-marker { display: none; }
.caploo-composer-sidebar__summary:hover { color: var(--text); }
.caploo-composer-sidebar__details[open] .caploo-composer-sidebar__summary {
    color: var(--text);
    border-bottom: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
}
.caploo-composer-sidebar__input {
    width: 100%;
    box-sizing: border-box;
    padding: 7px 10px;
    margin: 8px 0 0;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    font-size: var(--text-sm, 13px);
    line-height: 1.4;
}
.caploo-composer-sidebar__details > .caploo-composer-sidebar__input:first-of-type { margin-top: 8px; }
.caploo-composer-sidebar__details > * + * { margin-top: 0; }
.caploo-composer-sidebar__details[open] {
    padding: 0 10px 10px;
}
.caploo-composer-sidebar__exif {
    display: inline-flex;
    align-items: flex-start;
    gap: 6px;
    margin-top: 8px;
    font-size: 11.5px;
    color: var(--muted);
    line-height: 1.35;
}
.caploo-composer-sidebar__exif input[type="checkbox"] {
    margin-top: var(--space-0-25);
    flex-shrink: 0;
}

.caploo-composer-sidebar__progress {
    height: 4px;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--border) 80%, transparent);
    overflow: hidden;
}
.caploo-composer-sidebar__progress-fill {
    height: 100%;
    background: var(--accent);
    transition: width 200ms ease;
}
@media (prefers-reduced-motion: reduce) {
    .caploo-composer-sidebar__progress-fill { transition: none; }
}

.caploo-composer-sidebar__success {
    margin: 0;
    color: var(--accent);
    font-size: 12px;
    font-weight: normal;
}
.caploo-composer-sidebar__error {
    margin: 0;
    font-size: 12px;
}

.caploo-composer-sidebar__submit {
    width: 100%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 10px 12px;
    background: var(--accent);
    color: var(--text-on-accent);
    border: 1px solid var(--accent);
    border-radius: var(--radius-md, var(--radius-sm));
    font-size: var(--text-sm, 13px);
    font-weight: normal;
    cursor: pointer;
    transition: filter 120ms ease, transform 80ms ease;
}
.caploo-composer-sidebar__submit:hover:not(:disabled) {
    filter: brightness(1.05);
}
.caploo-composer-sidebar__submit:active:not(:disabled) {
    transform: translateY(1px);
}
.caploo-composer-sidebar__submit:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.caploo-composer-sidebar__submit:disabled {
    opacity: .55;
    cursor: not-allowed;
}
@media (prefers-reduced-motion: reduce) {
    .caploo-composer-sidebar__submit { transition: none; }
}

.caploo-composer-sidebar__disabled-hint {
    margin: 0;
    color: var(--muted);
    font-size: 12px;
    line-height: 1.45;
}

/* == Phase 19a - optional media / text-only posts ========================== */
/* The capture affordance is now an OPTIONAL secondary action (outline button)
   sitting above the primary "Post" submit; once a photo is attached it is
   replaced by a removable chip. WCAG 3.0 / APCA: body text uses --text, the
   helper hint uses --muted at >=14px, focus rings are always visible, and the
   remove control meets the 44px touch-target floor. */
.caploo-composer-sidebar__add-photo {
    width: 100%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 9px 12px;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-md, var(--radius-sm));
    font-size: var(--text-sm, 13px);
    font-weight: normal;
    cursor: pointer;
    transition: background 120ms ease, border-color 120ms ease;
}
.caploo-composer-sidebar__add-photo svg { color: var(--accent); flex-shrink: 0; }
.caploo-composer-sidebar__add-photo:hover:not(:disabled) {
    background: var(--surface-alt);
    border-color: var(--accent);
}
.caploo-composer-sidebar__add-photo:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.caploo-composer-sidebar__add-photo:disabled { opacity: .55; cursor: not-allowed; }

.caploo-composer-sidebar__media-chip {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 8px 8px 10px;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm, 13px);
    color: var(--text);
}
.caploo-composer-sidebar__media-chip > svg { color: var(--accent); flex-shrink: 0; }
.caploo-composer-sidebar__media-chip-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.caploo-composer-sidebar__media-remove {
    flex-shrink: 0;
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    color: var(--muted);
    cursor: pointer;
    /* 44px hit area without inflating the visual chrome */
    position: relative;
}
.caploo-composer-sidebar__media-remove::before {
    content: "";
    position: absolute;
    inset: -8px;
}
.caploo-composer-sidebar__media-remove:hover:not(:disabled) {
    background: var(--surface);
    border-color: var(--border);
    color: var(--text);
}
.caploo-composer-sidebar__media-remove:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}
.caploo-composer-sidebar__media-remove:disabled { opacity: .5; cursor: not-allowed; }
@media (prefers-reduced-motion: reduce) {
    .caploo-composer-sidebar__add-photo { transition: none; }
}

/* ── M-001: composer bottom sheet (mobile ≤480px) ──────────────────────────
   On narrow viewports the composer lives in the sidebar drawer.  Adding
   bottom-sheet chrome (rounded corners, drag handle, slide-up animation)
   makes the drawer feel native.  Desktop layout is untouched: .composer-sheet
   rules are all scoped to max-width:480px so they never fire on wider screens.
   z-index: 1000 sits above the sidebar drawer (z:50) but below modals (z:200+).
   ─────────────────────────────────────────────────────────────────────────── */

/* Drag-handle pill — shown only on mobile; aria-hidden so SR skips it. */
.sheet-drag-handle {
    display: none;
}

@keyframes sheet-slide-up {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
}

@keyframes sheet-slide-down {
    from { transform: translateY(0); }
    to   { transform: translateY(100%); }
}

@media (max-width: 480px) {
    .composer-sheet {
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        border-radius: 20px 20px 0 0;
        padding-bottom: calc(1.5rem + env(safe-area-inset-bottom, 0px));
        max-height: 85vh;
        overflow-y: auto;
        z-index: 1000;
        background: var(--surface);
        border: 1px solid var(--border-soft);
        border-bottom: none;
        box-shadow: var(--shadow-lg);
        animation: sheet-slide-up 250ms ease-out;
    }

    .sheet-drag-handle {
        display: block;
        width: 40px;
        height: 4px;
        background: var(--color-border, rgba(0, 0, 0, 0.2));
        border-radius: 2px;
        margin: 8px auto 12px;
        touch-action: none;
        cursor: grab;
    }
}

@media (max-width: 480px) and (prefers-reduced-motion: reduce) {
    .composer-sheet {
        animation: none;
    }
}

/* Text-only moment card: the message stands in for the media surface, so the
   wrap becomes a readable text panel instead of a fixed-aspect image box.
   Superseded by the handoff block below (~line 19521); kept here for legacy
   markup but updated to avoid stale token mismatch. */
.moment-media-wrap--text {
    background: radial-gradient(120% 80% at 20% 0%, rgba(255,255,255,0.12), transparent 60%), var(--brand-gradient);
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.moment-card--grid .moment-media-wrap--text,
.moment-card--large .moment-media-wrap--text {
    aspect-ratio: auto;
    min-height: 128px;
}
.moment-card--list .moment-media-wrap--text {
    aspect-ratio: 1 / 1;
}
.moment-text-hero {
    padding: 1rem 1rem 1.5rem;
    display: flex;
    flex-direction: column;
    gap: .4rem;
}
.moment-text-hero__body {
    margin: 0;
    color: var(--text);
    font-size: .95rem;
    line-height: 1.5;
    /* Cap the preview height; full message is shown in the modal. */
    display: -webkit-box;
    -webkit-line-clamp: 6;
    -webkit-box-orient: vertical;
    overflow: hidden;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}
.moment-card--list .moment-text-hero {
    padding: .5rem;
}
.moment-card--list .moment-text-hero__body {
    -webkit-line-clamp: 3;
    font-size: .8rem;
}

/* Text-only moment modal: no media panel, so the detail panel takes the full
   width instead of leaving an empty black column. */
.moment-modal--text-only {
    grid-template-columns: 1fr;
    width: min(560px, 100%);
}
.moment-modal--text-only .modal-media-panel { display: none; }

/* ── Calendar polish (Phase 6.3) ─────────────────────────────────────────── */
/* Single-letter weekday header — lighter weight, smaller font, more space. */
.caploo-calendar__weekdays {
    padding-bottom: 8px;
}
.caploo-calendar__weekday {
    font-size: 11px;
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
    color: var(--muted);
    padding: 4px 0;
}

/* Day cells: today reads as a subtle ring (not a filled border + underline);
   selected stays filled accent. */
.caploo-calendar-cell--today {
    border-color: transparent;
    box-shadow: inset 0 0 0 1.5px var(--accent);
}
.caploo-calendar-cell--today::after { display: none; }
.caploo-calendar-cell--selected {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-on-accent);
}
.caploo-calendar-cell--selected .caploo-calendar-cell__num { color: var(--text-on-accent); }
.caploo-calendar-cell__num { font-weight: 500; }

/* Single-dot density indicator under the date number (Phase 6.3 v1).
   Selectors here all use .caploo-calendar-cell__dot as a co-selector so they
   win over the legacy .caploo-density-{n} disc rules from Phase 4 (same single-
   class specificity but earlier in the cascade). */
.caploo-calendar-cell__dot,
.caploo-calendar-cell__dot.caploo-density-1,
.caploo-calendar-cell__dot.caploo-density-2,
.caploo-calendar-cell__dot.caploo-density-3,
.caploo-calendar-cell__dot.caploo-density-4 {
    position: absolute;
    bottom: 5px;
    left: 50%;
    right: auto;        /* override .caploo-calendar-cell__disc */
    transform: translateX(-50%);
    width: 4px;
    height: 4px;
    border-radius: var(--radius-pill);
    background: var(--accent);
    pointer-events: none;
    box-shadow: none;   /* override .caploo-density-4 glow */
}
.caploo-calendar-cell__dot.caploo-density-1 { opacity: 0.5; }
.caploo-calendar-cell__dot.caploo-density-2 { opacity: 0.7; }
.caploo-calendar-cell__dot.caploo-density-3 { opacity: 0.9; }
.caploo-calendar-cell__dot.caploo-density-4 { opacity: 1; }
.caploo-calendar-cell__dot { opacity: 0.85; }

/* Selected-cell dot reads on the accent fill — keep it visible in white. */
.caploo-calendar-cell--selected .caploo-calendar-cell__dot { background: #fff; }

/* Smooth hover/focus background transition gated on prefers-reduced-motion
   (already declared earlier; we just append the box-shadow channel). */
@media (prefers-reduced-motion: no-preference) {
    .caploo-calendar-cell {
        transition: background-color 80ms ease-out,
                    border-color 80ms ease-out,
                    box-shadow 80ms ease-out,
                    color 80ms ease-out;
    }
}

/* Calendar grid container — small inner padding tuned for the sidebar column. */
.caploo-sidebar-section__body--calendar .caploo-calendar {
    padding: 0;
}
.caploo-sidebar-section__body--calendar .caploo-calendar__grid {
    padding-top: 0;
}

/* ── Map markers (Phase 6.3 — divIcon-ready custom styling) ───────────────── */
/* The Leaflet defaults are still in effect; this rule is loaded so a future
   divIcon swap (caploo-map-marker class) renders without an extra deploy. */
.caploo-map-marker {
    width: 24px;
    height: 24px;
    border-radius: var(--radius-pill);
    background: var(--accent);
    border: 2px solid #fff;
    box-shadow: 0 1px 4px rgba(0,0,0,0.25);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-on-accent);
    font-size: 11px;
    font-weight: 700;
    line-height: 1;
}
.caploo-map-marker--cluster {
    background: color-mix(in srgb, var(--accent) 80%, #000);
}

/* ── Feed area: tiny host-only Settings strip (replaces the old capture-panel header) ── */
.feed-host-actions {
    display: flex;
    justify-content: flex-end;
    padding: var(--space-1) var(--space-1-5) 0;
}
@media (max-width: 600px) {
    .feed-host-actions { padding: var(--space-1) var(--space-1) 0; }
}


/* =============================================================================
 * Phase 6.5 — audit fixes from live caploo.app review
 *
 * 1. Sidebar header no longer renders a visible "SIDEBAR" title (the role +
 *    aria-label on the wrapping <aside> is sufficient for screen readers).
 *    Only chrome that survives is the collapse / mobile-close icon buttons,
 *    so we right-align them.
 * 2. Calendar + Map sections were converted from <SidebarPanel> (collapsible)
 *    to <SidebarSection> (always-visible). The chevron + max-height/overflow
 *    on the panel calendar variant are no longer reachable; we also clear
 *    the section calendar variant's max-height so the calendar grid takes
 *    its natural content height and the parent sidebar handles overflow.
 * 3. Calendar header is now a tight single row (Mode toggle removed).
 * 4. Composer "Capture & Post" hint + button styling.
 * 5. Search bar gets a desktop minimum width so it reads as a real input
 *    field instead of collapsing to an icon-only pill.
 * 6. List view rows are width-constrained (single column up to 840px,
 *    centred in the main area; two columns at very wide screens).
 * 7. Day-cell post-count dot is bumped from 4 px to 6 px and rendered with
 *    a thin ring so it survives at small sidebar widths.
 * 8. Navbar gains drop-info / meta / actions clusters so the drop name +
 *    countdown timer + PIN chip + Settings cog all live on one row.
 * ============================================================================= */

/* — 1. Sidebar header without title — ----------------------------------- */
.caploo-shell__sidebar-header--no-title {
    justify-content: flex-end;
    padding-top: var(--space-1);
}
.caploo-shell__sidebar-header--no-title .caploo-shell__sidebar-collapse {
    margin-inline-start: auto;
}

/* — 3. Calendar — clear forced overflow on the section body so the grid is
   its natural height and the parent sidebar (caploo-shell__sidebar) scrolls. */
.caploo-sidebar-section__body--calendar {
    max-height: none;
    overflow: visible;
}
/* Belt-and-braces: clear the legacy panel-variant rule too in case any
   downstream override re-introduces it. */
.caploo-sidebar-panel__body--calendar {
    max-height: none;
    overflow: visible;
}

/* — 6. Calendar header (single tight row, Month/Week/Day toggle removed) — */
.caploo-calendar-header {
    padding: 0 var(--space-1) var(--space-1);
}
.caploo-calendar-header__row {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    width: 100%;
}
.caploo-calendar-header__nav {
    width: 32px;
    height: 32px;
    flex: 0 0 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    color: var(--muted);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.caploo-calendar-header__nav:hover {
    background: var(--surface-alt, var(--surface));
    color: var(--text);
    border-color: var(--border);
}
.caploo-calendar-header__nav:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.caploo-calendar-header__title {
    flex: 1 1 auto;
    margin: 0;
    font-size: .9rem;
    font-weight: normal;
    color: var(--text);
    line-height: 1.2;
    text-transform: none;
    letter-spacing: 0;
    display: inline-flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .35rem;
    min-width: 0;
}
.caploo-calendar-header__title-label {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.caploo-calendar-header__count {
    font-size: var(--text-xs);
    font-weight: 400;
    color: var(--muted);
}
.caploo-calendar-header__today {
    height: 32px;
    padding: 0 .75rem;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    font-size: .8rem;
    font-weight: 500;
    cursor: pointer;
    transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}
.caploo-calendar-header__today:hover {
    border-color: var(--accent-dim, var(--accent));
    color: var(--accent);
}
.caploo-calendar-header__today:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* — 4. Composer camera hint above the submit button — */
.caploo-composer-sidebar__camera-hint {
    display: flex;
    align-items: flex-start;
    gap: .4rem;
    margin: 0;
    padding: .4rem .55rem;
    font-size: var(--text-micro);
    line-height: 1.35;
    color: var(--muted);
    background: color-mix(in srgb, var(--surface-alt, var(--surface)) 70%, transparent);
    border: 1px dashed var(--border);
    border-radius: var(--radius-sm);
}
.caploo-composer-sidebar__camera-hint svg {
    flex: 0 0 11px;
    margin-top: 2px;
    color: var(--accent);
}

/* — 4 / Polish #12. Composer chips for selected tags / location — */
.caploo-composer-sidebar__chips {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
    margin: 0 0 .15rem;
}
.caploo-composer-sidebar__chip {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    max-width: 100%;
    padding: var(--space-0-25) var(--space-1);
    font-size: .7rem;
    line-height: 1.25;
    color: var(--text);
    background: color-mix(in srgb, var(--accent) 12%, var(--surface));
    border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
    border-radius: var(--radius-pill);
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.caploo-composer-sidebar__chip svg { color: var(--accent); flex: 0 0 9px; }

/* — 5. Search bar — give the input real estate at desktop widths so it
   reads as the focal control. Mobile collapse rule (≤600px) already exists. */
@media (min-width: 600px) {
    .caploo-toolbar__row > .caploo-searchbar {
        flex: 1 1 320px;
        min-width: 280px;
        max-width: 480px;
    }
}

/* — 6 / 7. Calendar dot — slightly larger so the density indicator is
   actually visible. We also drop the bottom inset by 1 px so the dot sits
   more centrally relative to the date numeral. The legacy 4 px rule is
   overridden by the more-specific selectors below. */
.caploo-calendar-cell__dot,
.caploo-calendar-cell__dot.caploo-density-1,
.caploo-calendar-cell__dot.caploo-density-2,
.caploo-calendar-cell__dot.caploo-density-3,
.caploo-calendar-cell__dot.caploo-density-4 {
    width: 6px;
    height: 6px;
    bottom: 4px;
}
.caploo-calendar-cell__dot.caploo-density-1 { opacity: 0.65; }
.caploo-calendar-cell__dot.caploo-density-2 { opacity: 0.8; }
.caploo-calendar-cell__dot.caploo-density-3 { opacity: 0.95; }
.caploo-calendar-cell__dot.caploo-density-4 { opacity: 1; }
.caploo-calendar-cell__dot { opacity: 0.9; }

/* — 7. List view width — single column up to 840 px, centred in the main
   area. At ≥1400 px main-area widths we allow two 720 px columns so the
   right-side whitespace doesn't dominate. */
.feed--list {
    width: 100%;
    max-width: 840px;
    margin-inline-start: auto;
    margin-inline-end: auto;
}
.caploo-moment-list {
    width: 100%;
    max-width: 840px;
    margin-inline-start: auto;
    margin-inline-end: auto;
}
@media (min-width: 1400px) {
    .feed--list,
    .caploo-moment-list {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 720px));
        max-width: 1480px;
        gap: 1rem .75rem;
    }
}

/* — 9. Navbar drop-info / meta clusters (top-bar grouping fix) — */
.navbar {
    /* Tighten the existing flex layout so multiple slots can share the row.
       Existing `.navbar` declaration uses `display: flex` + `align-items:
       center` + a default `justify-content: space-between`. We add gap and
       allow children to shrink. */
    gap: var(--space-1);
}
.navbar-drop-info {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: .5rem;
    overflow: hidden;
}
.navbar-drop-info:empty { display: none; }
.navbar-drop-name {
    font-size: .95rem;
    font-weight: normal;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.navbar-meta {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    flex-shrink: 0;
}
.navbar-meta:empty { display: none; }
.navbar-countdown {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    height: 32px;
    padding: 0 .65rem;
    border-radius: var(--radius-pill);
    font-size: .8rem;
    font-weight: 500;
    color: var(--muted);
    background: color-mix(in srgb, var(--surface-alt, var(--surface)) 70%, transparent);
    border: 1px solid var(--border);
    line-height: 1;
}
.navbar-countdown svg { color: var(--muted); }
/* F-094: visible context label (e.g. "Expires in") so the chip reads clearly
   without relying on the clock icon alone. Styled smaller and muted to keep
   visual weight subordinate to the time value itself. */
.navbar-countdown__prefix {
    font-size: .7rem;
    font-weight: 400;
    color: var(--muted);
    letter-spacing: .01em;
}
.navbar-countdown.countdown--warn,
.navbar-countdown.countdown--danger {
    color: var(--warning);
    border-color: color-mix(in srgb, var(--warning) 40%, var(--border));
    background: color-mix(in srgb, var(--warning) 10%, var(--surface));
}
.navbar-countdown.countdown--danger {
    color: var(--danger, #d14343);
    border-color: color-mix(in srgb, var(--danger, #d14343) 40%, var(--border));
    background: color-mix(in srgb, var(--danger, #d14343) 12%, var(--surface));
}
.navbar-pin-chip {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    height: 32px;
    padding: 0 .25rem 0 .65rem;
    border-radius: var(--radius-pill);
    font-size: .8rem;
    font-weight: 500;
    color: var(--text);
    background: var(--surface-alt, var(--surface));
    border: 1px solid var(--border);
    line-height: 1;
}
.navbar-pin-chip__label {
    color: var(--muted);
    font-size: .7rem;
    font-weight: normal;
    letter-spacing: .04em;
    text-transform: uppercase;
}
.navbar-pin-chip__value {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: .85rem;
    font-weight: normal;
    letter-spacing: .04em;
}
.navbar-pin-chip__copy {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    height: 26px;
    padding: 0 .55rem;
    margin-inline-start: .15rem;
    font-size: var(--text-micro);
    font-weight: 500;
    color: var(--muted);
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.navbar-pin-chip__copy:hover {
    background: var(--surface);
    color: var(--text);
    border-color: var(--border);
}
.navbar-pin-chip__copy:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.navbar-pin-chip__copy.is-copied {
    color: var(--success, #2a9d52);
    border-color: color-mix(in srgb, var(--success, #2a9d52) 40%, var(--border));
}

/* On narrow viewports collapse the PIN chip to value+copy (drop the "PIN"
   label) and shrink the drop name. Countdown text stays visible — hosts
   need to know how much time their drop has left. */
@media (max-width: 720px) {
    .navbar-drop-name { font-size: .85rem; }
    .navbar-pin-chip__label { display: none; }
}

/* At ≤480px (portrait phone) the navbar has 8 competing flex items that
   cause the drop title to be obscured. Fix: hide the countdown chip and
   collapse share/report buttons to icon-only so the title can truncate
   cleanly in the remaining space. Settings is already icon-only. */
@media (max-width: 480px) {
    .navbar-countdown { display: none; }
    .navbar .share-trigger-btn .label-text { display: none; }
}

/* — 10 / Polish. Subtle contrast lift between sidebar and main column so the
   divide reads as a clear architectural seam. */
.caploo-shell__sidebar {
    background: color-mix(in srgb, var(--surface) 92%, var(--bg));
    border-inline-end: 1px solid var(--border);
}

/* — 11 / Polish. Toolbar cohesion — pull the "Moments • Live" header inline
   with the toolbar row at desktop widths so they read as one cohesive band. */
@media (min-width: 720px) {
    .feed-header {
        margin-bottom: 0;
    }
    .feed-header + #feed-content > .caploo-toolbar { padding-top: var(--space-1); }
}

/* — Mobile sidebar close button stays as the trailing affordance — */
.caploo-shell__sidebar-header--no-title .caploo-shell__sidebar-close {
    margin-inline-start: 0;
}


/* =============================================================================
 * Phase 6.6 — regression fixes.
 *
 * The Phase 6.5 audit landed on main but a live-site sweep found the calendar
 * header was missing the month label, the Today button was stretched, the
 * three view modes (Grid / List / Large) were rendering identically because
 * the legacy two-column .feed-layout was squeezing the cards into the
 * left-hand column, the composer's Post button was buried under the camera
 * hint, the day dot was too subtle, the search input was truncated, the
 * "Moments · Live" header lived in its own row above the toolbar, and the
 * sidebar / main divide had no contrast lift.
 *
 * Source order: this block lives at the very end so it wins the cascade
 * over earlier (Phase 6.5) declarations without resorting to !important.
 * ============================================================================= */

/* ── 1. Calendar header — bullet-proof single-row layout ───────────────────── */
/* The legacy block at line ~3465 set `display: flex; flex-direction: column`
   on the parent which stranded the title in its own track. Reset to a single
   horizontal row so [‹] [month-label] [›] cluster, with Today flushed right. */
.caploo-calendar-header {
    display: block;
    padding: 0 var(--space-1) var(--space-1);
}
.caploo-calendar-header__row {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    width: 100%;
    min-width: 0;
}
.caploo-calendar-header__nav {
    flex: 0 0 32px;
    width: 32px;
    height: 32px;
    min-width: 32px;
    min-height: 32px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    color: var(--muted);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.caploo-calendar-header__nav:hover {
    background: var(--surface-alt, var(--surface));
    color: var(--text);
    border-color: var(--border);
}
.caploo-calendar-header__title {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    display: inline-flex;
    align-items: baseline;
    gap: .35rem;
    font-size: 14px;
    font-weight: normal;
    line-height: 1.2;
    color: var(--text);
    text-transform: none;
    letter-spacing: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.caploo-calendar-header__title-label {
    font-weight: normal;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.caploo-calendar-header__count {
    font-weight: 400;
    font-size: 12px;
    color: var(--muted);
    white-space: nowrap;
    flex-shrink: 0;
}
/* Today is a compact pill, right-aligned via auto margin so the nav cluster
   stays tight and Today never stretches to fill remaining flex space. */
.caploo-calendar-header__today {
    flex: 0 0 auto;
    min-width: 0;
    width: auto;
    height: 32px;
    padding: 0 .75rem;
    margin-inline-start: auto;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
}
.caploo-calendar-header__today:hover {
    border-color: var(--accent-dim, var(--accent));
    color: var(--accent);
}

/* ── 3. View modes (Grid / List / Large) — distinct layouts at desktop ───── */
/* Root cause: .feed-layout used `grid-template-columns: 280px 1fr` which left
   a phantom 280-px column where the legacy capture-panel used to sit, so the
   feed (the only remaining child) was stranded in the 280-px slot regardless
   of viewport width. Collapse it to a single column so the feed gets the full
   main-area width and Grid / Large can fan out into multiple tiles. */
.feed-layout {
    display: flex;
    flex-direction: column;
    min-height: calc(100vh - var(--navbar-h));
    min-height: calc(100dvh - var(--navbar-h));
}

/* Re-assert mode-specific grid templates AFTER the layout fix so the cascade
   resolves cleanly even if a downstream override re-introduces the legacy
   max-width on .feed. */
.feed--grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr));
    gap: 1rem;
    width: 100%;
    max-width: none;
    margin-inline: 0;
}
.feed--large {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 360px), 1fr));
    gap: 1.25rem;
    width: 100%;
    max-width: 1280px;
    margin-inline: 0;
}
/* List is always single-column. The Phase 6.5 polish at >=1400px split list
   into a 2-column grid which broke the "thumbnail-left, content-right"
   contract — undo that and stay single-column at every width. */
.feed--list,
.caploo-moment-list {
    display: flex !important;
    flex-direction: column;
    gap: .75rem;
    width: 100%;
    max-width: 840px;
    margin-inline-start: auto;
    margin-inline-end: auto;
    grid-template-columns: none;
}

/* ── 5. Day-cell post-count dot — bigger, slightly outlined ────────────── */
.caploo-calendar-cell__dot,
.caploo-calendar-cell__dot.caploo-density-1,
.caploo-calendar-cell__dot.caploo-density-2,
.caploo-calendar-cell__dot.caploo-density-3,
.caploo-calendar-cell__dot.caploo-density-4 {
    width: 6px;
    height: 6px;
    background: var(--accent);
    border-radius: 50%;
    box-shadow: 0 0 0 1px var(--surface);
    bottom: 4px;
}
.caploo-calendar-cell__dot.caploo-density-1 { opacity: .75; }
.caploo-calendar-cell__dot.caploo-density-2 { opacity: .9; }
.caploo-calendar-cell__dot.caploo-density-3 { opacity: 1; }
.caploo-calendar-cell__dot.caploo-density-4 { opacity: 1; }

/* ── 6. Search input — wider real estate at desktop widths ────────────── */
@media (min-width: 1024px) {
    .caploo-toolbar__row > .caploo-searchbar {
        flex: 1 1 320px;
        min-width: 320px;
        max-width: 480px;
    }
}
@media (min-width: 1440px) {
    .caploo-toolbar__row > .caploo-searchbar {
        max-width: 520px;
    }
}

/* ── 7. Toolbar leading slot — "MOMENTS · Live" inline with the toolbar ── */
.caploo-toolbar__leading {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    flex: 0 0 auto;
    margin-inline-end: var(--space-1);
    padding-inline-end: var(--space-1);
    border-inline-end: 1px solid var(--border);
    min-height: 40px; /* F-cd-10: lifted 36→40 for navband rhythm consistency */
}
.caploo-toolbar__title {
    font-size: .8rem;
    font-weight: 700;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .08em;
    white-space: nowrap;
}
@media (max-width: 720px) {
    .caploo-toolbar__leading {
        border-inline-end: none;
        margin-inline-end: 0;
        padding-inline-end: 0;
        flex: 1 1 100%;
        order: -1;
    }
}

/* ── 8. Sidebar contrast — clearer architectural seam ─────────────────── */
:root {
    --surface-2: color-mix(in srgb, var(--surface) 86%, var(--bg));
}
.caploo-shell__sidebar {
    background: var(--surface-2);
    border-inline-end: 1px solid var(--border);
}

/* ── 4. Composer Capture & Post button — full-width, prominent ─────────── */
/* Re-state the submit-button affordance so the prior body-padding + reduced
   contrast can't visually demote it back into looking like a tertiary link. */
.caploo-composer-sidebar__submit {
    width: 100%;
    min-height: 44px;
    padding: var(--space-1-5, 10px) var(--space-2, 14px);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .45rem;
    background: var(--accent);
    color: var(--text-on-accent);
    border: 1px solid var(--accent);
    border-radius: var(--radius-md, var(--radius-sm));
    font-size: 14px;
    font-weight: normal;
    letter-spacing: 0.01em;
    cursor: pointer;
    box-shadow: 0 1px 2px color-mix(in srgb, var(--accent) 20%, transparent);
}
.caploo-composer-sidebar__submit:disabled {
    opacity: .5;
    cursor: not-allowed;
    box-shadow: none;
}
.caploo-composer-sidebar__camera-hint {
    margin-top: .5rem;
}

/* =============================================================================
 * Phase 6.7 — Precise CSS overrides (diagnosed from live DOM on ec049ac).
 *
 * 1) Calendar title was collapsing to width:0 because Today's `margin-left:auto`
 *    consumed the row's free space before the title's `flex: 1 1 auto` could
 *    grow. Result: title rendered as an empty inline-flex with min-width:0.
 * 2) `.feed--list` was getting `display:grid` with
 *    `grid-template-columns: 114px 114px` somewhere in the live build despite
 *    the Phase 6.6 `!important` flex override — re-stated here at maximum
 *    specificity so nothing downstream can re-introduce the 2-col grid.
 * 3) `--grid` and `--large` re-stated at the same specificity so the entire
 *    view-mode rule-set lives in one block at the end of the cascade.
 *
 * Source order matters: this block lives at the very end so it wins without
 * relying on `!important` (except for --list, where multiple downstream rules
 * have historically tried to set it back to grid).
 * ============================================================================= */

/* ── Fix A — Calendar header: title takes remaining space, Today no auto ───── */
.caploo-calendar-header__row {
    display: flex;
    align-items: center;
    gap: var(--space-1, 8px);
    width: 100%;
}

.caploo-calendar-header__title {
    flex: 1 1 auto;
    min-width: 0;
    display: block;             /* was inline-flex; that + flex-wrap was hiding spans */
    margin: 0;
    text-align: center;
    font-size: 14px;
    font-weight: normal;
    line-height: 1.2;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.caploo-calendar-header__title-label {
    font-weight: normal;
    color: var(--text);
}

.caploo-calendar-header__count,
.caploo-calendar-header__post-count,
.caploo-calendar-header__title-count {
    font-weight: 400;
    font-size: 12px;
    color: var(--muted);
    margin-inline-start: var(--space-0-5, 4px);
}

.caploo-calendar-header__nav {
    flex: 0 0 32px;
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-sm, 6px);
    color: var(--muted);
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}

.caploo-calendar-header__nav:hover {
    background: var(--surface-alt, rgba(255,255,255,0.05));
    border-color: var(--border);
    color: var(--text);
}

.caploo-calendar-header__today,
.caploo-calendar-header__today-btn {
    flex: 0 0 auto;
    height: 32px;
    padding: 0 .75rem;
    border-radius: var(--radius-pill);
    font-size: 12px;
    font-weight: normal;
    /* Critical: NO margin-left:auto — that ate the title's flex-grow space. */
    margin-inline-start: 0;
}

/* ── Fix B — List: single-column flex stack at every width ──────────────── */
/* Specificity: .feed.feed--list (0,2,0) beats
   bare .feed (0,1,0) and any mid-cascade .feed--list. */
.feed.feed--list,
.caploo-moment-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 12px);
    width: 100%;
    max-width: 840px;
    margin-inline-start: auto;
    margin-inline-end: auto;
    grid-template-columns: none;
}

/* Card chrome inside list mode */
.feed.feed--list .moment-card,
.caploo-moment-list .moment-card,
.moment-card--list {
    display: grid;
    grid-template-columns: 96px 1fr;
    gap: 1rem;
    align-items: center;
    padding: .6rem;
    border-radius: var(--radius-sm, 8px);
    background: var(--surface);
    border: 1px solid var(--border);
}

.feed.feed--list .moment-media-wrap,
.moment-card--list .moment-media-wrap {
    aspect-ratio: 1 / 1;
    width: 96px;
    height: 96px;
    border-radius: var(--radius-sm, 6px);
    overflow: hidden;
}

.feed.feed--list .moment-media-wrap > img,
.feed.feed--list .moment-media-wrap > video,
.moment-card--list .moment-media-wrap > img,
.moment-card--list .moment-media-wrap > video {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.feed.feed--list .moment-card-body,
.moment-card--list .moment-card-body {
    min-width: 0;
}

/* ── Fix C — Grid: multi-column auto-fill, full main-area width ────────── */
.feed.feed--grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr));
    gap: 1rem;
    width: 100%;
    max-width: none;
    margin-inline: 0;
}

.feed.feed--grid .moment-card,
.moment-card--grid {
    display: flex;
    flex-direction: column;
    border-radius: var(--radius-sm, 12px);
    background: var(--surface);
    border: 1px solid var(--border);
    overflow: hidden;
}

.feed.feed--grid .moment-media-wrap,
.moment-card--grid .moment-media-wrap {
    aspect-ratio: 1 / 1;
    width: 100%;
    overflow: hidden;
    border-radius: 0;
}

.feed.feed--grid .moment-media-wrap > img,
.feed.feed--grid .moment-media-wrap > video,
.moment-card--grid .moment-media-wrap > img,
.moment-card--grid .moment-media-wrap > video {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.feed.feed--grid .moment-card-caption .caption-text,
.moment-card--grid .moment-card-caption .caption-text {
    font-size: 13px;
    line-height: 1.4;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ── Fix D — Large: bigger media-rich cards, 4:3 media, full caption ────── */
.feed.feed--large {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 360px), 1fr));
    gap: 1.25rem;
    width: 100%;
    max-width: 1280px;
    margin-inline-start: auto;
    margin-inline-end: auto;
}

.feed.feed--large .moment-card,
.moment-card--large {
    display: flex;
    flex-direction: column;
    border-radius: var(--radius-sm, 12px);
    background: var(--surface);
    border: 1px solid var(--border);
    overflow: hidden;
}

.feed.feed--large .moment-media-wrap,
.moment-card--large .moment-media-wrap {
    aspect-ratio: 4 / 3;
    width: 100%;
    overflow: hidden;
}

.feed.feed--large .moment-media-wrap > img,
.feed.feed--large .moment-media-wrap > video,
.moment-card--large .moment-media-wrap > img,
.moment-card--large .moment-media-wrap > video {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.feed.feed--large .moment-card-caption .caption-text,
.moment-card--large .moment-card-caption .caption-text {
    font-size: 14px;
    line-height: 1.5;
    /* No clamp — show full caption in Large mode */
    display: block;
    -webkit-line-clamp: unset;
    overflow: visible;
}
/* === End Phase 6.7 ======================================================= */

/* =============================================================================
 * Phase 6.8 — Calendar header: two-row layout for narrow sidebar (~211px).
 *
 * Diagnosis: Phase 6.7 used a single horizontal row [‹] [title] [›] [Today].
 * Title needs ~150px just for "May 2026 · 8 posts"; nav buttons + Today + gaps
 * consume the remaining ~190px from the sidebar's ~211px. Result: title's
 * `flex: 1 1 auto` collapsed to width:0 because there was nothing left to grow
 * into. (`overflow:hidden` + zero width = invisible label.)
 *
 * Fix: stack the header as two rows.
 *   Row 1:  May 2026 · 8 posts          (centered, full width, own line)
 *   Row 2:  [‹]              [›] [Today] (nav + jump-to-today)
 *
 * Source order: lives at the very end so it wins over Phase 6 / 6.5 / 6.6 / 6.7
 * without `!important`.
 * ============================================================================= */

.caploo-calendar-header {
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 8px);
    padding: var(--space-1, 8px) var(--space-2, 12px);
    width: 100%;
}

/* Row 1 — title takes its own row, full width, centered. */
.caploo-calendar-header__title {
    flex: 0 0 auto;
    display: block;
    width: 100%;
    text-align: center;
    margin: 0;
    padding: 0;
    font-size: 14px;
    font-weight: normal;
    line-height: 1.2;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.caploo-calendar-header__title-label {
    font-weight: normal;
    color: inherit;
}

.caploo-calendar-header__count,
.caploo-calendar-header__post-count,
.caploo-calendar-header__title-count {
    font-weight: 400;
    font-size: 12px;
    color: var(--muted);
    margin-inline-start: var(--space-0-5, 4px);
}

/* Row 2 — nav buttons. Prev/Next on left cluster, Today pushed to the right. */
.caploo-calendar-header__row {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: var(--space-1, 8px);
    width: 100%;
}

.caploo-calendar-header__nav {
    flex: 0 0 32px;
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-sm, 6px);
    color: var(--muted);
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}

.caploo-calendar-header__nav:hover {
    background: var(--surface-alt, rgba(255,255,255,0.05));
    border-color: var(--border);
    color: var(--text);
}

.caploo-calendar-header__today,
.caploo-calendar-header__today-btn {
    flex: 0 0 auto;
    height: 32px;
    padding: 0 .75rem;
    margin-inline-start: auto;          /* push to the right of the nav cluster */
    border-radius: var(--radius-pill);
    font-size: 12px;
    font-weight: normal;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    cursor: pointer;
    white-space: nowrap;
}

.caploo-calendar-header__today:hover,
.caploo-calendar-header__today-btn:hover {
    border-color: var(--accent-dim, var(--accent));
    color: var(--accent);
}
/* === End Phase 6.8 ======================================================= */

/* =============================================================================
 * Phase 6.9 — Final cleanup polish
 *
 * Three small overrides that have to win against stacked rules from prior
 * Phase 6.x sections, so `!important` is used deliberately and minimally.
 *
 *  1) Calendar title row reliably centers its label + post-count
 *     (Phase 6.8 set text-align:center, but inline-flex / earlier rules
 *      kept packing content to the start).
 *  2) Today pill collapses to its content width (~60–80px) instead of
 *     stretching to the right edge of the nav row.
 *  3) Adds breathing room between the "Moments" toolbar title and the
 *     adjacent state badge ("Live", "Closed", or "View Only").
 * ============================================================================= */

/* 1) Calendar title — block-level row, centered text, inline children. */
.caploo-calendar-header__title {
    display: block !important;
    width: 100% !important;
    text-align: center !important;
}
.caploo-calendar-header__title-label,
.caploo-calendar-header__count,
.caploo-calendar-header__post-count,
.caploo-calendar-header__title-count {
    display: inline;
}

/* 2) Today button — content-width pill, no flex stretching. */
.caploo-calendar-header__today,
.caploo-calendar-header__today-btn {
    flex: 0 0 auto !important;
    width: auto !important;
    min-width: 0 !important;
    height: 32px;
    padding: 0 var(--space-1, 8px);
}

/* 3) Toolbar leading slot — gap between "Moments" title and status badge. */
.caploo-toolbar__leading {
    display: inline-flex;
    align-items: center;
    gap: .75rem; /* 12px breathing room between title and View Only / Live / Closed */
}
/* === End Phase 6.9 ======================================================= */

/* =============================================================================
 * Phase 6 polish 2 — auth cards, navbar buttons, composer affordance,
 * calendar regressions, map empty state.
 *
 * Goals (locked):
 *   A. Login / Signup / ForgotPassword / ResetPassword wear the Home Join-card
 *      pattern: ~480px max-width, dark card, section label, large CTA.
 *   B. Navbar Sign in / Sign up buttons get explicit styling (btn-ghost +
 *      btn-primary previously had no rules).
 *   C. Composer caption taller (3 lines), Add tags / Add location summaries
 *      read as tappable input rows.
 *   D. Calendar Today button stays compact (regression fix).
 *   E. First-week May 1/2 "highlight" was the surface-alt fill on in-month
 *      cells contrasting with transparent other-month cells. Clean fix:
 *      drop the per-cell fill — today/selected/hover still differentiate.
 *   F. Day post-count dot bumped to 7px with a 1.5px outline ring.
 *   G. Map sidebar shows an inline empty state when no moment has lat/lng.
 * ============================================================================= */

/* ── A. Auth pages — shell, brand, card ────────────────────────────────── */
.auth-shell {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2, 1rem);
    padding: var(--space-4, 2rem) var(--space-2, 1rem);
    max-width: 480px;
    margin: 0 auto;
    min-height: calc(100vh - var(--navbar-h, 52px));
    min-height: calc(100dvh - var(--navbar-h, 52px));
    justify-content: center;
}

/* feat/auth-nav-footer-compact-2026-05-17 — in-product back-to-home
   affordance on every auth page. Lives at the top-left of the auth-shell
   column (align-self: flex-start) so users entering the auth flow have a
   discoverable, persistent escape back to the marketing landing without
   relying on the navbar brand mark or browser back. Jakob's-Law style:
   matches the standard SaaS auth pattern (Stripe/Linear/Notion top-left
   wordmark-as-home). M-047: placed last in DOM order so Tab reaches form
   fields first; order:-1 keeps it visually at the top of the flex column. */
.auth-home-link {
    order: -1;
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    gap: var(--space-0-5, .25rem);
    min-height: 44px; /* primary CTA touch baseline per CLAUDE.md rule #7 */
    padding: .55rem .75rem;
    color: var(--muted);
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-md, 12px);
    font-size: var(--text-sm);
    font-weight: normal;
    letter-spacing: -.01em;
    text-decoration: none;
    transition: color var(--transition), background var(--transition), border-color var(--transition);
}
.auth-home-link:hover {
    color: var(--text);
    background: color-mix(in srgb, var(--surface-alt) 70%, transparent);
}
.auth-home-link:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}
.auth-home-link svg {
    flex-shrink: 0;
    width: 14px;
    height: 14px;
}

.auth-brand {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--space-1, .5rem);
    margin-bottom: var(--space-1, .5rem);
}

.auth-brand .caploo-logo { display: block; }

.caploo-wordmark {
    font-size: var(--text-fluid-auth, clamp(1.6rem, 6vw, 2.2rem));
    font-weight: 800;
    letter-spacing: -.04em;
    background: linear-gradient(135deg, var(--accent) 0%, #b48ef5 60%, #e0c3fc 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    line-height: 1.1;
    margin: 0;
}

.caploo-tagline {
    color: var(--muted);
    font-size: .95rem;
    margin: var(--space-1, .5rem) 0 0;
    max-width: 28ch;
}

.auth-card {
    width: 100%;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg, 16px);
    padding: var(--space-3, 1.5rem);
    display: flex;
    flex-direction: column;
    gap: var(--space-1-5, .75rem);
    box-shadow: var(--shadow);
}
.auth-card:focus-within {
    border-color: color-mix(in srgb, var(--accent) 40%, transparent);
}

.auth-card__heading {
    font-size: var(--text-lg);
    font-weight: 700;
    color: var(--text);
    margin: 0 0 var(--space-1) 0;
    line-height: 1.2;
    text-wrap: balance;
}

.auth-card__label {
    font-size: var(--text-xs);
    font-weight: 700;
    letter-spacing: .08em;
    color: var(--muted);
    text-transform: uppercase;
    margin-bottom: var(--space-1, .5rem);
}

.auth-card__hint {
    font-size: .9rem;
    color: var(--muted);
    margin: 0;
}

.auth-card__field {
    display: flex;
    flex-direction: column;
    gap: .35rem;
}

.auth-card__field > label,
.auth-card__field-header > label {
    font-size: var(--text-sm);
    color: var(--muted);
    font-weight: 500;
}
/* F-051: label + char-counter row */
.auth-card__field-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: .5rem;
}
.auth-card__char-count {
    font-size: .7rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    flex-shrink: 0;
}
.auth-card__char-count--warn { color: var(--warn, #f59e0b); }

.auth-card__field > input {
    width: 100%;
    height: 44px;
    padding: 0 var(--space-1-5, .75rem);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md, 12px);
    color: var(--text);
    font-size: var(--text-md);
}

.auth-card__field > input:focus-visible {
    outline: 2px solid var(--focus-ring-color, var(--accent));
    outline-offset: -1px;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-glow);
}

.auth-card__submit {
    width: 100%;
    height: 48px;
    background: var(--accent);
    color: var(--text-on-accent);
    border: none;
    border-radius: var(--radius-md, 12px);
    font-weight: normal;
    font-size: var(--text-md);
    cursor: pointer;
    margin-top: var(--space-1, .5rem);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
    transition: background var(--transition);
}
.auth-card__submit:hover:not(:disabled) { background: var(--accent-dim); }
.auth-card__submit:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}
.auth-card__submit:disabled { opacity: .55; cursor: not-allowed; }
.auth-card__submit--link { text-decoration: none; }
.auth-card__submit--link:hover { text-decoration: none; }

/* F-014b / F-014c: cross-cutting 44px guarantee for form-adjacent submit CTAs.
   The global `button { min-height: 44px }` already covers most cases; this rule
   makes the intent explicit for the card submit pattern and form submits. */
.card > button, form button[type="submit"] { min-height: 44px; }

/* F-062: always stack "Forgot password?" and "Create account" vertically so they
   don't compete on the same line — the sep (·) is hidden at all widths. */
.auth-card__footer {
    font-size: var(--text-sm);
    color: var(--muted);
    text-align: center;
    margin: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .5rem;
}
.auth-card__footer a {
    color: var(--accent);
    text-decoration: none;
    min-height: 24px;
    display: inline-flex;
    align-items: center;
    padding: 0 .25rem;
}
.auth-card__footer a:hover { text-decoration: underline; }
.auth-card__footer-sep { display: none; }
/* F-059: ≤48rem — widen tap zone to 44px */
@media (max-width: 48rem) {
    .auth-card__footer a { min-height: 44px; padding: 0 .5rem; }
}
/* Pass 9 — auth intro paragraph (FEE-D1/D2) and legal microcopy (FEE-A3) */
.auth-card__intro {
    font-size: var(--text-sm);
    color: var(--muted);
    text-align: center;
    margin: -.25rem 0 .5rem;
}
.auth-legal {
    font-size: var(--text-xs);
    color: var(--muted);
    text-align: center;
    margin: .75rem 0 0;
}
.auth-legal a {
    color: var(--muted);
    text-decoration: underline;
}
.auth-legal a:hover { color: var(--accent); }

/* Auth feedback states (re-style the existing .auth-error/.auth-success
   classes so the centered card-body messages read as semantic blocks). */
.auth-error {
    color: var(--danger);
    font-size: var(--text-sm);
    margin: 0;
    padding: .55rem .75rem;
    border-radius: var(--radius-sm, 8px);
    background: color-mix(in srgb, var(--danger) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
}
.auth-success {
    color: var(--text);
    font-size: .9rem;
    margin: 0;
    padding: .55rem .75rem;
    border-radius: var(--radius-sm, 8px);
    background: color-mix(in srgb, var(--success) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--success) 30%, transparent);
    text-align: start;
    display: block;
}

/* ── A.2 Password field — show/hide eye toggle ─────────────────────────── */
.auth-card__field--password { position: relative; }

.auth-card__password-wrapper {
    position: relative;
    display: block;
}

.auth-card__password-input {
    width: 100%;
    height: 44px;
    padding: 0 44px 0 var(--space-1-5, .75rem);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md, 12px);
    color: var(--text);
    font-size: var(--text-md);
}
.auth-card__password-input:focus-visible {
    outline: 2px solid var(--focus-ring-color, var(--accent));
    outline-offset: -1px;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-glow);
}

.auth-card__password-toggle {
    position: absolute;
    top: 50%;
    inset-inline-end: 4px;
    transform: translateY(-50%);
    width: 44px;   /* F-044 / F-044b: ≥44×44 touch target */
    height: 44px;
    padding: 0;
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm, 8px);
}
.auth-card__password-toggle:hover { color: var(--text); background: var(--surface-alt); }
.auth-card__password-toggle:focus-visible {
    outline: var(--focus-outline);
    outline-offset: -1px;
}

/* ── A.3 Password strength meter (signup + reset) ──────────────────────────
   Auth-security UX refresh — meter is now a 5-segment bar + label + visible
   plain-language criteria checklist. APCA-compliant tokens
   (var(--danger|warning|info|success|muted)) carry color meaning; the icon
   glyphs (✓ ✗ ○) carry shape meaning so the state survives color-blindness
   and high-contrast modes. Reduced-motion users only see the bar fill
   transition disabled by the global prefers-reduced-motion rule below. */
.auth-card__strength {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: .55rem;
    margin-top: -.25rem;
}
.auth-card__strength-bar {
    display: flex;
    gap: 4px;
    flex: 1 1 auto;
    align-items: center;
}
.auth-card__strength-seg {
    flex: 1 1 0;
    height: 6px;
    background: var(--surface-alt);
    border-radius: 3px;
    transition: background var(--transition);
}
.auth-card__strength-seg--filled.auth-card__strength-seg--score-1 { background: var(--danger); }
.auth-card__strength-seg--filled.auth-card__strength-seg--score-2 { background: var(--warning); }
.auth-card__strength-seg--filled.auth-card__strength-seg--score-3 { background: var(--info); }
.auth-card__strength-seg--filled.auth-card__strength-seg--score-4 { background: var(--success); }

.auth-card__strength-label {
    font-size: var(--text-xs);
    color: var(--muted);
    text-align: end;
    font-weight: 500;
}
.auth-card__strength-label--score-1 { color: var(--danger); }
.auth-card__strength-label--score-2 { color: var(--warning); }
.auth-card__strength-label--score-3 { color: var(--info); }
.auth-card__strength-label--score-4 { color: var(--success); }

.auth-card__strength-checklist {
    list-style: none;
    padding: 0;
    margin: .15rem 0 0 0;
    display: flex;
    flex-direction: column;
    gap: .4rem;
}
.auth-card__strength-check {
    display: flex;
    align-items: flex-start;
    gap: .55rem;
    font-size: .8125rem;
    line-height: 1.3;
    color: var(--muted);
}
.auth-card__strength-check-icon {
    flex: 0 0 auto;
    width: 1.1rem;
    height: 1.1rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-pill);
    font-size: .85rem;
    font-weight: normal;
    line-height: 1;
    background: var(--surface-alt);
    color: var(--muted);
    transition: background var(--transition), color var(--transition);
}
.auth-card__strength-check-text { display: flex; flex-direction: column; gap: 1px; }
.auth-card__strength-check-text strong { font-weight: normal; color: var(--text); }
.auth-card__strength-check-text small { color: var(--muted); font-size: var(--text-xs); }

.auth-card__strength-check--met .auth-card__strength-check-icon {
    background: color-mix(in srgb, var(--success) 18%, transparent);
    color: var(--success);
}
.auth-card__strength-check--met .auth-card__strength-check-text strong { color: var(--success); }
.auth-card__strength-check--unmet .auth-card__strength-check-icon {
    background: color-mix(in srgb, var(--danger) 18%, transparent);
    color: var(--danger);
}
.auth-card__strength-check--neutral .auth-card__strength-check-icon { /* default tokens */ }

@media (prefers-reduced-motion: reduce) {
    .auth-card__strength-seg,
    .auth-card__strength-check-icon { transition: none; }
}

/* ── B. Navbar Sign in / Sign up buttons ───────────────────────────────── */
/* `.btn-ghost` and `.btn-primary` were previously unstyled — the navbar
   anchors picked up the global <a> color and the global <button> rules,
   which is why "Sign up" rendered like a regular link instead of a CTA.
   Add explicit rules scoped to the navbar so they look like proper auth
   actions. The selector spec uses both `.navbar` (current markup) and the
   spec's `.app-navbar` alias for forward compatibility. */
.navbar .btn-ghost,
.navbar a.btn-ghost,
.app-navbar .btn-ghost,
.app-navbar a.btn-ghost {
    display: inline-flex;
    align-items: center;
    height: 32px;
    padding: 0 var(--space-1-5, .75rem);
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--text);
    background: transparent;
    text-decoration: none;
    border: 1px solid transparent;
    border-radius: var(--radius-sm, 8px);
    transition: background var(--transition), color var(--transition);
    width: auto;
}
.navbar .btn-ghost:hover,
.navbar a.btn-ghost:hover,
.app-navbar .btn-ghost:hover,
.app-navbar a.btn-ghost:hover {
    background: var(--surface-alt);
    color: var(--text);
    text-decoration: none;
}

.navbar .btn-primary,
.navbar a.btn-primary,
.app-navbar .btn-primary,
.app-navbar a.btn-primary {
    display: inline-flex;
    align-items: center;
    height: 32px;
    padding: 0 var(--space-1-5, .75rem);
    font-size: var(--text-sm);
    font-weight: normal;
    background: var(--accent) !important;
    color: var(--text-on-accent) !important;
    text-decoration: none;
    border: none;
    border-radius: var(--radius-sm, 8px);
    transition: background var(--transition);
    width: auto;
}
.navbar .btn-primary:hover,
.navbar a.btn-primary:hover,
.app-navbar .btn-primary:hover,
.app-navbar a.btn-primary:hover {
    background: var(--accent-dim) !important;
    color: var(--text-on-accent) !important;
    text-decoration: none;
    filter: brightness(1.05);
}

/* Gap between Sign in and Sign up inside the auth cluster. */
.navbar-auth {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1, .5rem);
}

/* ── C. Composer affordance ────────────────────────────────────────────── */
/* Caption textarea — taller default height (3 lines ≈ 72px), auto-grow up
   to a 200px cap, then scroll. `field-sizing: content` is supported in
   modern Chromium / Safari TP and degrades gracefully (rows attribute
   carries the floor in older engines). */
.caploo-composer-sidebar__caption {
    min-height: 72px !important;
    max-height: 200px !important;
    field-sizing: content;
    overflow-y: auto;
    resize: vertical;
}

/* "Add tags" / "Add location" toggles read as tappable input rows. The
   markup uses <details>/<summary> so the click still expands inline; we
   only re-skin the summary so it doesn't look like a plain link. */
.caploo-composer-sidebar__summary {
    display: flex !important;
    align-items: center;
    gap: var(--space-1, .5rem);
    width: 100%;
    padding: var(--space-1-5, .75rem) !important;
    background: var(--surface-alt) !important;
    border: 1px solid var(--border) !important;
    border-radius: var(--radius-sm, 8px) !important;
    color: var(--text) !important;
    font-size: var(--text-sm) !important;
    font-weight: 500 !important;
    cursor: pointer;
    text-align: start;
    transition: background var(--transition), border-color var(--transition);
    list-style: none;
}
.caploo-composer-sidebar__summary:hover {
    background: color-mix(in srgb, var(--surface-alt) 70%, var(--accent) 30%) !important;
    border-color: var(--accent) !important;
}
.caploo-composer-sidebar__summary svg {
    color: var(--accent);
    flex-shrink: 0;
}
.caploo-composer-sidebar__details[open] > .caploo-composer-sidebar__summary {
    border-radius: var(--radius-sm, 8px) var(--radius-sm, 8px) 0 0 !important;
    border-bottom-color: transparent !important;
}

/* Generic helper class kept for future inline action buttons that want the
   same input-row affordance (referenced in docs). */
.composer-action-btn {
    display: flex;
    align-items: center;
    gap: var(--space-1, .5rem);
    width: 100%;
    padding: var(--space-1-5, .75rem);
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm, 8px);
    color: var(--text);
    font-size: var(--text-sm);
    cursor: pointer;
    text-align: start;
    transition: background var(--transition), border-color var(--transition);
}
.composer-action-btn:hover {
    background: color-mix(in srgb, var(--surface-alt) 70%, var(--accent) 30%);
    border-color: var(--accent);
}
.composer-action-btn .icon {
    color: var(--accent);
    flex-shrink: 0;
}

/* ── D. Calendar Today button — re-pin compact (regression) ────────────── */
.caploo-calendar-header__today,
.caploo-calendar-header__today-btn {
    flex: 0 0 auto !important;
    width: auto !important;
    height: 32px !important;
    padding: 0 var(--space-1-5, .75rem) !important;
    font-size: .8125rem !important;
    border-radius: var(--radius-pill) !important;
    white-space: nowrap !important;
    min-width: 0 !important;
}

/* ── E. Calendar week-1 highlight cleanup ──────────────────────────────── */
/* Decision: REMOVE the per-cell fill. The May 1/2 "highlight" was just the
   `--surface-alt` fill on in-month cells contrasting with transparent
   other-month cells in the first row. Today (border + dot), selected
   (accent tint), and hover still differentiate cells without a baseline
   fill. */
.caploo-calendar-cell {
    background: transparent !important;
}
.caploo-calendar-cell:hover:not(:disabled) {
    background: color-mix(in srgb, var(--surface-alt) 50%, var(--accent) 25%) !important;
}
.caploo-calendar-cell--selected {
    background: color-mix(in srgb, var(--accent) 25%, var(--surface-alt)) !important;
}

/* ── F. Day post-count dot — 7px + 1.5px outline ───────────────────────── */
.caploo-calendar-cell__dot {
    width: 7px !important;
    height: 7px !important;
    background: var(--accent) !important;
    border-radius: 50% !important;
    margin: var(--space-0-25) auto 0 !important;
    display: block !important;
    box-shadow: 0 0 0 1.5px var(--bg) !important;
    /* Density still nudges opacity so dense days read slightly heavier. */
}
.caploo-calendar-cell--selected .caploo-calendar-cell__dot {
    background: #fff !important;
    box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--accent) 40%, var(--bg)) !important;
}

/* ── G. Map sidebar empty state ────────────────────────────────────────── */
.map-empty {
    text-align: center;
    padding: var(--space-2, 1rem);
    color: var(--muted);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .35rem;
}
.map-empty__icon {
    color: var(--muted);
    opacity: .5;
    line-height: 0;
}
.map-empty__title {
    font-size: var(--text-sm);
    margin: var(--space-1, .5rem) 0 0;
    color: var(--text);
    font-weight: 500;
}
.map-empty__hint {
    font-size: var(--text-xs);
    margin: 0;
    color: var(--muted);
    max-width: 28ch;
}
/* === End Phase 6 polish 2 ================================================ */


/* === Phase 6 polish 3 — sidebar section clipping fix === */
/* Each sidebar section should take its natural height, not be squeezed by flex distribution. */
/* Sidebar shell handles scroll when total content exceeds viewport. */

.caploo-shell__sidebar {
  overflow-y: auto !important;
  display: flex;
  flex-direction: column;
  /* allow scrolling when total > viewport */
}

.caploo-sidebar-section {
  flex: 0 0 auto !important;
  /* natural height — don't shrink, don't grow to fill */
  overflow: visible !important;
  /* let content render fully; sidebar handles scroll */
  height: auto !important;
  min-height: 0;
}

.caploo-sidebar-section__body {
  overflow: visible !important;
  height: auto !important;
}

/* Calendar body explicitly */
.caploo-sidebar-section__body--calendar {
  overflow: visible !important;
  height: auto !important;
}

/* Map body — when not collapsed, let it use its declared height (e.g. 220px); when empty state, natural height */
.caploo-sidebar-section--map .caploo-sidebar-section__body {
  overflow: visible !important;
}
/* === End Phase 6 polish 3 ================================================ */


/* === Phase 6 polish 4 — sidebar header tightening === */
/* When the sidebar has no title (just collapse chevron + mobile close ×), */
/* don't reserve a full title-bar height. Keep the chevron compact. */

.caploo-shell__sidebar-header--no-title {
  min-height: 0 !important;
  height: auto !important;
  padding: var(--space-2, 8px) var(--space-3, 12px) !important;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2, 8px);
}

/* Collapse chevron compact */
.caploo-shell__sidebar-header--no-title .caploo-shell__sidebar-collapse {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm, 6px);
  color: var(--muted);
  cursor: pointer;
  flex-shrink: 0;
}

.caploo-shell__sidebar-header--no-title .caploo-shell__sidebar-collapse:hover {
  background: var(--surface-2, rgba(255,255,255,0.05));
  color: var(--text);
}

/* Mobile close × — only shown when sidebar is in drawer mode (≤900px), */
/* hidden on desktop where the drawer overlay isn't active. */
@media (min-width: 901px) {
  .caploo-shell__sidebar-header--no-title .caploo-shell__sidebar-close {
    display: none;
  }
}

/* On mobile drawer the close button shows; keep it compact too */
.caploo-shell__sidebar-header--no-title .caploo-shell__sidebar-close {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm, 6px);
  color: var(--muted);
  cursor: pointer;
  flex-shrink: 0;
  font-size: var(--text-lg);
}

.caploo-shell__sidebar-header--no-title .caploo-shell__sidebar-close:hover {
  background: var(--surface-2, rgba(255,255,255,0.05));
  color: var(--text);
}
/* === End Phase 6 polish 4 ================================================ */

/* === Phase 6 polish 5 — hide sidebar close X on desktop (cascade order fix) === */
@media (min-width: 901px) {
  .caploo-shell__sidebar-close {
    display: none !important;
  }
}


/* =============================================================================
 * Phase 6 polish 6 — sidebar polish + auth CTAs on home pages + WCAG 3.0 pass
 *
 * Buckets:
 *   A. Calendar grid responsive — fix overlap + Saturday bleed by enforcing
 *      true 7-column 1fr grid and dropping any cell min-width carryovers.
 *   B. Hide sidebar --no-title header on desktop (≥901px); chevron is unused
 *      power-user UI on desktop, mobile close X already gone in polish 5.
 *   C. Auth aside (Sign in / Sign up secondary links) on Home + Create cards.
 *   D. WCAG 3.0 (Silver / APCA) — auditable hover/focus/active states for
 *      every interactive element. Pattern: bg flip to var(--accent) + #fff
 *      text yields APCA Lc >75 on the primary purple (#7c6ef5). Focus rings
 *      use 2px solid + 2px offset; animations gated on reduced-motion.
 * ============================================================================= */

/* ── A. Calendar grid responsive ──────────────────────────────────────────── */
/* Force the weekday header row, week rows, and grid wrapper into a true
   7-column grid distributed evenly across the available container width.
   This kills the prior 36px-fixed-cell × 7 = 252px overlap inside the 214px
   sidebar column (and the resulting Saturday-column bleed past the edge). */
.caploo-calendar,
.caploo-sidebar-section__body--calendar .caploo-calendar {
    width: 100% !important;
    box-sizing: border-box;
}

.caploo-calendar__weekdays,
.caploo-calendar__week {
    display: grid !important;
    grid-template-columns: repeat(7, minmax(0, 1fr)) !important;
    gap: var(--space-0-5, 4px) !important;
    width: 100% !important;
    box-sizing: border-box;
}

.caploo-calendar__grid {
    width: 100% !important;
    box-sizing: border-box;
    padding: 0 !important;
}

/* Day cells: drop any inherited min-width from the desktop hover-pointer
   media query (line ~3378) — grid 1fr distributes width evenly. Keep the
   square aspect so cells visually align with the weekday letters above. */
.caploo-calendar-cell {
    width: auto !important;
    min-width: 0 !important;
    aspect-ratio: 1 / 1;
    padding: 0 !important;
}

@media (hover: hover) and (pointer: fine) {
    .caploo-calendar-cell {
        min-width: 0 !important;
        min-height: 0 !important;
    }
}

.caploo-calendar-cell__num {
    width: auto;
    text-align: center;
}

/* The dot is absolute-positioned by an earlier rule; this just normalises
   bottom-offset so it never collides with the cell border on shorter cells. */
.caploo-calendar-cell__dot {
    bottom: 3px !important;
}

/* ── B. Hide sidebar --no-title header on desktop ─────────────────────────── */
/* Polish 4 trimmed the height; polish 5 hid the close X. Power-user
   collapse chevron offers no value on desktop where the sidebar is fixed
   width. Hide the whole header band and tighten the sidebar's top padding
   so the first section ("Add a Moment") sits cleanly against the top edge. */
@media (min-width: 901px) {
    .caploo-shell__sidebar-header--no-title {
        display: none !important;
    }
    .caploo-shell__sidebar {
        padding-top: var(--space-3, 12px) !important;
    }
}

/* ── C. Auth aside (Home + Create) ────────────────────────────────────────── */
/* Hick's Law: primary CTAs (Join Drop / Create Drop) stay dominant. Auth
   options sit as secondary text links so returning users find them
   without overwhelming first-time visitors. Inline row, centered. */
.auth-aside {
    margin-top: var(--space-3, 12px);
    text-align: center;
    font-size: var(--text-sm);
    color: var(--muted);
    display: flex;
    justify-content: center;
    align-items: center;
    gap: var(--space-1, 8px);
    flex-wrap: wrap;
}

.auth-aside a {
    color: var(--accent);
    text-decoration: none;
    font-weight: 500;
    padding: var(--space-0-25) var(--space-0-5);
    border-radius: var(--radius-xs);
}

.auth-aside a:hover {
    text-decoration: underline;
    color: color-mix(in srgb, var(--accent) 80%, #fff 20%);
}

.auth-aside a:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    text-decoration: underline;
}

.auth-aside a:active {
    filter: brightness(0.92);
}

/* ── D. WCAG 3.0 (APCA) hover / focus / active audit ──────────────────────── */
/* Pattern reused across components: hover/focus inverts to accent fill +
   white text. APCA Lc on white-on-#7c6ef5 ≈ 80 (passes Silver Lc ≥60 for
   body text and Lc ≥75 for small UI labels). Focus rings are 2px solid
   accent at 2px offset, giving ≥3:1 against any adjacent surface color. */

/* D1. Calendar Today button — user-flagged as having a barely-visible hover.
   Clear color inversion + accent border on hover; explicit focus-visible. */
.caploo-calendar-header__today,
.caploo-calendar-header__today-btn {
    background: transparent !important;
    color: var(--text) !important;
    border: 1px solid var(--border) !important;
}

.caploo-calendar-header__today:hover:not(:disabled),
.caploo-calendar-header__today-btn:hover:not(:disabled) {
    background: var(--accent) !important;
    color: var(--text-on-accent) !important;
    border-color: var(--accent) !important;
}

.caploo-calendar-header__today:focus-visible,
.caploo-calendar-header__today-btn:focus-visible {
    outline: 2px solid var(--accent) !important;
    outline-offset: 2px;
    background: var(--accent) !important;
    color: var(--text-on-accent) !important;
    border-color: var(--accent) !important;
}

.caploo-calendar-header__today:active,
.caploo-calendar-header__today-btn:active {
    filter: brightness(0.92);
}

@media (prefers-reduced-motion: no-preference) {
    .caploo-calendar-header__today,
    .caploo-calendar-header__today-btn {
        transition: background 150ms ease, color 150ms ease, border-color 150ms ease;
    }
}

/* D2. Calendar prev/next chevrons — same inversion pattern, smaller surface. */
.caploo-calendar-header__nav:hover:not(:disabled) {
    background: var(--accent) !important;
    color: var(--text-on-accent) !important;
    border-color: var(--accent) !important;
}

.caploo-calendar-header__nav:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    background: var(--accent) !important;
    color: var(--text-on-accent) !important;
    border-color: var(--accent) !important;
}

@media (prefers-reduced-motion: no-preference) {
    .caploo-calendar-header__nav {
        transition: background 150ms ease, color 150ms ease, border-color 150ms ease;
    }
}

/* D3. Day cells — strengthen hover so the bg change is unambiguous AND text
   contrast is preserved. Existing rule used a 25% accent-tint that drops
   APCA below threshold against muted text; flip to accent fill + white. */
.caploo-calendar-cell:hover:not(:disabled):not(.caploo-calendar-cell--selected) {
    background: color-mix(in srgb, var(--accent) 60%, var(--surface-alt)) !important;
    color: var(--text-on-accent) !important;
}

.caploo-calendar-cell:hover:not(:disabled):not(.caploo-calendar-cell--selected)
    .caploo-calendar-cell__num {
    color: var(--text-on-accent) !important;
}

.caploo-calendar-cell:focus-visible {
    outline: 2px solid var(--accent) !important;
    outline-offset: 2px;
    z-index: 2;
}

/* Selected cell already reads as accent fill + white; lock it in. */
.caploo-calendar-cell--selected,
.caploo-calendar-cell--selected:hover {
    background: var(--accent) !important;
    color: var(--text-on-accent) !important;
    border-color: var(--accent) !important;
}

/* D4. Generic focus-visible ring for interactive elements that don't yet
   have one. Scoped to common patterns to avoid the universal selector. */
.btn-primary:focus-visible,
.btn-ghost:focus-visible,
.btn-secondary:focus-visible,
.composer-action-btn:focus-visible,
button.caploo-btn:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
a.back-link:focus-visible,
a.host-link:focus-visible,
.host-link a:focus-visible,
.join-card:focus-within,
.segmented__option:focus-within {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius-sm, 6px);
}

/* D5. Composer action buttons — hover already fills with accent tint;
   raise the opacity so it crosses APCA threshold and add focus-visible. */
.composer-action-btn:hover {
    background: var(--accent) !important;
    color: var(--text-on-accent) !important;
    border-color: var(--accent) !important;
}
.composer-action-btn:hover .icon { color: var(--text-on-accent) !important; }
.composer-action-btn:focus-visible {
    background: var(--accent) !important;
    color: var(--text-on-accent) !important;
    border-color: var(--accent) !important;
}

/* D6. Existing host-link "Hosting? Create a drop →" focus state. */
.host-link a:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius-xs);
    text-decoration: underline;
}

/* === End Phase 6 polish 6 ================================================ */

/* ============================================================================
   Phase 6.7 — Calendar today indicator redesign
   ----------------------------------------------------------------------------
   Bug: prior pass placed today's dot directly under the day number, causing
   visible overlap on smaller cells.

   Redesign: today is rendered as an outlined accent circle around the day
   numeral. The post-count dot remains, but is repositioned to the bottom
   edge so it never collides with the numeral. Selected day stays as an
   accent fill (white text). The two states compose: today + selected →
   accent fill with the numeral remaining bold.

   WCAG 3.0:
   - Today's outlined circle uses --accent border ≥2px against light surface
     (Lc ≥3:1 against cell background).
   - Selected fill uses white-on-#7c6ef5 (Lc ≈80, well above Silver Lc ≥75).
   - Focus ring already in place (line ~6658).
   ========================================================================== */
.caploo-calendar-cell--today {
    position: relative;
}

/* Disable the legacy "today underline" pseudo-element added by Phase 4 CSS
   (line ~3404). The new circle replaces it. The earlier override at
   line 4757 only suppresses one of several stacked rules, so we lock it in. */
.caploo-calendar-cell--today::after {
    display: none !important;
    content: none !important;
}

/* Phase 6.8 — single-ring today indicator.
   Removed: the inner 28×28 numeral ring AND the post-count dot on today cells.
   Kept: the existing cell-level box-shadow ring (line ~4912) which now reads
   as the sole today affordance. WCAG 3.0: cell-ring uses 1.5px var(--accent)
   inset shadow against light surface (Lc ≥75 small-UI threshold). */

/* Selected (clicked / filtered) — accent fill, white numeral. */
.caploo-calendar-cell--selected .caploo-calendar-cell__num {
    background: transparent;
    color: var(--text-on-accent) !important;
}

/* Selected + today composes cleanly: accent fill takes priority over the ring,
   so we don't need a special compound selector. */

/* Non-today cells: existing dot rules (line 4770+) already handle this,
   but lock the size so adjacent today/non-today rows look balanced. */
.caploo-calendar-cell:not(.caploo-calendar-cell--today) .caploo-calendar-cell__dot {
    width: 4px !important;
    height: 4px !important;
}

/* Today cells: the dot is suppressed so the ring is the sole indicator. */
.caploo-calendar-cell--today .caploo-calendar-cell__dot {
    display: none !important;
}

/* Ensure cells have headroom for the numeral. */
.caploo-calendar-cell {
    min-height: 36px;
}

/* === End Phase 6.8 calendar today simplification ========================= */

/* ============================================================================
   Phase 6.7 — Composer intelligent inputs (tags + location autocomplete,
   EXIF photo-location smart card)
   ----------------------------------------------------------------------------
   Goals:
   - Replace the "Add tags" / "Add location" details/checkbox with autocomplete
     inputs surfaced inline in the composer.
   - Keep the visual rhythm of the existing composer (compact, single-column).
   - WCAG 3.0: 2px accent focus rings, ≥24×24 chip dismiss targets, accent
     fill on dropdown hover/focus.
   ========================================================================== */

.caploo-composer-ai {
    display: flex;
    flex-direction: column;
    gap: var(--space-1, 8px);
    margin-top: var(--space-1, 8px);
}

.caploo-composer-ai__field {
    position: relative;
    width: 100%;
}

.caploo-composer-ai__chips {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-bottom: 4px;
}

.caploo-composer-ai__chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: var(--space-0-25) var(--space-0-5) var(--space-0-25) var(--space-1);
    background: color-mix(in srgb, var(--accent) 15%, var(--surface));
    color: var(--text);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
    border-radius: var(--radius-pill);
    font-size: 0.8125rem;
    line-height: 1.2;
    max-width: 100%;
}

.caploo-composer-ai__chip-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.caploo-composer-ai__chip-remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Tap target: 32×32 minimum via padding; icon rendered at 14px. */
    min-width: 32px;
    min-height: 32px;
    margin: -2px -4px -2px 0;
    padding: 0;
    background: transparent;
    border: none;
    color: var(--muted, #555);
    cursor: pointer;
    border-radius: 50%;
    /* Fixed 14px — was var(--text-md) which was too large relative to the chip */
    font-size: 14px;
    line-height: 1;
}

.caploo-composer-ai__chip-remove:hover,
.caploo-composer-ai__chip-remove:focus-visible {
    /* Neutral surface-alt background instead of vibrant accent */
    background: color-mix(in srgb, var(--text) 10%, transparent);
    color: var(--text);
    outline: none;
}

.caploo-composer-ai__chip-remove:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.caploo-composer-ai__input {
    width: 100%;
    padding: 6px 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-alt);
    color: var(--text);
    font-size: var(--text-sm);
    box-sizing: border-box;
    /* Ticket 02 — color-scheme inherits from :root[data-theme]. */
}

.caploo-composer-ai__input:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-color: var(--accent);
}

/* Phase 19e: better contrast dropdown — surface-alt bg, accent-tinted border,
   stronger shadow for dark-theme elevation. Text: --text on --surface-alt Lc≈85
   (body ≥75 ✓), --muted on --surface-alt Lc≈63 (small ≥60 ✓). */
.caploo-composer-ai__dropdown {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: 50;
    background: var(--surface-alt);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border));
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow);
    max-height: 240px;
    overflow-y: auto;
    list-style: none;
    margin: 0;
    padding: 4px 0;
}

.caploo-composer-ai__option {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    cursor: pointer;
    font-size: var(--text-sm);
    color: var(--text);
    line-height: 1.2;
    /* Phase 18c — WCAG 3.0 / SKILL §8: suggestion items are pointer targets,
       so each option meets the 44x44 minimum touch-target size. */
    min-height: 44px;
    box-sizing: border-box;
}

.caploo-composer-ai__option-primary {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.caploo-composer-ai__option-secondary {
    color: var(--muted, #777);
    font-size: var(--text-xs);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 50%;
}

.caploo-composer-ai__option:hover,
.caploo-composer-ai__option--active {
    background: var(--accent);
    color: var(--text-on-accent);
}

.caploo-composer-ai__option:hover .caploo-composer-ai__option-secondary,
.caploo-composer-ai__option--active .caploo-composer-ai__option-secondary {
    color: rgba(255, 255, 255, 0.85);
}

.caploo-composer-ai__option--create {
    border-top: 1px dashed var(--border, #ddd);
    margin-top: var(--space-0-25);
    padding-top: 8px;
    font-style: italic;
}

.caploo-composer-ai__empty {
    padding: 8px 12px;
    color: var(--muted, #777);
    font-size: 0.8125rem;
}

/* Phase 18d - geocoder error state. Distinct from the muted "no matches"
   empty state: full-strength text plus a danger accent bar so the user reads
   it as "the search service is unavailable", not "nothing matched". APCA:
   var(--text) on var(--surface) is the body-text pairing, already compliant. */
.caploo-composer-ai__empty--error {
    color: var(--text);
    border-inline-start: 3px solid var(--danger, #d23b3b);
    background: color-mix(in srgb, var(--danger, #d23b3b) 8%, transparent);
}

/* Phase 18d - the descendant-of-field selector lifts specificity above the
   global `button:not([hidden])` reset (which pins min-height to 24px), so the
   44x44 touch-target minimum below actually wins the cascade. */
.caploo-composer-ai__field .caploo-composer-ai__locate {
    width: 100%;
    /* WCAG 3.0 / SKILL 8: the "use my current location" control is a pointer
       target, so it meets the 44x44 minimum touch-target size. */
    min-height: 44px;
    display: flex;
    align-items: center;
    padding: 6px 10px;
    background: transparent;
    border: 1px dashed color-mix(in srgb, var(--accent) 50%, transparent);
    border-radius: var(--radius-sm, 6px);
    color: var(--accent);
    font-size: 0.8125rem;
    cursor: pointer;
    text-align: start;
}

.caploo-composer-ai__locate:hover,
.caploo-composer-ai__locate:focus-visible {
    background: var(--accent);
    color: var(--text-on-accent);
    border-color: var(--accent);
    outline: none;
}

.caploo-composer-ai__locate:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Inline geolocation error — shown when the browser denies the permission
   or geolocation is unavailable. Uses role="alert" for screen readers. */
.caploo-composer-ai__geo-error {
    margin: var(--space-0-5) 0 0;
    font-size: var(--text-xs);
    color: var(--muted);
    line-height: 1.4;
}

/* Photo-location smart card */
.caploo-composer-ai__photo-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 8px 10px;
    margin-top: 6px;
    background: color-mix(in srgb, var(--accent) 8%, var(--surface, #fff));
    border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
    border-radius: var(--radius-sm, 6px);
    font-size: 0.8125rem;
}

.caploo-composer-ai__photo-card-text {
    display: flex;
    align-items: center;
    gap: 6px;
    color: var(--text);
}

.caploo-composer-ai__photo-card-actions {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}

.caploo-composer-ai__photo-card-btn {
    padding: 4px 10px;
    background: transparent;
    border: 1px solid var(--border, #ccc);
    border-radius: var(--radius-xs);
    color: var(--text);
    cursor: pointer;
    font-size: var(--text-xs);
    min-height: 24px;
}

/* F-114: full-width primary action */
.caploo-composer-ai__photo-card-btn--full {
    width: 100%;
    justify-content: center;
    text-align: center;
}
/* F-114: secondary row for Edit + Dismiss */
.caploo-composer-ai__photo-card-secondary {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}

.caploo-composer-ai__photo-card-btn--primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-on-accent);
}

.caploo-composer-ai__photo-card-btn:hover,
.caploo-composer-ai__photo-card-btn:focus-visible {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-on-accent);
    outline: none;
}

.caploo-composer-ai__photo-card-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.caploo-composer-ai__source-note {
    font-size: 0.6875rem;
    color: var(--muted, #888);
    margin-top: var(--space-0-25);
}

@media (prefers-reduced-motion: no-preference) {
    .caploo-composer-ai__chip-remove,
    .caploo-composer-ai__option,
    .caploo-composer-ai__locate,
    .caploo-composer-ai__photo-card-btn {
        transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
    }
}
/* === End Phase 6.7 composer intelligent inputs =========================== */


/* ============================================================================
   Phase 6.4 - Privacy controls UI (per-moment toggle, settings page, deletion)
   ============================================================================
   Tokens reused: --accent, --accent-glow, --accent-dim, --danger, --surface,
   --surface-alt, --border, --text, --text-sm, --radius-sm, --radius-pill,
   --focus-ring. The toggle switch is a button[role=switch] so it's
   keyboard-operable without a hidden checkbox; aria-checked carries the state.
   APCA Lc target: text on surface >= 75 (verified via the existing token
   palette - see docs/bibles/ux-ui.md).
   ========================================================================== */

/* -- visually-hidden helper (used by lock chip aria text). May already be
   defined elsewhere; the duplicate is harmless and keeps this block portable. */
.visually-hidden {
    position: absolute !important;
    width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0);
    white-space: nowrap; border: 0;
}

/* -- Generic toggle switch (button[role=switch]). ----------------------- */
.caploo-toggle-switch {
    --switch-w: 38px;
    --switch-h: 22px;
    --switch-pad: 2px;
    --thumb-size: calc(var(--switch-h) - 2 * var(--switch-pad));
    appearance: none;
    background: transparent;
    border: 0;
    padding: 0;
    margin: 0;
    cursor: pointer;
    line-height: 0;
    flex: 0 0 auto;
}
.caploo-toggle-switch:disabled { cursor: not-allowed; opacity: .55; }
.caploo-toggle-switch__track {
    display: inline-block;
    position: relative;
    width: var(--switch-w);
    height: var(--switch-h);
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    transition: background-color 140ms ease, border-color 140ms ease;
}
.caploo-toggle-switch__thumb {
    position: absolute;
    top: var(--switch-pad);
    left: var(--switch-pad);
    width: var(--thumb-size);
    height: var(--thumb-size);
    background: var(--text);
    border-radius: 50%;
    transition: transform 160ms ease, background-color 140ms ease;
    box-shadow: 0 1px 2px rgba(0,0,0,.45);
}
.caploo-toggle-switch--on .caploo-toggle-switch__track {
    background: var(--accent);
    border-color: var(--accent);
}
.caploo-toggle-switch--on .caploo-toggle-switch__thumb {
    background: #fff;
    transform: translateX(calc(var(--switch-w) - var(--switch-h)));
}
.caploo-toggle-switch:focus-visible .caploo-toggle-switch__track {
    box-shadow: var(--focus-ring);
    outline: none;
}

@media (prefers-reduced-motion: reduce) {
    .caploo-toggle-switch__thumb,
    .caploo-toggle-switch__track {
        transition: none;
    }
}

/* -- Composer privacy toggle row -------------------------------------------- */
.caploo-composer-privacy {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    column-gap: 10px;
    row-gap: 4px;
    padding: 10px 12px;
    margin-top: 8px;
    background: color-mix(in srgb, var(--surface-alt) 60%, transparent);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}
.caploo-composer-privacy__label {
    grid-column: 1 / 2;
    grid-row: 1;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--text);
    font-size: var(--text-sm);
    font-weight: normal;
}
.caploo-composer-privacy .caploo-toggle-switch {
    grid-column: 3 / 4;
    grid-row: 1;
    justify-self: end;
}
.caploo-composer-privacy__hint {
    grid-column: 1 / -1;
    grid-row: 2;
    margin: 0;
    color: color-mix(in srgb, var(--text) 70%, transparent);
    font-size: var(--text-xs);
    line-height: 1.35;
}

/* -- Modal edit-form privacy row ------------------------------------------- */
.modal-privacy-row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    column-gap: 10px;
    row-gap: 4px;
    padding: 10px 12px;
    margin-top: 10px;
    background: color-mix(in srgb, var(--surface-alt) 60%, transparent);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}
.modal-privacy-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--text);
    font-size: var(--text-sm);
    font-weight: normal;
}
.modal-privacy-row .caploo-toggle-switch {
    justify-self: end;
}
.modal-privacy-hint {
    grid-column: 1 / -1;
    grid-row: 2;
    margin: 0;
    color: color-mix(in srgb, var(--text) 70%, transparent);
    font-size: var(--text-xs);
    line-height: 1.35;
}

/* -- Private-moment chip on MomentCard (superseded by .media-badge--private) */
.media-badge--private svg { display: block; }

/* -- Profile page privacy section ------------------------------------------ */
.privacy-section h3 {
    margin: 0 0 .25rem;
    font-size: var(--text-md);
    font-weight: normal;
    color: var(--text);
}
.privacy-subsection {
    padding-top: 1rem;
    border-top: 1px solid var(--border);
    margin-top: 1rem;
}
.privacy-subsection:first-of-type {
    padding-top: 0;
    border-top: 0;
    margin-top: .25rem;
}
.privacy-subsection__intro {
    margin: 0 0 .75rem;
    line-height: 1.45;
}
.privacy-subsection--danger {
    border-top-color: color-mix(in srgb, var(--danger) 35%, var(--border));
}
.privacy-subsection--danger h3 { color: var(--danger); }

.privacy-toggle-row {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: .75rem;
    padding: .75rem 0;
    border-bottom: 1px dashed var(--border);
}
.privacy-toggle-row:last-of-type { border-bottom: 0; }
.privacy-toggle-row__text { min-width: 0; }
.privacy-toggle-row__label {
    margin: 0 0 .15rem;
    font-weight: normal;
    font-size: var(--text-sm);
    color: var(--text);
}
.privacy-toggle-row__hint {
    margin: 0;
    font-size: var(--text-xs);
    color: color-mix(in srgb, var(--text) 70%, transparent);
    line-height: 1.4;
}
.privacy-saved {
    margin-top: .5rem;
    font-size: var(--text-xs);
}

/* -- Danger button ---------------------------------------------------------- */
.btn-danger {
    background: var(--danger);
    color: var(--text-on-accent);
    border: 1px solid var(--danger);
    border-radius: var(--radius-sm);
    padding: .55rem 1rem;
    font-weight: normal;
    cursor: pointer;
}
.btn-danger:hover:not(:disabled) {
    background: color-mix(in srgb, var(--danger) 88%, #000);
    border-color: color-mix(in srgb, var(--danger) 88%, #000);
}
.btn-danger:disabled { opacity: .55; cursor: not-allowed; }
.btn-danger:focus-visible {
    outline: 2px solid var(--danger);
    outline-offset: 2px;
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--danger) 25%, transparent);
}

/* -- Account-deletion modal ------------------------------------------------- */
/* Handoff retrofit: chrome comes from .modal-scrim + .modal. Local overrides
   keep the danger-tinted border, height clamp for tall viewports, and slightly
   tighter padding than the .modal default. Selector compounds .modal so it
   wins over the .modal primitive declared later in the file. */
.privacy-delete-backdrop {
    /* Prevent click-outside-to-dismiss for the destructive flow. */
    cursor: default;
}
.modal.privacy-delete-modal {
    width: min(520px, calc(100vw - 2rem));
    max-height: calc(100vh - 2rem);
    max-height: calc(100dvh - 2rem);
    overflow: auto;
    border-color: color-mix(in srgb, var(--danger) 30%, var(--border));
    padding: 1.25rem 1.5rem 1.5rem;
}
.privacy-delete-modal__heading {
    margin: 0 0 .75rem;
    font-size: var(--text-lg);
    color: var(--danger);
}
.privacy-delete-modal__body p { margin: 0 0 .75rem; line-height: 1.5; }
.privacy-delete-modal__body ul {
    margin: 0 0 1rem;
    padding-inline-start: 1.25rem;
    line-height: 1.55;
}
.privacy-delete-modal__field {
    display: block;
    margin-top: 1rem;
}
.privacy-delete-modal__field span {
    display: block;
    margin-bottom: .35rem;
    font-weight: normal;
    font-size: var(--text-sm);
}
.privacy-delete-modal__field input {
    width: 100%;
    padding: .55rem .75rem;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
}
.privacy-delete-modal__field input:focus-visible {
    border-color: var(--danger);
    outline: none;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 25%, transparent);
}
.privacy-delete-modal__help {
    margin: .35rem 0 0;
    font-size: var(--text-xs);
}
.privacy-delete-modal__checkbox {
    display: flex;
    align-items: flex-start;
    gap: .55rem;
    margin: 1rem 0 .25rem;
    font-size: var(--text-sm);
    line-height: 1.45;
}
.privacy-delete-modal__checkbox input[type="checkbox"] {
    margin-top: .25rem;
    width: 1rem;
    height: 1rem;
    accent-color: var(--danger);
}
.privacy-delete-modal__actions {
    display: flex;
    gap: .75rem;
    justify-content: flex-end;
    margin-top: 1.25rem;
}

/* -- Account-deleted confirmation page ------------------------------------- */
.account-deleted-page {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 4rem 1rem;
    min-height: 60vh;
}
.account-deleted-card {
    width: min(560px, 100%);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 2rem;
    text-align: start;
    box-shadow: var(--shadow);
}
.account-deleted-card__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 64px; height: 64px;
    border-radius: 50%;
    background: color-mix(in srgb, var(--accent) 18%, transparent);
    color: var(--accent);
    margin-bottom: 1rem;
}
.account-deleted-card h1 {
    margin: 0 0 .5rem;
    font-size: 1.4rem;
}
.account-deleted-card__lede {
    margin: 0 0 1rem;
    color: color-mix(in srgb, var(--text) 80%, transparent);
    line-height: 1.5;
}
.account-deleted-card__list {
    margin: 0 0 1rem;
    padding-inline-start: 1.1rem;
    line-height: 1.55;
}
.account-deleted-card__footer { margin: 1rem 0 0; font-size: var(--text-sm); }
/* === End Phase 6.4 privacy UI ============================================ */


/* === Phase 7.1 — export controls (per-drop + per-moment) ================== */
/* Disclosure widget on the My Drops list. Native <details>/<summary> handles
 * keyboard activation (Enter/Space) and ARIA semantics; we only style the
 * visual container. Focus rings reuse the global accent so the WCAG 3 / APCA
 * contrast budget set by the rest of the app applies unchanged.
 */
.drop-export {
    margin-top: .55rem;
    position: relative;
}
.drop-export-trigger {
    list-style: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .3rem .65rem;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--surface, #1a1a22) 80%, transparent);
    border: 1px solid var(--border, #2a2a35);
    color: var(--text, #e8e8f0);
    font-size: .82rem;
    font-weight: 500;
    user-select: none;
}
.drop-export-trigger::-webkit-details-marker { display: none; }
.drop-export-trigger:hover {
    background: color-mix(in srgb, var(--accent, #b48ef5) 12%, var(--surface, #1a1a22));
}
.drop-export-trigger:focus-visible {
    outline: 2px solid var(--accent, #b48ef5);
    outline-offset: 2px;
}
.drop-export-menu {
    position: absolute;
    z-index: 20;
    margin-top: .35rem;
    min-width: 9rem;
    padding: .35rem;
    background: var(--surface, #1a1a22);
    border: 1px solid var(--border, #2a2a35);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow, 0 8px 24px rgba(0, 0, 0, 0.35));
    display: flex;
    flex-direction: column;
    gap: .15rem;
}
.drop-export-option {
    background: transparent;
    border: 0;
    text-align: start;
    padding: .5rem .65rem;
    border-radius: var(--radius-sm);
    font-size: .85rem;
    color: var(--text, #e8e8f0);
    cursor: pointer;
}
.drop-export-option:hover:not(:disabled) {
    background: color-mix(in srgb, var(--accent, #b48ef5) 16%, transparent);
}
.drop-export-option:disabled {
    opacity: .55;
    cursor: not-allowed;
}
.drop-export-option:focus-visible {
    outline: 2px solid var(--accent, #b48ef5);
    outline-offset: 1px;
}

/* Moment modal — same disclosure pattern, sized to fit alongside the existing
 * edit / delete / close icons in the header actions row.
 */
.modal-export-details {
    position: relative;
    display: inline-flex;
}
.modal-export-trigger {
    list-style: none;
    cursor: pointer;
}
.modal-export-trigger::-webkit-details-marker { display: none; }
.modal-export-menu {
    position: absolute;
    right: 0;
    top: calc(100% + .35rem);
    z-index: 25;
    min-width: 10.5rem;
    padding: .35rem;
    background: var(--surface, #1a1a22);
    border: 1px solid var(--border, #2a2a35);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow, 0 8px 24px rgba(0, 0, 0, 0.35));
    display: flex;
    flex-direction: column;
    gap: .15rem;
}
.modal-export-option {
    background: transparent;
    border: 0;
    text-align: start;
    padding: .5rem .65rem;
    border-radius: var(--radius-sm);
    font-size: .85rem;
    color: var(--text, #e8e8f0);
    cursor: pointer;
}
.modal-export-option:hover:not(:disabled) {
    background: color-mix(in srgb, var(--accent, #b48ef5) 16%, transparent);
}
.modal-export-option:disabled {
    opacity: .55;
    cursor: not-allowed;
}
.modal-export-option:focus-visible {
    outline: 2px solid var(--accent, #b48ef5);
    outline-offset: 1px;
}
/* === End Phase 7.1 export controls ======================================== */


/* === Phase 7.2 — styled export sub-menu ==================================== */
/* Three named HTML styles (Timeline / Gallery / Report) each with an inline
 * SVG preview thumbnail. Buttons share role=menuitem and are Tab-navigable;
 * focus rings inherit the global accent at APCA Lc ≥ 75 against surface.
 * Touch targets ≥ 24×24 (WCAG 3.0) — the style rows are >= 48px tall.
 */
.drop-export-menu { min-width: 16rem; }
.modal-export-menu { min-width: 17rem; }

.drop-export-section,
.modal-export-section {
    padding: .55rem .55rem .25rem;
    font-size: .68rem;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--muted, #a0a0b0);
    border-top: 1px solid var(--border, #2a2a35);
    margin-top: .25rem;
}
.drop-export-menu > .drop-export-section:first-of-type,
.modal-export-menu > .modal-export-section:first-of-type {
    border-top: 0;
    margin-top: 0;
}

.drop-export-option-json,
.drop-export-style,
.modal-export-option-json,
.modal-export-style {
    display: flex;
    align-items: center;
    gap: .65rem;
    min-height: 48px;
    padding: .5rem .55rem;
    text-align: start;
}
.drop-export-thumb,
.modal-export-thumb {
    flex: 0 0 auto;
    width: 56px;
    height: 38px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border, #2a2a35);
    border-radius: var(--radius-sm);
    background: color-mix(in srgb, var(--accent, #b48ef5) 6%, transparent);
    color: var(--text, #e8e8f0);
    overflow: hidden;
}
.drop-export-thumb svg,
.modal-export-thumb svg {
    width: 100%;
    height: 100%;
    display: block;
}
.drop-export-label,
.modal-export-label {
    display: flex;
    flex-direction: column;
    line-height: 1.25;
}
.drop-export-label strong,
.modal-export-label strong { font-size: .9rem; font-weight: normal; }
.drop-export-label small,
.modal-export-label small {
    font-size: .76rem;
    color: var(--muted, #a0a0b0);
}
.drop-export-option:hover:not(:disabled) .drop-export-thumb,
.modal-export-option:hover:not(:disabled) .modal-export-thumb {
    background: color-mix(in srgb, var(--accent, #b48ef5) 18%, transparent);
    border-color: color-mix(in srgb, var(--accent, #b48ef5) 50%, var(--border, #2a2a35));
}
/* === End Phase 7.2 styled export sub-menu ================================= */


/* =========================================================================
   Phase 7.3 — Adaptive share menu

   Layout adapts purely via CSS:
     • Desktop / fine pointer  → centered modal
     • Mobile  / coarse pointer → bottom sheet that slides up from the edge

   Tokens used (all already in the design system):
     --surface-alt, --bg, --border, --text, --muted,
     --accent, --accent-text, --focus-ring,
     --radius-md, --radius-lg, --shadow.
   ========================================================================= */

.share-menu-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(15, 15, 19, 0.55);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    z-index: 9000;
    animation: share-menu-fade-in 140ms ease-out;
}

.share-menu {
    position: fixed;
    z-index: 9001;
    background: var(--surface-alt);
    color: var(--text, #e8e8f0);
    border: 1px solid var(--border, #2a2a36);
    box-shadow: 0 24px 48px rgba(0, 0, 0, 0.5);
    display: flex;
    flex-direction: column;
    gap: 0;
    overflow: hidden;
    font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
}

/* ---- Desktop / centered modal ---- */
.share-menu--modal {
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: min(92vw, 420px);
    max-height: min(80vh, 560px);
    border-radius: var(--radius-md);
    animation: share-menu-pop-in 140ms ease-out;
}

/* ---- Mobile / bottom sheet ---- */
.share-menu--sheet {
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    max-height: 88vh;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    border-bottom: none;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    animation: share-menu-slide-up 180ms ease-out;
}

/* Pure-CSS layout swap when the device reports a coarse pointer (touch).
   This belts-and-braces the C#-side decision: even if the planner picked the
   modal layout for some reason, a touch device still gets the bottom-sheet
   chrome via this fallback. */
@media (pointer: coarse) {
    .share-menu--modal {
        top: auto;
        left: 0;
        right: 0;
        bottom: 0;
        transform: none;
        width: 100%;
        max-height: 88vh;
        border-radius: var(--radius-lg) var(--radius-lg) 0 0;
        border-bottom: none;
        padding-bottom: env(safe-area-inset-bottom, 0px);
        animation: share-menu-slide-up 180ms ease-out;
    }
}

.share-menu__header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 1rem 1.25rem 0.75rem;
    border-bottom: 1px solid var(--border, #2a2a36);
}

.share-menu__title {
    flex: 1;
    margin: 0;
    font-size: 1.05rem;
    font-weight: normal;
    line-height: 1.3;
    color: var(--text, #e8e8f0);
    /* Allow long drop names to wrap up to 2 lines, then ellipsis. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    word-break: break-word;
}

.share-menu__close {
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid transparent;
    background: transparent;
    color: var(--muted);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background 100ms ease, color 100ms ease;
}

.share-menu__close:hover {
    background: rgba(255, 255, 255, 0.06);
    color: var(--text, #e8e8f0);
}

.share-menu__close:focus-visible {
    outline: 2px solid var(--focus-ring-color, #7c6ef5);
    outline-offset: 2px;
}

.share-menu__body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.share-menu__option {
    display: flex;
    align-items: center;
    gap: 0.875rem;
    width: 100%;
    padding: 0.75rem 0.875rem;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    color: var(--text, #e8e8f0);
    text-align: start;
    cursor: pointer;
    min-height: 44px; /* WCAG 2.5.5 / APCA-friendly touch target */
    transition: background 120ms ease, border-color 120ms ease;
}

.share-menu__option:hover {
    background: rgba(255, 255, 255, 0.04);
}

.share-menu__option:focus-visible {
    outline: 2px solid var(--focus-ring-color, #7c6ef5);
    outline-offset: 2px;
}

.share-menu__option:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.share-menu__option.is-copied {
    background: rgba(124, 110, 245, 0.08);
    border-color: rgba(124, 110, 245, 0.35);
}

.share-menu__icon {
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, 0.05);
    color: var(--accent, #7c6ef5);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.share-menu__option-label {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    line-height: 1.25;
    min-width: 0;
}

.share-menu__option-label strong {
    font-size: 0.95rem;
    font-weight: normal;
    color: var(--text, #e8e8f0);
}

.share-menu__option-label small {
    font-size: 0.8rem;
    color: var(--muted);
}

.share-menu__fallback-row {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: 0.5rem 0.875rem 0.75rem;
}

.share-menu__fallback-label {
    font-size: var(--text-xs);
    font-weight: normal;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
}

.share-menu__fallback-input {
    width: 100%;
    padding: 0.6rem 0.7rem;
    background: rgba(255, 255, 255, 0.04);
    color: var(--text, #e8e8f0);
    border: 1px solid var(--border, #2a2a36);
    border-radius: var(--radius-sm);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.85rem;
    min-height: 36px;
}

.share-menu__fallback-input:focus-visible {
    outline: 2px solid var(--focus-ring-color, #7c6ef5);
    outline-offset: 1px;
    border-color: transparent;
}

.share-menu__footer {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    padding: 0.75rem 1rem 1rem;
    border-top: 1px solid var(--border, #2a2a36);
}

.share-menu__done {
    padding: 0.55rem 1rem;
    border: 1px solid var(--border, #2a2a36);
    background: rgba(255, 255, 255, 0.04);
    color: var(--text, #e8e8f0);
    border-radius: var(--radius-sm);
    font-weight: normal;
    cursor: pointer;
    min-height: 36px;
    transition: background 120ms ease, border-color 120ms ease;
}

.share-menu__done:hover {
    background: rgba(255, 255, 255, 0.08);
}

.share-menu__done:focus-visible {
    outline: 2px solid var(--focus-ring-color, #7c6ef5);
    outline-offset: 2px;
}

/* Bigger touch targets on coarse pointers. WCAG 2.5.5 minimum is 24×24,
   AAA / 2.5.8 is 44×44; we satisfy AAA on touch and AA elsewhere. */
@media (pointer: coarse) {
    .share-menu__close   { width: 44px; height: 44px; }
    .share-menu__option  { min-height: 48px; padding: 0.875rem 1rem; }
    .share-menu__icon    { width: 40px; height: 40px; }
    .share-menu__done    { min-height: 44px; padding: 0.7rem 1.1rem; }
}

/* Phase 19g — More Platforms grouped layout -------------------------------- */

/* Outer container — vertical stack of groups; slight indent matches the option rows */
.share-menu__platforms {
    display: flex;
    flex-direction: column;
    gap: 0.875rem;
    padding: 0.5rem 0.875rem 0.75rem;
}

/* Each labeled group section */
.share-menu__platform-group {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

/* Small-caps group header label (screen-reader hides this; aria-label on chips carries the accessible name) */
.share-menu__group-header {
    display: block;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
    padding: 0 0.25rem;
}

/* Chip row — auto-fill so the grid adapts on narrow + wide viewports */
.share-menu__platform-chips {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(68px, 1fr));
    gap: 0.5rem;
}

/* Individual platform chip */
.share-menu__platform {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    padding: 0.6rem 0.25rem 0.5rem;
    border-radius: var(--radius-md, 10px);
    background: var(--surface-alt, rgba(255, 255, 255, 0.04));
    color: var(--text, #e8e8f0);
    text-decoration: none;
    min-height: 64px; /* icon 20px + gap + label ~14px + padding = safe above 44px coarse target */
    cursor: pointer;
    transition: background 120ms ease, box-shadow 120ms ease;
}

.share-menu__platform:hover {
    background: rgba(255, 255, 255, 0.09);
    box-shadow: var(--shadow, 0 2px 8px rgba(0, 0, 0, 0.25));
}

.share-menu__platform:focus-visible {
    outline: 2px solid var(--focus-ring-color, #7c6ef5);
    outline-offset: 2px;
}

.share-menu__platform-label {
    font-size: var(--text-micro);
    font-weight: 500;
    text-align: center;
    line-height: 1.2;
    color: var(--muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
}

/* Coarse-pointer: bump touch target to WCAG 2.5.5 AAA (≥44×44) */
@media (pointer: coarse) {
    .share-menu__platform {
        min-height: 72px;
        padding: 0.75rem 0.25rem 0.6rem;
    }
    .share-menu__platform-chips {
        grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
        gap: 0.6rem;
    }
}

/* Suppress transitions for users who prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
    .share-menu__platform { transition: none; }
}

/* Visually-hidden focus-trap sentinels (must remain in tab order). */
.share-menu__sentinel {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

/* Visually-hidden live region for screen reader status. */
.share-menu__sr-status {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

@keyframes share-menu-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes share-menu-pop-in {
    from { opacity: 0; transform: translate(-50%, -46%) scale(0.97); }
    to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

@keyframes share-menu-slide-up {
    from { transform: translateY(8%); opacity: 0; }
    to   { transform: translateY(0);  opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .share-menu-backdrop,
    .share-menu--modal,
    .share-menu--sheet {
        animation: none;
    }
}

/* PA-01 — ShareMenu landscape-phone adaptation.
   Both the modal and sheet variants need a capped max-height so the footer
   (Done button) is reachable on 320px-tall screens. __body already has
   overflow-y:auto; we reduce padding on header and footer. The sheet variant
   (which coarse-pointer devices always receive) gets a tighter max-height
   cap; the centered modal gets the same treatment. */
@media (orientation: landscape) and (max-height: 500px) {
    .share-menu--modal,
    .share-menu--sheet {
        max-height: calc(100vh - 16px);
    }
    /* On pointer:coarse the --modal collapses to sheet geometry; target both */
    @media (pointer: coarse) {
        .share-menu--modal {
            max-height: calc(100vh - 16px);
        }
    }
    .share-menu__header {
        padding-block: 0.5rem;
        position: sticky;
        top: 0;
        z-index: 1;
        background: var(--surface-alt);
    }
    .share-menu__body {
        padding-block: 0.25rem;
    }
    .share-menu__footer {
        padding-block: 0.4rem;
        position: sticky;
        bottom: 0;
        z-index: 1;
        background: var(--surface-alt);
    }
    .share-menu__platforms {
        padding-block: 0.35rem;
    }
}

/* Adaptive header - the share button on the navbar / moment modal. Mirrors
   the existing modal-icon-btn pattern so the visual rhythm is consistent. */
.share-trigger-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    padding: 0.4rem 0.65rem;
    background: rgba(255, 255, 255, 0.05);
    color: var(--text, #e8e8f0);
    border: 1px solid var(--border, #2a2a36);
    border-radius: var(--radius-sm);
    cursor: pointer;
    font: inherit;
    font-size: 0.85rem;
    line-height: 1;
    min-height: 44px;   /* F-142: unconditional 44px */
    transition: background 120ms ease, border-color 120ms ease;
}

.share-trigger-btn:hover {
    background: rgba(255, 255, 255, 0.09);
    border-color: rgba(124, 110, 245, 0.45);
}

.share-trigger-btn:focus-visible {
    outline: 2px solid var(--focus-ring-color, #7c6ef5);
    outline-offset: 2px;
}

.share-trigger-btn svg {
    flex: 0 0 auto;
}

@media (pointer: coarse) {
    /* min-height already 44px unconditionally (F-142); keep padding bump for touch ergonomics. */
    .share-trigger-btn { padding: 0.55rem 0.85rem; }
}

/* ────────────────────────────────────────────────────────────────────────
   Polish (Phase 7.4) — navbar right-cluster unification.

   Goals (priority order):
     • No-wrap labels across every right-cluster control (Sign in must never
       break to "Sign / in" at any viewport width).
     • Unified 36 px height across Share / Settings / Sign in / Sign up so
       the cluster reads as a single visual band and meets the ≥36 px
       desktop WCAG 3.0 touch-target floor (≥44 px on coarse pointers).
     • Consistent radius / padding / font-size / focus ring.
     • Clear gap rhythm — 8 px inside the auth pair, 12 px between Share-
       /Settings and the auth pair, 16 px between the meta and action
       clusters.
     • Responsive collapse — <900 px drops the (redundant) PIN chip;
       <640 px drops the secondary "Sign in" link, keeping the primary
       "Sign up" CTA (Sign-up page links back to Sign-in). Avoids
       introducing a new dropdown component for a single auth link.

   These rules intentionally live at the file's end so they override the
   earlier sizing without rewriting the original blocks — keeps the diff
   focused on this polish pass.
   ──────────────────────────────────────────────────────────────────── */

/* 1 ─ No-wrap labels on every right-cluster control. */
.navbar .navbar-auth a,
.navbar .navbar-auth .btn-ghost,
.navbar .navbar-auth .btn-primary,
.navbar .share-trigger-btn,
.navbar .nav-settings,
.navbar .navbar-pin-chip,
.navbar .navbar-pin-chip__value,
.navbar .navbar-pin-chip__copy,
.navbar .user-chip {
    white-space: nowrap;
}

/* 2 ─ Unified 36 px sizing for ghost / primary text buttons. */
.navbar .btn-ghost,
.navbar a.btn-ghost,
.app-navbar .btn-ghost,
.app-navbar a.btn-ghost,
.navbar .btn-primary,
.navbar a.btn-primary,
.app-navbar .btn-primary,
.app-navbar a.btn-primary {
    height: 36px;
    padding: 0 14px;
    font-size: 14px;
    line-height: 36px;          /* centers without depending on flex */
    border-radius: var(--radius-sm, 8px);
}

/* Share-trigger matches the ghost button rhythm (icon + label). F-142: 44px. */
.navbar .share-trigger-btn,
.share-trigger-btn {
    min-height: 44px;
    height: 44px;
    padding: 0 14px;
    font-size: 14px;
    line-height: 44px;
    border-radius: var(--radius-sm, 8px);
    gap: 8px;
}
.navbar .share-trigger-btn svg,
.share-trigger-btn svg { width: 16px; height: 16px; }

/* Icon-only Settings gear normalises to 36×36, matching siblings. */
.navbar .nav-settings,
.nav-settings {
    width: 36px;
    height: 36px;
    padding: 0;
    border-radius: var(--radius-sm, 8px);
}
.navbar .nav-settings svg { width: 18px; height: 18px; }

/* 3 ─ Spacing — clear visual groupings in the right cluster.
       12 px between Share / Settings and the auth pair;
       8 px between Sign in and Sign up;
       16 px between the meta cluster (PIN chip) and actions. */
.navbar-actions {
    gap: var(--nav-cluster-gap);
    margin-inline-start: var(--space-2, 16px);
}
.navbar-auth { gap: var(--space-1, 8px); }

/* 4 ─ Coarse-pointer (touch) — re-assert 44 px primary target so the 36 px
       desktop override above does not regress touch ergonomics. */
@media (pointer: coarse) {
    .navbar .btn-ghost,
    .navbar a.btn-ghost,
    .navbar .btn-primary,
    .navbar a.btn-primary,
    .navbar .share-trigger-btn,
    .share-trigger-btn,
    .navbar .nav-settings,
    .nav-settings {
        height: 44px;
        min-height: 44px;
        line-height: 44px;
    }
    .navbar .nav-settings,
    .nav-settings { width: 44px; }
}

/* 5 ─ Responsive collapse.
       <640 px → drop the secondary "Sign in" ghost link; primary
                 "Sign up" CTA remains and the Sign-up page links back
                 to Sign-in for users who already have an account.
       NOTE: the .navbar-pin-chip <900px hide rule was removed — the chip was
       assumed redundant because a body-level PIN display existed, but that
       display is not reliably visible on mobile. The chip is now shown at all
       widths (label hidden at <720px via rule above; value+copy always visible). */
@media (max-width: 640px) {
    .navbar .navbar-auth a.btn-ghost { display: none; }
}

/* 6 ─ WCAG 3.0 focus ring — re-state scoped to the navbar so the 2 px
       accent outline holds ≥3:1 contrast against the navbar's translucent
       backdrop, in addition to the global rule earlier in this file. */
.navbar .btn-ghost:focus-visible,
.navbar a.btn-ghost:focus-visible,
.navbar .btn-primary:focus-visible,
.navbar a.btn-primary:focus-visible,
.navbar .share-trigger-btn:focus-visible,
.navbar .nav-settings:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* 7 ─ prefers-reduced-motion — strip hover/focus transitions on the
       polished controls for motion-sensitive users. */
@media (prefers-reduced-motion: reduce) {
    .navbar .btn-ghost,
    .navbar a.btn-ghost,
    .navbar .btn-primary,
    .navbar a.btn-primary,
    .navbar .share-trigger-btn,
    .navbar .nav-settings { transition: none; }
}


/* ════════════════════════════════════════════════════════════════
   Phase 8.4 — accessibility conformance hardening (WCAG 2.2 AA + APCA)
   ════════════════════════════════════════════════════════════════
   The earlier blocks (Phase 1 — accessibility primitives, reduced-motion,
   skip-link, focus-visible rings) cover the bulk of keyboard / motion
   conformance. This trailer adds:

     1. .visually-hidden utility — sr-only text for icon-only buttons
        and live-region helpers. Critical for axe label checks.
     2. Footer styling for the new <footer role="contentinfo"> landmark
        in MainLayout (Phase 8.4 #10 — adds /accessibility link).
     3. forced-colors / Windows High-Contrast fallback — keeps focus
        rings, button outlines, and link underlines visible when the OS
        replaces our palette with system colors.
     4. Minimum touch-target safety net (WCAG 2.2 SC 2.5.8) for any
        bare interactive element that didn't get a per-component rule.
*/

/* ── 1. Visually hidden (sr-only) ───────────────────────────────── */
.visually-hidden,
.sr-only {
    position: absolute !important;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}
/* Opt-in focus reveal — apply the `-focusable` modifier when a
   visually-hidden element should become visible on keyboard focus
   (skip-link-style shortcuts). Plain `.sr-only` / `.visually-hidden`
   stay clipped even when focused so wrapper-label patterns (e.g.
   `<label class="join-card"><input class="sr-only">`) don't expose a
   native checkbox when the label receives a click. The page-level
   skip-link uses its own `.skip-link` rules above. */
.visually-hidden-focusable:focus,
.visually-hidden-focusable:focus-within,
.sr-only-focusable:focus,
.sr-only-focusable:focus-within {
    position: static !important;
    width: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    clip: auto;
    white-space: normal;
}

/* ── 2. Footer landmark (MainLayout) ──────────────────────────────
   feat/auth-nav-footer-compact-2026-05-17 — collapsed the footer from
   ~120px (32px padding × 2 + 48px margin-top + ~17px content) down to
   ~48px on desktop / ~96px on mobile (two rows). The legal links sit
   in a single inline row with middle-dot separators (modern SaaS
   convention). On ≤30rem the copyright drops onto its own row.
   • Hit area still hits the WCAG 2.2 SC 2.5.8 24×24 baseline on
     desktop (via min-height + inline-flex centering), bumped to
     44×44 on mobile so tap accuracy survives the dense layout.
   • All link labels remain inside ILocalizer's common.layout.footer.* */
.site-footer {
    margin-top: var(--space-3);
    padding: var(--space-1-5) var(--space-3) calc(var(--space-1-5) + env(safe-area-inset-bottom, 0));
    border-top: 1px solid var(--border);
    background: color-mix(in srgb, var(--surface) 70%, transparent);
    color: var(--muted);
    font-size: var(--text-xs);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    column-gap: var(--space-0-5);
    row-gap: var(--space-0-5);
    text-align: center;
}
.site-footer__copyright {
    display: inline-flex;
    align-items: center;
    min-height: 24px;
    padding: 0 .15rem;
}
.site-footer__legal {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    column-gap: var(--space-0-5);
    row-gap: var(--space-0-5);
}
.site-footer a {
    color: var(--accent-text);
    text-decoration: none;
    /* WCAG 2.2 SC 2.5.8 — 24×24 hit area enforced via min-height +
       inline-flex centering so the visual padding stays slim. */
    display: inline-flex;
    align-items: center;
    min-height: 24px;
    padding: 0 .35rem;
    border-radius: var(--radius-sm);
}
.site-footer a:hover { text-decoration: underline; }
.site-footer a:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
    text-decoration: underline;
}
.site-footer .footer-sep {
    color: var(--border);
    user-select: none;
    display: inline-flex;
    align-items: center;
    line-height: 1;
}

/* ≤48rem — mobile bottom nav is visible; push footer bottom edge above it so
   the legal links are not obscured by the fixed bar. The landscape override
   below restores the normal padding when the bar is hidden in landscape. */
@media (max-width: 48rem) {
    .site-footer {
        padding-bottom: calc(var(--m-bottomnav-total) + var(--space-2));
    }
}
@media (max-width: 48rem) and (orientation: landscape) and (max-height: 500px) {
    /* Bottom nav hidden on landscape phones — restore compact padding. */
    .site-footer {
        padding-bottom: calc(var(--space-1-5) + env(safe-area-inset-bottom, 0));
    }
}

/* ≤30rem (~480px) — split into two rows: copyright above the legal nav.
   Touch targets bumped to ≥44px so dense links remain tappable. */
@media (max-width: 30rem) {
    .site-footer {
        flex-direction: column;
        row-gap: var(--space-0-25);
        padding-inline: var(--space-2);
        padding-top: var(--space-2);
        /* padding-bottom set by the ≤48rem rule above (bottomnav-aware) */
    }
    .site-footer__sep--copyright {
        display: none;
    }
    .site-footer a {
        min-height: 44px;
        padding: 0 .5rem;
    }
    .site-footer__copyright {
        min-height: auto;
    }
}

/* ── 2.5 Cookie consent banner (feat/legal-pages) ──────────────────
   Bottom-anchored disclosure that respects safe-area insets so it
   sits above iOS home-indicator. Non-modal: keyboard users can keep
   navigating without dismissing it. Buttons hit the WCAG 2.2 SC 2.5.8
   24×24 minimum and inherit the project focus ring. */
.cookie-consent {
    position: fixed;
    left: var(--space-3);
    right: var(--space-3);
    bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0));
    z-index: 60;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    align-items: center;
    justify-content: space-between;
    max-width: 720px;
    margin: 0 auto;
    padding: var(--space-3);
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: 0 8px 24px rgba(0,0,0,.25);
    font-size: var(--text-sm);
}
.cookie-consent__body { flex: 1 1 320px; min-width: 0; }
.cookie-consent__heading { margin: 0 0 .15rem; font-weight: normal; font-size: var(--text-sm); }
.cookie-consent__text { margin: 0; color: var(--muted); line-height: 1.45; }
.cookie-consent__text a { color: var(--accent-text); }
.cookie-consent__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    flex: 0 0 auto;
}
.cookie-consent__btn {
    min-height: 44px;
    padding: .5rem 1rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: transparent;
    color: var(--text);
    font: inherit;
    cursor: pointer;
}
.cookie-consent__btn:hover { background: color-mix(in srgb, var(--accent-text) 8%, transparent); }
.cookie-consent__btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.cookie-consent__btn--primary {
    background: var(--accent-text);
    color: var(--surface);
    border-color: var(--accent-text);
}
.cookie-consent__btn--primary:hover { filter: brightness(1.05); }
@media (max-width: 480px) {
    .cookie-consent { flex-direction: column; align-items: stretch; }
    .cookie-consent__actions { justify-content: flex-end; }
}
@media (forced-colors: active) {
    .cookie-consent { border: 1px solid CanvasText; background: Canvas; color: CanvasText; }
    .cookie-consent__btn,
    .cookie-consent__btn--primary { background: ButtonFace; color: ButtonText; border: 1px solid ButtonText; }
}

/* ── 3. forced-colors (Windows High-Contrast, etc.) ─────────────── */
@media (forced-colors: active) {
    /* Let the OS pick: keep semantic structure intact, drop decorative
       gradients that don't survive forced-colors. */
    .navbar-brand,
    .hero h1 {
        background: none !important;
        -webkit-background-clip: initial !important;
        -webkit-text-fill-color: currentColor !important;
        color: CanvasText !important;
    }
    :focus-visible {
        outline: 2px solid Highlight !important;
        outline-offset: 2px !important;
        box-shadow: none !important;
    }
    button,
    .btn-primary,
    .btn-secondary,
    .btn-ghost {
        border: 1px solid ButtonText !important;
    }
    a { color: LinkText !important; }
}

/* ── 4. Touch-target safety net (WCAG 2.2 SC 2.5.8) ─────────────── */
/* MD-T1: Touch-target floor — 44×44 unconditionally on all interactive
   button-like elements (WCAG 2.2 SC 2.5.8 + MD audit §6.1 #2).
   Was 24×24; raised to 44×44 so the floor is met without coarse-pointer
   gating. The (pointer: coarse) block below bumps .btn-primary to 48px. */
button:not([hidden]),
[role="button"]:not([hidden]),
a.btn,
a.btn-primary,
a.btn-secondary,
a.btn-ghost {
    min-height: 44px;
    min-width: 44px;
}

/* F-014b cascade fix: `.card .btn-primary` has specificity (0,2,0) which beats
   `button:not([hidden])` (0,1,1) above, restoring the 44px primary-CTA floor
   for fine-pointer (desktop) contexts where the coarse-pointer bump below
   doesn't apply. */
.card .btn-primary { min-height: 44px; }

/* A-class handoff (Pass 2): landing CTA must be 52px per prototype spec.
   [role="button"]:not([hidden]) (0,2,0) beats a.btn-primary--landing (0,1,1),
   so use a[role="button"].btn-primary--landing (0,2,1) to win cleanly. */
a[role="button"].btn-primary--landing { min-height: 52px; }

/* Primary mobile actions — bump to 44×44 on coarse pointers
   (WCAG 2.2 AAA / Apple HIG / Material guidance). */
@media (pointer: coarse) {
    button:not([hidden]),
    [role="button"]:not([hidden]),
    a.btn,
    a.btn-primary,
    a.btn-secondary,
    a.btn-ghost,
    .calendar-cell,
    .filter-chip,
    .share-chip,
    .btn-copy {
        min-height: 44px;
        min-width: 44px;
    }

    /* MD-T1: Primary CTAs hit Material's 48dp comfortable target on touch.
       Applies to .btn-primary (button and a-link variants), the landing CTA,
       and auth submit — these are the page's main action on every surface. */
    .btn-primary,
    a.btn-primary,
    .btn-primary--landing,
    .auth-card__submit {
        min-height: 48px;
    }
}

/* ── 5. role="alert" form-error live region ─────────────────────── */
/* Phase 8.4 #6 — when validation fails on submit, the first invalid
   message gets role="alert" so it's announced. Keep visual style
   aligned with .error utility above. */
.form-error[role="alert"] {
    color: var(--danger-text);
    font-size: var(--text-sm);
    margin-top: .35rem;
    display: block;
}
.form-required-marker {
    color: var(--danger-text);
    margin-inline-start: .15rem;
    font-weight: normal;
}
.form-required-marker::after {
    content: "";
}


/* ═══════════════════════════════════════════════════════════════════════
   Phase 12 — Onboarding + First-Run Experience
   ═══════════════════════════════════════════════════════════════════════ */

/* Landing page: widen container, add breathing room for the hero + 3-step. */
.home-container--landing {
    max-width: 540px;
    gap: 1.5rem;
}
.hero--landing { padding: var(--section-py) 0; }
.hero--landing h1 {
    font-size: var(--text-fluid-h1, clamp(1.7rem, 7.5vw, 2.8rem));
    line-height: 1.05;
    text-align: center;
    text-wrap: balance;
}
.hero--landing .tagline {
    max-width: 28rem;
    line-height: 1.45;
    font-size: var(--text-md);
}

.how-it-works {
    list-style: none;
    display: flex;
    gap: .5rem;
    padding: var(--section-py) 1rem;
    margin: 0;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    align-items: stretch;
    justify-content: space-between;
    box-shadow: var(--shadow);
}
.how-it-works__step {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: .35rem;
    padding: .5rem .25rem;
    position: relative;
}
/* F-031: numerals de-emphasized so icon+label carry the visual weight */
.how-it-works__index {
    font-size: .7rem;
    font-weight: 400;
    letter-spacing: .04em;
    color: var(--muted);
    text-transform: uppercase;
}
.how-it-works__icon { color: var(--accent-text); }
.how-it-works__step strong {
    font-size: var(--text-md);
    font-weight: 700;
    color: var(--text);
}
.how-it-works__step small {
    font-size: .82rem;
    color: var(--muted);
    line-height: 1.35;
    max-width: 11rem;
}
.how-it-works__arrow {
    align-self: center;
    color: var(--muted);
    font-size: var(--text-lg);
    font-weight: 700;
    user-select: none;
}
@media (max-width: 560px) {
    .how-it-works { flex-direction: column; gap: 1rem; }
    .how-it-works__arrow { transform: rotate(90deg); }
    .how-it-works__step small { max-width: none; }
}

.how-details {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: .25rem 1rem;
}
.how-details[open] { padding-bottom: 1rem; }
.how-details__trigger {
    cursor: pointer;
    padding: .75rem 0;
    font-weight: normal;
    list-style: none;
    color: var(--text);
}
.how-details__trigger::-webkit-details-marker { display: none; }
.how-details__trigger::after { content: " ↓"; color: var(--muted); }
.how-details[open] .how-details__trigger::after { content: " ↑"; }
.how-details__body { color: var(--muted); font-size: .9rem; line-height: 1.55; }
.how-details__body p { margin: 0 0 .75rem; }
.how-details__body p:last-child { margin: 0; }

.host-link--landing { text-align: center; color: var(--muted); font-size: .85rem; }

/* ─── Phase 12 / NAV-B3 — empty states (reusable across surfaces) ───── */
/* Renamed caploo-empty → empty-state to match handoff; overrides the legacy
   .empty-state utility at line ~652 (later source order wins). */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .75rem;
    padding: 2.5rem 1.25rem;
    text-align: center;
    color: var(--muted);
}
.empty-state__icon { color: var(--muted); }
.empty-state__title { color: var(--text); font-size: 1.05rem; font-weight: 700; margin: 0; }
.empty-state__body { font-size: .9rem; max-width: 28rem; line-height: 1.5; margin: 0; }
.empty-state__actions { display: flex; gap: .5rem; margin-top: .25rem; }

/* ─── Phase 12 — first-moment guided flow (non-blocking inline) ──────── */
.first-moment-guide {
    background: var(--surface);
    border: 1px solid var(--border);
    border-inline-start: 3px solid var(--accent);
    border-radius: var(--radius);
    padding: 1rem 1.1rem;
    margin: 0 0 1rem;
    box-shadow: var(--shadow);
}
.first-moment-guide__title {
    font-size: var(--text-md);
    font-weight: 700;
    color: var(--text);
    margin: 0 0 .4rem;
}
.first-moment-guide__subtitle {
    color: var(--muted);
    font-size: .85rem;
    margin: 0 0 .75rem;
}
.first-moment-guide__steps {
    list-style: none;
    margin: 0 0 .75rem;
    padding: 0;
    counter-reset: step;
}
.first-moment-guide__steps li {
    counter-increment: step;
    padding: .25rem 0 .25rem 1.75rem;
    position: relative;
    color: var(--text);
    font-size: .9rem;
    line-height: 1.45;
}
.first-moment-guide__steps li::before {
    content: counter(step);
    position: absolute;
    left: 0;
    top: .25rem;
    width: 1.25rem;
    height: 1.25rem;
    border-radius: var(--radius-pill);
    background: var(--accent-glow);
    color: var(--accent);
    font-weight: 700;
    font-size: .7rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.first-moment-guide__dismiss {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text);
    padding: .35rem .85rem;
    border-radius: var(--radius-sm);
    font-size: .8rem;
    font-weight: normal;
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition);
}
.first-moment-guide__dismiss:hover { background: var(--surface-alt); border-color: var(--border); }
.first-moment-guide__dismiss:focus-visible { box-shadow: var(--focus-ring); outline: none; }

/* ─── Phase 12 — sample-drop badge in My Drops ──────────────────────── */
.sample-badge {
    display: inline-flex;
    align-items: center;
    background: var(--accent-glow);
    color: var(--accent);
    font-size: .65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .08em;
    padding: .1rem .45rem;
    border-radius: var(--radius-pill);
    margin-inline-start: .35rem;
}

/* ─── Phase 12 — help drawer ─────────────────────────────────────────── */
.help-trigger {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text);
    width: 2rem;
    height: 2rem;
    border-radius: var(--radius-pill);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: var(--text-md);
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition);
}
.help-trigger:hover { background: var(--surface-alt); border-color: var(--border); }
.help-trigger:focus-visible { box-shadow: var(--focus-ring); outline: none; }

/* Phase 19h - the scrim + drawer used to sit at z-index 90/91 - BELOW the
   sticky .navbar (z-index 100). That meant the drawer's <header> (title +
   close button) rendered under the navbar's translucent backdrop-filter,
   producing the "first label clipped" finding in Phase 20a F-20a-7. A
   modal dialog must overlay every other chrome surface, so both layers
   are lifted above the navbar. */
/* F-178: bump scrim to 0.5 to match ReportModal / ShareMenu focus weight */
.help-drawer-scrim {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, .5);
    z-index: 200;
    animation: fadeIn .15s ease forwards;
}
.help-drawer {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(420px, 92vw);
    background: var(--surface);
    box-shadow: var(--shadow-drawer);
    z-index: 201;
    display: flex;
    flex-direction: column;
    animation: slideInRight .22s cubic-bezier(.2,.8,.2,1) forwards;
}
.help-drawer__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1rem 1.25rem;
    border-bottom: 1px solid var(--border);
}
.help-drawer__title { font-size: 1.05rem; font-weight: 700; margin: 0; }
.help-drawer__close {
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    font-size: 1.4rem;
    line-height: 1;
    padding: .25rem .5rem;
}
.help-drawer__close:hover { color: var(--text); }
.help-drawer__close:focus-visible { box-shadow: var(--focus-ring); outline: none; }
.help-drawer__body { overflow-y: auto; padding: 1rem 1.25rem 2rem; flex: 1; }
.help-drawer__section { margin-bottom: 1.5rem; }
.help-drawer__section h3 {
    font-size: var(--text-xs);
    font-weight: 700;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .07em;
    margin: 0 0 .5rem;
}
/* F-171: 44px unconditional touch target on FAQ rows. */
.help-drawer__faq summary { cursor: pointer; padding: .625rem 0; color: var(--text); min-height: 44px; display: flex; align-items: center; }
.help-drawer__faq[open] summary { color: var(--accent); }
.help-drawer__faq p { color: var(--muted); font-size: .88rem; line-height: 1.5; margin: .25rem 0 .5rem; }
/* M-116: keyboard shortcuts are irrelevant on touch devices — hide on coarse
   pointer so the drawer's limited height is given to FAQ content instead. */
@media (pointer: coarse), (max-width: 48rem) {
    .help-drawer__shortcuts-section { display: none; }
}
.help-drawer__shortcuts { display: grid; grid-template-columns: auto 1fr; gap: .25rem .75rem; font-size: .88rem; }
.help-drawer__shortcuts kbd {
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 1px 6px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: .8rem;
}
/* Phase 19b - each help-drawer link sits on its own line with enough
   vertical rhythm to scan. Adjacent links get a generous gap so the
   "email contact@..." row and "browse the user guides" row never feel
   crowded. The 44 px min-height keeps the touch target accessible. */
.help-drawer__link {
    display: block;
    color: var(--accent);
    text-decoration: none;
    font-size: .9rem;
    padding: .5rem 0;
    min-height: 44px;
    line-height: 1.4;
    display: flex;
    align-items: center;
}
.help-drawer__link + .help-drawer__link { margin-top: .25rem; }
.help-drawer__link:hover { text-decoration: underline; }
.help-drawer__link:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius-sm, 8px);
}
/* Slight breathing room after the shortcuts grid so the "Need more help?"
   block doesn't feel glued to the previous section. */
.help-drawer__shortcuts { margin-bottom: .25rem; }

@keyframes slideInRight {
    from { transform: translateX(100%); }
    to   { transform: translateX(0); }
}
@keyframes fadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* PA-01 — HelpDrawer landscape-phone adaptation.
   HelpDrawer is full-height (top:0 bottom:0). On landscape phones the
   available height is ~320px: a 1rem header + 2rem body padding + content
   quickly overflows. We compact the header padding and let __body continue
   to scroll (already overflow-y:auto flex:1). Header becomes sticky so
   the close button is always reachable. */
@media (orientation: landscape) and (max-height: 500px) {
    .help-drawer__header {
        padding-block: 0.5rem;
        position: sticky;
        top: 0;
        z-index: 1;
        background: var(--surface);
    }
    .help-drawer__body {
        padding-block: 0.5rem;
    }
    .help-drawer__section {
        margin-bottom: 0.75rem;
    }
}

/* ─── Phase 12 — what's-new banner ──────────────────────────────────── */
.whats-new-banner {
    background: var(--info-glow);
    border: 1px solid color-mix(in srgb, var(--info) 30%, transparent);
    border-radius: var(--radius);
    padding: .75rem 1rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    margin: 0 0 1rem;
    color: var(--text);
}
.whats-new-banner__body { font-size: .88rem; line-height: 1.4; }
.whats-new-banner__close {
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    padding: .25rem .5rem;
    font-size: var(--text-md);
}
.whats-new-banner__close:hover { color: var(--text); }

@media (prefers-reduced-motion: reduce) {
    .help-drawer, .help-drawer-scrim { animation: none; }
}



/* ── Phase 16d — floating scroll-navigation control ─────────────────────────
   Two-way jump-to-top / jump-to-bottom pill for long pages (the Drop feed).
   Fixed bottom-right, in the one-handed thumb band on mobile. Offset by the
   safe-area insets so it clears notch / home-indicator chrome. Pattern
   rationale: docs/phase-16/scroll-navigation.md. */
.scroll-nav {
    position: fixed;
    /* Ticket 14 — inset-inline-end positions in the end thumb band in both
       LTR (physical right) and RTL (physical left). The safe-area fallback
       keeps the pill clear of device notches; the [dir="rtl"] override below
       switches to safe-area-left so the notch compensation is also mirrored. */
    inset-inline-end: calc(var(--space-3) + var(--safe-area-right, 0px));
    bottom: calc(var(--space-3) + var(--safe-area-bottom, 0px));
    /* Above feed content; below the moment modal / overlays (50+) and the
       mobile drawer (60/70). */
    z-index: 45;
    display: flex;
    flex-direction: column;
    /* The 1px gap lets the --border background show through as a hairline
       divider between the two stacked buttons. */
    gap: 1px;
    background: var(--border);
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow);
    overflow: hidden;
}
@media (prefers-reduced-motion: no-preference) {
    .scroll-nav { animation: caplooFadeIn 160ms var(--easing-default) both; }
}

.scroll-nav__btn {
    /* WCAG 2.2 SC 2.5.8 — 44×44 minimum hit target. */
    width: 44px;
    height: 44px;
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    background: var(--surface);
    color: var(--text);
    border: none;
    border-radius: 0;
    cursor: pointer;
    transition: background var(--duration-base) var(--easing-default),
                color var(--duration-base) var(--easing-default);
}
.scroll-nav__btn--up {
    border-top-left-radius: var(--radius-pill);
    border-top-right-radius: var(--radius-pill);
}
.scroll-nav__btn--down {
    border-bottom-left-radius: var(--radius-pill);
    border-bottom-right-radius: var(--radius-pill);
}
.scroll-nav__btn:hover:not(:disabled) {
    background: var(--accent);
    color: var(--text-on-accent);
}
.scroll-nav__btn:focus-visible {
    outline: var(--focus-outline);
    outline-offset: -2px;
    /* Lift the focused button so its ring is not clipped by the sibling. */
    z-index: 1;
}
.scroll-nav__btn:disabled {
    /* Visible-but-muted rather than the global 45%-opacity fade — the user
       must still see that the control exists and which direction is spent. */
    opacity: 1;
    color: var(--muted);
    cursor: default;
}

/* Coarse-pointer / mobile — bump to Material's comfortable 48px target. */
@media (max-width: 768px) and (pointer: coarse) {
    .scroll-nav__btn { width: 48px; height: 48px; }
}

/* Phase 16d — scroll-navigation focus landmarks. #main-content (the
   jump-to-top target, also the skip-link target) and the per-page
   end-of-feed sentinel are focused programmatically after a jump; they are
   structural landmarks, not interactive controls, so a focus outline around
   the whole region would mislead rather than help. The focus *move* still
   benefits screen-reader and keyboard users. */
#main-content:focus,
.caploo-feed-end:focus {
    outline: none;
}
.caploo-feed-end {
    /* Zero-footprint sentinel — it only exists as a focus destination. */
    height: 0;
    overflow: hidden;
}






/* ════════════════════════════════════════════════════════════════════════
   Phase 14 (F-006) — reaction picker + stack
   ════════════════════════════════════════════════════════════════════════ */

.reaction-bar {
    /* Mounted at the bottom of every MomentCard and inside the post-detail
       modal. Owns its own focus ring so the Tab order stays predictable. */
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: .35rem;
}

.reaction-trigger {
    appearance: none;
    background: transparent;
    border: 1.5px solid var(--border);
    border-radius: var(--radius-pill);
    padding: .35rem .7rem;
    font-size: .82rem;
    color: var(--muted);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .35rem;
    /* Phase 16c — full 44x44 pointer target (WCAG 2.5.8 / 3.0 target size). */
    min-height: 44px;
    min-width: 44px;
    transition: background 120ms ease, border-color 120ms ease,
                color 120ms ease, transform 120ms ease;
}
.reaction-trigger:hover,
.reaction-trigger:focus-visible {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.reaction-trigger:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.reaction-trigger:active { transform: scale(.94); }

/* ── Phase 16c interaction states ─────────────────────────────────────────
   unliked  → resting (no modifier)
   liked    → --liked (filled thumb, accent treatment, aria-pressed=true)
   loading  → --loading (spinner swapped in, input guarded)
   success  → --success (brief confirmation pop)
   --active covers any non-Like reaction picked from the popover.            */
.reaction-trigger--active {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    color: var(--accent);
}
.reaction-trigger--liked {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 16%, transparent);
    color: var(--accent);
}
.reaction-trigger--loading {
    cursor: progress;
    opacity: .9;
}
.reaction-trigger--success {
    animation: reaction-pop 320ms ease;
}

.reaction-trigger__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
}

.reaction-thumb,
.reaction-spinner {
    width: 18px;
    height: 18px;
    display: block;
}
.reaction-thumb {
    fill: none;
    transition: fill 140ms ease;
}
/* liked + success fill the thumb so the active state reads at a glance,
   while the accent-coloured stroke keeps the outline crisp. */
.reaction-trigger--liked .reaction-thumb,
.reaction-trigger--success .reaction-thumb {
    fill: color-mix(in srgb, var(--accent) 28%, transparent);
}
.reaction-spinner {
    animation: reaction-spin 720ms linear infinite;
    transform-origin: 50% 50%;
}

@keyframes reaction-spin {
    to { transform: rotate(360deg); }
}
@keyframes reaction-pop {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.14); }
    100% { transform: scale(1); }
}

.reaction-emoji {
    font-size: 1.05rem;
    line-height: 1;
}

.reaction-count {
    font-variant-numeric: tabular-nums;
    font-size: .78rem;
    font-weight: normal;
}

/* Phase 16c — reaction bar inside the post-detail modal: a little breathing
   room between the caption block and the divider above the comments. */
.modal-reactions {
    margin: .1rem 0 .35rem;
}

.reaction-picker {
    /* Popover anchored above the trigger. Closed via outside-click on
       the parent or Escape inside the popover. */
    position: absolute;
    bottom: 110%;
    left: 0;
    display: flex;
    gap: .2rem;
    padding: .35rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    box-shadow: 0 4px 16px rgba(0,0,0,.16);
    z-index: 20;
}

.reaction-picker__option {
    appearance: none;
    background: transparent;
    border: none;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    font-size: 1.1rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 120ms ease, transform 120ms ease;
}
.reaction-picker__option:hover,
.reaction-picker__option:focus-visible {
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    transform: scale(1.15);
}
.reaction-picker__option:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
/* Phase 16c — the caller's currently-selected reaction is marked in the
   popover so the picker and the trigger never disagree. */
.reaction-picker__option--active {
    background: color-mix(in srgb, var(--accent) 20%, transparent);
}

.reaction-stack {
    display: inline-flex;
    align-items: center;
    gap: .2rem;
}
.reaction-stack__avatar {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--surface-alt);
    border: 1.5px solid var(--surface);
    color: var(--text);
    font-size: var(--text-micro);
    font-weight: normal;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-inline-start: -6px;
}
.reaction-stack__avatar:first-child { margin-inline-start: 0; }

@media (prefers-reduced-motion: reduce) {
    /* Phase 16c — no spin, no pop, no scale; the state changes are still
       fully conveyed by colour, fill, aria-pressed and the live region. */
    .reaction-trigger,
    .reaction-trigger:active,
    .reaction-trigger--success { transition: none; transform: none; animation: none; }
    .reaction-spinner { animation: none; }
    .reaction-picker__option { transition: none; }
    .reaction-picker__option:hover,
    .reaction-picker__option:focus-visible { transform: none; }
}

/* ════════════════════════════════════════════════════════════════════════
   Mobile Responsive Overhaul (fix/mobile-web-responsive-overhaul)
   ════════════════════════════════════════════════════════════════════════
   Purpose:
     • Centralize previously ad-hoc mobile rules into one cascade-winning
       block; this section lives at the END of app.css so it overrides any
       upstream legacy stanzas without `!important`.
     • Fix post layout centering / width on mobile (cards now span the
       viewport with sensible gutters and stay centered).
     • Zoom safety: at user zoom up to 500% the top nav must remain
       reachable and the page must not require horizontal scroll for
       primary content at any zoom level ≤200%.
     • Distortion control: flex-shrink:0 on icons, min-width:0 on flex
       children with media, box-sizing already global. Type stays in rem.
     • Formalize breakpoints (SM/MD/LG/XL/XXL) + orientation rules.
     • Touch targets: 24×24 baseline (WCAG 2.2 SC 2.5.8), 44×44 primary
       CTAs already enforced.
     • No clipping or horizontal scroll at 320 CSS-px viewport width.

   Documentation: docs/mobile/responsive-overhaul.md
   ════════════════════════════════════════════════════════════════════════ */

:root {
    /* ── Formal breakpoint scale (values in CSS pixels) ──────────────────
       Use these constants in component CSS via custom media queries
       (when supported) or by literal repetition. Documenting them once
       prevents the 480/520/600/700/720/900/1024/1280/1400/1440/1600 sprawl
       seen in earlier phases. Existing media queries are NOT rewritten —
       this scale governs new and refactored code. */
    --bp-sm:  20rem;    /* 320px — smallest supported phone (iPhone SE 1) */
    --bp-md:  48rem;    /* 768px — tablet portrait                       */
    --bp-lg:  64rem;    /* 1024px — tablet landscape / small desktop      */
    --bp-xl:  90rem;    /* 1440px — desktop                              */
    --bp-xxl: 100rem;   /* 1600px — wide desktop                         */

    /* Gutter scale (clamp keeps text/cards readable from 320 → 1600+) */
    --gutter-x: clamp(0.75rem, 2.5vw, 1.5rem);

    /* Page content rail — caps line length on huge displays without
       starving the rail on narrow ones. */
    --content-rail: min(100%, 68rem);
}

/* ── F-cd-5: Tablet portrait/landscape (768–1024) content-rail lift ─────────
   At phone widths the 680px/480px caps leave sensible gutters.
   At tablet landscape (≥1024px) those same caps create 170px dead-rail each
   side — too sparse for the iPad audience. This block lifts key surfaces
   into the range while leaving desktop (≥64rem) behaviour untouched.
   ──────────────────────────────────────────────────────────────────────── */
@media (min-width: 48rem) and (max-width: 64rem) {
    /* Recap page — lift 680px cap to 760px on tablet */
    .recap-container {
        max-width: 760px;
    }

    /* Auth shell — lift 480px cap to 560px on tablet; stays centred */
    .auth-shell {
        max-width: 560px;
    }

    /* Home / CreateDrop page container — lift 440px cap to 520px on tablet */
    .home-container {
        max-width: 520px;
    }
}

/* ── Defensive root rules (prevent rogue overflow) ─────────────────────── */
html {
    /* `overflow-x: clip` retains scroll behavior on the y-axis while
       making any oversized child get clipped horizontally rather than
       extending the viewport (which would force horizontal scroll on
       phones). Falls back gracefully on older browsers via `hidden`. */
    overflow-x: clip;
    overflow-x: hidden;
    /* iOS Safari: stop auto-zooming text after orientation change. */
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}
body {
    /* Mirror html so a stuck `position: fixed` overlay can't expand the
       page either. */
    overflow-x: clip;
    overflow-x: hidden;
    /* Wrap long words rather than letting them blow out the rail. */
    overflow-wrap: anywhere;
}

/* `min-width: 0` is the most-cited “flex child squish” fix. Apply
   conservatively to known flex containers' direct children so a long
   string (URL, drop name, tag) collapses instead of pushing the row
   off-screen. Limited to specific selectors to avoid clobbering layouts
   that intentionally use intrinsic widths. */
.feed-header > *,
.feed-header__left > *,
.navbar > *,
.navbar-actions > *,
.navbar-drop-info > *,
.navbar-meta > *,
.caploo-toolbar > *,
.caploo-toolbar__row > *,
.moment-card-byline > *,
.moment-card-meta > * {
    min-width: 0;
}

/* SVG icons inside flex rows must NOT shrink when neighbors grow.
   This is the “icons go oval” bug class — squished width but full
   height produces ellipsoids. */
.navbar svg,
.navbar-brand svg,
.btn-ghost svg,
.btn-primary svg,
.btn-secondary svg,
.btn-copy svg,
.btn-recap-link svg,
.feed-header svg,
.feed-header__left svg,
.caploo-toolbar svg,
.moment-card svg,
.moment-card-byline svg,
.moment-card-meta svg,
.share-trigger-btn svg,
.nav-settings svg,
.help-trigger svg,
.notification-bell svg,
.reaction-trigger svg,
.user-chip svg {
    flex-shrink: 0;
}

/* Media inside cards must never exceed the card width (defensive — the
   `.moment-media` rule already sets width:100% but third-party embeds
   sometimes inject inline width attributes). */
.moment-card img,
.moment-card video,
.moment-card picture,
.timeline-content .media {
    max-width: 100%;
    height: auto;
}

/* ── Tier 1 — Viewport / zoom-safe navbar ──────────────────────────────
   Root cause of the "nav disappears on pinch-zoom" bug: a `position: sticky`
   header is scroll-anchored, which means when the viewport zooms and the
   user pans, the header travels off-screen with the rest of the document.
   On narrow viewports (≤ tablet portrait) we switch to `position: fixed`
   so the bar is anchored to the visual viewport instead, which keeps it
   reachable at every zoom factor up to ~500%.

   On wider viewports we keep `sticky` (the existing behavior) because
   desktop users don't pinch-zoom and `sticky` is friendlier to long
   pages. The toggle is gated by `(max-width: 48rem)` only — no
   `(pointer: coarse)` — because a desktop browser at 200% zoom shrinks
   the layout into the same narrow band and benefits from the same fix. */
@media (max-width: 48rem) {
    .navbar {
        position: fixed;
        inset-inline: 0;
        top: 0;
        /* 100vw is more zoom-stable than 100% on iOS Safari and modern
           Android: pinch zoom changes the *visual* viewport but leaves
           the *layout* viewport's width alone, so a width pinned to vw
           stays pinned to the device edge. */
        width: 100vw;
        max-width: 100vw;
        /* Notched / foldable safe-area on the sides. */
        padding-left: calc(1rem + env(safe-area-inset-left, 0px));
        padding-right: calc(1rem + env(safe-area-inset-right, 0px));
        /* Sits above bottom-anchored capture-panel (z: 50) and below the
           expiry overlay (z: 200). */
        z-index: 110;
    }

    /* Reserve scroll-room for the now-fixed navbar so page content
       doesn't slide under it. Pages without a navbar (`html.no-navbar`)
       can opt out. */
    body {
        padding-top: var(--navbar-h);
    }

    /* The skip-link target needs scroll-margin so that landing on
       #main-content via keyboard doesn't tuck under the fixed bar. */
    #main-content {
        scroll-margin-top: calc(var(--navbar-h) + var(--safe-area-top, 0px));
    }
}

/* On portrait phones below 480 the brand wordmark can crowd the actions
   cluster; let the brand truncate before the actions do. */
@media (max-width: 30rem) {
    .navbar {
        gap: 0.5rem;
        padding-left: calc(0.75rem + env(safe-area-inset-left, 0px));
        padding-right: calc(0.75rem + env(safe-area-inset-right, 0px));
    }
    .navbar-brand {
        font-size: var(--text-md);
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    /* The drop-info slot is the most likely culprit for horizontal
       overflow because it carries the user-supplied drop name. Give it a
       hard ellipsis so the rest of the bar stays reachable. */
    .navbar-drop-info,
    .navbar-drop-name {
        min-width: 0;
        max-width: 40vw;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

/* ── Tier 2 — Post layout: centered & fluid on mobile ──────────────────
   Existing `.feed` paints `padding: 1.5rem 1.25rem` everywhere; at 320px
   that leaves 280 CSS-px for cards and centers them only because the
   layout collapses to a single column. Two issues remain:
     (a) the inline gutter doesn't scale with viewport (lock to 1.25rem),
     (b) the feed itself isn't horizontally centered when content is
         narrower than the rail (e.g. List mode capped at 840px).
   The block below addresses both via clamp() gutters and an explicit
   inline-margin centering for the inner content rails. */
@media (max-width: 48rem) {
    .feed {
        padding-inline: var(--gutter-x);
        gap: 1rem;
    }

    /* Center single-card layouts (List + Large) inside the main column,
       and let auto-fill grids fan out edge-to-edge. */
    .feed--list,
    .caploo-moment-list {
        max-width: 100%;
        margin-inline: 0;
    }
    .feed--large {
        max-width: 100%;
        margin-inline: 0;
    }

    /* Moment cards should fluidly fill the rail rather than be capped
       by an inherited max-width. */
    .moment-card {
        max-width: 100%;
    }

    /* In list mode the legacy 96px thumbnail + 1rem gap leaves ~190px
       for body text on a 320px viewport. Tighten the gap and shrink
       the thumbnail slightly so captions don't get clipped. */
    .feed.feed--list .moment-card,
    .caploo-moment-list .moment-card,
    .moment-card--list {
        grid-template-columns: 80px 1fr;
        gap: 0.75rem;
        padding: 0.5rem;
    }
    .feed.feed--list .moment-media-wrap,
    .moment-card--list .moment-media-wrap {
        width: 80px;
        height: 80px;
    }
}

/* When the feed is empty or sparse, center it vertically as well so the
   page reads as intentional rather than top-anchored white space. */
@media (max-width: 48rem) {
    .feed:has(.empty-state):only-child,
    .feed.is-empty {
        justify-content: center;
        min-height: calc(100dvh - var(--navbar-h) - 88px);
    }
}

/* ── Tier 3 — Distortion fixes (icons, type, flex children) ────────────
   The flex-shrink:0 / min-width:0 selectors at the top of this section
   handle most of the "icons go oval" + "row blows out" cases. The block
   below addresses the remaining failure modes:
     • bare px font-sizes in cramped UI (Calendar header, Toolbar title)
       can break ZoomText accessibility tools; we normalize to rem.
     • capture-panel input flex behavior already uses flex:1 + min-width:0
       but pages that use the legacy single-column .feed can still see
       the bottom bar's button cluster wrap onto two rows on 320px
       phones — clamp its width and tighten gaps.
   We don't rewrite existing rules; we just override the painfully
   small px values with rem equivalents at narrow widths. */

@media (max-width: 48rem) {
    /* iOS Safari auto-zooms any <input> with font-size < 16px on focus.
       Force the inline-form inputs (drop name, PIN entry, search) to
       use the 1rem baseline so the page doesn't double-zoom on focus. */
    input,
    select,
    textarea {
        font-size: max(1rem, var(--text-md, 1rem));
    }

    /* Capture-panel bottom bar — squeeze gutters and let the input fill. */
    .capture-panel {
        gap: 0.5rem;
        padding: 0.75rem calc(0.75rem + env(safe-area-inset-left, 0px))
                 calc(0.75rem + env(safe-area-inset-bottom, 0px))
                 calc(0.75rem + env(safe-area-inset-right, 0px));
    }
    .capture-panel input {
        min-width: 0;
        font-size: max(1rem, var(--text-md, 1rem));
    }
    .capture-panel .btn-capture {
        padding: 0.55rem 0.9rem;
        font-size: 0.9rem;
    }

    /* Feed-level bottom padding must clear the fixed capture-panel even
       on devices with a home-indicator (iPhone X+). */
    .feed {
        padding-bottom: calc(88px + env(safe-area-inset-bottom, 0px));
    }
}

/* Touch-target floor (WCAG 2.2 SC 2.5.8 — 24×24 minimum, 44×44 primary).
   We don't bump every button to 44px because that breaks several dense
   utility chips (tag rows, share menu icon, calendar day). Instead we
   set a hard 24×24 floor on every bare <button> + role="button" so
   nothing accidentally falls below the WCAG line, then nudge the most
   common primary CTAs to 44×44. */
@media (pointer: coarse), (max-width: 48rem) {
    button,
    [role="button"],
    a.btn-ghost,
    a.btn-primary,
    a.btn-secondary {
        min-height: 24px;
        min-width: 24px;
    }
    .btn-primary,
    .auth-card__submit,
    .btn-capture,
    button.btn-primary,
    a.btn-primary {
        min-height: 44px;
    }

    /* The legacy access-rotated dismiss is exactly at the 24-floor; keep
       it visually compact but ensure adequate hit area via padding. */
    .access-rotated-banner__dismiss {
        min-height: 24px;
        min-width: 24px;
    }
}

/* Hide decorative SVG outlines from assistive tech (defensive — any new
   icon that forgets aria-hidden still won't reach the AT tree). */
.navbar svg:not([aria-label]):not([role]),
.btn-ghost svg:not([aria-label]):not([role]),
.btn-primary svg:not([aria-label]):not([role]),
.btn-secondary svg:not([aria-label]):not([role]),
.btn-copy svg:not([aria-label]):not([role]),
.moment-card svg:not([aria-label]):not([role]) {
    /* Cascades only when the markup omits an explicit label; never
       fights an authored one. */
    pointer-events: none;
}

/* ── Tier 4 — Breakpoint formalization + orientation handling ──────────
   Existing pages already have a wide range of breakpoints (480/520/600/
   700/720/900/1024/1280/1400/1440/1600). They are NOT rewritten here —
   the cost of touching every media query in 9k lines of CSS would
   outweigh the win. Instead we use the formal SM/MD/LG/XL/XXL scale
   defined above for net-new rules and document the mapping in
   docs/mobile/responsive-overhaul.md so future authors stop adding new
   one-off values. */

/* Landscape phone — when the user rotates a phone into landscape we get
   a viewport that is wider than tall (≥568px wide, ≤500px tall on every
   modern phone). At that point the bottom-anchored capture-panel eats
   half the vertical real-estate, so we shrink its footprint and let the
   feed lift. */
@media (orientation: landscape) and (max-height: 500px) {
    .navbar {
        height: 44px;
    }
    .capture-panel {
        padding-top: 0.5rem;
        padding-bottom: calc(0.5rem + env(safe-area-inset-bottom, 0px));
    }
    .capture-panel .btn-capture {
        padding: 0.45rem 0.8rem;
        font-size: 0.85rem;
    }
    .feed {
        padding-top: 0.75rem;
        padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px));
    }
    body {
        /* Match the shorter navbar so content lines up. */
        padding-top: 44px;
    }
    /* Hide the live-badge text in landscape to free up toolbar room
       (the pulsing dot stays). */
    .live-badge::after {
        content: none;
    }
}

/* Tablet portrait (md → lg) — the existing layout already collapses
   capture-panel into a bottom bar at ≤700px. We extend the fluid gutter
   one breakpoint up (768px) so iPad mini portrait gets the same
   centered card treatment without a sudden jump in horizontal padding. */
@media (min-width: 30rem) and (max-width: 48rem) {
    .feed {
        padding-inline: 1.25rem;
    }
}

/* Large desktop wide rail — already handled by existing min-width rules;
   we just re-cap the feed to a sensible reading rail when no sidebar is
   open so cards don't stretch to 1600px width. */
@media (min-width: 90rem) {
    .feed:not(.feed-full) {
        max-width: 100%;
    }
}

/* ── Reduced-motion + reduced-data safe-net ────────────────────────────
   The existing motion-reduction stanzas are scattered; we add a single
   data-saver stanza here so background blur (cheap on desktop, costly
   on low-end Android) drops out under Save-Data. */
@media (prefers-reduced-data: reduce) {
    .navbar,
    .capture-panel,
    .mobile-bottom-nav {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
    }
}

/* ── Forced-colors (Windows High Contrast) ─────────────────────────────
   Make sure custom focus rings survive forced-colors mode. */
@media (forced-colors: active) {
    :focus-visible {
        outline: 2px solid CanvasText;
        outline-offset: 2px;
    }
    .moment-card,
    .navbar,
    .capture-panel {
        border-color: CanvasText;
    }
}

/* ── Phase 20b P1 batch 1 — mobile-specific fixes ──────────────────────────
   M-013 / M-016 / M-017 / M-019 / M-021 / M-023 / M-024 / M-029 / M-041
   / M-065 / M-067 / M-068 / M-083 / M-150
   (M-020 / M-099 / M-100 / M-022 applied at their source rules above)
   ──────────────────────────────────────────────────────────────────────── */

/* M-011 — Home page hero 960px tall on 320px phone, CTAs below fold.
   The 3-step explainer is duplicated by the "How does it work?" <details>
   further down the page. On mobile we hide the inline list to bring the
   Create + Join CTAs nearer the fold. Desktop keeps the visual diagram. */
@media (max-width: 48rem) {
    .how-it-works {
        display: none;
    }
}

/* M-015 / M-039 — visible labels for Join form inputs.
   Placeholders vanish on focus; soft keyboard covers the page heading, so
   mobile users lose all input context mid-entry. Compact visible labels
   persist throughout the keyboard lifecycle. */
.join-field-label {
    display: block;
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--muted);
    margin-block-end: 0.25rem;
}

/* M-016 — Android Chrome pull-to-refresh draft loss.
   overscroll-behavior-y: contain blocks the pull-to-refresh trigger at
   scrollTop=0, preventing accidental page reloads that lose unsaved composer
   captions during a fast live-event scroll-back. */
@media (max-width: 48rem) {
    html, body {
        overscroll-behavior-y: contain;
    }
}

/* M-013 — iOS Safari URL-bar collapse body-gap.
   The static var(--navbar-h) doesn't track the dynamic-viewport shift when
   iOS URL bar collapses. Adding env(safe-area-inset-top) keeps the reserved
   scroll-room aligned with the actual top-of-visible-content on notched
   devices and is a no-op (0px) on flat-screen phones. */
@media (max-width: 48rem) {
    body {
        padding-top: calc(var(--navbar-h) + env(safe-area-inset-top, 0px));
    }
}

/* M-017 — landscape-phone countdown prefix overflow.
   The rule at ~10770 hides .live-badge::after in landscape. The __prefix
   span ("Expires in") was missed; both must vanish to free the 44px bar. */
@media (orientation: landscape) and (max-height: 500px) {
    .navbar-countdown__prefix {
        display: none;
    }
}

/* M-019 + M-021 — extend touch-target floor to secondary/action CTAs.
   M-019: access-rotated-banner dismiss × was at the 24px floor — destructive
          action (dismissing a security-rotation notice) warrants 44px.
   M-021: secondary CTAs were not in the original 44px allowlist; they are
          hit by all mobile personas on every page.
   DEPRECATED — remove after Phase 20d verify. New code uses [data-cta-tier]
   attribute opt-in (PA-02) so this class-based allowlist is no longer the
   mechanism — it remains for backward compat on elements not yet annotated. */
@media (pointer: coarse), (max-width: 48rem) {
    .access-rotated-banner__dismiss {       /* M-019 */
        min-width: 44px;
        min-height: 44px;
        align-self: center;
    }
    .btn-secondary,                         /* M-021 */
    button.btn-secondary,
    a.btn-secondary,
    .btn-empty-cta,
    .btn-load-more,
    .btn-regen,
    .btn-close-drop,
    .btn-recap-link,
    .btn-copy,
    .share-trigger-btn,
    .nav-settings {
        min-height: 44px;
    }
}

/* ============================================================
   PA-02 — Touch-target floor: [data-cta-tier] attribute token system
   Stage E addition. Attribute opt-in replaces the class-selector allowlist
   above (M-021). Elements annotated with data-cta-tier get the correct
   min touch-target unconditionally (primary: 44px / comfortable: 48px on
   coarse; secondary: 44px on all pointer types).

   Usage in Razor:
     data-cta-tier="primary"    — Submit / Save / Confirm / Create / Post / Apply
     data-cta-tier="secondary"  — Cancel / Back / Close / Skip / Dismiss / stepper
   ============================================================ */

/* Primary CTA tier — guaranteed 44px baseline, bumped to 48px on coarse */
[data-cta-tier="primary"] {
    min-height: var(--touch-target-min, 44px);
    min-width: var(--touch-target-min, 44px);
}

@media (pointer: coarse) {
    [data-cta-tier="primary"] {
        min-height: var(--touch-target-comfortable, 48px);
    }
}

/* Secondary CTA tier — 44px on all pointer types (these are real actions) */
[data-cta-tier="secondary"] {
    min-height: var(--touch-target-min, 44px);
    min-width: var(--touch-target-min, 44px);
}

/* Story-progress segments: visual height is 3px; the attribute expands
   the tap area via block padding without changing visual layout. */
.story-progress__track[data-cta-tier="secondary"] {
    padding-block: calc((var(--touch-target-min, 44px) - 3px) / 2);
    margin-block: calc(-1 * (var(--touch-target-min, 44px) - 3px) / 2);
}

/* Stepper buttons: prevent flex container from shrinking below the floor */
.stepper > [data-cta-tier="secondary"],
.duration-stepper__btn[data-cta-tier="secondary"] {
    flex-shrink: 0;
    width: var(--touch-target-min, 44px);
    height: var(--touch-target-min, 44px);
}

/* Notif chips: override the 36px unconditional base so floor is always met */
.notif-chips .chip[data-cta-tier="secondary"] {
    min-height: var(--touch-target-min, 44px);
}

/* M-029 — first content row cleared by navbar on Android Chrome.
   scroll-margin-top on #main-content is correct but a small top-padding on
   the page container ensures content never visually touches the fixed bar. */
@media (max-width: 48rem) {
    .home-container,
    .auth-shell,
    .recap-container,
    .notifications-page,
    .admin-moderation,
    .help-page {
        padding-top: 0.5rem;
    }
}

/* M-041 — iOS focus-zoom guard escapes PIN input.
   The global font-size: max(1rem,…) rule (Phase 16b) overrides the
   deliberate 18.4px Phase 19c PIN size. Carve an exception; 1.15rem is
   above the 16px iOS guard so no zoom fires, yet glyphs remain readable. */
@media (max-width: 48rem) {
    .pin-input--alphanum {
        font-size: 1.15rem;
    }
}

/* M-065 — navbar PIN chip overflows 320px.
   At ≤ 30rem the pin-chip __label ("PIN:") is already hidden at 720px, but
   the __copy button still shows its text span ("Copy" / "Copied"), widening
   the chip past the available rail. Keep only the icon. */
@media (max-width: 30rem) {
    .navbar-pin-chip__copy > span {
        display: none;
    }
}

/* M-067 — moment-card byline unreadable in grid mode.
   var(--text-xs) ≈ 11px is below the iOS body-readability floor; bump to
   13px (0.8125rem) on mobile. Applies to grid + list via the cascade. */
@media (max-width: 48rem) {
    .moment-card-byline {
        font-size: 0.8125rem;
    }
}

/* M-068 — list-mode caption clips too aggressively on narrow phones.
   The existing 2-line clamp was tuned for desktop; on 320px the text column
   is ~190px wide so 4 lines gives the right content/density balance. */
@media (max-width: 48rem) {
    .moment-card--list .moment-card-caption .caption-text {
        -webkit-line-clamp: 4;
    }
}

/* M-023 — HelpDrawer header occluded by fixed navbar.
   The drawer slides from the right; its header sits at top:0 of the drawer
   but the navbar is fixed at top:0 of the viewport — the first FAQ summary
   is partially under the bar on narrow phones. */
@media (max-width: 48rem) {
    .help-drawer__header {
        padding-top: calc(1rem + var(--navbar-h) + env(safe-area-inset-top, 0px));
    }
}

/* M-024 — story-progress tap zones too small.
   Each segment track is 3px tall — far below WCAG 2.2 SC 2.5.8.
   Expand the hit area via padding-block while keeping the visual bar 3px
   by using a transparent padding (the fill/done color won't change). */
@media (pointer: coarse), (max-width: 48rem) {
    .story-progress__track {
        padding-block: 10px;
        margin-block: -10px;
    }
}

/* M-083 / D-105 — carousel chevrons: 44px hit-target applies at every pointer type,
   not just coarse. Click targets matter for mouse precision too. */
.modal-carousel-prev,
.modal-carousel-next {
    min-width: 44px;
    min-height: 44px;
}

/* M-150 — notification filter chips wrap to 2 rows on 320px.
   Horizontal scroll with snap is the standard pattern for chip rows on
   mobile; wrapping doubles the visual height and buries chips below the
   page heading. */
@media (max-width: 30rem) {
    .notif-chips {
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scroll-snap-type: x mandatory;
        scrollbar-width: none;
    }
    .notif-chips::-webkit-scrollbar { display: none; }
    .notif-chips .chip {
        flex-shrink: 0;
        scroll-snap-align: start;
    }
}

/* M-025 — Photo-card secondary buttons (Edit/Dismiss) under 44px floor.
   The composer photo-card secondary row buttons have no min-height guarantee
   on coarse pointer. */
@media (pointer: coarse), (max-width: 48rem) {
    .caploo-composer-ai__photo-card-btn {
        min-height: 44px;
    }
}

/* M-030 — Navbar user-name truncation at ≤30rem.
   Long display-names push the avatar off-screen at 320px. Cap to 16ch. */
@media (max-width: 30rem) {
    .user-name {
        max-width: 16ch;
        overflow: hidden;
        text-overflow: ellipsis;
        display: inline-block;
        vertical-align: middle;
    }
}

/* M-048 — Password show/hide toggle hit-area too small (36×36px).
   Extend via min dimensions; the visual size stays the same. */
@media (pointer: coarse), (max-width: 48rem) {
    .password-toggle {
        min-width: 44px;
        min-height: 44px;
        display: flex;
        align-items: center;
        justify-content: center;
    }
}

/* M-049 — PasswordStrengthMeter segment label too small (≈11px).
   Bump to 14px on mobile so P-03 Eleanor can read it. */
@media (max-width: 48rem) {
    .password-strength__label {
        font-size: 0.875rem;
    }
}


/* M-057 — Info-tooltip (ⓘ) uses title attribute which touch devices cannot
   hover. Show the title text as a positioned pseudo-element on :focus/:active
   for coarse-pointer contexts so mobile users can tap to reveal. */
@media (pointer: coarse) {
    .info-tooltip {
        position: relative;
        display: inline-block;
    }
    .info-tooltip:focus::after,
    .info-tooltip:active::after {
        content: attr(title);
        position: absolute;
        left: 0;
        bottom: calc(100% + 6px);
        min-width: 200px;
        max-width: min(260px, 90vw);
        background: var(--bg-surface, #fff);
        color: var(--text);
        border: 1px solid var(--border);
        border-radius: var(--radius-sm, 8px);
        padding: 8px 12px;
        font-size: 0.8125rem;
        line-height: 1.45;
        white-space: normal;
        box-shadow: var(--shadow-md);
        pointer-events: none;
        z-index: 50;
    }
}

/* M-058 — Native number-input spinners conflict with custom stepper buttons.
   iOS hides them; Android Chrome shows them. Remove across all browsers. */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    appearance: none;
    margin: 0;
}
input[type="number"] {
    -moz-appearance: textfield;
    appearance: textfield;
}

/* M-064 — Feed page host action text labels overflow at ≤360px.
   Share/Report text labels widen the cluster past available rail.
   Hide the text span at very narrow widths; icon + aria-label remain. */
@media (max-width: 22.5rem) {
    .share-trigger-btn > span:not([aria-hidden]) {
        display: none;
    }
}

/* M-082 — Moment modal text-only scroll affordance.
   A top gradient cue on the detail panel signals scrollable content above. */
@media (max-width: 48rem) {
    .modal-detail-panel {
        background: linear-gradient(var(--surface) 0%, var(--surface) 30px, transparent 30px),
                    var(--surface);
        background-size: 100% 30px, 100% 100%;
        background-attachment: local, scroll;
    }
}

/* M-085 — Modal header-actions overflow at ≤30rem.
   Up to 5 icon buttons in ~100px. The .modal-header-more-trigger ⋯ button
   is shown at narrow widths; individual overflow actions are toggled via
   the .modal-header-actions--overflow class added in the Razor. */
@media (max-width: 30rem) {
    .modal-header-actions--overflow {
        display: none;
    }
    .modal-header-more-trigger {
        display: inline-flex;
    }
}
.modal-header-more-trigger {
    display: none;
}

/* M-088 — Privacy toggle switch hit-area under 44px.
   Visual switch is 38×22px; transparent padding extends hit zone to 44×44. */
@media (pointer: coarse), (max-width: 48rem) {
    .caploo-toggle-switch {
        padding: calc((44px - var(--switch-h, 22px)) / 2)
                 calc((44px - var(--switch-w, 38px)) / 2);
    }
}

/* M-112 — ReportModal → bottom-sheet at ≤48rem.
   Center-modal causes viewport overflow on 320×568; anchor to bottom. */
@media (max-width: 48rem) {
    .report-modal-backdrop {
        align-items: flex-end;
        padding: 0;
    }
    .report-modal {
        width: 100%;
        max-width: 100%;
        max-height: 90svh;
        border-radius: var(--radius-lg, 20px) var(--radius-lg, 20px) 0 0;
        border-bottom: none;
    }
    .report-modal::before {
        content: '';
        display: block;
        width: 36px;
        height: 4px;
        border-radius: 2px;
        background: var(--border);
        margin: 8px auto 4px;
        flex-shrink: 0;
    }
}

/* M-113 — Report modal reason rows: 44px min-height for coarse pointer.
   The existing min-height is 40px; bump to 44px and enlarge the radio glyph. */
@media (pointer: coarse), (max-width: 48rem) {
    .report-modal__reason {
        min-height: 44px;
    }
    .report-modal__reason input[type="radio"] {
        width: 1.25rem;
        height: 1.25rem;
    }
}

/* M-126 — Calendar sidebar grid overflow at narrow drawer width (294px).
   7 × 44px cells = 308px > 294px. Allow horizontal scroll so cells stay
   the required 44px and don't get clipped. */
@media (max-width: 30rem) {
    .caploo-sidebar-section__body--calendar {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }
    .caploo-sidebar-section__body--calendar .caploo-calendar {
        min-width: 308px;
    }
}

/* M-127 — Calendar month-nav chevrons below 44px hit area.
   Extend to 44×44 on coarse pointer. */
@media (pointer: coarse), (max-width: 48rem) {
    .caploo-calendar-header__nav {
        min-width: 44px;
        min-height: 44px;
        display: flex;
        align-items: center;
        justify-content: center;
    }
}

/* M-130 — OverlayShell breakpoint 900px → 48rem.
   Devices 768–900px (large phones landscape, small tablets split-view)
   should get the desktop popover, not the mobile sheet.
   The original min-width:900px rules are left in place for specificity order;
   these 48rem rules appear later in the cascade and override them. */
@media (min-width: 48rem) {
    .caploo-overlay-backdrop {
        display: none;
    }
    .caploo-overlay {
        position: absolute;
        left: auto;
        right: 0;
        bottom: auto;
        top: calc(100% + 8px);
        max-width: 360px;
        width: 360px;
        max-height: 480px;
        border-radius: var(--radius-lg, 20px);
        border-bottom: 1px solid var(--border);
        box-shadow: var(--shadow-lg);
    }
    .caploo-overlay--map {
        max-width: 720px;
        width: min(720px, 90vw);
        height: 480px;
        max-height: 480px;
    }
    .caploo-overlay::before {
        display: none;
    }
}

/* M-134 B-resolved: StoryRecapViewer.razor.css already sets
   .story-media { object-fit: contain } for all media including video. */

/* M-135 B-resolved: .story-header has z-index:10 > .story-nav z-index:5,
   so close/pause buttons intercept taps before the nav zones. */

/* M-141 — MyDrops export thumb too wide at ≤30rem.
   SVG previews are 120×80px; scale down to 80×56 to fit 294px columns. */
@media (max-width: 30rem) {
    .drop-export-thumb {
        flex: 0 0 80px;
        width: 80px;
    }
    .drop-export-thumb svg {
        width: 80px;
        height: 56px;
    }
}

/* M-145 — Avatar preview unbound size can force horizontal scroll.
   Cap at 96×96px regardless of the user-supplied image dimensions. */
.avatar-preview {
    max-width: 96px;
    max-height: 96px;
    object-fit: cover;
    border-radius: var(--radius-sm);
    display: block;
}

/* M-151 — Notifications unread-only toggle label under 44px on mobile.
   Extend the label's tap target to 44px minimum height. */
@media (pointer: coarse), (max-width: 48rem) {
    .toggle-label {
        min-height: 44px;
        display: flex;
        align-items: center;
    }
}

/* M-155 — Admin moderation table overflows 320px viewport.
   Wrap table in horizontal-scroll container; stack action buttons vertically
   on narrow screens so they don't clip off-viewport. */
@media (max-width: 48rem) {
    .admin-moderation__table-wrapper {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }
    .admin-moderation__table td.actions {
        display: flex;
        flex-direction: column;
        gap: var(--space-0-5);
        align-items: flex-end;
    }
    .admin-moderation__table td.actions .btn + .btn {
        margin-inline-start: 0;
    }
}

/* ════════════════════════════════════════════════════════════════════════
   End — Mobile Responsive Overhaul
   ════════════════════════════════════════════════════════════════════════ */


/* =========================================================================
   feat/i18n - localization styles
   -------------------------------------------------------------------------
   Three concerns folded into one section:
     1. Korean font fallback. Inter has no Hangul glyphs so we explicitly
        list Pretendard / Noto Sans KR / Apple SD Gothic Neo. Browsers walk
        the stack until they find a face that contains the requested glyph
        so Latin text still renders in Inter.
     2. Locale switcher component (Caploo.Client/Shared/LocaleSwitcher.razor).
        Accessible (radio group, visible focus, 44x44 touch targets, APCA
        Lc>=75 body / >=60 native-name).
     3. RTL prep. Logical inset/margin properties win wherever this section
        replaces left/right values - currently a small surface (just the
        switcher) because the existing app.css uses logical properties in
        most places already. Documented in docs/i18n/README.md.
   ========================================================================= */

/* Specificity bump: the existing `body { font-family: Inter, ... }` rule
   in app.css has equal specificity (0,1,0) to a bare `html[lang="ko"]`
   selector. Browsers' source-order tiebreaker meant `body { font-family }`
   won and Hangul rendered through Inter's missing-glyph fallback (which
   on Chrome/Windows lands on a serif system font - off-brand and also
   slightly heavier per character). Adding `html[lang="ko"] body` brings
   the selector to (0,2,0) so it beats body unconditionally; the
   :where() wrapper around descendants keeps the boost from leaking into
   any nested KO-language island a future component might mount. */
html[lang="ko"],
html[lang="ko"] body {
  font-family: 'Pretendard', 'Noto Sans KR', 'Apple SD Gothic Neo',
               'Malgun Gothic', Inter, -apple-system, BlinkMacSystemFont,
               'Segoe UI', Roboto, sans-serif;
  /* Hangul reads a touch tighter than Latin; bump line-height so descender
     stacks (vertical jamo with dotted finals) don't kiss the next line. */
  line-height: 1.6;
}

/* Future RTL hook - currently inert because SupportedLocales has no RTL
   entries; lives here so adding `ar` is a single SupportedLocales edit. */
html[dir="rtl"] .locale-switcher,
html[dir="rtl"] .switcher-option {
  text-align: end;
}

/* ---- locale-switcher: inline (settings) and compact (nav) variants ---- */
.locale-switcher {
  border: 0;
  padding: 0;
  margin-block: 1rem;
}
.locale-switcher legend {
  font-weight: normal;
  font-size: 0.95rem;
  margin-block-end: 0.5rem;
  color: var(--text);
}
.locale-switcher__hint {
  margin-block: 0.25rem 0.75rem;
  font-size: 0.9rem;
  /* APCA Lc 75 vs --surface #1a1a22; --muted measures Lc 76.4 -> passes. */
  color: var(--muted);
}
.locale-switcher__options {
  display: grid;
  gap: 0.5rem;
}
.locale-switcher.is-compact .locale-switcher__options {
  /* Nav popover variant: horizontal pill row, single line. */
  grid-auto-flow: column;
  grid-auto-columns: max-content;
}
.switcher-option {
  display: flex;
  flex-direction: row;   /* override global label { flex-direction: column } */
  align-items: center;
  gap: 0.75rem;
  /* 44x44 minimum touch target (WCAG 2.1 / 3.0). */
  min-height: 44px;
  padding-inline: 0.875rem;
  padding-block: 0.625rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  background: var(--surface);
  transition: background-color 120ms ease, border-color 120ms ease;
}
.switcher-option:hover {
  background: var(--surface-alt);
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
}
.switcher-option input[type="radio"] {
  /* Native radio is preserved but visually subdued; focus ring on the LABEL. */
  inline-size: 1rem;
  block-size: 1rem;
  accent-color: var(--accent, #7c6ef5);
  flex: 0 0 auto;
}
.switcher-option:focus-within {
  outline: 2px solid var(--accent, #7c6ef5);
  outline-offset: 2px;
}
.switcher-option.is-active {
  border-color: var(--accent, #7c6ef5);
  background: linear-gradient(180deg,
    rgba(124, 110, 245, 0.12),
    rgba(124, 110, 245, 0.06));
}
.switcher-option__native {
  font-weight: normal;
  /* APCA Lc 78.2 vs --surface -> passes body threshold. */
  color: var(--text);
}
.switcher-option__english {
  font-size: 0.8rem;
  /* APCA Lc 60.5 vs --surface -> passes small threshold; this label is
     secondary (English gloss for users who recognize the native name). */
  color: var(--muted);
}
.locale-switcher__status {
  margin-block-start: 0.5rem;
  font-size: 0.85rem;
  color: var(--muted);
}

@media (prefers-reduced-motion: reduce) {
  .switcher-option { transition: none; }
}

/* ════════════════════════════════════════════════════════════════════════
   Phase 16b — Drop-page controls + responsive layout
   Design source: docs/phase-16/drop-page-controls.md
   ------------------------------------------------------------------------
   Goal: every action control on the Drop page (navbar action band + feed
   toolbar) has consistent sizing, a clear primary/secondary/tertiary
   hierarchy, and is fully responsive desktop → 320px with nothing wrapped,
   clipped, or shifted off-screen.

   Per the Phase B2 mobile-responsive convention this block is appended at
   the file end and intentionally overrides the earlier Phase 6 / 6.4 / 7.4
   sizing rather than rewriting them in place — keeps the diff auditable.

   Button hierarchy / sizing scale (navbar action band):
     • Geometry — every control: 36px tall desktop / 44px coarse-pointer &
       ≤768px, --radius-sm corners, 14px type, 16px icons, 8px icon gap.
     • Tier 1 (primary CTA)  — Sign up.  Filled --accent, white text, 600.
     • Tier 2 (secondary)    — Sign in, Share, Report.  Transparent fill,
                               1px --border outline, --text, 500.
     • Tier 3 (tertiary)     — Settings cog, Help.  Borderless until hover,
                               --muted icon, square hit area.
   The feed toolbar keeps its own 40px baseline (search is the 44px focal
   point); only the named bugs are fixed there.
   ════════════════════════════════════════════════════════════════════════ */

:root {
    --ctl-h:        44px;   /* F-019/F-019b: all navbar controls 44px per WCAG 2.5.5 */
    --ctl-h-touch:  44px;   /* coarse-pointer / ≤768px primary touch target  */
    --ctl-pad-x:    14px;   /* inline padding for labelled controls          */
    --ctl-font:     14px;   /* unified action-band type size                 */
    --ctl-icon:     16px;   /* unified action-band icon size                 */
    --ctl-gap:      8px;    /* icon ↔ label gap                              */
}

/* ── 1. Unified geometry across the navbar action band ──────────────────── */
.navbar .btn-primary,
.navbar a.btn-primary,
.navbar .btn-ghost,
.navbar a.btn-ghost,
.navbar .share-trigger-btn,
.navbar .nav-settings,
.navbar .help-trigger {
    box-sizing: border-box;
    height: var(--ctl-h);
    min-height: var(--ctl-h);
    border-radius: var(--radius-sm);
    font-size: var(--ctl-font);
    font-family: inherit;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    white-space: nowrap;
    flex-shrink: 0;
    transition: background var(--transition), border-color var(--transition),
                color var(--transition);
}

/* Labelled controls share inline padding + icon gap. */
.navbar .btn-primary,
.navbar a.btn-primary,
.navbar .btn-ghost,
.navbar a.btn-ghost,
.navbar .share-trigger-btn,
.navbar .help-trigger {  /* F-148: help-trigger gets paired text label, so it's labeled not square */
    padding-inline: var(--ctl-pad-x);
    gap: var(--ctl-gap);
    line-height: 1;
    width: auto;
}

/* Icon-only tertiary controls are square. */
.navbar .nav-settings {  /* F-148: help-trigger removed from icon-only square rule */
    width: var(--ctl-h);
    padding: 0;
}

/* ── 2. Tier treatments (fill / weight only — geometry is shared) ────────── */
/* Tier 1 — primary CTA (Sign up). The earlier Phase 6 polish set the accent
   fill with !important, so the override must match. */
.navbar .btn-primary,
.navbar a.btn-primary {
    background: var(--accent) !important;
    color: var(--text-on-accent) !important;
    border: 1px solid var(--accent);
    font-weight: normal;
    /* Primary CTAs must meet 44px at every width per standing rule §4 / WCAG 2.5.5 */
    min-height: 44px;
}
.navbar .btn-primary:hover,
.navbar a.btn-primary:hover {
    background: var(--accent-dim) !important;
    border-color: var(--accent-dim);
    filter: none;
}

/* Tier 2 — secondary (Sign in, Share, Report). Outlined, no fill. */
.navbar a.btn-ghost,
.navbar .share-trigger-btn {
    background: transparent;
    color: var(--text);
    border: 1px solid var(--border);
    font-weight: 500;
}
.navbar a.btn-ghost:hover,
.navbar .share-trigger-btn:hover {
    background: var(--surface-alt);
    border-color: var(--accent-dim);
    color: var(--text);
}

/* Tier 3 — tertiary (Settings, Help). Subtle; border appears on hover. */
.navbar .nav-settings,
.navbar .help-trigger {
    background: transparent;
    color: var(--muted);
    border: 1px solid transparent;
    font-weight: normal;
}
.navbar .nav-settings:hover,
.navbar .nav-settings:focus-visible,
.navbar .help-trigger:hover,
.navbar .help-trigger:focus-visible {
    background: var(--surface-alt);
    border-color: var(--border);
    color: var(--text);
}
.navbar .nav-settings[aria-expanded="true"] {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--accent-glow);
}

/* Icon sizing parity across the band. */
.navbar .share-trigger-btn svg,
.navbar .btn-primary svg,
.navbar .btn-ghost svg { width: var(--ctl-icon); height: var(--ctl-icon); }
.navbar .nav-settings svg { width: 18px; height: 18px; }

/* Single consistent focus ring across the whole band. */
.navbar .btn-primary:focus-visible,
.navbar a.btn-primary:focus-visible,
.navbar .btn-ghost:focus-visible,
.navbar a.btn-ghost:focus-visible,
.navbar .share-trigger-btn:focus-visible,
.navbar .nav-settings:focus-visible,
.navbar .help-trigger:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    box-shadow: none;
}

/* ── 3. Navbar layout — actions can never be pushed off-screen ───────────
   Only the drop-info slot (user-supplied drop name) is elastic; it absorbs
   all overflow pressure by truncating to an ellipsis. The brand holds its
   intrinsic width and the action cluster is pinned to the inline-end edge,
   so Sign in / Sign up stay in-bounds at every width down to 320px. */
.navbar > .navbar-brand    { flex: 0 0 auto; }
.navbar > .navbar-drop-info { flex: 1 1 auto; min-width: 0; }
.navbar > .navbar-meta     { flex: 0 1 auto; min-width: 0; }
.navbar > .navbar-actions  { flex: 0 0 auto; min-width: auto; margin-inline-start: auto; }

/* ── 4. Coarse-pointer / ≤768px — 44px touch targets across the band ────── */
@media (pointer: coarse), (max-width: 48rem) {
    .navbar .btn-primary,
    .navbar a.btn-primary,
    .navbar .btn-ghost,
    .navbar a.btn-ghost,
    .navbar .share-trigger-btn,
    .navbar .nav-settings,
    .navbar .help-trigger {
        height: var(--ctl-h-touch);
        min-height: var(--ctl-h-touch);
    }
    .navbar .nav-settings { width: var(--ctl-h-touch); }  /* F-148: help-trigger is labeled, stays auto-width */
}

/* ── 5. Narrow viewports — keep Sign in + Sign up on-screen with labels ───
   ≤640px: Share / Report drop their text labels and become icon-only square
   buttons (the title + aria-label still carry the name), which frees the
   horizontal room needed to keep BOTH auth buttons visible and readable.
   This intentionally supersedes the Phase 7.4 rule that hid "Sign in"
   below 640px — the redesign keeps the affordance instead of removing it. */
@media (max-width: 40rem) {
    .navbar .share-trigger-btn span { display: none; }
    .navbar .share-trigger-btn {
        width: var(--ctl-h-touch);
        padding-inline: 0;
        gap: 0;
    }
    .navbar .navbar-auth a.btn-ghost { display: inline-flex; }
    .navbar-actions { gap: var(--space-1, 8px); }
}

/* ≤480px: drop the secondary navbar meta (countdown pill) — the drop's
   live/expiry status is still surfaced by the toolbar Live/Closed badge and
   in the page body — so the brand, title, Share, Help and the auth pair all
   stay on-screen on the smallest supported phones. */
@media (max-width: 30rem) {
    .navbar > .navbar-meta { display: none; }
}

/* ── 6. View-mode segmented control (Grid / List / Large) ────────────────
   Root cause of the "Large" label wrapping: the label span had no
   white-space guard and the segments had no shared width, so a slightly
   wider label could break onto a second line inside a short button.
   Fix: lock white-space, give every segment one shared min-width so the
   control reads as a true equal-segment toggle and the widest label can
   never wrap or clip. */
.caploo-view-toggle__btn { white-space: nowrap; min-width: 4.5rem; }
.caploo-view-toggle__label { white-space: nowrap; }
@media (max-width: 600px) {
    /* Labels are hidden here (existing rule); make each segment a square,
       touch-sized target instead of the cramped 36px. */
    .caploo-view-toggle { height: var(--ctl-h-touch); }
    .caploo-view-toggle__btn { min-width: var(--ctl-h-touch); padding-inline: var(--space-1); }
}
@media (pointer: coarse) and (min-width: 601px) {
    .caploo-view-toggle { height: var(--ctl-h-touch); }
}

/* ── 7. Sort By select — custom chevron with proper edge padding ─────────
   The native select chevron sat hard against the control edge. Suppress the
   native appearance, draw a chevron as a background image, and reserve
   enough inline-end padding (chevron + a symmetric gutter each side) so the
   glyph and the "Newest first" label both have breathing room. */
.caploo-toolbar__sort select {
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='14'%20height='14'%20viewBox='0%200%2024%2024'%20fill='none'%20stroke='%23b8b8d0'%20stroke-width='2.5'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M6%209l6%206%206-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.75rem center;
    background-size: 14px 14px;
    padding-inline-start: 0.75rem;
    padding-inline-end: 2.25rem;   /* 14px chevron + ~12px gutter each side */
}
@media (pointer: coarse), (max-width: 48rem) {
    .caploo-toolbar__sort select { height: var(--ctl-h-touch); }
}

/* ── 8. Navbar brand + drop title — legible, never clipped ───────────────
   The brand holds its width (it yields only as an ellipsis at the smallest
   widths via the existing ≤30rem rule); the drop name keeps its ellipsis +
   title attribute but gets a more generous cap than the earlier 40vw so it
   isn't cut prematurely on mid-size phones. */
.navbar-brand { flex-shrink: 0; }
.navbar-drop-name { max-width: min(60vw, 28rem); }
@media (max-width: 30rem) {
    .navbar-drop-name { max-width: 44vw; }
}

/* ── 9. prefers-reduced-motion — strip the band's state transitions ─────── */
@media (prefers-reduced-motion: reduce) {
    .navbar .btn-primary,
    .navbar a.btn-primary,
    .navbar .btn-ghost,
    .navbar a.btn-ghost,
    .navbar .share-trigger-btn,
    .navbar .nav-settings,
    .navbar .help-trigger { transition: none; }
}

/* ════════════════════════════════════════════════════════════════════════
   End — Phase 16b
   ════════════════════════════════════════════════════════════════════════ */

/* ===========================================================================
   Phase 17b - discoverable language selector
   ---------------------------------------------------------------------------
   A compact trigger in the navbar action band (sibling of the help trigger)
   that always shows the active language and opens a popover reusing the B3
   LocaleSwitcher. Geometry tracks the --ctl-* action-band tokens. No flag
   iconography - a neutral globe glyph + native language name (flags denote
   countries, not languages; rationale in docs/i18n/README.md).
   =========================================================================== */
.lang-menu-wrap {
    position: relative;
    display: inline-flex;
}
.navbar .lang-trigger {
    box-sizing: border-box;
    height: var(--ctl-h);
    min-height: var(--ctl-h);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding-inline: 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text);
    font-family: inherit;
    font-size: var(--ctl-font);
    line-height: 1;
    cursor: pointer;
    white-space: nowrap;
    flex-shrink: 0;
    transition: background var(--transition), border-color var(--transition);
}
.navbar .lang-trigger:hover {
    background: var(--surface-alt);
    border-color: var(--accent);
}
.navbar .lang-trigger:focus-visible {
    box-shadow: var(--focus-ring);
    outline: none;
}
.navbar .lang-trigger[aria-expanded="true"] {
    border-color: var(--accent);
    background: var(--surface-alt);
}
.lang-trigger__icon { flex: 0 0 auto; color: var(--muted); }
.lang-trigger__name {
    max-inline-size: 9ch;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Coarse-pointer / <=768px: full 44x44 touch target. */
@media (pointer: coarse), (max-width: 48rem) {
    .navbar .lang-trigger {
        height: var(--ctl-h-touch);
        min-height: var(--ctl-h-touch);
    }
}
/* Very narrow (<=22.5rem ~ 360px): drop text labels from tools cluster so
   the navbar fits on 360px devices without overflow. The globe icon + aria-
   label carry full semantics for AT; the Help "?" glyph + aria-label do the
   same for the Help trigger. */
@media (max-width: 22.5rem) {
    .lang-trigger__name { display: none; }
    .help-trigger__label { display: none; }
    .navbar .lang-trigger {
        padding-inline: 0;
        width: var(--ctl-h-touch);
        justify-content: center;
    }
}
/* Outside-click dismissal layer - transparent, full-viewport, below popover. */
.lang-menu-backdrop {
    position: fixed;
    inset: 0;
    z-index: 80;
    background: transparent;
}
/* Popover dialog: anchored under the trigger, inline-end aligned, never
   clipped at 320px (max-inline-size is viewport-relative). */
.lang-menu {
    position: absolute;
    inset-block-start: calc(100% + 6px);
    inset-inline-end: 0;
    z-index: 81;
    inline-size: max-content;
    min-inline-size: 200px;
    max-inline-size: min(280px, calc(100vw - 1.5rem));
    padding: 0.5rem 0.75rem 0.75rem;
    background: var(--surface-alt);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border));
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
}
@media (prefers-reduced-motion: no-preference) {
    .lang-menu { animation: fadeIn .12s ease forwards; }
}
/* Narrow viewports: anchoring the popover to the trigger's inline-end can
   push its leading edge off-screen once the trigger sits hard against the
   viewport edge. Pin it to the viewport instead - fixed, inline-end aligned,
   width-clamped - so it can never clip at 320px. The backdrop already covers
   the viewport, so fixed positioning keeps the dismissal model intact. */
@media (max-width: 30rem) {
    .lang-menu {
        position: fixed;
        inset-block-start: calc(var(--navbar-h, 52px) + 4px);
        inset-inline: auto 0.75rem;
        min-inline-size: 0;
        max-inline-size: calc(100vw - 1.5rem);
    }
}
/* The popover hosts the B3 LocaleSwitcher; strip its standalone outer margin
   so it sits flush within the popover padding. */
.lang-menu .locale-switcher { margin-block: 0.25rem 0; }
.lang-menu__settings {
    display: block;
    margin-block-start: 0.5rem;
    padding-block-start: 0.5rem;
    border-block-start: 1px solid var(--border);
    font-size: 0.85rem;
    color: var(--muted);
}
.lang-menu__settings:hover { color: var(--text); }
/* Active-option check glyph - Phase 17b extension to .switcher-option. The
   radio input carries the semantic state; this is the matching at-a-glance
   visual affordance. The native/english labels now stack in a column so the
   check can sit at the inline-end of the row. */
.switcher-option__labels {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-inline-size: 0;
}
.switcher-option__check {
    margin-inline-start: auto;
    color: var(--accent, #7c6ef5);
    font-weight: 700;
    font-size: var(--text-md);
    flex: 0 0 auto;
}
/* End - Phase 17b */

/* ── NavTray (Phase 2C desktop tools consolidation) ─────────────── */
.nav-tray-wrap { position: relative; }

.nav-tray-trigger {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-soft);
    background: transparent;
    color: var(--muted);
    cursor: pointer;
    transition: background var(--duration-fast), color var(--duration-fast), border-color var(--duration-fast);
    flex-shrink: 0;
}
.nav-tray-trigger:hover { background: var(--surface-alt); color: var(--text); border-color: var(--border); }
.nav-tray-trigger:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.nav-tray-trigger[aria-expanded="true"] { background: var(--surface-alt); border-color: var(--accent); color: var(--text); }

.nav-tray {
    position: absolute;
    inset-block-start: calc(100% + var(--space-1));
    inset-inline-end: 0;
    min-width: 260px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
    padding: 0;
    z-index: 150;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* v3: Settings heading + explicit close X */
.nav-tray__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-2) var(--space-1-5) var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--border-soft);
}
.nav-tray__title {
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--text);
    margin: 0;
    letter-spacing: 0.01em;
}
.nav-tray__close {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    min-width: 44px;
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--muted);
    cursor: pointer;
    transition: background var(--duration-fast), color var(--duration-fast);
}
.nav-tray__close:hover { background: var(--surface-alt); color: var(--text); }
.nav-tray__close:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* v3: Section groups */
.nav-tray__section { padding: 0; }
.nav-tray__section-heading {
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
    margin: 0;
    padding: var(--space-2) var(--space-3) var(--space-0-5);
}
.nav-tray__divider {
    border: none;
    border-top: 1px solid color-mix(in srgb, var(--border) 80%, transparent);
    margin: 0;
}

/* v3: Help link row (grid: icon | label | chevron) */
.nav-tray__row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: var(--space-3);
    align-items: center;
    padding: var(--space-3) var(--space-4);
    min-height: 56px;
    background: transparent;
    border: 0;
    text-align: left;
    cursor: pointer;
    color: var(--text);
    text-decoration: none;
    font-size: var(--text-sm);
    width: 100%;
    box-sizing: border-box;
}
.nav-tray__row:hover { background: var(--surface-alt); }
.nav-tray__row:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.nav-tray__row-icon { color: var(--muted); }
.nav-tray__row-label { font-weight: 500; }
.nav-tray__row-chevron { color: var(--muted); }

/* v3: Override LanguageMenu + ThemeMenu triggers to match row style */
.nav-tray .lang-menu-wrap,
.nav-tray .theme-menu-wrap { width: 100%; }

.nav-tray .lang-trigger {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    padding: var(--space-3) var(--space-4);
    min-height: 56px;
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    text-align: left;
    cursor: pointer;
    color: var(--text);
    font-size: var(--text-sm);
}
.nav-tray .lang-trigger:hover { background: var(--surface-alt); border-color: transparent; color: var(--text); }
.nav-tray .lang-trigger:focus-visible { box-shadow: var(--focus-ring); outline: none; border-color: transparent; }
.nav-tray .lang-trigger[aria-expanded="true"] { background: var(--surface-alt); border-color: transparent; }
.nav-tray .lang-trigger__name { font-weight: 500; flex: 1; text-align: left; }
.nav-tray .lang-trigger__chip { display: none; }
.nav-tray .lang-trigger__icon { color: var(--muted); }
.nav-tray .lang-trigger__chevron { color: var(--muted); }

.nav-tray .theme-trigger {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    padding: var(--space-3) var(--space-4);
    min-height: 56px;
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    text-align: left;
    cursor: pointer;
    color: var(--text);
    font-size: var(--text-sm);
}
.nav-tray .theme-trigger:hover { background: var(--surface-alt); }
.nav-tray .theme-trigger:focus-visible { box-shadow: var(--focus-ring); outline: none; }
.nav-tray .theme-trigger__name { font-weight: 500; flex: 1; }

/* NavTray is desktop-only — hidden at ≤480px where NavOverflowSheet handles tools. */
@media (max-width: 30rem) {
    .nav-tray-wrap { display: none; }
}
/* End - Phase 2C/v3 NavTray */

/* MD-072 — single-line native · English at ≤30rem so 13px sub-label
   doesn't consume its own line on a 320px screen. The separator is
   presentational only; screen readers already see two separate spans. */
@media (max-width: 30rem) {
    .switcher-option__labels {
        flex-direction: row;
        align-items: baseline;
        gap: 0;
    }
    .switcher-option__english::before {
        content: " · ";
        color: var(--muted);
    }
    .switcher-option__english {
        font-size: 0.75rem;
    }
}

/* ════════════════════════════════════════════════════════════════════════
   2026-05-17 — Locale rationalization: region-grouped switcher.
   The switcher now wraps each region (AmericasEurope, Asia) in a
   role="group" with a visible heading. Heading is small caps, ambient
   color, sits above the first option in the group with subtle vertical
   separation between groups. Single active-state indicator (radio dot
   + tinted .is-active row) — checkmark glyph removed per the IA
   decision in docs/i18n/locale-strategy.md §3.
   ════════════════════════════════════════════════════════════════════════ */
.locale-switcher__region {
    border: 0;
    padding: 0;
}
.locale-switcher__region + .locale-switcher__region {
    margin-block-start: 0.875rem;
    padding-block-start: 0.75rem;
    border-block-start: 1px solid var(--border);
}
.locale-switcher__region-heading {
    margin: 0 0 0.375rem 0;
    padding-inline-start: 0.25rem;
    font-size: 0.72rem;
    font-weight: normal;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    /* APCA Lc 60+ vs --surface — passes small-text threshold; muted
       compared to the option labels so the group heading reads as
       structural, not as a selectable item. */
    color: var(--muted);
}
/* End — Locale rationalization 2026-05-17 */


/* =============================================================
   Phase 19c — Join-a-Drop + host-access UX polish
   -------------------------------------------------------------
   • #join scroll-margin so the smooth-scroll target clears the
     fixed top nav after the host-link jump.
   • .host-link__jump rendered as a discoverable button chip
     with an up-arrow glyph — replaces a bare hyperlink that
     gave no feedback under either pointer or AT.
   • .host-mgmt-disclosure tucks the host-management URL behind
     a discreet <details> in the settings panel; the URL is the
     equivalent of a write password, so leaving it on-screen by
     default is a security-UX smell. The Stage C remediation
     reintroduced the one-time reveal step at create-time; this
     phase makes it match in the host settings.
   ============================================================= */
.join-card {
    /* Smooth-scroll landing target. 84px clears the fixed top nav
       (height 64 + 20 visual breathing room). Falls back gracefully
       in browsers without scroll-margin-top — they just don't get
       the breathing room. */
    scroll-margin-top: 84px;
}

.host-link__prefix {
    color: var(--muted, #6b7280);
    margin-inline-end: 0.4rem;
}

.host-link__jump {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.45rem 0.85rem;
    min-height: 44px;                  /* F-20a-11: ≥44px tap target */
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--accent, #7c6ef5) 10%, transparent);
    color: var(--accent, #7c6ef5);
    font-weight: normal;
    text-decoration: none;
    transition: background-color 120ms ease, transform 120ms ease;
}
.host-link__jump:hover {
    background: color-mix(in srgb, var(--accent, #7c6ef5) 18%, transparent);
    text-decoration: none;
}
.host-link__jump:active {
    transform: translateY(1px);
}
.host-link__jump:focus-visible {
    outline: 2px solid var(--accent, #7c6ef5);
    outline-offset: 2px;
}
.host-link__arrow {
    display: inline-block;
    transition: transform 160ms ease;
}
.host-link__jump:hover .host-link__arrow {
    transform: translateY(-2px);
}
@media (prefers-reduced-motion: reduce) {
    .host-link__jump,
    .host-link__arrow {
        transition: none;
    }
    .host-link__jump:hover .host-link__arrow {
        transform: none;
    }
}

/* ── Duration / extend stepper (F-20a-3) ───────────────────────────────────
   Replaces native <input type="number"> spinner with ± buttons so touch
   users get ≥44×44 tap targets on each side of the value field. */
.duration-stepper {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    margin-top: .25rem;
}
.duration-stepper__label {
    font-size: var(--text-sm);
    color: var(--muted);
}
.duration-stepper__row {
    display: flex;
    align-items: center;
    gap: .25rem;
}
.duration-stepper__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    border-radius: var(--radius-md);
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    font-size: var(--text-lg);
    font-weight: normal;
    cursor: pointer;
    transition: background-color 120ms ease;
}
.duration-stepper__btn:hover:not(:disabled) {
    background: color-mix(in srgb, var(--accent, #7c6ef5) 10%, var(--surface, #fff));
}
.duration-stepper__btn:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}
.duration-stepper__btn:disabled {
    opacity: .4;
    cursor: not-allowed;
}
.duration-stepper__input {
    width: 6rem;
    min-height: 44px;
    padding: .4rem .6rem;
    font-size: var(--text-md);
    text-align: center;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
}
.duration-stepper__input::-webkit-inner-spin-button,
.duration-stepper__input::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
.duration-stepper__input[type=number] { -moz-appearance: textfield; }
@media (prefers-reduced-motion: reduce) {
    .duration-stepper__btn { transition: none; }
}

/* -------------------------------------------------------------
   Host-management disclosure — discreet by default, reachable
   on demand. The Stage C remediation reintroduced the host-
   secret reveal step at create time (one-time modal). On the
   live host's settings panel the URL was still rendered
   verbatim under a warning label, which is the same secret
   sitting in the open. Wrap it in a <details>, mute the
   summary, and surface a lock icon so the host understands
   what's behind the disclosure. Keyboard + screen reader
   reachable via the native <summary> element.
   ------------------------------------------------------------- */
/* R-B-2': disclosure now lives inside Access Methods section — no standalone border */
.host-mgmt-disclosure {
    padding-top: 0;
}
.host-mgmt-disclosure[open] {
    padding-bottom: 0.25rem;
}
.host-mgmt-disclosure > summary {
    list-style: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.35rem 0.55rem;
    margin: 0 0 0 -0.55rem;            /* visually flush with section label */
    border-radius: var(--radius-sm);
    color: var(--muted, #6b7280);
    font-size: 0.8rem;
    font-weight: 500;
    user-select: none;
}
.host-mgmt-disclosure > summary::-webkit-details-marker {
    display: none;
}
.host-mgmt-disclosure > summary:hover {
    background: color-mix(in srgb, var(--muted, #6b7280) 8%, transparent);
    color: var(--text, #111827);
}
.host-mgmt-disclosure > summary:focus-visible {
    outline: 2px solid var(--accent, #7c6ef5);
    outline-offset: 2px;
}
.host-mgmt-disclosure__icon {
    flex: 0 0 auto;
}
.host-mgmt-disclosure__chevron {
    flex: 0 0 auto;
    transition: transform 150ms ease;
}
.host-mgmt-disclosure[open] > summary .host-mgmt-disclosure__chevron {
    transform: rotate(90deg);
}
@media (prefers-reduced-motion: reduce) {
    .host-mgmt-disclosure__chevron {
        transition: none;
    }
}
.host-mgmt-disclosure__body {
    padding-top: 0.5rem;
}
/* End — Phase 19c */

/* =============================================================
   Phase 19e — Component Consistency
   -------------------------------------------------------------
   1. Radius token scale (--radius-xs/sm/md/lg/pill) — defined in :root.
   2. ViewModeToggle segmented control redesign — see .caploo-view-toggle.
   3. Global dropdown overhaul — shared pattern documented below.

   Shared dropdown visual contract (apply .caploo-dropdown-panel to any
   floating listbox/popover panel, or match its values in specific classes):
     bg:     var(--surface-alt)  → text Lc≈85 (body ≥75 ✓), muted Lc≈63 (small ≥60 ✓)
     border: color-mix(in srgb, var(--accent) 35%, var(--border))
     radius: var(--radius-sm)
     shadow: var(--shadow)
   Applied to: composer tag/location dropdown, filter popover, lang-menu.
   Sort <select> uses --surface-alt bg + inherits color-scheme from
   :root[data-theme] (Ticket 02).
   ============================================================= */

/* Shared dropdown panel utility — add alongside specific layout classes. */
.caploo-dropdown-panel {
    background: var(--surface-alt);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border));
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow);
}

/* Active filter chips — tight badge-like rounding */
.caploo-active-chip {
    border-radius: var(--radius-xs);
}

/* ── Dropdown hover/focus/selected state standardization ─────────────────
   All listbox options (tag, location, filter) share this pattern:
   idle  → transparent bg, --text color
   hover → accent bg, white text
   active/selected → accent bg, white text
   These are already correct in .caploo-composer-ai__option. Native selects
   inherit color-scheme from :root[data-theme] (Ticket 02). */

/* Ensure filter popover inputs / selects are readable on --surface-alt bg. */
.caploo-filter-popover input,
.caploo-filter-popover select,
.caploo-filter-popover textarea {
    background: var(--bg);
    border-color: var(--border);
}

/* prefers-reduced-motion for new Phase 19e transitions */
@media (prefers-reduced-motion: reduce) {
    .caploo-view-toggle__btn,
    .caploo-dropdown-panel,
    .caploo-composer-ai__dropdown {
        transition: none;
        animation: none;
    }
}
/* End — Phase 19e */

/* ── Phase 19f — DateRangePicker (drp__*) + scope overflow fix ─────────── */

/* ── Scope overflow fix: at narrow widths the ES "Descripciones + comentarios"
   option overflows the inline-flex group. Below 480 px make the group fill its
   row and split the space equally so long labels wrap gracefully. ────────── */
@media (max-width: 480px) {
    .caploo-filter-scope__group {
        width: 100%;
    }
    .caploo-filter-scope__option {
        flex: 1;
        white-space: normal;
        text-align: center;
        min-width: 0;
        padding-inline: .5rem;
    }
}

/* ── Preset strip ────────────────────────────────────────────────────────── */
.drp {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.drp__presets {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
}

/* R-D-1: compact chips — explicit flex: 0 0 auto so chips size to content, not row width */
.drp__preset {
    flex: 0 0 auto;
    width: auto;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    padding: .35rem .75rem;
    font-size: var(--text-xs);
    font-weight: 500;
    cursor: pointer;
    min-height: 32px;
    white-space: nowrap;
    transition: background var(--duration-base) var(--easing-default),
                border-color var(--duration-base) var(--easing-default),
                color var(--duration-base) var(--easing-default);
}
.drp__preset:hover:not(.drp__preset--active) {
    border-color: var(--accent-dim);
    background: var(--surface-alt);
}
.drp__preset:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}
.drp__preset--active {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-on-accent);
    font-weight: normal;
}
@media (prefers-reduced-motion: reduce) {
    .drp__preset { transition: none; }
}
@media (pointer: coarse) {
    .drp__preset { min-height: 44px; }
}

/* ── Inline calendar ─────────────────────────────────────────────────────── */
.drp__calendar {
    inline-size: min(100%, 22rem); /* F-204: cap at 22rem so grid cells don't stretch to popover edge */
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: .625rem;
    animation: fadeSlideUp var(--duration-slow) var(--easing-emphasis) both;
}
@media (prefers-reduced-motion: reduce) {
    .drp__calendar { animation: none; }
}

/* Month navigation row */
.drp__cal-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: .5rem;
    gap: var(--space-1);
}
.drp__cal-nav-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius-xs);
    color: var(--muted);
    cursor: pointer;
    padding: 0;
    flex-shrink: 0;
    transition: background var(--duration-base), color var(--duration-base),
                border-color var(--duration-base);
}
.drp__cal-nav-btn:hover {
    background: var(--surface);
    color: var(--text);
    border-color: var(--accent-dim);
}
.drp__cal-nav-btn:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
    .drp__cal-nav-btn { transition: none; }
}

.drp__cal-month {
    flex: 1;
    text-align: center;
    font-size: var(--text-sm);
    font-weight: normal;
    color: var(--text);
}

/* Grid */
.drp__cal-grid {
    display: flex;
    flex-direction: column;
    gap: 1px;
}
.drp__cal-row {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
}

/* Weekday header */
.drp__cal-header .drp__cal-weekday {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: .2rem 0 .4rem;
    font-size: var(--text-micro);
    font-weight: normal;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .04em;
}

/* Day cells — carry the in-range band as a full-width background */
.drp__cal-cell {
    display: flex;
    align-items: center;
    justify-content: center;
}
/* Band: full-width tint for days strictly inside the range */
.drp__cal-cell:has(.drp__cal-day--in-range) {
    background: color-mix(in srgb, var(--accent) 14%, transparent);
}
/* Clip the band at the start edge (left half transparent) */
.drp__cal-cell:has(.drp__cal-day--start):not(:has(.drp__cal-day--end)) {
    background: linear-gradient(
        to right,
        transparent 50%,
        color-mix(in srgb, var(--accent) 14%, transparent) 50%
    );
}
/* Clip the band at the end edge (right half transparent) */
.drp__cal-cell:has(.drp__cal-day--end):not(:has(.drp__cal-day--start)) {
    background: linear-gradient(
        to left,
        transparent 50%,
        color-mix(in srgb, var(--accent) 14%, transparent) 50%
    );
}

/* Day button — base */
.drp__cal-day {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    background: transparent;
    border: none;
    border-radius: var(--radius-xs);
    font-size: var(--text-xs);
    font-weight: 500;
    color: var(--text);
    cursor: pointer;
    z-index: 1;
    transition: background var(--duration-fast), color var(--duration-fast);
}
.drp__cal-day:hover:not(.drp__cal-day--start):not(.drp__cal-day--end):not(.drp__cal-day--empty) {
    background: var(--surface-alt);
}
.drp__cal-day:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 1px;
    z-index: 2;
}
@media (prefers-reduced-motion: reduce) {
    .drp__cal-day { transition: none; }
}

/* Today indicator: small accent dot below the day number */
.drp__cal-day--today::after {
    content: '';
    position: absolute;
    bottom: 3px;
    left: 50%;
    transform: translateX(-50%);
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: var(--accent);
}
.drp__cal-day--today.drp__cal-day--start::after,
.drp__cal-day--today.drp__cal-day--end::after {
    background: rgba(255, 255, 255, .7);
}

/* Selected endpoints — Lc ≥72 (white on --accent-dim) */
.drp__cal-day--start,
.drp__cal-day--end {
    background: var(--accent-dim);
    color: var(--text-on-accent);
    font-weight: 700;
    border-radius: var(--radius-xs);
}
.drp__cal-day--start:hover,
.drp__cal-day--end:hover {
    background: var(--accent);
}

/* In-range (middle) days render transparent over the cell band */
.drp__cal-day--in-range {
    background: transparent;
    border-radius: 0;
}

/* Empty filler cells (outside current month) */
.drp__cal-day--empty {
    width: 32px;
    height: 32px;
    cursor: default;
}

/* Coarse-pointer (touch): expand day cells to ≥36 px */
@media (pointer: coarse) {
    .drp__cal-day,
    .drp__cal-day--empty {
        width: 100%;
        height: auto;
        min-height: 36px;
        aspect-ratio: 1;
    }
}

/* Selection hint text */
.drp__cal-hint {
    font-size: var(--text-xs);
    color: var(--muted);
    text-align: center;
    margin-top: .5rem;
    min-height: 1.4em;
}
/* End — Phase 19f */

/* =============================================================================
   Ticket 15 — /admin/analytics dashboard
   Mirrors the BEM shape used by .admin-moderation__* and leans on the
   existing Phase 19e tokens (--radius-md, --surface-alt, --shadow, --text-*).
   Minimal — the page is admin-only; readability beats polish.
   ============================================================================= */

.admin-analytics {
    max-width: 1100px;
    margin: 0 auto;
    padding: 1.5rem 1rem;
}

.admin-analytics__header {
    margin-bottom: 1rem;
}

.admin-analytics__title {
    font-size: var(--text-xl, 1.5rem);
    margin: 0;
}

.admin-analytics__subtitle {
    margin: 0.25rem 0 0;
    color: var(--muted, #999);
    font-size: var(--text-sm, 0.875rem);
}

.admin-analytics__loading,
.admin-analytics__empty {
    padding: 2rem;
    text-align: center;
    color: var(--muted, #999);
}

.admin-analytics__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 1rem;
    margin-top: 1rem;
}

.admin-analytics__card {
    padding: 1rem;
    background: var(--surface-alt, #22222e);
    border-radius: var(--radius-md, 12px);
    box-shadow: var(--shadow-sm, 0 2px 8px rgba(0,0,0,.3));
}

.admin-analytics__total {
    font-size: var(--text-2xl, 1.75rem);
    font-weight: normal;
    margin: 0.25rem 0;
}

.admin-analytics__total-label {
    font-size: var(--text-xs, 0.75rem);
    font-weight: normal;
    color: var(--muted, #999);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.admin-analytics__sparkline {
    width: 100%;
    height: 48px;
    color: var(--accent, #ff6b35);
    margin-top: 0.5rem;
}

.admin-analytics__retention {
    margin-top: 2rem;
    background: var(--surface-alt, #22222e);
    border-radius: var(--radius-md, 12px);
    padding: 1rem;
    box-shadow: var(--shadow-sm, 0 2px 8px rgba(0,0,0,.3));
}

.admin-analytics__retention table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm, 0.875rem);
}

.admin-analytics__retention th,
.admin-analytics__retention td {
    padding: 0.5rem 0.75rem;
    text-align: start;
    border-bottom: 1px solid rgba(255,255,255,0.08);
}

.admin-analytics__retention th {
    font-weight: normal;
    color: var(--muted, #999);
    text-transform: uppercase;
    font-size: var(--text-xs, 0.75rem);
    letter-spacing: 0.04em;
}
/* End — Ticket 15 */

/* === Ticket 17 — Public status page in-app banner ============================
   StatusBanner.razor renders here when the BetterStack-compatible status.json
   reports a non-operational indicator. Sits at the top of <main>, below the
   navbar. Three severity variants share the layout and trade only on color:
     • --degraded   = amber, role="status"      (vendor "minor"/"maintenance")
     • --partial    = orange, role="status"     (vendor "major")
     • --major      = red, role="alert"         (vendor "critical")
   Dismiss is per-session (in-memory only).
   ========================================================================== */
.status-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-3);
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--border);
    font-size: var(--text-sm);
    color: var(--text);
    /* Subtle slide-in; suppressed under prefers-reduced-motion below. */
    animation: status-banner-slide 240ms ease-out;
}
.status-banner--degraded {
    background: #fff7d6;
    border-bottom-color: #d9a900;
    color: #5a3d00;
}
.status-banner--partial {
    background: #ffe6cc;
    border-bottom-color: #cc6a00;
    color: #5a2b00;
}
.status-banner--major {
    background: #ffd9d9;
    border-bottom-color: #b00020;
    color: #5a0010;
}
.status-banner__body {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    flex: 1 1 auto;
    min-width: 0;
}
.status-banner__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    background: currentColor;
    color: #fff;
    font-weight: 700;
    flex: 0 0 auto;
}
.status-banner--degraded .status-banner__icon { color: #d9a900; }
.status-banner--partial .status-banner__icon  { color: #cc6a00; }
.status-banner--major .status-banner__icon    { color: #b00020; }
.status-banner__message {
    flex: 1 1 auto;
    min-width: 0;
    line-height: 1.4;
    overflow-wrap: anywhere;
}
.status-banner__link {
    color: inherit;
    text-decoration: underline;
    font-weight: normal;
    flex: 0 0 auto;
}
.status-banner__link:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
    border-radius: var(--radius-sm, 4px);
}
.status-banner__dismiss {
    min-width: 44px;
    min-height: 44px;
    border: 1px solid currentColor;
    border-radius: var(--radius-sm, 4px);
    background: transparent;
    color: inherit;
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
    flex: 0 0 auto;
}
.status-banner__dismiss:hover {
    background: color-mix(in srgb, currentColor 10%, transparent);
}
.status-banner__dismiss:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}
@media (max-width: 480px) {
    .status-banner { flex-direction: column; align-items: stretch; }
    .status-banner__dismiss { align-self: flex-end; }
}
@media (prefers-reduced-motion: reduce) {
    .status-banner { animation: none; }
}
@media (forced-colors: active) {
    .status-banner {
        background: Canvas;
        color: CanvasText;
        border-bottom: 2px solid CanvasText;
    }
    .status-banner__icon { background: CanvasText; color: Canvas; }
    .status-banner__dismiss {
        background: ButtonFace;
        color: ButtonText;
        border: 1px solid ButtonText;
    }
}
@keyframes status-banner-slide {
    from { transform: translateY(-100%); opacity: 0; }
    to   { transform: translateY(0); opacity: 1; }
}
/* End — Ticket 17 */

/* =============================================================
   Ticket 02 — ThemeMenu (tri-state theme affordance in navbar)
   -------------------------------------------------------------
   Sits next to LanguageMenu in nav-cluster--tools. Geometry mirrors
   the lang-trigger so the action band reads as a clean row at any
   width. The inline variant (Settings page) drops the trigger +
   backdrop and just renders the radiogroup as a card.
   ============================================================= */
.theme-menu-wrap {
    position: relative;
    display: inline-flex;
}
.navbar .theme-trigger {
    box-sizing: border-box;
    height: var(--ctl-h);
    min-height: var(--ctl-h);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding-inline: 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text);
    font-family: inherit;
    font-size: var(--ctl-font);
    line-height: 1;
    cursor: pointer;
    white-space: nowrap;
    flex-shrink: 0;
    transition: background var(--transition), border-color var(--transition);
}
.navbar .theme-trigger:hover {
    background: var(--surface-alt);
    border-color: var(--accent);
}
.navbar .theme-trigger:focus-visible {
    box-shadow: var(--focus-ring);
    outline: none;
}
.navbar .theme-trigger[aria-expanded="true"] {
    border-color: var(--accent);
    background: var(--surface-alt);
}
.theme-glyph { flex: 0 0 auto; color: var(--muted); }
.navbar .theme-trigger:hover .theme-glyph,
.navbar .theme-trigger[aria-expanded="true"] .theme-glyph { color: var(--text); }
.theme-trigger__name {
    max-inline-size: 8ch;
    overflow: hidden;
    text-overflow: ellipsis;
}
@media (pointer: coarse), (max-width: 48rem) {
    .navbar .theme-trigger {
        height: var(--ctl-h-touch);
        min-height: var(--ctl-h-touch);
    }
}
@media (max-width: 22rem) {
    .theme-trigger__name { display: none; }
    .navbar .theme-trigger {
        padding-inline: 0;
        width: var(--ctl-h-touch);
        justify-content: center;
    }
}
.theme-menu-backdrop {
    position: fixed;
    inset: 0;
    z-index: 80;
    background: transparent;
}
.theme-menu {
    position: absolute;
    inset-block-start: calc(100% + 6px);
    inset-inline-end: 0;
    z-index: 81;
    inline-size: max-content;
    min-inline-size: 130px;
    max-inline-size: min(220px, calc(100vw - 1.5rem));
    padding: .5rem .75rem .75rem;
    background: var(--surface-alt);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border));
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
}
@media (prefers-reduced-motion: no-preference) {
    .theme-menu { animation: fadeIn .12s ease forwards; }
}
@media (max-width: 30rem) {
    .theme-menu {
        position: fixed;
        inset-block-start: calc(var(--navbar-h, 52px) + 4px);
        inset-inline: auto .75rem;
        min-inline-size: 0;
        max-inline-size: calc(100vw - 1.5rem);
    }
}
/* Inline variant for the settings page card — no popover chrome. */
.theme-menu.is-inline {
    position: static;
    inline-size: 100%;
    max-inline-size: none;
    background: transparent;
    border: 0;
    box-shadow: none;
    padding: 0;
}
.theme-menu__group {
    border: 0;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: .25rem;
}
.theme-menu__option {
    display: flex;
    flex-direction: row;   /* override global label { flex-direction: column } */
    align-items: center;
    gap: .65rem;
    padding: .55rem .65rem;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--text);
    min-height: 44px;
    transition: background var(--transition);
    position: relative;
}
.theme-menu__option:hover { background: var(--surface); }
.theme-menu__option input[type="radio"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
    width: 1px;
    height: 1px;
    margin: 0;
}
.theme-menu__option input[type="radio"]:focus-visible + .theme-menu__option-glyph {
    box-shadow: var(--focus-ring);
    border-radius: var(--radius-xs);
}
.theme-menu__option.is-active {
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    border-inline-start: 2px solid var(--accent);
    padding-inline-start: calc(.65rem - 2px);
}
.theme-menu__option.is-active .theme-glyph { color: var(--accent); }
.theme-menu__option-glyph {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
}
.theme-menu__option-labels {
    display: flex;
    align-items: center;
    gap: .35rem;
    flex: 1 1 auto;
    min-inline-size: 0;
}
.theme-menu__option-name {
    color: var(--text);
    font-size: var(--text-sm);
    font-weight: 500;
}
.theme-menu__check {
    color: var(--accent);
    font-weight: 700;
    font-size: var(--text-sm);
    flex-shrink: 0;
    margin-inline-start: auto; /* push checkmark to row-end — selection indicator UX pattern */
}
.theme-menu__hint {
    margin: .5rem 0 0;
    color: var(--muted);
    font-size: var(--text-xs);
    line-height: 1.4;
}
/* Screen-reader-only utility — local fallback if not already defined globally. */
.theme-menu .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}
/* End — Ticket 02 */


/* =========================================================
   Ticket 18 — Live Component Playbook (/playbook)
   ========================================================= */
.playbook-shell {
    display: grid;
    grid-template-columns: 260px 1fr;
    grid-template-rows: 1fr;
    height: 100vh;
    height: 100dvh;
    overflow: hidden;
    background: var(--bg);
    color: var(--text);
}
.playbook-nav {
    grid-column: 1;
    display: flex;
    flex-direction: column;
    border-inline-end: 1px solid var(--border, #2a2a38);
    overflow: hidden;
}
.playbook-nav__search {
    padding: .75rem;
    border-bottom: 1px solid var(--border, #2a2a38);
    flex-shrink: 0;
}
.playbook-nav__search input {
    width: 100%;
    padding: .4rem .6rem;
    background: var(--surface, #1a1a22);
    border: 1px solid var(--border, #2a2a38);
    border-radius: 6px;
    color: var(--text);
    font-size: .875rem;
}
.playbook-nav__list {
    flex: 1;
    overflow-y: auto;
    list-style: none;
    margin: 0;
    padding: .25rem 0;
}
.playbook-nav__item {
    width: 100%;
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: .45rem .75rem;
    background: none;
    border: none;
    text-align: start;
    color: var(--muted);
    font-size: .875rem;
    cursor: pointer;
    border-radius: 0;
}
.playbook-nav__item:hover { background: var(--surface, #1a1a22); color: var(--text); }
.playbook-nav__item.is-active { background: color-mix(in srgb, var(--accent) 15%, transparent); color: var(--accent); font-weight: normal; }
.playbook-nav__item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.playbook-nav__count {
    padding: .5rem .75rem;
    font-size: .75rem;
    color: var(--muted);
    border-top: 1px solid var(--border, #2a2a38);
    margin: 0;
    flex-shrink: 0;
}
.playbook-main {
    grid-column: 2;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    padding: 1.5rem;
    gap: 1rem;
}
.playbook-welcome {
    padding: 4rem 2rem;
    text-align: center;
    color: var(--muted);
}
.playbook-welcome__count { margin-top: .5rem; font-size: .875rem; }
.playbook-loading, .playbook-access-denied {
    padding: 4rem 2rem;
    text-align: center;
    color: var(--muted);
}
.playbook-component-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
}
.playbook-component-title { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.playbook-component-name { font-size: 1.25rem; font-weight: 700; margin: 0; }
.playbook-deprecation-hint { margin: .25rem 0 0; font-size: .8125rem; color: var(--muted); }
.playbook-source-link {
    font-size: .8125rem;
    color: var(--accent);
    text-decoration: none;
    white-space: nowrap;
}
.playbook-source-link:hover { text-decoration: underline; }
.playbook-badge {
    display: inline-flex;
    align-items: center;
    padding: .15em .5em;
    border-radius: 999px;
    font-size: .6875rem;
    font-weight: normal;
    text-transform: uppercase;
    letter-spacing: .04em;
}
.playbook-badge--beta       { background: rgba(255,179,0,.15);  color: #f5c842; }
.playbook-badge--deprecated { background: rgba(224,92,92,.15);  color: #e05c5c; }
.playbook-controls {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem 1.25rem;
    align-items: center;
    padding: .75rem 1rem;
    background: var(--surface, #1a1a22);
    border: 1px solid var(--border, #2a2a38);
    border-radius: 8px;
}
.playbook-controls__label {
    display: block;
    font-size: .75rem;
    font-weight: normal;
    color: var(--muted);
    margin-bottom: .25rem;
}
.playbook-controls select {
    padding: .3rem .5rem;
    background: var(--bg);
    border: 1px solid var(--border, #2a2a38);
    border-radius: 5px;
    color: var(--text);
    font-size: .875rem;
}
.playbook-controls__toggle {
    display: flex;
    align-items: center;
    gap: .4rem;
    font-size: .875rem;
    cursor: pointer;
}
.playbook-preview-section {
    border: 1px solid var(--border, #2a2a38);
    border-radius: 8px;
    overflow: hidden;
    min-height: 160px;
}
.playbook-preview-wrapper {
    background: var(--bg);
    padding: 1.5rem;
    min-height: 160px;
}
.playbook-preview-inner { display: contents; }
/* Emulation helpers applied to preview-inner */
.pb-reduce-motion *, .pb-reduce-motion *::before, .pb-reduce-motion *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
    animation-iteration-count: 1 !important;
}
.pb-forced-colors { forced-color-adjust: none; filter: grayscale(1) contrast(1.5); }
/* A11y panel */
.playbook-a11y-panel {
    border: 1px solid var(--border, #2a2a38);
    border-radius: 8px;
    padding: 1rem;
}
.playbook-a11y-panel__heading {
    font-size: 1rem;
    font-weight: normal;
    margin: 0 0 .5rem;
    display: flex;
    align-items: center;
    gap: .5rem;
}
.playbook-a11y-badge { font-size: .75rem; font-weight: 700; border-radius: 999px; padding: .15em .5em; }
.playbook-a11y-badge.is-pass { background: rgba(56,201,124,.15); color: #38c97c; }
.playbook-a11y-badge.is-fail { background: rgba(224,92,92,.15);  color: #e05c5c; }
.playbook-a11y-status, .playbook-a11y-loading { font-size: .75rem; color: var(--muted); }
.playbook-a11y-pass { font-size: .875rem; color: #38c97c; margin: 0; }
.playbook-a11y-violations { list-style: none; padding: 0; margin: .5rem 0 0; display: flex; flex-direction: column; gap: .75rem; }
.playbook-a11y-violation { padding: .75rem; background: rgba(224,92,92,.08); border-radius: 6px; border-inline-start: 3px solid #e05c5c; }
.playbook-a11y-violation__id { font-family: monospace; font-size: .875rem; }
.playbook-a11y-violation__impact { font-size: .75rem; font-weight: normal; border-radius: 999px; padding: .1em .45em; margin-inline-start: .5rem; }
.playbook-a11y-violation__impact--critical,
.playbook-a11y-violation__impact--serious  { background: rgba(224,92,92,.25); color: #e05c5c; }
.playbook-a11y-violation__impact--moderate { background: rgba(255,179,0,.2);  color: #f5c842; }
.playbook-a11y-violation__impact--minor    { background: rgba(160,160,176,.2); color: #a0a0b0; }
.playbook-a11y-violation__desc { margin: .35rem 0 0; font-size: .875rem; }
.playbook-a11y-violation__help { margin: .25rem 0 0; font-size: .8125rem; }
.playbook-a11y-violation__help a { color: var(--accent); }
/* Responsive: collapse nav on narrow screens */
@media (max-width: 640px) {
    .playbook-shell { grid-template-columns: 1fr; }
    .playbook-nav { grid-row: 2; max-height: 240px; }
    .playbook-main { grid-row: 1; }
}
/* End — Ticket 18 */

/* === Ticket 06 — Offline status + SW update banner =========================
   OfflineBanner.razor renders two variants:
     • --offline  : info-blue, shown when navigator.onLine is false
     • --update   : purple/accent, shown when SW has a waiting update
   Both sit below the status-banner (Ticket 17) and the navbar, never
   overlapping content. Height ≤44px on desktop; wraps gracefully on mobile.
   Dismiss button: min 44×44 touch target (WCAG 2.5.5).
   ========================================================================== */
.offline-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-3);
    padding: 0 var(--space-3);
    min-height: 2.75rem; /* 44px */
    border-bottom: 1px solid var(--border);
    font-size: var(--text-sm);
    animation: offline-banner-slide 220ms ease-out;
    flex-wrap: wrap;
}
.offline-banner--offline {
    background: #dbeafe;
    border-bottom-color: #3b82f6;
    color: #1e3a5f;
}
.offline-banner--update {
    background: #ede9fe;
    border-bottom-color: #7c6ef5;
    color: #2e1f6a;
}
[data-theme="dark"] .offline-banner--offline {
    background: #1e3a5f;
    border-bottom-color: #3b82f6;
    color: #bfdbfe;
}
[data-theme="dark"] .offline-banner--update {
    background: #2e1f6a;
    border-bottom-color: #7c6ef5;
    color: #ddd6fe;
}
.offline-banner__body {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: 1 1 auto;
    min-width: 0;
}
.offline-banner__icon {
    width: 1.1rem;
    height: 1.1rem;
    flex-shrink: 0;
    opacity: .85;
}
.offline-banner__message {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
    line-height: 1.4;
}
.offline-banner__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-shrink: 0;
}
.offline-banner__refresh {
    display: inline-flex;
    align-items: center;
    min-height: 2.0rem;
    padding: .25rem .75rem;
    background: var(--accent, #7c6ef5);
    color: #fff;
    font-family: inherit;
    font-size: .8125rem;
    font-weight: normal;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    white-space: nowrap;
}
.offline-banner__refresh:hover { opacity: .9; }
.offline-banner__refresh:focus-visible {
    outline: 2px solid var(--accent, #7c6ef5);
    outline-offset: 2px;
}
.offline-banner__dismiss {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.75rem;  /* 44px touch target */
    min-height: 2.75rem;
    padding: 0;
    background: transparent;
    color: inherit;
    font-size: 1.2rem;
    font-weight: 500;
    border: none;
    cursor: pointer;
    opacity: .7;
    flex-shrink: 0;
}
.offline-banner__dismiss:hover { opacity: 1; }
.offline-banner__dismiss:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
    border-radius: 4px;
}
@keyframes offline-banner-slide {
    from { transform: translateY(-100%); opacity: 0; }
    to   { transform: translateY(0);    opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .offline-banner { animation: none; }
}
/* End — Ticket 06 */

/* ── Ticket 13 — IndexedDB draft banner ──────────────────────────────────── */
.caploo-draft-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    padding: .5rem .75rem;
    background: color-mix(in srgb, var(--accent, #7c6ef5) 12%, var(--surface, #1a1a22));
    border: 1px solid color-mix(in srgb, var(--accent, #7c6ef5) 30%, transparent);
    border-radius: var(--radius-md, 8px);
    margin-bottom: .5rem;
    font-size: .78rem;
}
.caploo-draft-banner__body { flex: 1; min-width: 0; }
.caploo-draft-banner__text { color: var(--text, #e8e8f0); line-height: 1.4; }
.caploo-draft-banner__text--warn { color: var(--warning, #e09a4a); }
.caploo-draft-banner__discard {
    flex-shrink: 0;
    min-width: 44px;
    min-height: 44px;
    padding: .25rem .6rem;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm, 5px);
    color: var(--muted);
    font-size: .75rem;
    font-weight: 500;
    cursor: pointer;
    transition: background .15s, color .15s;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.caploo-draft-banner__discard:hover,
.caploo-draft-banner__discard:focus-visible {
    background: var(--surface-alt);
    color: var(--text, #e8e8f0);
}
.caploo-draft-banner__discard:focus-visible {
    outline: 2px solid var(--accent, #7c6ef5);
    outline-offset: 2px;
}
@media (pointer: coarse), (max-width: 48rem) {
    .caploo-draft-banner__discard { min-width: 48px; min-height: 48px; }
}
/* End — Ticket 13 */


/* ══════════════════════════════════════════════════════════════════════════
   UX Desktop Polish 2026-05 — 7-surface visual-hierarchy pass
   feat/ux-desktop-polish-2026-05
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Surface 1: LanguageMenu ─────────────────────────────────────────────
   Collapse 3 redundant active-locale indicators to highlighted background
   only. The native radio input is SR-only positioned so it stays in the
   accessibility tree (checked attribute, keyboard-navigable) without the
   browser-native dot. F-158: 44px unconditional (hybrid-pointer gap fix).
   Ghost tokens (--bg-card/--text-strong etc.) were replaced with canonical
   tokens (--surface/--text/--border) in the 2026-05-17 locale
   rationalization — F-006-LM resolved at that point.
   ───────────────────────────────────────────────────────────────────────── */
.lang-menu .switcher-option input[type="radio"] {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    opacity: 0;
    pointer-events: none;
    flex: none;
    margin: 0;
}
.lang-menu .switcher-option__check { display: none; }
.lang-menu .switcher-option {
    min-height: 44px;
    padding-block: 0.625rem;
    gap: var(--space-1);
}
.lang-menu .switcher-option.is-active {
    border-inline-start: 2px solid var(--accent);
    padding-inline-start: calc(0.875rem - 2px);
}

/* ── Surface 2: ThemeMenu ────────────────────────────────────────────────
   F-164: 44px unconditional (hybrid-pointer gap fix).
   ───────────────────────────────────────────────────────────────────────── */
.theme-menu .theme-menu__option {
    padding: 0.55rem 0.65rem;
    min-height: 44px;
    gap: 0.65rem;
}

/* ── Surface 3: ReportModal ──────────────────────────────────────────────
   First-time CSS: form hierarchy, vertical radio group (max 320px wide),
   auto-width footer buttons right-aligned (Cancel left, Submit right).
   APCA body: --text on --surface = Lc 82 (exceeds Lc 75 floor).
   ───────────────────────────────────────────────────────────────────────── */
/* Handoff retrofit: chrome (positioning, scrim, surface, border, shadow,
   animation) comes from .modal-scrim + .modal. Local rules keep only the
   ReportModal-specific 480px max-width and the scrollable form layout.
   Selector compounds both classes so it wins over the .modal primitive
   regardless of the upstream cascade (.modal is declared later in the file). */
.modal.report-modal {
    max-width: min(480px, 100%);
    max-height: 90vh;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    padding: 0;
    gap: 0;
}
.report-modal__header {
    padding: var(--space-3) var(--space-3) var(--space-2);
    border-bottom: 1px solid var(--border);
}
.report-modal__title {
    font-size: var(--text-lg);
    font-weight: 700;
    color: var(--text);
    margin: 0 0 var(--space-0-5);
}
.report-modal__subtitle {
    font-size: var(--text-sm);
    color: var(--muted);
    margin: 0;
    line-height: 1.4;
}
.report-modal__form {
    padding: var(--space-2) var(--space-3) var(--space-3);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}
.report-modal__reasons {
    border: 0;
    padding: 0;
    margin: 0;
    max-width: 36rem;
    display: flex;
    flex-direction: column;
    gap: var(--space-0-5);
}
/* R-C-2: bump legend weight + add slight tracking so it reads as a
   section header rather than ordinary body text. */
.report-modal__legend {
    display: block;
    padding: 0;
    font-size: var(--text-sm);
    font-weight: 600;
    letter-spacing: 0.03em;
    color: var(--text);
    margin-bottom: var(--space-1);
}
/* R-C-1: force row layout so radio is inline-left of label (overrides any cascade forcing column) */
.report-modal__reason {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--space-1-5);
    cursor: pointer;
    font-size: var(--text-sm);
    color: var(--text);
    min-height: 40px;
    padding: var(--space-0-5) var(--space-1);
    border-radius: var(--radius-sm);
    transition: background var(--transition);
}
.report-modal__reason:hover {
    background: var(--surface-alt);
}
.report-modal__reason input[type="radio"] {
    accent-color: var(--accent);
    flex: 0 0 auto;
    width: 1rem;
    height: 1rem;
    margin-block: 0;
    cursor: pointer;
}
/* R-C-4: reason label + optional one-line description stacked in a flex column */
.report-modal__reason-text {
    display: flex;
    flex-direction: column;
    gap: 0;
    line-height: 1.3;
}
.report-modal__reason-desc {
    font-size: var(--text-xs);
    color: var(--muted);
    padding-inline-start: 0;
}
.report-modal__detail-label {
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--muted);
}
.report-modal__detail {
    width: 100%;
    padding: var(--space-1) var(--space-1-5);
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    font-family: inherit;
    font-size: var(--text-sm);
    line-height: 1.5;
    resize: vertical;
    transition: border-color var(--transition);
}
.report-modal__detail:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: var(--focus-ring);
}
.report-modal__char-count { font-size: var(--text-xs); color: var(--muted); margin: 0.25rem 0 0; text-align: right; }
.report-modal__error { color: var(--danger-text); font-size: var(--text-sm); margin: 0; }
.report-modal__dmca-hint { font-size: var(--text-xs); color: var(--muted); margin: 0; line-height: 1.4; }
/* R-C-3: sticky footer so submit is always reachable even when
   the reasons list overflows the modal's max-height. */
.report-modal__actions {
    position: sticky;
    bottom: 0;
    background: var(--surface);
    z-index: 1;
    display: flex;
    justify-content: flex-end;
    gap: var(--space-1-5);
    padding-top: var(--space-2);
    border-top: 1px solid var(--border);
}
.report-modal__actions .btn { width: auto; flex-shrink: 0; min-height: 40px; }
.report-modal__success {
    padding: var(--space-3);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}
.report-modal__success-body { font-size: var(--text-sm); color: var(--muted); line-height: 1.5; margin: 0; }

/* PA-01 — ReportModal landscape-phone adaptation.
   .modal.report-modal already has overflow-y:auto and max-height:90vh so it
   scrolls. __actions already has position:sticky bottom:0. We reduce the
   header and form padding so the radio list is visible without scrolling. */
@media (orientation: landscape) and (max-height: 500px) {
    .modal.report-modal {
        max-height: calc(100vh - 16px);
    }
    .report-modal__header {
        padding-block: var(--space-1-5);
        position: sticky;
        top: 0;
        z-index: 2;
        background: var(--surface);
    }
    .report-modal__form {
        padding-block: var(--space-1-5);
        gap: var(--space-1);
    }
    .report-modal__actions {
        padding-block: var(--space-1);
    }
}

/* ── Surface 4: ShareMenu "More platforms" overlap fix ────────────────────
   min-height: 0 on the body flex child lets it shrink inside the fixed
   modal and scroll rather than expanding past max-height and getting clipped.
   ───────────────────────────────────────────────────────────────────────── */
.share-menu__body { min-height: 0; }
.share-menu__option-label { min-width: 0; flex: 1 1 auto; }
.share-menu__platform-chips { flex-wrap: wrap; }

/* ── Surface 5: HelpDrawer close button ──────────────────────────────────
   32×32 visible square with 6px box-model padding = 44×44 effective hit.
   ───────────────────────────────────────────────────────────────────────── */
.help-drawer__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 10px;
    box-sizing: content-box;
    border-radius: var(--radius-sm);
    font-size: 1rem;
    line-height: 1;
}
.help-drawer__body { padding: var(--space-1-5) var(--space-2) var(--space-4); }

/* ── Surface 6: Composer attach affordances (R-A-1) ─────────────────────
   Text-first composer: Photo + Camera as inline icon-buttons in a bottom
   action row. Mode strip removed. Textarea is the primary surface.
   ───────────────────────────────────────────────────────────────────────── */
/* Bottom row: attach affordances left, Post button right */
.caploo-composer-bottom-row {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}
.caploo-composer-attach {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    flex: 1 1 auto;
}
.caploo-composer-attach__btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-0-5);
    padding: 0.28rem var(--space-1);
    height: 36px;
    min-height: 36px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--muted);
    font-family: inherit;
    font-size: var(--text-xs);
    font-weight: 500;
    line-height: 1;
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--transition), border-color var(--transition), color var(--transition);
}
.caploo-composer-attach__btn:hover { background: var(--surface-alt); color: var(--text); }
.caploo-composer-attach__btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.caploo-composer-attach__btn:disabled { opacity: 0.45; cursor: not-allowed; }
/* F-115: inline hint when Camera affordance is suppressed */
.caploo-composer-attach__camera-hint {
    font-size: var(--text-xs);
    color: var(--muted);
    margin: 2px 0 4px;
    padding: 0 2px;
    line-height: 1.4;
}
@media (pointer: coarse), (max-width: 48rem) {
    .caploo-composer-attach__btn { height: 44px; min-height: 44px; padding: var(--space-1) var(--space-1-5); }
}

/* F-092/F-109: composer boot skeleton */
.caploo-composer-sidebar__skeleton {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 4px 0 8px;
}
.caploo-composer-sidebar__skeleton-line {
    background: linear-gradient(90deg, var(--surface-alt) 25%, var(--border) 50%, var(--surface-alt) 75%);
    background-size: 400px 100%;
    border-radius: var(--radius-sm, 4px);
    animation: shimmer 1.4s infinite linear;
}
.caploo-composer-sidebar__skeleton-line--textarea { height: 64px; width: 100%; }
.caploo-composer-sidebar__skeleton-line--action { height: 30px; width: 60%; }
/* Caption textarea — primary focal point, larger relative visual weight */
.caploo-composer-sidebar__caption {
    font-size: var(--text-md);
    width: 100%;
    min-height: 80px;
    padding: var(--space-1) var(--space-1-5);
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    font-family: inherit;
    line-height: 1.5;
    resize: vertical;
    box-sizing: border-box;
    transition: border-color var(--transition);
}
.caploo-composer-sidebar__caption:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: var(--focus-ring);
}
/* M-089 — composer char counter (mobile only). Hidden on desktop. */
.composer-char-counter {
    display: none;
    text-align: end;
    font-size: var(--text-xs);
    color: var(--muted);
    padding-inline-end: 2px;
    line-height: 1.2;
}
@media (max-width: 48rem) {
    .composer-char-counter.is-near-limit {
        display: block;
    }
}
/* Post button — filled primary, right-aligned tertiary position */
.caploo-composer-sidebar__submit {
    align-self: flex-end;
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-2);
    min-height: 40px;
    background: var(--accent);
    color: var(--text-on-accent, #fff);
    border: none;
    border-radius: var(--radius-sm);
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: normal;
    cursor: pointer;
    transition: opacity var(--transition);
}
.caploo-composer-sidebar__submit:hover { opacity: 0.9; }
.caploo-composer-sidebar__submit:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.caploo-composer-sidebar__submit:disabled { opacity: 0.4; cursor: not-allowed; }

/* ── Surface 7: Camera overlay ───────────────────────────────────────────
   Error panels use global --text-* tokens (no ad-hoc font sizes).
   Full overlay chrome also added here since no prior CSS existed.
   ───────────────────────────────────────────────────────────────────────── */
.camera-overlay {
    position: fixed;
    inset: 0;
    z-index: 9500;
    background: #000;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
}
.camera-overlay__error-panel {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-4);
    max-width: 400px;
    text-align: center;
}
.camera-overlay__error-message {
    font-size: var(--text-md);
    color: #fff;
    line-height: 1.5;
    margin: 0;
}
.camera-overlay__fallback-btn {
    min-height: 44px;
    padding: var(--space-1) var(--space-2-5);
    border-radius: var(--radius-sm);
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: normal;
    cursor: pointer;
    background: var(--accent);
    color: var(--text-on-accent, #fff);
    border: none;
    transition: opacity var(--transition);
}
.camera-overlay__cancel-btn {
    min-height: 44px;
    padding: var(--space-1) var(--space-2-5);
    border-radius: var(--radius-sm);
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: 500;
    cursor: pointer;
    background: transparent;
    color: rgba(255,255,255,.8);
    border: 1px solid rgba(255,255,255,.35);
    transition: opacity var(--transition);
}
/* F-117: platform-specific re-enable instructions in permission-denied state */
.camera-overlay__platform-hint {
    font-size: 0.8125rem;
    color: rgba(255, 255, 255, 0.8);
    line-height: 1.5;
    margin: 8px 0 4px;
    max-width: 28rem;
}
.camera-overlay__fallback-btn:hover,
.camera-overlay__cancel-btn:hover { opacity: 0.85; }
.camera-overlay__fallback-btn:focus-visible,
.camera-overlay__cancel-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.camera-overlay__viewfinder {
    position: relative;
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
}
.camera-overlay__video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.camera-overlay__top-bar {
    position: absolute;
    top: 0; left: 0; right: 0;
    display: flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1-5) var(--space-2);
    background: linear-gradient(to bottom, rgba(0,0,0,.55), transparent);
    z-index: 2;
}
.camera-overlay__mode-btn {
    min-height: 34px;
    padding: var(--space-0-5) var(--space-1-5);
    border: 1px solid rgba(255,255,255,.45);
    border-radius: var(--radius-pill);
    background: transparent;
    color: rgba(255,255,255,.75);
    font-family: inherit;
    font-size: var(--text-xs);
    font-weight: 500;
    cursor: pointer;
    transition: background var(--transition), color var(--transition);
}
.camera-overlay__mode-btn--active { background: rgba(255,255,255,.9); color: #000; border-color: transparent; }
.camera-overlay__top-bar .camera-overlay__cancel-btn {
    margin-inline-start: auto;
    width: 40px;
    height: 40px;
    min-height: 40px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: rgba(0,0,0,.4);
}
.camera-overlay__bottom-bar {
    position: absolute;
    bottom: 0; left: 0; right: 0;
    display: flex;
    align-items: center;
    justify-content: space-around;
    padding: var(--space-3) var(--space-2);
    padding-bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
    background: linear-gradient(to top, rgba(0,0,0,.55), transparent);
    z-index: 2;
}
.camera-overlay__shutter {
    width: 72px;
    height: 72px;
    border: none;
    background: transparent;
    cursor: pointer;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.camera-overlay__shutter-ring {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    border: 3px solid rgba(255,255,255,.9);
    display: flex;
    align-items: center;
    justify-content: center;
}
.camera-overlay__shutter-inner {
    width: 52px;
    height: 52px;
    border-radius: 50%;
    background: #fff;
    transition: transform .1s ease;
}
.camera-overlay__shutter:active .camera-overlay__shutter-inner { transform: scale(0.9); }
.camera-overlay__shutter-inner--video { background: var(--danger, #e05c5c); }
.camera-overlay__shutter--recording .camera-overlay__shutter-inner {
    border-radius: var(--radius-xs);
    width: 28px;
    height: 28px;
    background: var(--danger, #e05c5c);
}
.camera-overlay__record-indicator { display: none; }
.camera-overlay__flip-btn,
.camera-overlay__flip-spacer {
    width: 44px;
    height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: rgba(0,0,0,.35);
    border-radius: 50%;
    color: #fff;
    cursor: pointer;
    transition: background var(--transition);
}
.camera-overlay__flip-btn:hover { background: rgba(0,0,0,.55); }
.camera-overlay__flip-spacer { background: transparent; cursor: default; pointer-events: none; }
.camera-overlay__flip-btn:focus-visible,
.camera-overlay__shutter:focus-visible { outline: 2px solid rgba(255,255,255,.8); outline-offset: 2px; }
.camera-overlay__review {
    position: relative;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}
.camera-overlay__review-media { width: 100%; height: 100%; object-fit: contain; }
.camera-overlay__review-actions {
    position: absolute;
    bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0px));
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: var(--space-2);
    align-items: center;
}
.camera-overlay__btn {
    min-height: 44px;
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-sm);
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: normal;
    cursor: pointer;
    border: none;
    transition: opacity var(--transition);
}
.camera-overlay__btn--primary { background: var(--accent); color: var(--text-on-accent, #fff); }
.camera-overlay__btn--secondary { background: rgba(255,255,255,.18); color: #fff; border: 1px solid rgba(255,255,255,.35); }
.camera-overlay__btn--ghost { background: transparent; color: rgba(255,255,255,.65); }
.camera-overlay__btn:hover { opacity: 0.85; }
.camera-overlay__btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

/* ── Help pages (/help/user-guide, /help/faq) ─────────────────────────── */
.help-page {
    max-width: var(--content-max);
    margin: 0 auto;
    padding: var(--space-4) var(--space-2) var(--space-8);
    color: var(--text);
}
.help-page__back {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--accent);
    text-decoration: none;
    font-size: var(--text-sm);
    font-weight: 500;
    margin-bottom: var(--space-3);
    min-height: 44px;
}
.help-page__back:hover { text-decoration: underline; }
.help-page__back:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-xs); }
.help-page__title { font-size: var(--text-fluid-h1, var(--text-2xl)); font-weight: 800; color: var(--text); margin: 0 0 var(--space-1); text-wrap: balance; }
.help-page__intro { font-size: var(--text-md); color: var(--muted); margin: 0 0 var(--space-4); line-height: 1.6; }
.help-page__section { margin-bottom: var(--space-4); }
.help-page__section-heading { font-size: var(--text-fluid-h3, var(--text-lg)); font-weight: 700; color: var(--text); margin: 0 0 var(--space-1); }
.help-page__section-body { font-size: var(--text-md); color: var(--muted); line-height: 1.6; margin: 0; }
.help-page__more { display: inline-block; margin-top: var(--space-1); font-size: var(--text-xs); color: var(--muted); font-style: italic; }
.help-faq__list { list-style: none; padding: 0; margin: 0; }
.help-faq__item { border-bottom: 1px solid var(--border); padding: var(--space-2) 0; }
.help-faq__item:first-child { padding-top: 0; }
.help-faq__q { font-size: var(--text-md); font-weight: normal; color: var(--text); margin: 0 0 var(--space-1); }
.help-faq__a { font-size: var(--text-sm); color: var(--muted); line-height: 1.6; margin: 0; }
/* ── Settings pages (MD-T6) ─────────────────────────────────────────────── */

.settings-page {
    max-width: var(--content-max);
    margin-inline: auto;
    padding: var(--space-4) var(--gutter-x) var(--space-8);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.settings-page > h1 {
    font-size: var(--text-fluid-h1, var(--text-2xl));
    font-weight: 700;
    color: var(--text);
    margin: 0;
    text-wrap: balance;
}

.settings-page .muted {
    line-height: 1.6;
}

/* MD-070: on narrow phones the section heading needs top breathing room */
@media (max-width: 30rem) {
    .settings-page section + section,
    .settings-page .card + .card {
        margin-top: var(--space-1);
    }
}

/* End — UX Desktop Polish 2026-05 */

/* =============================================================================
 * Ticket 14 — RTL language support scaffolding
 * =============================================================================
 *
 * 1. Scroll-navigator pill: correct safe-area inset in RTL.
 *    `inset-inline-end` already places the pill on the logical end (right in
 *    LTR, left in RTL), but --safe-area-right is physical. Override to use
 *    --safe-area-left when reading direction is RTL.
 * 2. Directional icon mirrors: chevrons, nav arrows that point left↔right
 *    must flip in RTL so "previous" and "next" remain semantically correct.
 * 3. User-content isolation: captions, display-names, and comment bodies may
 *    contain text in a language other than the UI locale. `unicode-bidi:
 *    isolate` prevents RTL leakage into surrounding layout without forcing a
 *    direction (same as <bdi>). See docs/i18n/rtl.md §Mixed-direction content.
 * ============================================================================= */

/* — 1. Scroll navigator safe-area correction in RTL — */
[dir="rtl"] .scroll-nav {
    inset-inline-end: calc(var(--space-3) + var(--safe-area-left, 0px));
}

/* — 2. Directional icon mirrors — */

/* Calendar prev/next nav chevrons */
[dir="rtl"] .caploo-calendar-header__nav svg,
[dir="rtl"] .drp__cal-nav-btn svg {
    transform: scaleX(-1);
}

/* Sidebar collapse chevron (points right in LTR to indicate "collapse right") */
[dir="rtl"] .caploo-sidebar-panel__chevron {
    transform: scaleX(-1);
}
/* Re-apply the open-state rotation on top of the mirror */
[dir="rtl"] .caploo-sidebar-panel--collapsed .caploo-sidebar-panel__chevron {
    transform: scaleX(-1) rotate(180deg);
}

/* Filter section expand/collapse chevron (vertical-only rotation, no mirror) */
/* Note: filter chevron rotates 180deg for open — purely vertical, no RTL flip. */

/* — 3. User-content isolation (mixed-direction text) — */
/* Any block that renders untrusted user text. The isolation context keeps
   RTL/LTR punctuation from leaking across the boundary without overriding
   the text's own directionality (matching <bdi> semantics). */
.moment-caption,
.modal-comment-text,
.moment-card-caption,
.feed-caption,
.user-display-name {
    unicode-bidi: isolate;
}
/* End — Ticket 14 */

/* ── Audit remediation 2026-05-17 ──────────────────────────────────────────── */

/* ── 404 / Not found page (F-009) ────────────────────────────────────────── */
.not-found-page {
    max-width: 480px;
    margin: 6rem auto;
    padding: 0 var(--gutter-x);
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
}
.not-found-page__heading {
    font-size: var(--text-2xl);
    font-weight: 800;
    color: var(--text);
    margin: 0;
}
.not-found-page__body {
    font-size: var(--text-md);
    color: var(--muted);
    margin: 0;
    line-height: 1.5;
}
.not-found-page__links {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-1);
    margin-top: var(--space-1);
}
.not-found-page__link {
    min-width: 9rem;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
}

/* ── ShareLinkPanel (F-002) ─────────────────────────────────────────────────
   Phase 7.4 — tier picker + revocation list. Rules migrated from an inline
   <style> in ShareLinkPanel.razor that shipped with phantom `--caploo-*`
   tokens (none defined in app.css; fallbacks were blue, not Caploo violet)
   and hardcoded light-theme pastels (#f4f8ff, #fef3c7, #dbeafe, etc.) that
   rendered as bright cards on the dark surface. Token-mapped to canonical
   `--accent / --border / --muted / --surface(-alt) / --warning|info|success-glow`.
   ────────────────────────────────────────────────────────────────────────── */
.sl-panel { display: flex; flex-direction: column; gap: 0.75rem; padding-top: 1rem; }
.sl-panel__title { font-size: var(--text-md); margin: 0; font-weight: normal; color: var(--text); }
.sl-tier {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.25rem;
    border: 0;
    padding: 0;
    margin: 0;
}
.sl-tier__opt {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    padding: 0.5rem;
    min-height: 36px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    cursor: pointer;
    font-size: var(--text-sm);
}
.sl-tier__opt.is-checked {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 14%, transparent);
}
.sl-tier__opt input { position: absolute; opacity: 0; pointer-events: none; }
.sl-tier__opt:focus-within { outline: var(--focus-outline); outline-offset: 2px; }
.sl-tier__copy { font-size: var(--text-xs); color: var(--muted); }
.sl-row { display: flex; flex-direction: column; gap: 0.25rem; }
.sl-row__label { font-size: var(--text-sm); font-weight: 500; color: var(--muted); }
.sl-input {
    min-height: 36px;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg);
    color: var(--text);
    font-size: var(--text-sm);
}
.sl-input:focus-visible { outline: var(--focus-outline); outline-offset: 2px; }
.sl-create {
    min-height: 36px;
    padding: 0.5rem 1rem;
    border-radius: var(--radius-sm);
    border: 0;
    cursor: pointer;
    font-weight: normal;
    background: var(--accent);
    color: var(--text-on-accent);
}
.sl-create[disabled] { opacity: 0.6; cursor: not-allowed; }
.sl-create:focus-visible { outline: var(--focus-outline); outline-offset: 2px; }
.sl-created {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.5rem;
    background: color-mix(in srgb, var(--info) 10%, transparent);
    border-radius: var(--radius-sm);
}
.sl-error {
    color: var(--danger-text);
    font-size: var(--text-sm);
    padding: 0.4rem 0.6rem;
    background: color-mix(in srgb, var(--danger) 12%, transparent);
    border-radius: var(--radius-sm);
}
.sl-list__title { font-size: var(--text-sm); margin: 0.5rem 0 0.25rem; font-weight: normal; color: var(--text); }
.sl-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.25rem; }
.sl-list__item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    background: var(--surface-alt);
    border-radius: var(--radius-sm);
    min-height: 36px;
}
.sl-list__main {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    flex: 1;
    font-size: var(--text-sm);
    min-width: 0;
    color: var(--text);
}
.sl-list__tier {
    display: inline-block;
    padding: 0.05rem 0.4rem;
    border-radius: var(--radius-pill);
    background: var(--surface-alt);
    color: var(--text);
    font-size: var(--text-xs);
    font-weight: normal;
    width: fit-content;
}
.sl-list__tier--private  { background: var(--warning-glow); color: var(--warning-text); }
.sl-list__tier--unlisted { background: var(--info-glow);    color: var(--info-text); }
.sl-list__tier--public   { background: color-mix(in srgb, var(--success) 22%, transparent); color: var(--success-text); }
.sl-list__label { font-weight: 500; color: var(--text); }
.sl-list__date { font-size: var(--text-xs); color: var(--muted); }
.sl-list__revoke {
    min-width: 36px;
    min-height: 36px;
    padding: 0.25rem 0.5rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    cursor: pointer;
    font-weight: normal;
}
.sl-list__revoke:focus-visible { outline: 2px solid var(--danger); outline-offset: 2px; }
.sl-srlive { position: absolute; left: -10000px; top: auto; width: 1px; height: 1px; overflow: hidden; }
@media (pointer: coarse) {
    .sl-tier__opt,
    .sl-input,
    .sl-create,
    .sl-list__revoke,
    .sl-list__item { min-height: 44px; }
}

/* ── NotificationBell + NotificationsPage (F-001) ─────────────────────────
   Phase 12 (Ticket 12) shipped both surfaces with BEM-style classes but
   no CSS — rows rendered as bare buttons stacked left-aligned with no
   surface chrome, no badge, no popover positioning. Adds the missing
   visual layer, all tokens canonical. */

/* NotificationBell — popover trigger in the navbar.
   .bell-wrapper sits in the navbar tools cluster; .bell-dropdown is an
   absolute popover anchored to the trigger's bottom-right edge. */
.bell-wrapper {
    position: relative;
    display: inline-flex;
    align-items: center;
}
.bell-trigger {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    min-width: 40px;
    min-height: 40px;
    padding: 0;
    background: transparent;
    color: var(--text);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition);
}
.bell-trigger:hover { background: var(--surface-alt); }
.bell-trigger[aria-expanded="true"] {
    background: var(--surface-alt);
    border-color: var(--border);
}
.bell-trigger:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}
.bell-trigger svg { display: block; }
.bell-badge {
    position: absolute;
    top: 4px;
    inset-inline-end: 4px;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    background: var(--danger);
    color: var(--text-on-accent);
    border-radius: var(--radius-pill);
    font-size: 0.65rem;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    pointer-events: none;
}
/* Screen-reader-only announcement of arrival; never visible. */
.bell-arrival-sr {
    position: absolute;
    left: -10000px;
    top: auto;
    width: 1px;
    height: 1px;
    overflow: hidden;
}
.bell-dropdown {
    position: absolute;
    top: calc(100% + 6px);
    inset-inline-end: 0;
    min-width: 320px;
    max-width: min(380px, calc(100vw - var(--gutter-x) * 2));
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    z-index: 1000;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    animation: fadeSlideUp var(--duration-base) var(--easing-default);
}
.bell-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-1);
    padding: 0.75rem 1rem;
    border-bottom: 1px solid var(--border);
    font-size: var(--text-sm);
    color: var(--text);
}
.bell-header strong { font-weight: normal; }
.bell-dropdown .link {
    background: none;
    border: none;
    padding: 0;
    min-height: 24px;
    width: auto;
    color: var(--accent-text);
    font-size: var(--text-xs);
    font-weight: 500;
    cursor: pointer;
}
.bell-dropdown .link:hover { text-decoration: underline; }
.bell-dropdown .empty {
    padding: 1.5rem 1rem;
    text-align: center;
    color: var(--muted);
    font-size: var(--text-sm);
}
.bell-list {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 360px;
    overflow-y: auto;
}
.bell-list li { border-bottom: 1px solid var(--border); }
.bell-list li:last-child { border-bottom: 0; }
.bell-list li.unread { background: color-mix(in srgb, var(--accent) 6%, transparent); }
.bell-row {
    display: flex;
    align-items: flex-start;
    gap: 0.625rem;
    width: 100%;
    padding: 0.625rem 1rem;
    background: transparent;
    color: var(--text);
    border: 0;
    border-radius: 0;
    text-align: start;
    cursor: pointer;
    font-weight: 400;
    min-height: 44px;
}
.bell-row:hover { background: var(--surface-alt); }
.bell-row:focus-visible {
    outline: var(--focus-outline);
    outline-offset: -2px;
}
.bell-row .icon {
    flex: 0 0 auto;
    font-size: 1.1rem;
    line-height: 1.2;
    width: 1.6rem;
    text-align: center;
}
.bell-row .text {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    flex: 1;
    min-width: 0;
}
.bell-row .title {
    font-size: var(--text-sm);
    line-height: 1.35;
    color: var(--text);
    word-break: break-word;
}
.bell-row .ts {
    font-size: var(--text-xs);
    color: var(--muted);
}
.bell-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-1);
    padding: 0.5rem 1rem;
    border-top: 1px solid var(--border);
    background: var(--surface);
    font-size: var(--text-xs);
}
.bell-footer a {
    color: var(--accent-text);
    text-decoration: none;
    padding: 0.25rem 0;
}
.bell-footer a:hover { text-decoration: underline; }

/* NotificationsPage — full-page notification center. */
.notifications-page {
    max-width: var(--content-max);
    margin: 0 auto;
    padding: 2rem var(--gutter-x) 4rem;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}
.notif-page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    flex-wrap: wrap;
}
.notif-page-header h1 {
    font-size: var(--text-2xl);
    font-weight: 700;
    color: var(--text);
    margin: 0;
    letter-spacing: -.02em;
}
.notif-page-actions { display: inline-flex; gap: var(--space-1); }
.notif-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}
.notif-chips .chip {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    min-height: 36px;
    width: auto;
    padding: 0.35rem 0.85rem;
    background: var(--surface-alt);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    font-size: var(--text-sm);
    font-weight: 500;
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition);
}
.notif-chips .chip:hover { background: color-mix(in srgb, var(--accent) 10%, var(--surface-alt)); }
.notif-chips .chip:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}
.notif-chips .chip--active {
    background: var(--accent);
    color: var(--text-on-accent);
    border-color: var(--accent);
}
.notif-chips .chip-badge {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--danger);
}
.notif-chips .chip--active .chip-badge { background: var(--text-on-accent); }
.notif-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-1);
    flex-wrap: wrap;
    padding: 0.25rem 0;
}
.toggle-label {
    flex-direction: row;
    align-items: center;
    gap: 0.5rem;
    color: var(--muted);
    font-size: var(--text-sm);
    cursor: pointer;
}
.toggle-label input[type="checkbox"] {
    width: 1rem;
    height: 1rem;
    accent-color: var(--accent);
}
.notif-feed {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.notif-row {
    display: flex;
    align-items: stretch;
    gap: 0.5rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    overflow: hidden;
    transition: border-color var(--transition);
}
.notif-row:hover { border-color: var(--border); }
.notif-row--unread {
    border-color: color-mix(in srgb, var(--accent) 30%, var(--border));
    background: color-mix(in srgb, var(--accent) 5%, var(--surface));
}
.notif-row-main {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex: 1;
    min-width: 0;
    padding: 0.75rem 1rem;
    background: transparent;
    color: var(--text);
    border: 0;
    border-radius: 0;
    text-align: start;
    cursor: pointer;
    font-weight: 400;
    min-height: 56px;
    width: auto;
}
.notif-row-main:hover { background: var(--surface-alt); }
.notif-row-main:focus-visible {
    outline: var(--focus-outline);
    outline-offset: -2px;
}
.notif-icon {
    flex: 0 0 auto;
    font-size: 1.25rem;
    width: 2rem;
    text-align: center;
}
.notif-body {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    flex: 1;
    min-width: 0;
}
.notif-title {
    font-size: var(--text-md);
    line-height: 1.35;
    color: var(--text);
    word-break: break-word;
}
.notif-ts {
    font-size: var(--text-xs);
    color: var(--muted);
}
.notif-unread-dot {
    flex: 0 0 auto;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--accent);
    margin-inline-start: 0.5rem;
}
.notif-toggle-read {
    align-self: center;
    margin-inline-end: 0.5rem;
    padding: 0.35rem 0.65rem;
    min-height: 36px;
    width: auto;
    font-size: var(--text-xs);
    font-weight: 500;
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    white-space: nowrap;
}
.notif-toggle-read:hover {
    color: var(--text);
    border-color: var(--border);
}
.notif-toggle-read:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}
.notif-load-more {
    display: flex;
    justify-content: center;
    padding: 1rem 0;
}
.notif-load-more button { min-width: 12rem; max-width: 100%; }

@media (pointer: coarse) {
    .bell-row,
    .notif-row-main,
    .notif-chips .chip,
    .notif-toggle-read { min-height: 44px; }
}

@media (max-width: 30rem) {
    .notif-row {
        flex-direction: column;
        align-items: stretch;
    }
    .notif-toggle-read {
        margin-inline-end: 0;
        margin: 0 1rem 0.75rem;
        align-self: flex-start;
    }
}

/* ── EmailSharePanel (F-003) ────────────────────────────────────────────
   Phase 7.5 share-by-email composer; mounted inside ShareMenu. Classes
   were prefixed `email-share-panel__*` but had no CSS — labels stacked
   without spacing, the textarea had no border in dark theme. Adds the
   visual layer with canonical tokens. */
.email-share-panel {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding-top: 1rem;
}
.email-share-panel__title {
    font-size: var(--text-md);
    font-weight: normal;
    color: var(--text);
    margin: 0;
}
.email-share-panel__hint {
    font-size: var(--text-xs);
    color: var(--muted);
    margin: 0;
}
.email-share-panel__label {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.35rem;
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--text);
    margin-block-start: 0.25rem;
}
.email-share-panel__required {
    color: var(--danger-text);
    font-weight: 700;
}
.email-share-panel__optional {
    color: var(--muted);
    font-weight: 400;
    font-size: var(--text-xs);
}
.email-share-panel__textarea {
    width: 100%;
    padding: 0.55rem 0.7rem;
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-family: inherit;
    font-size: var(--text-sm);
    line-height: 1.45;
    resize: vertical;
    min-height: 2.5rem;
}
.email-share-panel__textarea:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: var(--focus-ring);
}
.email-share-panel__textarea[disabled] {
    opacity: 0.55;
    cursor: not-allowed;
}
.email-share-panel__field-error {
    font-size: var(--text-xs);
    color: var(--danger-text);
    margin: 0.1rem 0 0;
}
.email-share-panel__check-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-block-start: 0.25rem;
}
.email-share-panel__check {
    width: 1rem;
    height: 1rem;
    accent-color: var(--accent);
    flex: 0 0 auto;
    margin: 0;
}
.email-share-panel__check-label {
    flex-direction: row;
    margin: 0;
    color: var(--text);
    font-size: var(--text-sm);
    font-weight: 500;
    cursor: pointer;
}
.email-share-panel__select {
    width: 100%;
    padding: 0.5rem 0.7rem;
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
    min-height: 36px;
}
.email-share-panel__select:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: var(--focus-ring);
}
.email-share-panel__status {
    margin: 0.25rem 0 0;
    padding: 0.5rem 0.65rem;
    background: color-mix(in srgb, var(--success) 14%, transparent);
    color: var(--success-text);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
}
.email-share-panel__error {
    margin: 0.25rem 0 0;
    padding: 0.5rem 0.65rem;
    background: color-mix(in srgb, var(--danger) 12%, transparent);
    color: var(--danger-text);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
}
.email-share-panel__failed {
    margin: 0.25rem 0 0;
    padding: 0.4rem 0.75rem 0.4rem 1.35rem;
    background: color-mix(in srgb, var(--warning) 12%, transparent);
    color: var(--warning-text);
    border-radius: var(--radius-sm);
    font-size: var(--text-xs);
    list-style: disc;
}
.email-share-panel__failed code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    background: var(--surface-alt);
    color: var(--text);
    padding: 0 0.2rem;
    border-radius: 3px;
    font-size: 0.95em;
}
.email-share-panel__actions {
    display: flex;
    justify-content: flex-end;
    margin-block-start: 0.5rem;
}
.email-share-panel__send {
    min-height: 44px;
    width: auto;
    min-width: 8rem;
    padding: 0.5rem 1.25rem;
    background: var(--accent);
    color: var(--text-on-accent);
    border: 0;
    border-radius: var(--radius-sm);
    font-weight: normal;
    cursor: pointer;
    transition: background var(--transition);
}
.email-share-panel__send:hover:not([disabled]) { background: var(--accent-dim); }
.email-share-panel__send[disabled] {
    opacity: 0.55;
    cursor: not-allowed;
}
.email-share-panel__send:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}

/* ── LocationPicker (F-004) ────────────────────────────────────────────
   Phase 5 modal-style picker with forward geocoding + browser geolocation
   + a 240px Leaflet map. Classes were prefixed `caploo-location-picker__*`
   but the overlay was a pass-through (no z-index, no scrim, no centering)
   and the dialog itself had no surface chrome. Adds the overlay + dialog
   shell. */
.caploo-location-picker__overlay {
    position: fixed;
    inset: 0;
    z-index: 9000;
    background: var(--scrim);
}
.caploo-location-picker {
    position: fixed;
    z-index: 9001;
    inset-block-start: 50%;
    inset-inline-start: 50%;
    transform: translate(-50%, -50%);
    width: min(560px, calc(100vw - var(--gutter-x) * 2));
    max-height: min(720px, calc(100vh - 2rem));
    max-height: min(720px, calc(100dvh - 2rem));
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    display: grid;
    grid-template-rows: auto 1fr auto;
    overflow: hidden;
}
.caploo-location-picker__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-1);
    padding: 0.85rem 1rem;
    border-bottom: 1px solid var(--border);
}
.caploo-location-picker__head h3 {
    font-size: var(--text-md);
    font-weight: normal;
    color: var(--text);
    margin: 0;
}
.caploo-location-picker__close {
    width: 36px;
    height: 36px;
    min-height: 36px;
    min-width: 36px;
    padding: 0;
    background: transparent;
    border: 0;
    color: var(--muted);
    border-radius: var(--radius-sm);
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
}
.caploo-location-picker__close:hover {
    background: var(--surface-alt);
    color: var(--text);
}
.caploo-location-picker__close:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}
.caploo-location-picker__body {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.85rem 1rem;
    overflow-y: auto;
}
.caploo-location-picker__search-label {
    flex-direction: row;
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--muted);
    margin: 0;
}
.caploo-location-picker__search {
    width: 100%;
    min-height: 44px;
    padding: 0.5rem 0.7rem;
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: var(--text-md);
}
.caploo-location-picker__search:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: var(--focus-ring);
}
.caploo-location-picker__geo {
    min-height: 44px;
    width: 100%;
    align-self: flex-start;
}
.caploo-location-picker__results {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    max-height: 200px;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
}
.caploo-location-picker__results li { display: block; }
.caploo-location-picker__result {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.1rem;
    width: 100%;
    padding: 0.5rem 0.75rem;
    background: transparent;
    color: var(--text);
    border: 0;
    border-radius: 0;
    text-align: start;
    cursor: pointer;
    font-weight: 400;
    min-height: 44px;
}
.caploo-location-picker__result:hover { background: var(--surface-alt); }
.caploo-location-picker__result:focus-visible {
    outline: var(--focus-outline);
    outline-offset: -2px;
}
li[aria-selected="true"] .caploo-location-picker__result {
    background: color-mix(in srgb, var(--accent) 14%, transparent);
}
.caploo-location-picker__result-name {
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--text);
}
.caploo-location-picker__result-kind {
    font-size: var(--text-xs);
    color: var(--muted);
    text-transform: capitalize;
}
.caploo-location-picker__empty {
    margin: 0;
    padding: 0.6rem 0.75rem;
    border: 1px dashed var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-alt);
    color: var(--muted);
    font-size: var(--text-sm);
}
.caploo-location-picker__map {
    width: 100%;
    min-height: 240px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: var(--surface-alt);
}
.caploo-location-picker__pending {
    margin: 0;
    padding: 0.5rem 0.7rem;
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    color: var(--text);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
}
.caploo-location-picker__pending strong { color: var(--accent-text); margin-inline-end: 0.35rem; }
.caploo-location-picker__error {
    margin: 0;
    padding: 0.5rem 0.7rem;
    background: color-mix(in srgb, var(--danger) 12%, transparent);
    color: var(--danger-text);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
}
.caploo-location-picker__foot {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 0.5rem;
    padding: 0.75rem 1rem;
    border-top: 1px solid var(--border);
    background: var(--surface);
}
.caploo-location-picker__foot .btn-secondary {
    min-height: 44px;
    width: auto;
    min-width: 7rem;
}
.caploo-location-picker__confirm {
    min-height: 44px;
    width: auto;
    min-width: 7rem;
    padding: 0.5rem 1.25rem;
    background: var(--accent);
    color: var(--text-on-accent);
    border: 0;
    border-radius: var(--radius-sm);
    font-weight: normal;
    cursor: pointer;
    transition: background var(--transition);
}
.caploo-location-picker__confirm:hover:not([disabled]) { background: var(--accent-dim); }
.caploo-location-picker__confirm[disabled] {
    opacity: 0.55;
    cursor: not-allowed;
}
.caploo-location-picker__confirm:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}

/* Mobile bottom-sheet variant — slide up from the bottom of the viewport. */
@media (max-width: 30rem) {
    .caploo-location-picker {
        inset-block-start: auto;
        inset-block-end: 0;
        inset-inline-start: 0;
        inset-inline-end: 0;
        transform: none;
        width: 100%;
        max-height: 88vh;
        border-radius: var(--radius-md) var(--radius-md) 0 0;
        padding-bottom: env(safe-area-inset-bottom, 0px);
    }
}

/* PA-01 — LocationPicker landscape-phone adaptation.
   The picker uses grid-template-rows:auto 1fr auto so __head/__body/__foot
   map to the three rows. On landscape phones (320px tall) the map alone
   (~200px) can exceed available height. We cap max-height, shrink the header
   and footer padding, and let __body (overflow-y:auto) scroll. __head
   becomes sticky inside the grid via position:sticky top:0. */
@media (orientation: landscape) and (max-height: 500px) {
    .caploo-location-picker {
        max-height: calc(100vh - 16px);
        /* Switch grid so body uses remaining space (grid already set this way;
           restating ensures it holds after the bottom-sheet override above) */
        grid-template-rows: auto 1fr auto;
    }
    .caploo-location-picker__head {
        padding-block: 0.5rem;
        position: sticky;
        top: 0;
        z-index: 1;
        background: var(--surface);
    }
    .caploo-location-picker__body {
        padding-block: 0.5rem;
    }
    .caploo-location-picker__foot {
        padding-block: 0.5rem;
        position: sticky;
        bottom: 0;
        z-index: 1;
        background: var(--surface);
    }
}

/* ── AdminModerationPage (F-008b) ───────────────────────────────────────────
   /admin/moderation — moderation queue + audit log. Admin-only surface;
   moderators need readable, scannable layout more than visual polish.
   Mirrors the BEM shape of .admin-analytics and uses Phase 16b/19e tokens.
   ─────────────────────────────────────────────────────────────────────────── */

.admin-moderation {
    max-width: 1100px;
    margin: 0 auto;
    padding: var(--space-3) var(--space-2);
}

/* ── Header ── */
.admin-moderation__header {
    margin-bottom: var(--space-2);
}

.admin-moderation__title {
    font-size: var(--text-fluid-h2, var(--text-xl));
    font-weight: 700;
    margin: 0;
    color: var(--text);
    text-wrap: balance;
}

.admin-moderation__subtitle {
    margin: var(--space-0-5) 0 0;
    color: var(--muted);
    font-size: var(--text-sm);
}

/* ── Tabs ── */
.admin-moderation__tabs {
    display: flex;
    gap: var(--space-0-5);
    border-bottom: 1px solid var(--border);
    margin-bottom: var(--space-2);
}

.admin-moderation__tabs .tab {
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    color: var(--muted);
    font-size: var(--text-sm);
    font-weight: 500;
    padding: var(--space-1) var(--space-1-5);
    min-height: 44px;
    width: auto;
    cursor: pointer;
    transition: color var(--transition), border-color var(--transition);
    margin-bottom: -1px;
}

.admin-moderation__tabs .tab:hover:not(:disabled) {
    color: var(--text);
    background: transparent;
}

.admin-moderation__tabs .tab.tab--active {
    color: var(--accent-text, var(--accent));
    border-bottom-color: var(--accent);
    background: transparent;
}

.admin-moderation__tabs .tab:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
    border-radius: var(--radius-xs);
}

/* ── Filter chips ── */
.admin-moderation__filters {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    margin-bottom: var(--space-2);
}

.filter-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-0-5);
    padding: var(--space-0-5) var(--space-1-5);
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    color: var(--muted);
    font-size: var(--text-sm);
    font-weight: 500;
    min-height: 44px;
    width: auto;
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.filter-chip:hover:not(:disabled) {
    background: var(--surface-alt);
    border-color: var(--accent);
    color: var(--text);
}

.filter-chip.filter-chip--active {
    background: var(--accent-glow);
    border-color: var(--accent);
    color: var(--accent-text, var(--accent));
}

.filter-chip:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}

/* ── Loading + empty states ── */
.admin-moderation__loading,
.admin-moderation__empty {
    padding: var(--space-6) var(--space-4);
    text-align: center;
    color: var(--muted);
    font-size: var(--text-sm);
}

.admin-moderation__empty h2 {
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--text);
    margin: 0 0 var(--space-1);
    text-transform: none;
    letter-spacing: 0;
}

/* ── Table ── */
.admin-moderation__table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
    background: var(--surface-alt);
    border-radius: var(--radius-md);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
}

.admin-moderation__table thead {
    background: transparent;
}

.admin-moderation__table th {
    padding: var(--space-1) var(--space-1-5);
    text-align: start;
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}

.admin-moderation__table td {
    padding: var(--space-1) var(--space-1-5);
    border-bottom: 1px solid rgba(255,255,255,0.05);
    vertical-align: middle;
    color: var(--text);
}

.admin-moderation__table tbody tr:last-child td {
    border-bottom: none;
}

.admin-moderation__table tbody tr:hover td {
    background: rgba(255,255,255,0.03);
}

/* ── Target cell ── */
.target-kind {
    display: inline-block;
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    margin-inline-end: var(--space-1);
}

.target-id {
    font-family: ui-monospace, 'Cascadia Code', 'Menlo', monospace;
    font-size: var(--text-xs);
    background: var(--bg);
    color: var(--muted);
    padding: var(--space-0-25) var(--space-0-5);
    border-radius: var(--radius-xs);
    border: 1px solid var(--border);
}

/* ── Badge ── */
.badge {
    display: inline-flex;
    align-items: center;
    padding: var(--space-0-25) var(--space-1);
    border-radius: var(--radius-pill);
    font-size: var(--text-xs);
    font-weight: 600;
    line-height: 1.4;
    vertical-align: middle;
    margin-inline-start: var(--space-1);
    border: 1px solid transparent;
}

.badge--auto-flag {
    background: rgba(224, 154, 58, 0.15);
    border-color: var(--warning, #e09a3a);
    color: var(--warning-text, #f3c184);
}

/* ── Ticket 11 — severity chips ── */
.severity-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-0-5);
    padding: var(--space-0-25) var(--space-1);
    border-radius: var(--radius-pill);
    font-size: var(--text-xs);
    font-weight: 600;
    line-height: 1.4;
    vertical-align: middle;
    border: 1px solid transparent;
    white-space: nowrap;
}

.severity-chip--low {
    background: var(--surface-alt);
    border-color: var(--border);
    color: var(--text-secondary, var(--muted));
}

.severity-chip--medium {
    background: rgba(224, 154, 58, 0.12);
    border-color: rgba(224, 154, 58, 0.4);
    color: var(--warning-text, #c8890a);
}

.severity-chip--high {
    background: rgba(220, 90, 30, 0.12);
    border-color: rgba(220, 90, 30, 0.4);
    color: #c04a10;
}

.severity-chip--critical {
    background: rgba(200, 30, 30, 0.12);
    border-color: rgba(200, 30, 30, 0.4);
    color: #b91c1c;
}

/* Reasons disclosure (expandable list under a report row) */
.severity-reasons {
    margin-block-start: var(--space-0-5);
}

.severity-reasons__toggle {
    background: none;
    border: none;
    padding: 0;
    font-size: var(--text-xs);
    color: var(--accent);
    cursor: pointer;
    text-decoration: underline;
    font-family: inherit;
}

.severity-reasons__toggle:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
    border-radius: 2px;
}

.severity-reasons__list {
    margin: var(--space-0-5) 0 0;
    padding-inline-start: var(--space-2);
    font-size: var(--text-xs);
    color: var(--muted);
    list-style: disc;
}

.severity-reasons__list li {
    margin-block-end: var(--space-0-25);
}

/* Confidence indicator */
.severity-confidence {
    font-size: var(--text-xs);
    color: var(--muted);
    display: block;
    margin-block-start: var(--space-0-25);
}

/* ── Actions cell ── */
.admin-moderation__table td.actions {
    white-space: nowrap;
    text-align: end;
}

/* ── .btn + .btn-sm + .btn-warn (scoped to admin-moderation table) ── */
.admin-moderation .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    font-size: var(--text-sm);
    font-weight: 500;
    padding: var(--space-0-5) var(--space-1-5);
    min-height: 32px;
    width: auto;
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition), color var(--transition);
    font-family: inherit;
}

.admin-moderation .btn:hover:not(:disabled) {
    background: var(--surface-alt);
    border-color: var(--accent);
    color: var(--text);
}

.admin-moderation .btn:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}

.admin-moderation .btn.btn-sm {
    font-size: var(--text-xs);
    padding: var(--space-0-25) var(--space-1);
    min-height: 28px;
    border-radius: var(--radius-xs);
}

.admin-moderation .btn.btn-warn {
    border-color: var(--warning, #e09a3a);
    color: var(--warning-text, #f3c184);
}

.admin-moderation .btn.btn-warn:hover:not(:disabled) {
    background: rgba(224, 154, 58, 0.12);
    border-color: var(--warning, #e09a3a);
    color: var(--warning-text, #f3c184);
}

.admin-moderation .btn:disabled {
    opacity: .4;
    cursor: not-allowed;
}

/* Gap between action buttons */
.admin-moderation__table td.actions .btn + .btn {
    margin-inline-start: var(--space-1);
}

/* ── Pager ── */
.admin-moderation__pager {
    display: flex;
    justify-content: center;
    padding: var(--space-2) 0 var(--space-1);
}

/* ── Toast ── */
.admin-moderation__toast {
    position: fixed;
    inset-block-end: var(--space-4);
    inset-inline-start: 50%;
    transform: translateX(-50%);
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: var(--space-1) var(--space-3);
    font-size: var(--text-sm);
    color: var(--text);
    box-shadow: var(--shadow);
    z-index: 200;
    white-space: nowrap;
    animation: fadeSlideUp .2s ease forwards;
    pointer-events: none;
}

/* ── Muted utility on code/inline elements ── */
.admin-moderation code.muted {
    color: var(--muted);
}

/* ── Responsive: narrow viewport — table scrolls horizontally ── */
@media (max-width: 48rem) {
    .admin-moderation {
        padding: var(--space-2) var(--space-1);
    }

    .admin-moderation__table {
        display: block;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    .admin-moderation__table th:nth-child(4),
    .admin-moderation__table td:nth-child(4) {
        display: none;
    }
}

@media (max-width: 30rem) {
    .admin-moderation__tabs .tab {
        font-size: var(--text-xs);
        padding: var(--space-1) var(--space-1);
    }

    .admin-moderation__table th:nth-child(3),
    .admin-moderation__table td:nth-child(3) {
        display: none;
    }
}

/* ── Coarse pointer — 44px touch targets on chips ── */
@media (pointer: coarse) {
    .filter-chip {
        min-height: 44px;
    }
}
/* End — F-008b */

/* ════════════════════════════════════════════════════════════════════════════
   Phase 20b — Mobile P0 CSS fixes
   M-006 / M-008 / M-070
   ════════════════════════════════════════════════════════════════════════════ */

/* M-006 — Expiry-actions overflow: at ≤30rem, let action buttons stack
   full-width so they never clip or overlap on 320-480px viewports. */
@media (max-width: 30rem) {
    .expiry-actions {
        flex-wrap: wrap;
        gap: var(--space-2, .5rem);
    }
    .expiry-actions > * {
        flex: 1 1 100%;
    }
}

/* M-008 — Composer textarea too tall on mobile: cap max-height at 120px
   (≈4 lines) on narrow viewports so the send button stays visible without
   scrolling. The desktop 200px cap is unaffected. */
@media (max-width: 30rem) {
    .caploo-composer-sidebar__caption {
        min-height: 56px !important;
        max-height: 120px !important;
    }
}

/* M-009 — QR disclosure in settings drawer: Blazor-controlled toggle button.
   Collapsed by default on mobile; on desktop the toggle is hidden so the QR
   is always visible. Replaces the old <details> pattern which broke in Chrome
   120+ when ::details-content switched to content-visibility:hidden (not
   overrideable by author display:flex !important on descendants). */
.qr-disclosure__summary {
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: var(--space-1, 8px);
    font-size: var(--text-sm);
    color: var(--accent);
    padding: var(--space-1) 0;
    background: none;
    border: none;
    width: 100%;
    text-align: left;
}
.qr-disclosure__summary::before {
    content: "▶";
    font-size: .6em;
    transition: transform 120ms ease;
}
.qr-disclosure__summary--open::before { transform: rotate(90deg); }
@media (min-width: 48rem) {
    .qr-disclosure__summary { display: none; }
}

/* M-007 — Host-secret reveal: stack the Copy-link button below the URL on
   narrow viewports so the <code> block always has full width to word-break. */
@media (max-width: 30rem) {
    .create-reveal-card .link-display {
        flex-direction: column;
        align-items: stretch;
    }
    .create-reveal-card .link-display button {
        width: 100%;
    }
}

/* M-070 — QR projection canvas: ensure the canvas never overflows its
   container on narrow viewports. aspect-ratio: 1 preserves the square
   geometry without a fixed width/height. Now a div wrapping an SVG. */
.qr-projection-canvas {
    max-width: 100%;
    height: auto;
    aspect-ratio: 1;
}
.qr-projection-canvas svg { width: 100%; height: 100%; display: block; }

/* =============================================================================
 * Phase 20b — Mobile P2/P3 fixes
 * M-035 M-037 M-042 M-043 M-044 M-045 M-046 M-053 M-062 M-071 M-074 M-078
 * M-079 M-080 M-096 M-097 M-103 M-104 M-117 M-118 M-136 M-142 M-153 M-158
 * M-159 M-161
 * =========================================================================== */

/* M-035 — NotificationBell badge collision with user-chip at <=48rem.
   A small gap between the bell and the auth chip prevents the unread-badge
   from visually merging with the avatar at narrow viewports. */
@media (max-width: 48rem) {
    .bell-wrapper {
        margin-inline-end: 4px;
    }
}

/* M-037 — Site-footer font-size below iOS body-readability floor at mobile.
   var(--text-xs) ~11px is fine for desktop but strains low-vision users at
   320px. 0.8125rem (13px) stays compact yet readable. */
@media (max-width: 48rem) {
    .site-footer {
        font-size: 0.8125rem;
    }
}

/* M-042 — How-does-it-work disclosure focus ring suppressed by Phase 19e
   reset. Re-instate a 2px accent outline for keyboard / switch-access users. */
.how-details__trigger:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/* M-043 / M-063 — auth-aside separator (dot) orphans to its own line at <=30rem
   in es/ko where the combined text is longer. Hide the visual separator on
   narrow viewports; flex-wrap + gap already provide rhythm without the dot. */
@media (max-width: 30rem) {
    .auth-aside > [aria-hidden="true"] {
        display: none;
    }
}

/* M-044 — Hero droplet logo is 42px on a 320x568 phone — half the hero
   height before any CTA renders. Shrink to 28px so the heading + tagline +
   CTA reach closer to the fold. */
@media (max-width: 48rem) {
    .hero--landing > svg:first-child {
        width: 28px;
        height: 28px;
    }
}

/* M-045 — Join card scroll-margin-top: the #join anchor target lands under
   the fixed navbar when smooth-scrolled to. scroll-margin-top ensures the
   heading clears the navbar + a small breathing gap. */
.join-card[id="join"] {
    scroll-margin-top: calc(var(--navbar-h, 52px) + 1rem);
}

/* M-046 — Host-link chip overflows at <=30rem in long-locale strings (ko, es).
   max-width: 100% + flex-wrap stops the chip from forcing horizontal scroll. */
.host-link--landing {
    max-width: 100%;
    flex-wrap: wrap;
}

/* M-053 — Auth-card footer wraps awkwardly in Korean at 320px. word-break:
   keep-all prevents mid-word splits on CJK while still wrapping at word
   boundaries. */
@media (max-width: 30rem) {
    .auth-card__footer {
        word-break: keep-all;
    }
}

/* M-062 — Reveal-overlay Copy link button has no visual affordance on mobile.
   Ensure the min-height:44px touch target on narrow viewports. */
@media (pointer: coarse), (max-width: 48rem) {
    .create-reveal-card .link-display button {
        min-height: 44px;
    }
}

/* M-071 — Settings drawer footer-actions row (regen trigger + host-mgmt
   disclosure) has no flex-wrap, causing overflow at 320px. */
@media (max-width: 30rem) {
    .settings-section__footer-actions {
        flex-direction: row;
        flex-wrap: wrap;
        gap: var(--space-1);
    }
}

/* M-074 — Close-drop button sits immediately below the Extend section. A top
   divider prevents P-06 Marcus from accidentally closing instead of extending. */
@media (max-width: 48rem) {
    .btn-close-drop {
        margin-top: var(--space-2);
        padding-top: var(--space-2);
        border-top: 1px solid color-mix(in srgb, var(--danger, #e53e3e) 30%, transparent);
    }
}

/* M-078 — feed-load-error inline style="" moved to CSS. */
.feed-load-error {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1-5);
    padding: var(--space-5) var(--space-3);
}
.feed-load-error .error {
    margin: 0;
}

/* M-079 — feed-live-paused inline style="" moved to CSS. */
.feed-live-paused {
    text-align: center;
    font-size: 0.8rem;
    color: var(--muted);
    padding: var(--space-1-5) var(--space-3) 0;
}

/* M-080 — Regen-access dialog checkboxes: 2-column grid locks the checkbox to
   column 1 so description text stays aligned on narrow viewports. */
@media (max-width: 30rem) {
    .regen-option {
        display: grid;
        grid-template-columns: 20px 1fr;
        gap: 0 var(--space-1-5);
    }
}

/* M-096 — Access-method switch hit area: visible 32x18px toggle is below the
   44px mandate. Extend hit area without changing the visual track size. */
@media (pointer: coarse), (max-width: 48rem) {
    .access-switch {
        min-width: 44px;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}

/* M-097 / D-118 — PIN/link Copy buttons: 44px min-height is unconditional;
   pointer-coarse gate was wrong — mouse users also benefit from the full target. */
.pin-display button,
.link-display button {
    min-height: 44px;
}

/* M-103 — DateRangePicker preset chips wrap to 2+ rows inside the <=30rem
   bottom-sheet. Single horizontally-scrolling row is more compact. */
@media (max-width: 30rem) {
    .drp__presets {
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        padding-bottom: 2px;
    }
    .drp__presets::-webkit-scrollbar { display: none; }
}

/* M-104 — Filter bottom-sheet action row: promote Apply to full-width primary,
   demote Clear to a text-only button, compact Close. Reverse column order
   keeps Apply closest to the thumb at the bottom of the sheet. */
@media (max-width: 48rem) {
    .caploo-filter-popover__actions {
        flex-direction: column-reverse;
        gap: var(--space-1);
    }
    .caploo-filter-popover__actions .btn-primary {
        width: 100%;
        justify-content: center;
    }
    .caploo-filter-popover__actions .btn-secondary {
        background: transparent;
        border-color: transparent;
        color: var(--accent);
        width: auto;
        align-self: center;
        min-height: 44px;
    }
}

/* M-117 — HelpDrawer FAQ body text is ~14px on mobile — below readability
   comfort for P-03 Eleanor. 15px (0.9375rem) is still compact but legible. */
@media (max-width: 48rem) {
    .help-drawer__faq p {
        font-size: 0.9375rem;
    }
}

/* M-118 — Help-drawer support link word-break for CJK text (Korean "문의하기"
   has no ASCII break opportunity and wraps awkwardly in the 294px column). */
.help-drawer__link {
    word-break: keep-all;
    overflow-wrap: anywhere;
}

/* M-136 — Recap play-story CTA is companion to the h1. On mobile it wraps
   below the heading via flex-wrap. Full-width + centred makes it immediately
   obvious to P-09 Tova who wants to tap Play before scrolling the timeline. */
@media (max-width: 48rem) {
    .recap-play-story-btn {
        width: 100%;
        justify-content: center;
    }
}

/* M-142 — MyDrops filter tabs wrap to multiple lines at 320px in ko/es.
   Horizontal-scroll with snap keeps the tabs on one line. */
.filter-row {
    display: flex;
    gap: var(--space-1);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    scroll-snap-type: x mandatory;
    padding-bottom: 2px;
    margin-bottom: var(--space-1-5);
}
.filter-row::-webkit-scrollbar { display: none; }
.tab,
.tab.tab-active {
    flex-shrink: 0;
    scroll-snap-align: start;
    white-space: nowrap;
    padding: var(--space-1) var(--space-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: var(--surface);
    color: var(--muted);
    font-size: var(--text-sm);
    cursor: pointer;
    min-height: 36px;
    transition: background var(--transition), color var(--transition),
                border-color var(--transition);
}
.tab.tab-active {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-on-accent);
    font-weight: 500;
}
.tab:not(.tab-active):hover {
    background: var(--surface-alt);
    border-color: var(--accent-dim, var(--accent));
    color: var(--text);
}
.tab:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}
@media (pointer: coarse), (max-width: 48rem) {
    .tab, .tab.tab-active { min-height: 44px; }
}

/* M-153 — "Mark all read" and notifications settings link use .btn-ghost.btn-sm
   which renders at ~28px on mobile. Bump to 44px on coarse-pointer devices. */
@media (pointer: coarse), (max-width: 48rem) {
    .btn-ghost,
    .btn-ghost.btn-sm {
        min-height: 44px;
    }
}

/* M-158 — HelpDrawer FAQ uses browser-native <details> disclosure marker.
   Remove native marker; replace with CSS-animated custom chevron consistent
   with Phase 19e and the .qr-disclosure pattern already in the codebase. */
.help-drawer__faq summary::-webkit-details-marker { display: none; }
.help-drawer__faq summary {
    list-style: none;
    gap: var(--space-1);
}
.help-drawer__faq summary::before {
    content: "\203A";
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1rem;
    font-size: 1.1em;
    color: var(--muted);
    transition: transform 120ms ease;
    flex-shrink: 0;
    line-height: 1;
}
.help-drawer__faq[open] > summary::before {
    transform: rotate(90deg);
    color: var(--accent);
}
@media (prefers-reduced-motion: reduce) {
    .help-drawer__faq summary::before { transition: none; }
}

/* M-159 — ScrollNavigator z-index: the pill at z-index:45 is occluded by the
   capture-panel (z-index:50 per Phase 16b). Bump to 55 so it stays above the
   capture-panel but below modals (200) and the mobile drawer (60-70). */
.scroll-nav {
    z-index: 55;
}

/* M-161 — QR projection close button: add safe-area-inset-top to the inner
   container so the close button + canvas clear the iPhone notch in portrait. */
.qr-projection-inner {
    padding-top: calc(0.5rem + env(safe-area-inset-top, 0px));
}

/* ══════════════════════════════════════════════════════════════════════════
   Phase 19h — Desktop + Tablet P0 remediation (D-001 … D-007)
   Fixes: narrow columns at 1920 px, zero-styled page wrappers, settings /
   help drawer widths, and the 820 px tablet-portrait navbar overflow.
   All max-width values reuse existing Phase 16b tokens:
     --content-rail  min(100%, 68rem) = 1088 px
     --content-max   680 px
     --gutter-x      clamp(0.75rem, 2.5vw, 1.5rem)
   ══════════════════════════════════════════════════════════════════════════ */

/* ── D-003: MyDrops page — full CSS ruleset (was completely unstyled) ──── */

.my-drops-page {
    max-width: var(--content-rail);
    margin-inline: auto;
    padding: var(--space-4) var(--gutter-x) var(--space-8);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}
.my-drops-page > h1 {
    font-size: var(--text-fluid-h1, var(--text-2xl));
    font-weight: 700;
    color: var(--text);
    margin: 0;
    text-wrap: balance;
}

/* PV-06 — device-hosted drops section */
.device-drops {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}
.device-drops__heading {
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--text);
    margin: 0;
}
.device-drops__empty {
    color: var(--text);
    font-size: var(--text-sm);
    margin: 0;
}
.device-drops__hint {
    font-size: var(--text-sm);
    margin: 0;
}
.my-drops-section-divider {
    border: none;
    border-top: 1px solid var(--border);
    margin: 0;
}
/* "Open as host" CTA — full-width, 44px min touch target (Fitts) */
.device-drop-open-host {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding-inline: var(--space-3);
    width: 100%;
    background: color-mix(in srgb, var(--accent) 10%, var(--surface));
    border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
    border-radius: var(--radius-md);
    color: var(--accent);
    text-decoration: none;
    font-size: var(--text-sm);
    font-weight: 600;
    transition: background 120ms, border-color 120ms;
}
.device-drop-open-host:hover {
    background: color-mix(in srgb, var(--accent) 18%, var(--surface));
    border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
}
.device-drop-open-host:focus-visible {
    box-shadow: var(--focus-ring);
    outline: none;
}

/* PV-06 Pass 2 — recover-a-drop disclosure + form */
.recover-drop {
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface-alt);
    margin-top: var(--space-2);
}
.recover-drop-trigger {
    display: flex;
    align-items: center;
    gap: var(--space-1-5);
    padding: var(--space-2) var(--space-3);
    cursor: pointer;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--muted);
    list-style: none;
    min-height: 44px;
    user-select: none;
    transition: color 120ms;
}
.recover-drop-trigger::-webkit-details-marker { display: none; }
.recover-drop-trigger:hover,
.recover-drop[open] .recover-drop-trigger { color: var(--text); }
.recover-drop-trigger:focus-visible { box-shadow: var(--focus-ring); outline: none; border-radius: var(--radius-md); }
.recover-drop-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: 0 var(--space-3) var(--space-3);
}
.recover-drop-label {
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--text);
}
.recover-drop-row {
    display: flex;
    gap: var(--space-2);
    align-items: stretch;
}
.recover-drop-row .input {
    flex: 1;
    min-width: 0;
    min-height: 44px;
}
.recover-drop-submit {
    min-height: 44px;
    min-width: 44px;
    white-space: nowrap;
    flex-shrink: 0;
}
.recover-drop-status { min-height: 1.25rem; }
.recover-drop-message {
    font-size: var(--text-sm);
    margin: 0;
}
.recover-drop-message--success { color: var(--success, #1a7f4b); }

@media (max-width: 30rem) {
    .recover-drop-row { flex-direction: column; }
    .recover-drop-submit { width: 100%; }
}

/* Filter tab strip */
.filter-row {
    display: flex;
    gap: var(--space-1);
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
}
.filter-row .tab {
    padding: var(--space-1) var(--space-2);
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    color: var(--muted);
    font-size: var(--text-sm);
    font-weight: 500;
    cursor: pointer;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    min-height: 36px;
    line-height: 1;
    white-space: nowrap;
    transition: color 120ms, border-color 120ms;
    margin-bottom: -1px;
}
.filter-row .tab:hover { color: var(--text); }
.filter-row .tab:focus-visible { box-shadow: var(--focus-ring); outline: none; }
.filter-row .tab.tab-active {
    color: var(--accent);
    border-bottom-color: var(--accent);
    font-weight: 600;
}

/* Drop card grid */
.drop-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr));
    gap: var(--space-3);
}

.drop-card {
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: var(--space-3);
    display: flex;
    flex-direction: column;
    gap: var(--space-1-5);
    transition: border-color 150ms;
}
.drop-card:hover {
    border-color: var(--border);
}
.drop-card-link {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1);
    color: var(--text);
    text-decoration: none;
    font-size: var(--text-md);
    font-weight: 500;
}
.drop-card-link:hover strong { color: var(--text); }
.drop-card-link:focus-visible {
    box-shadow: var(--focus-ring);
    outline: none;
    border-radius: var(--radius-sm);
}

/* Sample badge */
.sample-badge {
    display: inline-flex;
    align-items: center;
    padding: 1px 7px;
    background: color-mix(in srgb, var(--accent) 14%, var(--surface));
    color: var(--accent);
    border-radius: var(--radius-pill);
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: .03em;
}

/* Status badges (live inside .drop-card-link) */
.drop-card-link .status {
    display: inline-flex;
    align-items: center;
    padding: 1px 8px;
    border-radius: var(--radius-pill);
    font-size: var(--text-xs);
    font-weight: 600;
    margin-inline-start: auto;
}
.drop-card-link .status.status-active {
    background: color-mix(in srgb, var(--success) 16%, var(--surface));
    color: var(--success-text);
}
.drop-card-link .status.status-expired {
    background: color-mix(in srgb, var(--muted) 16%, var(--surface));
    color: var(--muted);
}
.drop-card-link .status.status-closed {
    background: color-mix(in srgb, var(--danger) 14%, var(--surface));
    color: var(--danger-text);
}
.drop-card-link .status.status-other {
    background: var(--surface-alt);
    color: var(--muted);
}

/* ── D-004: Profile page wrapper (was completely unstyled) ──────────────── */

.profile-page {
    max-width: var(--content-max);
    margin-inline: auto;
    padding: var(--space-4) var(--gutter-x) var(--space-8);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}
.profile-page > h1 {
    font-size: var(--text-fluid-h1, var(--text-2xl));
    font-weight: 700;
    color: var(--text);
    margin: 0;
    text-wrap: balance;
}
.profile-page__auth-required {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--space-3);
    padding: var(--space-8) var(--gutter-x);
    min-height: 50vh;
    justify-content: center;
}
.profile-page__auth-icon {
    color: var(--muted);
    flex-shrink: 0;
}
.profile-page__auth-heading {
    font-size: var(--text-xl);
    font-weight: 700;
    margin: 0;
}
.profile-page__auth-copy {
    max-width: 28rem;
    margin: 0;
    font-size: var(--text-sm);
}
.profile-page__auth-actions {
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
    justify-content: center;
    margin-top: var(--space-1);
}
/* C-003 (Phase 20a Pass 2): label spacing + sticky save bar on mobile.
   Label spacing: 4px below each label before the next input/select/textarea
   (Gestalt Proximity). Sticky save: keeps Save visible without scrolling;
   negative margin-inline offsets .card's 1.5rem padding so the background
   bleeds edge-to-edge; safe-area-inset-bottom handles home-bar notch. */
@media (max-width: 48rem) {
    .profile-page .card > label {
        margin-block-end: 4px;
        display: block;
    }

    .profile-save-bar {
        position: sticky;
        bottom: 0;
        padding-bottom: env(safe-area-inset-bottom, 0.5rem);
        background: var(--color-surface, var(--surface, #fff));
        padding-block-start: 0.5rem;
        margin-inline: -1.5rem;
        padding-inline: 1.5rem;
        box-shadow: 0 -1px 0 var(--color-border, var(--border-soft, #eee));
        z-index: 10;
    }
}

/* ── D-005: Settings drawer — wider at tablet and desktop ───────────────── */
/* Base: 360 px (mobile). Tablet portrait+: 480 px. Desktop: 560 px. */

@media (min-width: 48rem) {
    .settings-drawer {
        width: min(480px, 60vw);
    }
}
@media (min-width: 64rem) {
    .settings-drawer {
        width: min(560px, 45vw);
    }
}

/* ── D-006: HelpDrawer — wider at desktop ───────────────────────────────── */
/* Base: 420 px. Desktop 1024 px+: 560 px so chord catalog renders single-line. */

@media (min-width: 64rem) {
    .help-drawer {
        width: min(560px, 40vw);
    }
}

/* ── D-007: Navbar overflow at 820 px tablet portrait ───────────────────── */
/* Collapse lang + theme triggers to icon-only; tighten drop-info cap. */

@media (min-width: 48rem) and (max-width: 64rem) {
    .lang-trigger__name { display: none; }
    .navbar .lang-trigger {
        padding-inline: 0;
        width: var(--ctl-h, 40px);
        justify-content: center;
    }
    .theme-trigger__name { display: none; }
    .navbar .theme-trigger {
        padding-inline: 0;
        width: var(--ctl-h, 40px);
        justify-content: center;
    }
    .navbar-countdown__prefix { display: none; }
    .navbar-drop-info {
        max-width: 30vw;
    }
    .navbar-actions .nav-cluster + .nav-cluster {
        padding-inline-start: 8px;
    }
}

/* ── D-001: Home page — 2-column desktop layout at >= 64 rem ─────────────── */
/* Switch to 2-column grid: hero + explainer + CTA in column 1,
   the Join card sticky in column 2. */

@media (min-width: 64rem) {
    .home-container--landing {
        max-width: var(--content-rail);
        display: grid;
        grid-template-columns: 1fr min(400px, 42%);
        column-gap: var(--space-6);
        row-gap: var(--space-3);
        align-items: start;
        padding-top: var(--space-6);
    }
    .home-container--landing > * {
        grid-column: 1;
    }
    .home-container--landing .join-card {
        grid-column: 2;
        grid-row: 1 / span 6;
        align-self: start;
        position: sticky;
        top: calc(var(--navbar-h, 52px) + var(--space-3));
    }
    /* Redundant at desktop: navbar already exposes Sign in / Sign up */
    .home-container--landing .auth-aside {
        display: none;
    }
}

/* ── D-002: CreateDrop — lift .home-container from 440 px at desktop ─────── */
/* .home-container--landing overrides this with its own grid rule above.
   This selector covers CreateDrop and any other page using .home-container
   without the --landing modifier. */

@media (min-width: 64rem) {
    .home-container {
        max-width: var(--content-rail);
    }
}

/* ── D-010: Sidebar responsive width — fluid between 260–360px on wide viewports */
@media (min-width: 64rem) {
    .caploo-shell:not(.caploo-shell--collapsed) {
        --sidebar-w: clamp(260px, 22vw, 360px);
    }
}

/* ── D-043: Navbar drop-info region max-width to prevent overflow past the
   centre cluster on very wide monitors. */
@media (min-width: 64rem) {
    .navbar-drop-info {
        max-width: clamp(20rem, 30vw, 40rem);
        overflow: hidden;
    }
}

/* ── D-017: NotificationSettings page — content rail + preferences matrix */
.notification-settings {
    max-width: var(--content-max);
    margin-inline: auto;
    padding: var(--space-4) var(--gutter-x) var(--space-8);
}
.prefs-matrix {
    width: 100%;
    border-collapse: collapse;
}
.prefs-matrix th,
.prefs-matrix td {
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--border);
    text-align: start;
    vertical-align: middle;
}
.prefs-matrix th {
    font-weight: 600;
    font-size: var(--text-sm);
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .03em;
}

/* ── D-026: Playbook component content area — cap at content rail width */
@media (min-width: 64rem) {
    .playbook-main {
        max-width: var(--content-rail, 960px);
        margin-inline: auto;
        width: 100%;
    }
}

/* ── D-047: Notification badge — clip if the count overflows the badge circle */
.notif-indicator,
.bell-badge {
    max-width: 24px;
    overflow: hidden;
    text-overflow: clip;
}

/* ── D-048: User chip in navbar — truncate long display names gracefully */
button.user-chip {
    max-inline-size: 16ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── D-052: Navbar backdrop-filter fallback for browsers that don't support it */
@supports not (backdrop-filter: blur(8px)) {
    .navbar {
        background: var(--surface);
    }
}

/* ── D-058: Host-access how-details trigger hover state */
.how-details__trigger:hover,
summary.how-details__trigger:hover {
    background: var(--surface-alt);
    border-radius: var(--radius-sm);
}

/* ── D-076: Create-drop host-access code row — stacks label + input + copy */
.create-reveal__code-row {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-2);
}

/* ── Create-drop reveal QR code — centered join QR matching prototype revealPin modal */
.create-reveal-qr {
    display: flex;
    justify-content: center;
    margin: var(--space-2) 0 var(--space-1);
}
.create-reveal-qr svg {
    width: 160px;
    height: 160px;
}

/* ── D-082: Duration help-text — semantic class instead of inline style */
.duration-help {
    font-size: var(--text-sm);
    color: var(--muted);
    margin: .15rem 0 0;
}

/* ── D-119: Access-rotated-banner dismiss button — 44px tap target */
.access-rotated-banner__dismiss {
    min-height: 44px;
    min-width: 44px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   Phase 19h — Desktop + Tablet P1 remediation batch 2
   Covers: D-012 D-018b D-028 D-034 D-039 D-042 D-056 D-086 D-091
           D-111 D-128 D-147 D-150
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── D-012: Feed search input wider at 1600 px+ ──────────────────────────── */
@media (min-width: 100rem) {
    .caploo-toolbar__row > .caploo-searchbar {
        max-width: 720px;
    }
}

/* ── D-018b: Admin moderation table — sticky header + defined column widths */
.admin-moderation__table thead th {
    position: sticky;
    top: var(--navbar-h, 52px);
    z-index: 2;
    background: var(--surface-alt);
}
.admin-moderation__table th:nth-child(1),
.admin-moderation__table td:nth-child(1) { width: 28%; }
.admin-moderation__table th:nth-child(2),
.admin-moderation__table td:nth-child(2) { width: 18%; }
.admin-moderation__table th:nth-child(3),
.admin-moderation__table td:nth-child(3) { width: 14%; }
.admin-moderation__table th:nth-child(4),
.admin-moderation__table td:nth-child(4) { width: 18%; }
.admin-moderation__table th:nth-child(5),
.admin-moderation__table td:nth-child(5) { width: 22%; }

/* ── D-034: prefers-contrast: more — bump border + muted contrast ─────────
   Non-forced-colors high-contrast preference (Windows "More contrast" /
   macOS "Increase contrast"). Bumps border opacity and muted text toward
   APCA Lc ≥ 60. */
@media (prefers-contrast: more) {
    :root {
        --border: color-mix(in srgb, currentColor 35%, transparent);
        --muted:  color-mix(in srgb, var(--text) 75%, transparent);
    }
    .caploo-searchbar,
    .caploo-composer-sidebar__caption,
    input[type="text"],
    input[type="email"],
    input[type="search"],
    input[type="password"],
    select,
    textarea {
        border-width: 2px;
    }
}

/* ── D-039: Recap container — wider rail at ultrawide viewports ───────────── */
@media (min-width: 90rem) {
    .recap-container {
        max-width: 960px;
    }
}

/* ── D-042: Navbar — increase side padding at ultrawide (≥ 1600 px) ─────── */
@media (min-width: 100rem) {
    .navbar {
        padding-inline: clamp(1.5rem, 4vw, 4rem);
    }
}

/* ── D-056: Home how-it-works — wider at ultrawide viewports ─────────────── */
@media (min-width: 90rem) {
    .how-it-works {
        max-width: 720px;
    }
}

/* ── D-086: ViewModeToggle — keyboard-shortcut catalog hints on the wrapper
   The aria-keyshortcuts value is added to the radiogroup via the Razor change
   for the same finding; this CSS rule anchors the tooltip appearance. */
.caploo-view-toggle[aria-keyshortcuts] {
    position: relative;
}

/* ── D-091: QR projection canvas — larger at ultrawide for venue display ─── */
@media (min-width: 90rem) {
    .qr-projection-canvas {
        width: 800px;
        height: 800px;
        max-width: 80vmin;
    }
}

/* ── D-111: Composer character counter — show on desktop when ≥ 800 chars ──
   M-089 only showed the counter at ≤ 48rem (mobile).  The counter element is
   always rendered (aria-live); we make it visible at desktop via a CSS class
   added by the Razor component.  At ≥ 49rem, hidden by default; .is-near-limit
   overrides. */
@media (min-width: 48rem) {
    .composer-char-counter.is-near-limit {
        display: block;
    }
}

/* ── D-128: ShareMenu modal — wider at ultrawide (≥ 90rem / 1440 px) ────── */
@media (min-width: 90rem) {
    .share-menu--modal {
        width: min(92vw, 680px);
    }
}

/* ── D-147: LanguageMenu — max-height + overflow so it doesn't clip on
   small-screen laptops (700 px-tall display) ──────────────────────────── */
.lang-menu {
    max-height: 70vh;
    overflow-y: auto;
}

/* ── D-150: LanguageMenu ghost tokens in light theme ─────────────────────── */
[data-theme="light"] .lang-menu .switcher-option {
    color: var(--text);
}
[data-theme="light"] .lang-menu .switcher-option:hover {
    background: color-mix(in srgb, var(--accent) 8%, transparent);
}
[data-theme="light"] .lang-menu .switcher-option.is-active {
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 12%, transparent);
}

/* ═══════════════════════════════════════════════════════════════════════════
   Phase 19h — Desktop + Tablet P2/P3 remediation
   Covers: D-045 D-049 D-057 D-059 D-062 D-063 D-073 D-080 D-083 D-084
           D-093 D-094 D-100 D-102 D-103 D-114 D-115 D-125 D-146 D-160
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── D-045: LanguageMenu + ThemeMenu icon-only at 64–80rem (1024–1280px) ── */
/* Between tablet (handled by D-007 at 48–64rem) and wide desktop (1280px+)
   the triggers carry the full native-name label; at 1024–1280px the navbar
   can be cramped on form-factor laptops — collapse to icon-only. */
@media (min-width: 64rem) and (max-width: 80rem) {
    .lang-trigger__name { display: none; }
    .navbar .lang-trigger {
        padding-inline: 0;
        width: var(--ctl-h, 40px);
        justify-content: center;
    }
    .theme-trigger__name { display: none; }
    .navbar .theme-trigger {
        padding-inline: 0;
        width: var(--ctl-h, 40px);
        justify-content: center;
    }
}

/* ── D-049: User-menu dropdown — proper absolute positioning ────────────── */
/* The .user-menu was rendered without any CSS; it would flow inline after
   the user-chip. Add absolute positioning so it drops below the trigger. */
.navbar-auth {
    position: relative;
}
.user-menu {
    position: absolute;
    inset-block-start: calc(100% + 6px);
    inset-inline-end: 0;
    z-index: 90;
    min-width: 200px;
    padding: var(--space-1) 0;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    display: flex;
    flex-direction: column;
}
@media (prefers-reduced-motion: no-preference) {
    .user-menu { animation: fadeIn .12s ease forwards; }
}
.user-menu a[role="menuitem"],
.user-menu button[role="menuitem"] {
    display: flex;
    align-items: center;
    width: 100%;
    padding: var(--space-1) var(--space-2);
    font-size: var(--text-sm);
    color: var(--text);
    text-decoration: none;
    background: transparent;
    border: none;
    font-family: inherit;
    cursor: pointer;
    text-align: start;
    min-height: 44px;
    transition: background var(--transition);
}
.user-menu a[role="menuitem"]:hover,
.user-menu button[role="menuitem"]:hover {
    background: var(--surface);
    color: var(--text);
}
.user-menu a[role="menuitem"]:focus-visible,
.user-menu button[role="menuitem"]:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}
.user-menu hr {
    margin: var(--space-1) 0;
    border: none;
    border-top: 1px solid var(--border);
}

/* ── D-057: JOIN card — constrained width in the desktop 2-column layout ── */
/* The join card already lives in its own grid column at ≥ 64rem; narrow the
   interior card so it doesn't balloon past 400px in a wide right column. */
@media (min-width: 64rem) {
    .home-container--landing .join-card {
        max-width: 400px;
        margin-inline: auto;
    }
}

/* ── D-059: Host-link — flex-wrap for long locales (Korean) ──────────────── */
.host-link--landing {
    flex-wrap: wrap;
    justify-content: center;
}

/* ── D-062: Hero droplet SVG — scale up at ultrawide for visual presence ── */
@media (min-width: 90rem) {
    .hero--landing > svg:first-child {
        width: 64px;
        height: 64px;
    }
}

/* ── D-063: Hero H1 — fluid type scale at ultrawide ─────────────────────── */
@media (min-width: 90rem) {
    .hero--landing h1 {
        font-size: clamp(2.4rem, 4.5vw, 3.6rem);
    }
}

/* ── D-073: /account-deleted — CTA row below existing copy ──────────────── */
/* CTAs added to AccountDeletedPage.razor; these classes style the wrapper. */
.account-deleted__ctas {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    justify-content: center;
    margin-top: var(--space-4);
}

/* ── D-080: "Clear template" link — more tappable padding ───────────────── */
.template-clear-btn,
button.link-button.template-clear-btn {
    padding: var(--space-1) var(--space-2);
    min-height: 36px;
}

/* ── D-083: "Join a drop instead" escape-hatch — visible at desktop top ──── */
/* At ≥64rem, float the link to the top-right of the create page. */
@media (min-width: 64rem) {
    .create-join-escape {
        align-self: flex-end;
        text-align: end;
        order: -10;
    }
}

/* ── D-084: CreateDrop hero heading — fluid scale at ultrawide ───────────── */
@media (min-width: 90rem) {
    .home-container .hero h1:not(.hero__title),
    .home-container--create .hero h1:not(.hero__title) {
        font-size: clamp(2rem, 3.5vw, 2.8rem);
    }
}

/* ── D-093: FeedToolbar live-badge — aria-live state changes ─────────────── */
/* The badge itself is already in the toolbar; the wrapper needs aria-live
   so SR users hear "Live → Closed" transitions. Applied to the title wrapper
   in FeedPage.razor via the .feed-status-live class added to the badge. */
.caploo-toolbar__title[aria-live] {
    /* preserve layout; the attribute is set on the element in Razor */
}

/* ── D-094: Settings overlay scrim — below navbar so toolbar stays usable ─ */
/* At desktop the scrim at z-index 250 occludes the navbar (z-index 100).
   Drop it to 95 so the navbar floats above the scrim while the drawer
   (260) still overlays everything. */
.settings-overlay {
    z-index: 95;
}
/* The navbar must explicitly sit above the scrim when the drawer is open.
   We bump it via a modifier on the navbar that FeedPage.razor toggles. */
.navbar.drawer-open {
    z-index: 110;
}

/* ── D-100: Share/Report/Settings text labels at ≤ 80rem (≤ 1280px) ────── */
/* At 1280px the navbar cluster budget is tight; collapse text labels from
   the context cluster buttons to icon-only. */
@media (max-width: 80rem) {
    .share-trigger-btn .share-trigger-label { display: none; }
}

/* ── D-102: Reaction stack — expand on hover at desktop (pointer: fine) ─── */
@media (pointer: fine) {
    .reaction-stack:hover .reactor-avatar {
        transform: translateX(0);
    }
}

/* ── D-103: Scroll-nav pill — reposition near feed toolbar at desktop ────── */
@media (min-width: 64rem) {
    .caploo-scroll-pill {
        bottom: 80px;
        right: 24px;
    }
}

/* ── D-114: Privacy switch — 44px tap area at coarse pointer ─────────────── */
@media (pointer: coarse) {
    .caploo-toggle-switch {
        min-width: 44px;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
    }
}

/* ── D-115: Composer tag/location dropdowns — extend slightly beyond sidebar */
.caploo-composer-sidebar .caploo-tag-dropdown,
.caploo-composer-sidebar .caploo-location-dropdown,
.caploo-composer-sidebar [role="listbox"] {
    min-width: max(100%, 320px);
}

/* ── D-125: Active-filter chip — wider cap at desktop ───────────────────── */
@media (min-width: 64rem) {
    .active-filter-chip,
    .caploo-filter-chip {
        max-width: 18rem;
    }
}

/* ── D-146: HelpDrawer — wider at ultrawide (≥ 1600px) ──────────────────── */
@media (min-width: 100rem) {
    .help-drawer {
        width: min(600px, 40vw);
    }
}

/* ── D-160: Calendar month-nav chevrons — 44px unconditional ────────────── */
/* M-127 already applies 44px on coarse pointer; extend unconditionally so
   keyboard and mouse desktop users also benefit from the larger target. */
.caploo-calendar-header__nav {
    min-width: 44px;
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* ── Ticket 19: Host Theming MVP ─────────────────────────────────────────── */
/* .drop-themed is the outermost feed wrapper; when a host applies a theme the
   inline style on this div injects --accent / --accent-soft / --accent-strong.
   data-theme="light"|"dark" overrides the colour scheme for all descendants. */

.drop-themed {
    color-scheme: light dark;
}

.drop-themed[data-theme="light"] {
    color-scheme: light;
}

.drop-themed[data-theme="dark"] {
    color-scheme: dark;
}

/* ── Theme-picker UI in the host settings drawer ──────────────────────── */

.theme-picker-section {
    margin-block: 0.5rem;
}

.theme-picker-section summary {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
    font-size: 0.875rem;
    font-weight: 600;
    padding: 0.5rem 0;
    list-style: none;
    user-select: none;
}

.theme-picker-section summary::-webkit-details-marker { display: none; }

.theme-picker-section .picker-chevron {
    margin-inline-start: auto;
    transition: transform 0.18s ease;
    display: inline-block;
}

.theme-picker-section[open] .picker-chevron {
    transform: rotate(180deg);
}

.theme-swatches {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    padding-block: 0.5rem;
}

.theme-swatch {
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    border: 2px solid transparent;
    cursor: pointer;
    flex-shrink: 0;
    /* Each swatch sets --swatch-color inline; .theme-swatch--none omits it so
       its background falls back to transparent (CSS custom-property undefined → initial). */
    background-color: var(--swatch-color);
    background-clip: padding-box;
    transition: transform 0.12s ease, border-color 0.12s ease;
}

.theme-swatch:hover,
.theme-swatch:focus-visible {
    transform: scale(1.15);
    outline: 2px solid var(--focus-ring-color, var(--accent));
    outline-offset: 2px;
}

.theme-swatch.selected,
.theme-swatch[aria-pressed="true"] {
    border-color: var(--color-fg, #111);
    transform: scale(1.12);
}

.theme-swatch--none {
    background: transparent;
    border-color: var(--border);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
}
.theme-swatch--none:hover,
.theme-swatch--none:focus-visible {
    border-color: var(--text);
    color: var(--text);
    transform: none;
}

.theme-pref-group {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    padding-block: 0.25rem 0.625rem;
}

.theme-pref-group label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.875rem;
    cursor: pointer;
}

/* UX-2.5: Gestalt proximity fix — add breathing room between the "Color scheme for
   this drop" legend and the segmented control below it so they don't visually merge. */
.theme-preference-group > legend {
    display: block;
    margin-block-end: var(--space-1);
}

/* UX-2.5: theme preference 3-col segmented — reuses base .segmented grid layout.
   Adds block-start gap from the legend and coarse-pointer 44px touch floor. */
.theme-pref-segmented { margin-block-start: .5rem; }
@media (pointer: coarse) {
    .theme-pref-segmented .segmented__option { min-height: 44px; }
}

.custom-hex-details {
    margin-block: 0.25rem;
    font-size: 0.875rem;
}

.custom-hex-details summary {
    font-size: 0.8125rem;
    color: var(--color-fg-muted, #666);
    cursor: pointer;
    padding-block: 0.25rem;
    list-style: none;
}

.custom-hex-details summary::-webkit-details-marker { display: none; }

.custom-hex-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-block-start: 0.375rem;
}

.custom-hex-row input[type="text"] {
    flex: 1;
    font-family: var(--font-mono, monospace);
    font-size: 0.875rem;
}

.custom-hex-row input[type="color"] {
    width: 2.5rem;
    height: 2.5rem;
    border: none;
    padding: 0;
    cursor: pointer;
    border-radius: 0.25rem;
    background: none;
}

.theme-error {
    color: var(--color-danger, #c0392b);
    font-size: 0.8125rem;
    margin-block-start: 0.25rem;
}

.theme-saved-badge {
    font-size: 0.8125rem;
    color: var(--color-success, #27ae60);
    margin-inline-start: 0.5rem;
}

/* UX-2.5: 44×44 coarse-pointer touch floor for accent swatches */
@media (pointer: coarse) {
    .theme-swatch { width: 44px; height: 44px; }
}

/* reduced-motion: disable swatch transform animations */
@media (prefers-reduced-motion: reduce) {
    .theme-swatch {
        transition: none;
    }
    .theme-picker-section .picker-chevron {
        transition: none;
    }
}

/* ══════════════════════════════════════════════════════════════════════════
   MD-T1 — Mobile Foundation (2026-05-18)
   Closes: MD-001, MD-002, MD-013, MD-040, MD-041, MD-051 (partial).
   These rules are the unblocker for all other MD-Tn tickets.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── MD-T1 §3: Grid auto-stack utility
   Replaces explicit `grid-template-columns: 1fr 1fr` layouts that don't
   collapse at narrow viewports. Apply to any 2-col layout that should
   become 1-col below ~448px (28rem per item minimum). */
.grid--auto-stack {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 28rem), 1fr));
    gap: var(--space-3);
}

/* ── MD-T1 §3: Card stack narrow utility
   Flips horizontal flex cards (icon + content side-by-side) to a vertical
   column at ≤30rem (480px) so titles and descriptions never character-wrap. */
@media (max-width: 30rem) {
    .card--stack-narrow {
        flex-direction: column;
    }
}

/* ── MD-T1 §3: Landscape phone — force single-column layouts
   At (orientation: landscape) and (max-height: 500px) the phone is on its
   side with very limited vertical space. Kill any 2-col grid. */
@media (orientation: landscape) and (max-height: 500px) {
    .grid--auto-stack {
        grid-template-columns: 1fr;
    }
    .home-container--landing {
        display: flex;
        flex-direction: column;
    }
    .card--stack-narrow {
        flex-direction: column;
    }
    /* Hide decorative hero gradient on landscape phones to free vertical space */
    .hero::before {
        display: none;
    }
}

/* ── MD-T1 §5: Character-wrap defense — body additions
   `overflow-wrap: anywhere` is already on body from Phase 16b.
   `hyphens: auto` prevents the browser from inserting character-breaks mid-word
   when a long token genuinely needs a break (e.g. "Birthday" → "Birth-day"
   rather than "Bir/thd/ay"). `word-break: normal` is explicit to prevent
   inheritance of `break-word` or `break-all` from ancestor rules. */
body {
    word-break: normal;
    hyphens: auto;
}

/* ── MD-T1 §5: `text-wrap: balance` on headings
   Distributes line breaks evenly across heading lines rather than leaving a
   short "orphan" word alone on the last line. Progressive enhancement —
   browsers without support fall back to default wrapping.
   `hyphens: none` overrides the `hyphens: auto` on body — headings should
   break at word boundaries only, never mid-word. */
h1, h2, h3 {
    text-wrap: balance;
    hyphens: none;
}

/* ══════════════════════════════════════════════════════════════════════════
   MD-T2 — Auth Pages Polish + Password Disclosure (2026-05-18)
   Closes: MD-012 (forgot/signup footer → single concern per page),
   MD-014 (back-home link always visible above the fold),
   MD-021 (strength checklist compact on narrow viewports),
   MD-022 (char counter below input),
   MD-023 (strength bar 6px height + label already present).
   MD-010/020 (wordmark) already shipped in MD-T1 via --text-fluid-auth.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── §1: Auth shell — fix justify-content center clip on narrow viewports
   With justify-content: center and a tall form (signup + strength checklist),
   the flex container's centred block starts above y=0. Browsers cannot scroll
   to a negative position, so the back-home link and wordmark are unreachable.
   Fix: flex-start on narrow viewports so content always starts at the top. */
@media (max-width: 30rem) {
    .auth-shell {
        justify-content: flex-start;
        padding-block-start: var(--space-2, 1rem);
        gap: var(--space-1-5, .75rem);
    }
}

/* ── §2: Strength bar height — upgraded to 6px in base rule above (MD-023). */

/* ── §3: Compact strength checklist on mobile (MD-021)
   At ≤30rem (320px phones), hide the <small> explanation line under each
   criterion. The ✓/○/✗ icon + rule title carry the meaning; the explanation
   text was consuming ~200–240px on the signup page, pushing the submit button
   off-screen. Tighten the inter-item gap too. */
@media (max-width: 30rem) {
    .auth-card__strength-check-text small {
        display: none;
    }
    .auth-card__strength-checklist {
        gap: .3rem;
    }
}

/* ── §4: Display-name char counter footer row (MD-022)
   Counter moves from the label row to a right-aligned helper row below the
   input so the label row is uncluttered. The new .auth-card__field-footer
   class is the mounting point. */
.auth-card__field-footer {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    margin-block-start: .2rem;
}

/* ── §5: Forgot-password inline link (MD-012)
   Lives in .auth-card__field-header (right side, space-between layout) next
   to the password label. 44px touch target via min-height + vertical padding
   even though the visual glyph is smaller (compact-touch pattern from audit §1). */
.auth-card__forgot-link {
    font-size: var(--text-xs);
    color: var(--accent);
    text-decoration: none;
    flex-shrink: 0;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    padding: 0 .25rem;
    border-radius: var(--radius-sm, 8px);
    transition: color var(--transition);
}
.auth-card__forgot-link:hover {
    text-decoration: underline;
}
.auth-card__forgot-link:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}

/* ── §6: Legal microcopy below submit button (FEE-A3) ────────── */
.auth-legal {
    font-size: var(--text-xs);
    color: var(--muted);
    text-align: center;
    margin-top: var(--space-2);
}
.auth-legal .link {
    color: var(--accent);
    text-decoration: underline;
}

/* ═══════════════════════════════════════════════════════════════
   MD-T3 — Navbar 4-item mobile layout + bottom-sheet menus
   Closes: MD-006, MD-014, MD-080, MD-090, MD-091
   ═══════════════════════════════════════════════════════════════ */

@keyframes slideInUp {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
}

/* ── 1. Navbar collapse at <480px (30rem) ────────────────────── */
/* At <480px: tools cluster → lang-chip only; overflow ⋮ visible.
   Max 4 items: Logo · Lang chip · Auth · ⋮  */
@media (max-width: 30rem) {
    /* Hide theme and help from tools cluster */
    .navbar .theme-menu-wrap { display: none; }
    .navbar .help-trigger    { display: none; }
    /* Hide Sign up — moves to overflow sheet */
    .navbar-signup { display: none; }
    /* Hide notification bell — low-frequency at narrow widths */
    .navbar .notification-bell-wrap { display: none; }
    /* Show overflow trigger */
    .nav-overflow-trigger { display: inline-flex !important; }
}

/* ── 2. Lang trigger: 2-char chip on mobile, native name on desktop ── */
/* Base: chip hidden, native name shown */
.lang-trigger__chip { display: none; }
/* At ≤768px: swap to chip */
@media (max-width: 48rem) {
    .lang-trigger__name { display: none; }
    .lang-trigger__chip {
        display: inline;
        font-size: var(--text-xs);
        font-weight: 700;
        letter-spacing: .04em;
        line-height: 1;
    }
    /* Re-enable inline padding so chip + globe have room */
    .navbar .lang-trigger {
        padding-inline: var(--space-1);
        gap: var(--space-0-5);
    }
}
/* Override the existing 22.5rem icon-only rule that hid __name — chip
   already takes over, so padding reset applies from this rule. */
@media (max-width: 22.5rem) {
    .navbar .lang-trigger {
        padding-inline: 0;
        width: var(--ctl-h-touch);
        justify-content: center;
        gap: 0;
    }
    .lang-trigger__chip { display: none; }
}

/* ── 3. Theme trigger: icon-only on mobile ───────────────────── */
@media (max-width: 48rem) {
    .theme-trigger__name { display: none; }
    .navbar .theme-trigger {
        padding-inline: 0;
        width: var(--ctl-h-touch);
        justify-content: center;
    }
}

/* ── 4. Overflow trigger button ──────────────────────────────── */
/* FIX: background changed from transparent to --surface-alt. Transparent let
   the body's ambient orb gradients (purple, background-attachment:fixed) show
   through the navbar glass, making the button appear accent-purple. Per
   standing direction, accent is reserved for primary CTAs / brand marks /
   focus rings — the hamburger trigger gets a neutral elevated surface.
   Hover/expanded borders also changed from --accent to neutral --border so
   no state of this button shows the purple accent. */
.nav-overflow-trigger {
    display: none; /* shown at <30rem by the collapse rule above */
    box-sizing: border-box;
    width: var(--ctl-h-touch, 44px);   /* icon-only: square touch target */
    height: var(--ctl-h-touch, 44px);
    min-height: var(--ctl-h-touch, 44px);
    min-width: var(--ctl-h-touch, 44px);
    padding-inline: 0;
    background: var(--surface-alt);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-sm);
    color: var(--muted);
    font-size: 1.15rem;
    font-weight: 700;
    letter-spacing: .08em;
    cursor: pointer;
    align-items: center;
    justify-content: center;
    gap: 0;
    transition: background var(--transition), border-color var(--transition), color var(--transition);
    flex-shrink: 0;
}
.nav-overflow-trigger:hover { background: var(--surface-hi); border-color: var(--border); color: var(--text); }
.nav-overflow-trigger:focus-visible { box-shadow: var(--focus-ring); outline: none; }
.nav-overflow-trigger[aria-expanded="true"] { background: var(--surface-hi); border-color: var(--border); color: var(--text); }

/* ── 5. Lang-menu → bottom sheet at ≤768px ──────────────────── */
@media (max-width: 48rem) {
    .lang-menu {
        position: fixed;
        inset-block-start: auto;
        inset-block-end: 0;
        inset-inline: 0;
        /* Override inline-size:max-content from base rule so sheet spans full width */
        width: 100%;
        max-inline-size: none;
        /* Explicit max-height so we don't rely on the global 70vh fallback */
        max-height: min(80dvh, 600px);
        /* Shell clips; the locale-switcher child handles scrolling (iOS-safe) */
        overflow: hidden;
        overscroll-behavior: contain;
        display: flex;
        flex-direction: column;
        border-radius: var(--bottom-sheet-radius, 20px) var(--bottom-sheet-radius, 20px) 0 0;
        padding: var(--space-2) var(--space-3) calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
        animation: slideInUp .22s cubic-bezier(.2, .8, .2, 1) forwards;
        z-index: 200;
    }
    /* Drag handle */
    .lang-menu::before {
        content: "";
        display: block;
        flex-shrink: 0;
        width: var(--bottom-sheet-handle-w, 36px);
        height: var(--bottom-sheet-handle-h, 4px);
        border-radius: var(--radius-pill);
        background: var(--bottom-sheet-handle-c, color-mix(in srgb, var(--muted) 50%, transparent));
        margin: 0 auto var(--space-1-5);
    }
    /* Non-transparent backdrop on mobile */
    .lang-menu-backdrop { background: rgba(0, 0, 0, .45); }
    /* Scrollable body: grows to fill sheet, momentum-scrolls on iOS */
    .lang-menu .locale-switcher {
        flex: 1;
        min-height: 0;           /* required for overflow in a flex child */
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
        max-height: none;        /* parent flex + max-height on .lang-menu caps it */
    }
    @media (prefers-reduced-motion: reduce) {
        .lang-menu { animation: none; }
    }
}

/* ── 6. Theme-menu → bottom sheet at ≤768px ─────────────────── */
@media (max-width: 48rem) {
    .theme-menu:not(.is-inline) {
        position: fixed;
        inset-block-start: auto;
        inset-block-end: 0;
        inset-inline: 0;
        max-inline-size: none;
        border-radius: var(--bottom-sheet-radius, 20px) var(--bottom-sheet-radius, 20px) 0 0;
        padding: var(--space-2) var(--space-3) calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
        animation: slideInUp .22s cubic-bezier(.2, .8, .2, 1) forwards;
        z-index: 200;
    }
    .theme-menu:not(.is-inline)::before {
        content: "";
        display: block;
        width: var(--bottom-sheet-handle-w, 36px);
        height: var(--bottom-sheet-handle-h, 4px);
        border-radius: var(--radius-pill);
        background: var(--bottom-sheet-handle-c, color-mix(in srgb, var(--muted) 50%, transparent));
        margin: 0 auto var(--space-1-5);
    }
    .theme-menu-backdrop { background: rgba(0, 0, 0, .45); }
    @media (prefers-reduced-motion: reduce) {
        .theme-menu:not(.is-inline) { animation: none; }
    }
}

/* ── 7. Help-drawer → bottom sheet at ≤768px ────────────────── */
@media (max-width: 48rem) {
    .help-drawer {
        top: auto;
        left: 0;
        right: 0;
        bottom: 0;
        width: 100%;
        max-width: none;
        height: auto;
        min-height: 50vh;
        max-height: 90vh;
        border-radius: var(--bottom-sheet-radius, 20px) var(--bottom-sheet-radius, 20px) 0 0;
        animation: slideInUp .22s cubic-bezier(.2, .8, .2, 1) forwards;
        overflow: hidden;
    }
    /* Drag handle rendered before the header */
    .help-drawer::before {
        content: "";
        display: block;
        flex-shrink: 0;
        width: var(--bottom-sheet-handle-w, 36px);
        height: var(--bottom-sheet-handle-h, 4px);
        border-radius: var(--radius-pill);
        background: var(--bottom-sheet-handle-c, color-mix(in srgb, var(--muted) 50%, transparent));
        margin: var(--space-1-5) auto var(--space-1);
    }
    /* Reset the navbar-height top-padding compensation — drawer anchors at
       the bottom now, not behind the navbar. */
    .help-drawer__header {
        padding-top: 0.75rem;
    }
    @media (prefers-reduced-motion: reduce) {
        .help-drawer { animation: none; }
    }
}

/* ── 8. Nav overflow sheet ───────────────────────────────────── */
/* Handoff retrofit: scrim + sheet chrome (positioning, surface, border-radius,
   blur, slide-up animation, safe-area inset, grabber) all come from
   .sheet-scrim + .sheet + .sheet__grabber. Rules below cover NavOverflow's
   content-layout BEM (header / rows / section labels / divider). */
.nav-overflow-sheet__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-1) var(--space-3) var(--space-1);
    border-bottom: 1px solid var(--border);
}
.nav-overflow-sheet__title {
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--muted);
    margin: 0;
    text-transform: uppercase;
    letter-spacing: .07em;
}
.nav-overflow-sheet__close {
    background: transparent;
    border: none;
    color: var(--muted);
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
    padding: var(--space-1);
    min-height: 44px;
    min-width: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
}
.nav-overflow-sheet__close:hover { color: var(--text); background: var(--surface-alt); }
.nav-overflow-sheet__close:focus-visible { box-shadow: var(--focus-ring); outline: none; }
.nav-overflow-sheet__rows {
    padding: var(--space-1) 0 var(--space-2);
    overflow-y: auto;
    max-height: 70vh;
}
.nav-overflow-sheet__section-label {
    padding: var(--space-1) var(--space-3) var(--space-0-5);
    font-size: var(--text-xs);
    font-weight: 700;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .07em;
}
/* Inline ThemeMenu inside overflow sheet */
.nav-overflow-sheet .theme-menu-wrap { width: 100%; }
.nav-overflow-sheet .theme-menu.is-inline {
    padding: var(--space-0-5) var(--space-3);
}
.nav-overflow-sheet .theme-menu__option {
    padding: var(--space-1) var(--space-1);
    min-height: 48px;
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.nav-overflow-sheet__divider {
    border: none;
    border-top: 1px solid var(--border);
    margin: var(--space-1) 0;
}
/* Tappable rows: Help, Sign out, Sign up */
.nav-overflow-sheet__row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    width: 100%;
    padding: var(--space-2) var(--space-3);
    min-height: 48px;
    background: transparent;
    border: none;
    color: var(--text);
    font-size: var(--text-sm);
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    text-align: start;
    transition: background var(--transition);
}
.nav-overflow-sheet__row:hover { background: var(--surface-alt); }
.nav-overflow-sheet__row:focus-visible { box-shadow: var(--focus-ring); outline: none; }
.nav-overflow-sheet__row--destructive { color: var(--error, #e05c5c); }
.nav-overflow-sheet__row-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    flex-shrink: 0;
    color: var(--muted);
    font-size: 1rem;
}

/* ═══════════════════════════════════════════════════════════════════════════
   MD-T4 — Drop page mobile: composer route, reaction row, host action bar
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1. .bottom-action-bar utility ────────────────────────────────────────
   Safe-area-aware sticky footer bar. Used by the feed mobile action bar and
   the mobile composer's submit bar. Hidden on desktop via display:none;
   enabled at ≤768px via the media query below. */
.bottom-action-bar {
    display: none; /* hidden on desktop — activated by media query */
}

@media (max-width: 768px) {
    .bottom-action-bar {
        display: flex;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 35;
        align-items: center;
        padding: var(--space-2) var(--space-2)
                 calc(var(--space-2) + env(safe-area-inset-bottom, 0px));
        background: color-mix(in srgb, var(--surface) 96%, transparent);
        backdrop-filter: blur(10px);
        -webkit-backdrop-filter: blur(10px);
        border-top: 1px solid var(--border);
        box-shadow: 0 -4px 16px rgba(0,0,0,.12);
        gap: var(--space-1-5);
    }
}

/* Pad the feed's bottom so the last moment card clears the MobileBottomNav. */
@media (max-width: 768px) {
    .feed-layout .feed {
        padding-bottom: var(--m-bottomnav-total, 72px);
    }
}

/* ── 3. Reaction picker — horizontal scroll snap at ≤480px ─────────────
   The 6-emoji picker that opens on long-press becomes a scrollable row so
   it never overflows the viewport edge on narrow phones. */
@media (max-width: 480px) {
    .reaction-picker {
        /* Override the absolute popover width to be scroll-contained. */
        max-width: calc(100vw - 2 * var(--space-2));
        overflow-x: auto;
        overflow-y: hidden;
        scroll-snap-type: x mandatory;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        flex-wrap: nowrap; /* already flex from base rule; ensure no wrap */
    }
    .reaction-picker::-webkit-scrollbar { display: none; }
    .reaction-picker__option {
        flex-shrink: 0;
        scroll-snap-align: start;
        /* Bump to touch-target floor */
        width: 44px;
        height: 44px;
    }
}

/* ── 4. Mobile composer page ─────────────────────────────────────────────
   Full-page layout for /drop/{id}/post. Uses the full viewport height with
   a sticky-bottom submit bar so the on-screen keyboard never covers Post. */
.mobile-composer-page {
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
    min-height: 100vh; /* fallback */
    background: var(--surface);
}

.mobile-composer-page__header {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: calc(var(--space-2) + env(safe-area-inset-top, 0px))
             var(--space-3)
             var(--space-2);
    border-bottom: 1px solid var(--border);
    background: var(--surface);
    position: sticky;
    top: 0;
    z-index: 10;
}

.mobile-composer-page__back {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: 44px;
    min-width: 44px;
    color: var(--accent-text);
    text-decoration: none;
    font-size: var(--text-sm);
    font-weight: 500;
    flex-shrink: 0;
    border-radius: var(--radius);
    padding: var(--space-1) var(--space-1-5);
    transition: background var(--duration-base) var(--easing-default);
}
.mobile-composer-page__back:hover,
.mobile-composer-page__back:focus-visible {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    outline: none;
    box-shadow: var(--focus-ring);
}

.mobile-composer-page__title {
    font-size: var(--text-base);
    font-weight: 600;
    margin: 0;
    flex: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 20ch; /* audit MD-T4 change 4 drop-name 20ch cap */
}

.mobile-composer-page__body {
    flex: 1;
    overflow-y: auto;
    padding: var(--space-3);
    padding-bottom: calc(var(--space-3) + 72px + env(safe-area-inset-bottom, 0px));
}

.mobile-composer-page__form { display: flex; flex-direction: column; gap: var(--space-2); }

.mobile-composer-page__disabled {
    padding: var(--space-4);
    text-align: center;
    color: var(--muted);
}

.mobile-composer-page__textarea {
    width: 100%;
    min-height: 120px;
    padding: var(--space-2) var(--space-2-5);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-alt);
    color: var(--text);
    font-family: inherit;
    font-size: var(--text-base);
    line-height: 1.5;
    resize: vertical;
    transition: border-color var(--duration-base);
}
.mobile-composer-page__textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

.mobile-composer-page__char-counter {
    font-size: var(--text-xs);
    color: var(--muted);
    text-align: right;
    font-variant-numeric: tabular-nums;
}
.mobile-composer-page__char-counter.is-near-limit { color: var(--warning-text); }

.mobile-composer-page__ai { display: flex; flex-direction: column; gap: var(--space-2); }

.mobile-composer-page__privacy {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-alt);
}
.mobile-composer-page__privacy-label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    flex: 1;
    font-size: var(--text-sm);
    color: var(--muted);
}

.mobile-composer-page__media-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-0-5) var(--space-1-5) var(--space-0-5) var(--space-0-5);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-size: var(--text-sm);
    color: var(--text);
    background: var(--surface-alt);
}
/* Photo thumbnail — shown when a preview data URL is available */
.mobile-composer-page__media-thumb {
    width: 80px;
    height: 80px;
    object-fit: cover;
    border-radius: calc(var(--radius) - 2px);
    flex-shrink: 0;
    border: 1px solid var(--border);
    display: block;
}
.mobile-composer-page__media-name {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
.mobile-composer-page__media-remove {
    appearance: none;
    background: transparent;
    border: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    padding: 2px;
    border-radius: 50%;
    /* A-001 (Phase 20a): width-axis Fitts fix — global button rule covers min-height (44px)
       but not min-width; a 24px tap strip on a destructive remove action fails the 44×44
       touch-target floor on narrow phones. */
    min-width: var(--touch-target-min, 44px);
    min-height: var(--touch-target-min, 44px);
}

.mobile-composer-page__progress {
    height: 4px;
    border-radius: 2px;
    background: var(--border);
    overflow: hidden;
}
.mobile-composer-page__progress-fill {
    height: 100%;
    background: var(--accent);
    border-radius: 2px;
    transition: width 200ms linear;
}

.mobile-composer-page__success { color: var(--success-text); font-size: var(--text-sm); }
.mobile-composer-page__error   { color: var(--error);         font-size: var(--text-sm); }

.mobile-composer-page__attach-row {
    display: flex;
    gap: var(--space-1-5);
    flex-wrap: wrap;
}
.mobile-composer-page__attach-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: 44px;
    padding: var(--space-1) var(--space-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-alt);
    color: var(--text);
    font-size: var(--text-sm);
    cursor: pointer;
    transition: background var(--duration-base);
}
.mobile-composer-page__attach-btn:hover { background: var(--border); }

/* Sticky submit bar — part of the .bottom-action-bar pattern but always
   visible (not gated to ≤768px) so the page works even on wide screens
   when navigated directly. */
.mobile-composer-page__submit-bar {
    /* Override the base .bottom-action-bar display:none on desktop since
       this is a dedicated page, not an overlay on top of another page. */
    display: flex !important;
    justify-content: stretch;
}
.mobile-composer-page__submit-btn {
    flex: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-1);
    min-height: 48px;
    font-size: var(--text-base);
    font-weight: 600;
}

/* Skeleton loader */
.mobile-composer-page__skeleton { display: flex; flex-direction: column; gap: var(--space-2); }
.mobile-composer-page__skeleton-line {
    background: var(--skeleton-bg, color-mix(in srgb, var(--surface) 80%, var(--border)));
    border-radius: var(--radius);
    animation: caplooShimmer var(--skeleton-duration, 1.4s) ease-in-out infinite;
}
.mobile-composer-page__skeleton-line--textarea { height: 120px; }
.mobile-composer-page__skeleton-line--action   { height: 44px; width: 40%; }

/* ── 5. Scroll-nav pill hidden on landscape phones ───────────────────── */
@media (orientation: landscape) and (max-height: 500px) {
    .scroll-nav { display: none !important; }
}

/* ── 6. Drop-page navbar trim at ≤480px ─────────────────────────────────
   On the drop page: ← back · Drop name (≤20ch) · PIN chip · ⋮.
   The 4-item collapse from MD-T3 already handles most of this;
   these rules trim the drop-specific meta to fit narrow screens. */
@media (max-width: 480px) {
    .navbar-drop-name {
        max-width: 20ch;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    /* Hide the countdown prefix text "Expires in" — leave only the time value */
    .navbar-countdown__prefix { display: none; }
    /* Compress the PIN chip copy label — just show the PIN value */
    .navbar-pin-chip__label { display: none; }
    /* UX-6A Fix 4 — hide Share / Report / Settings from the navbar context
       cluster; they surface in the ⋮ NavOverflowSheet ("overflow-drop-actions"
       slot) to free horizontal space for the drop title + countdown. */
    .nav-cluster--context .share-trigger-btn,
    .nav-cluster--context .nav-settings { display: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   MD-T5 — Landscape phone treatment + audit-wide pass (2026-05-18)
   Closes: MD-008 (home landing hero), §6.3 side-sheet overrides, §6.1 #7.
   Targets: 568×320 (iPhone SE landscape) – 896×414 (iPhone XR landscape).
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Utility classes ─────────────────────────────────────────────────── */
@media (orientation: landscape) and (max-height: 500px) {
    .collapse-on-landscape-phone { display: none !important; }
    .compress-on-landscape-phone {
        flex-direction: row !important;
        gap: var(--space-1) !important;
        padding-block: var(--space-1) !important;
    }
}

/* ── 2. Home landing hero (MD-008) ──────────────────────────────────────── */
@media (orientation: landscape) and (max-height: 500px) {
    /* Collapse the drop icon + tagline; keep only the h1 at a smaller size
       so the hero is a single compact row instead of a tall stack. */
    .hero--landing > svg:first-child { display: none; }
    .hero--landing .tagline          { display: none; }
    .hero--landing {
        padding: var(--space-1) 0;
        text-align: center;
    }
    .hero--landing h1 {
        font-size: clamp(1.1rem, 3.5vw, 1.6rem);
        line-height: 1.15;
    }
}

/* ── 3. Auth pages ──────────────────────────────────────────────────────── */
@media (orientation: landscape) and (max-height: 500px) {
    /* Tagline gone; wordmark shrinks below the fluid clamp floor so the
       form card fits above the fold in 320px height. */
    .caploo-tagline { display: none; }
    .auth-brand {
        gap: var(--space-0-5);
        margin-bottom: var(--space-1);
    }
    .caploo-wordmark { font-size: clamp(1.1rem, 3.5vw, 1.5rem); }
    .auth-brand .caploo-logo { width: 28px; height: 28px; }
    /* Card spacing: trimmed to keep form fields reachable. */
    .auth-card {
        padding: var(--space-1-5) var(--space-2);
        gap: var(--space-1);
    }
    .auth-card__field { gap: 0.2rem; }
}

/* ── 4. Feed page ───────────────────────────────────────────────────────── */
@media (orientation: landscape) and (max-height: 500px) {
    /* svh: small viewport height — never counts the URL bar. */
    .feed-layout { min-height: calc(100svh - var(--navbar-h)); }
    /* Capture panel: slim so the feed is visible next to it. */
    .capture-panel {
        padding: var(--space-1) var(--space-2);
        gap: var(--space-1);
        max-height: calc(100svh - 44px);
    }
    /* Tighter card gaps on landscape. */
    .feed {
        gap: var(--space-2);
        padding-top: var(--space-1);
    }
    .feed-header { padding-block: var(--space-1); }
}

/* ── 5. Drop page: host strip + sticky action bar ───────────────────────── */
@media (orientation: landscape) and (max-height: 500px) {
    .feed-host-actions {
        padding: var(--space-0-5) var(--space-1);
    }
    /* Sticky bar: eat ≤48px total so it doesn't consume a third of the screen. */
    .bottom-action-bar {
        padding-top: var(--space-1);
        padding-bottom: calc(var(--space-1) + env(safe-area-inset-bottom, 0px));
    }
}

/* ── 6. Mobile composer page ────────────────────────────────────────────── */
@media (orientation: landscape) and (max-height: 500px) {
    /* Header bar: 44px-equivalent (matches landscape navbar height). */
    .mobile-composer-page__header {
        padding-top: var(--space-1);
        padding-bottom: var(--space-1);
    }
    /* Body scroll area: tighter so the textarea + submit bar fit. */
    .mobile-composer-page__body {
        padding: var(--space-1-5) var(--space-2);
        padding-bottom: calc(var(--space-1-5) + 56px + env(safe-area-inset-bottom, 0px));
    }
    /* Textarea: shorter floor; max-height caps so submit bar is never obscured. */
    .mobile-composer-page__textarea {
        min-height: 64px;
        max-height: 30svh;
    }
    /* Submit bar: slim height. */
    .mobile-composer-page__submit-bar {
        padding-top: var(--space-1);
        padding-bottom: calc(var(--space-1) + env(safe-area-inset-bottom, 0px));
    }
    /* Use svh for the page height — URL bar should never count on landscape. */
    .mobile-composer-page {
        min-height: 100svh;
    }
}


/* ── 8. Navbar: 4-item layout holds in landscape ────────────────────────── */
/* T3's 4-item collapse fires at max-width:480px. Landscape phones are
   568–896px wide so the full cluster shows — that's correct; the 44px
   navbar height from §10800 still trims vertical space. No new rule. */

/* ── 9. 100vh → 100svh progressive enhancement ──────────────────────────── */
/* svh (small viewport height) never counts the URL bar. Patch the one
   container that's phone-visible at full-screen height. The composer
   already has 100dvh; we add svh as the strictest guarantee. */
.mobile-composer-page {
    min-height: 100svh;
    min-height: 100dvh; /* dvh wins when supported, wider compat */
}

/* ── 10. Suppress MobileBottomNav on the composer page (Fix 1) ───────────
   MobileBottomNav is position:fixed bottom:0 z-index:90.
   The Post submit bar is position:fixed bottom:0 z-index:35.
   :has() lets us hide the nav in pure CSS without any JS class toggling.
   iOS Safari 15.4+ and all other modern browsers support :has(). */
html:has(.mobile-composer-page) .mobile-bottom-nav { display: none !important; }

/* ══════════════════════════════════════════════════════════════════════════
   MD-T6 — Mobile design tokens adoption sweep (2026-05-18)
   Closes: §2 dormant-token gap, text-wrap audit, overflow-wrap audit,
   safe-area audit, .m-cta application, .collapse-on-landscape-phone sweep.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. text-wrap: balance on section-level headings ───────────────────── */
/* Apply to every structural heading that hasn't already received it.
   This prevents orphan words (a single word on the last line) on narrow
   phones without affecting desktop rendering. */

.admin-moderation__empty h2,
.admin-moderation__subtitle,
.privacy-delete-modal__heading,
.drop-export-label strong,
.card h2,
.card h3,
.empty-state__title,
.empty-state__body {
    text-wrap: balance;
}

/* ── 2. overflow-wrap: anywhere for hex/UUID code cells in admin ─────── */
/* Phase 16b sets overflow-wrap: anywhere on body, which handles inline
   text. Tables with fixed-layout or cells that don't inherit need it
   explicitly to prevent horizontal overflow on 320px viewports. */
.admin-moderation__table code,
.admin-moderation__table .target-id,
.drop-card-link strong {
    overflow-wrap: anywhere;
    word-break: break-all;
}

/* ── 3. .m-cta — primary CTAs get the 48dp Material comfortable floor
   on coarse-pointer devices (complements the unconditional 44px from
   MD-T1). Applying to the major CTA selectors site-wide. ──────────────── */
@media (pointer: coarse) {
    .btn-primary,
    button[type="submit"].btn-primary,
    a.btn-primary {
        min-height: var(--touch-target-comfortable, 48px);
    }
}

/* ── 4. .collapse-on-landscape-phone on decorative page headers ─────────
   Apply to non-essential hero-level decorative elements on settings,
   profile, recap, admin, and help pages so portrait-only illustrations
   don't consume precious height on 568×320 phones. ─────────────────────── */
@media (orientation: landscape) and (max-height: 500px) {
    /* Profile page: the main H1 "Profile" sits above form cards. On landscape
       phone the heading pushes the first card off-screen. Compact it. */
    .profile-page > h1,
    .my-drops-page > h1 {
        font-size: var(--text-fluid-h3, 1.15rem);
        margin-bottom: var(--space-1);
    }

    /* Settings page: same treatment. */
    .settings-page > h1 {
        font-size: var(--text-fluid-h3, 1.15rem);
        margin-bottom: var(--space-1);
    }

    /* Recap header: hide the decorative timeline meta row on landscape
       (date / time columns still visible in the timeline items). */
    .recap-meta {
        display: none;
    }

    /* Admin: subtitle below the H1 is supplementary; hide on landscape. */
    .admin-moderation__subtitle {
        display: none;
    }

    /* Help pages: intro paragraph is supplementary on landscape. */
    .help-page__intro {
        display: none;
    }
}

/* ── 5. Safe-area-inset-bottom for remaining fixed/sticky bottom elements
   Audit: the following elements are fixed or sticky at the bottom of the
   viewport but were missing env(safe-area-inset-bottom) padding. ─────── */

/* Privacy delete modal: rendered as a centered modal, not bottom-anchored,
   but its actions row sits near the bottom of a scrollable container.
   Add to the actions row so buttons aren't cut off by home indicator. */
.privacy-delete-modal__actions {
    padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* Admin moderation: the pager "Load more" button sits at the bottom of a
   long scrollable table. On Safari with a home indicator the button can
   disappear under the bar. */
.admin-moderation__pager {
    padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* Filter popover: positioned absolutely at bottom of button, but on very
   short phones the popover might extend to the bottom edge. */
@media (max-width: 48rem) {
    .caploo-filter-popover {
        padding-bottom: env(safe-area-inset-bottom, 0px);
    }
}

/* ── 6. MyDrops export <details> mobile treatment ───────────────────────
   The export disclosure sits at the bottom of a drop card. On narrow
   phones the popup menu should receive enough bottom padding so that the
   last menu item is reachable above the home indicator. The <details>
   pattern doesn't support .bottom-sheet (which requires aria-hidden JS)
   so we apply safe-area padding directly to the menu panel. ─────────── */
@media (max-width: 48rem) {
    .drop-export-menu {
        padding-bottom: env(safe-area-inset-bottom, 0px);
    }
    /* Make each export button hit the 44px floor on touch devices. */
    .drop-export-option {
        min-height: 44px;
    }
}

/* ── 7. Filter tab strip on MyDrops: scrollable on narrow phones ────────
   When the three tabs ("All 5 / Active 2 / Expired 3") can't fit in one
   row on 320px, wrapping doubles the height. Prefer horizontal scroll
   with snap (matching the notification chips pattern from M-150). ────── */
@media (max-width: 30rem) {
    .filter-row {
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scroll-snap-type: x mandatory;
        scrollbar-width: none;
    }
    .filter-row::-webkit-scrollbar { display: none; }
    .filter-row .tab {
        flex-shrink: 0;
        scroll-snap-align: start;
    }
}

/* ── 8. Admin moderation table: horizontal scroll on mobile ─────────────
   The table has 7 columns — impossible to fit at 320px. The existing
   .admin-moderation__table-wrapper rule at ≤768px gives horizontal
   scroll; ensure actions column buttons also meet the 44px floor. ────── */
@media (max-width: 48rem) {
    .admin-moderation__filters {
        overflow-x: auto;
        flex-wrap: nowrap;
        -webkit-overflow-scrolling: touch;
        scroll-snap-type: x mandatory;
        scrollbar-width: none;
        padding-bottom: var(--space-1);
    }
    .admin-moderation__filters::-webkit-scrollbar { display: none; }
    .admin-moderation__filters .filter-chip {
        flex-shrink: 0;
        scroll-snap-align: start;
    }
    .admin-moderation__table td.actions .btn {
        min-height: 44px;
    }
}

/* ── 9. Recap page safe-area ────────────────────────────────────────────
   The recap container has bottom padding of 5rem (80px) which comfortably
   clears the home indicator on most phones. Add env() so it adapts when
   the notch is taller. */
.recap-container {
    padding-bottom: max(5rem, calc(2.5rem + env(safe-area-inset-bottom, 0px)));
}

/* ── 10. Help page: safe-area padding-inline ──────────────────────────
   MD-070: pages should respect gutter-x on narrow phones. The help-page
   padding was var(--space-2) which is 1rem — below the 0.75rem gutter
   the token system guarantees (--gutter-x = clamp(0.75rem, 2.5vw, 1.5rem)).
   Override to use gutter-x for consistency. ────────────────────────────── */
.help-page {
    padding-inline: var(--gutter-x);
}

/* ── End MD-T6 ─────────────────────────────────────────────────────────── */

/* ═══════════════════════════════════════════════════════════════════════════
   MD-T7 — Lucide icon library + tooltip pattern
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Lucide icon base ─────────────────────────────────────────────────── */
.lucide {
    display: inline-block;
    vertical-align: middle;
    flex-shrink: 0;
    /* currentColor inherits from parent — no override needed */
}

/* ── 2. btn-icon base (icon-only button shell) ───────────────────────────── */
/* Already defined in earlier app.css; this section extends with tooltip support. */
.btn-icon {
    position: relative; /* anchor for ::after tooltip */
}

/* ── 3. Desktop hover / focus-visible tooltip (CSS-only, no JS) ─────────── */
/* Applies to any element carrying data-tooltip="Label" that is either
   a .btn-icon or sits inside a component that should show a tooltip.
   Uses CSS attr() so the label comes directly from the attribute — no
   duplication. The tooltip sits ABOVE the element by default; if there
   is not enough room the author should add data-tooltip-below to flip it. */
[data-tooltip] {
    position: relative;
}
[data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    background: var(--surface-elevated, #1e1e2e);
    color: var(--text, #e8e8f0);
    padding: 3px 8px;
    border-radius: var(--radius-sm, 4px);
    white-space: nowrap;
    font-size: 0.72rem;
    font-weight: 500;
    letter-spacing: 0.01em;
    pointer-events: none;
    z-index: 9000;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
    opacity: 0;
    transition: opacity 0.12s ease;
    /* Hidden by default; shown on :hover and :focus-visible below. */
}
/* Show on hover (pointer: fine) only — suppress on coarse (touch uses JS). */
@media (pointer: fine) {
    [data-tooltip]:hover::after,
    [data-tooltip]:focus-visible::after {
        opacity: 1;
    }
}
/* Below variant: flip tooltip below the element. */
[data-tooltip][data-tooltip-below]::after {
    bottom: auto;
    top: calc(100% + 6px);
}

/* ── 4. JS long-press tooltip overlay (caploo.tooltip.js) ───────────────── */
.caploo-tooltip {
    position: absolute; /* JS sets left/top via style */
    background: var(--surface-elevated, #1e1e2e);
    color: var(--text, #e8e8f0);
    padding: 4px 10px;
    border-radius: var(--radius-sm, 4px);
    font-size: 0.78rem;
    font-weight: 500;
    white-space: nowrap;
    pointer-events: none;
    z-index: 9001;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
    opacity: 0;
    transition: opacity 0.15s ease;
}
.caploo-tooltip--visible {
    opacity: 1;
}

/* ── 5. theme-glyph sizing ───────────────────────────────────────────────── */
/* ThemeMenu's .theme-glyph is now a <LucideIcon> (or <svg><use>); ensure it
   keeps its existing 16×16 display size regardless of the .lucide default. */
.theme-glyph {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

/* ── End MD-T7 ─────────────────────────────────────────────────────────── */

/* ── MomentQRModal ────────────────────────────────────────────────────────
   QR code share modal. z-index 6000 so it sits above MomentModal (5000)
   and ShareMenu (5500). APCA Lc ≥75 body; touch target ≥44×44 close btn.
   ───────────────────────────────────────────────────────────────────────── */
/* Handoff retrofit: chrome (positioning, scrim, surface, border, shadow,
   animation) comes from .modal-scrim + .modal. Local rules keep only the
   narrower 320px QR width and column layout. Selector compounds both
   classes so it wins over the .modal primitive declared later in the file. */
.modal.moment-qr-modal {
    max-width: min(320px, 100%);
    display: flex;
    flex-direction: column;
    padding: 0;
    gap: 0;
}
.moment-qr-modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-2) var(--space-2) var(--space-1-5);
    border-bottom: 1px solid var(--border);
}
.moment-qr-modal__title-row {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}
.moment-qr-modal__title-icon {
    color: var(--muted);
    flex-shrink: 0;
}
.moment-qr-modal__title {
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--text);
    margin: 0;
}
.moment-qr-modal__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 6px;
    box-sizing: content-box;
    border: none;
    background: none;
    border-radius: var(--radius-sm);
    color: var(--muted);
    cursor: pointer;
    transition: background var(--transition), color var(--transition);
    flex-shrink: 0;
}
.moment-qr-modal__close:hover {
    background: var(--surface-alt);
    color: var(--text);
}
.moment-qr-modal__close:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}
/* PA-02 — 44px touch target floor */
@media (pointer: coarse), (max-width: 48rem) {
    .moment-qr-modal__close {
        min-width: var(--touch-target-min, 44px);
        min-height: var(--touch-target-min, 44px);
    }
}
.moment-qr-modal__body {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-2);
}
.moment-qr-modal__qr {
    width: 200px;
    height: 200px;
    flex-shrink: 0;
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: #fff;
}
.moment-qr-modal__qr svg {
    width: 100%;
    height: 100%;
    display: block;
}
.moment-qr-modal__desc {
    font-size: var(--text-sm);
    color: var(--muted);
    text-align: center;
    margin: 0;
    line-height: 1.4;
}
/* Very-narrow viewport: pin the QR sheet to the bottom edge. Overrides the
   .modal-scrim `place-items: center` default for this specific surface. */
@media (max-width: 20rem) {
    .modal-scrim.moment-qr-backdrop {
        align-items: end;
        padding: 0;
    }
    .modal.moment-qr-modal {
        width: 100%;
        max-width: 100%;
        border-radius: var(--radius-md) var(--radius-md) 0 0;
    }
}

/* ============================================================
   ────────────────────────────────────────────────────────────
   FRONTEND-HANDOFF MIGRATION — page-level overrides.
   Rules below override older sections of this file via source
   order. Each block is a 1:1 port of the prototype's component
   styling (caploo-frontend-handoff/project/styles.css). They
   replace the corresponding pre-migration rules from Phases 6-19
   that accumulated above. Once the migration is complete the
   superseded blocks above will be pruned in a separate cleanup.
   ────────────────────────────────────────────────────────────
   ============================================================ */

/* ============================================================
   Home / Join — refined hero + join card + how-it-works + FAQ
   ============================================================ */

.home-page {
    display: block;
}

.hero {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: clamp(var(--space-3), 7vw, var(--space-8)) 0 var(--space-3);
    position: relative;
    gap: var(--space-1);
}

.hero__logo {
    width: clamp(56px, 12vw, 72px);
    height: clamp(56px, 12vw, 72px);
    filter: drop-shadow(0 16px 40px var(--accent-glow));
    margin-bottom: var(--space-2);
}

.hero__eyebrow {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
    background: var(--surface-alt);
    border: 1px solid var(--border-soft);
    padding: 6px 12px;
    border-radius: var(--radius-pill);
    margin-bottom: var(--space-2);
    white-space: nowrap;
}
.hero__eyebrow-dot {
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--success);
    box-shadow: 0 0 8px var(--success);
    display: inline-block;
}

.hero__title {
    font-size: var(--fs-display);
    line-height: var(--lh-display);
    font-weight: 800;
    letter-spacing: var(--ls-display);
    background: var(--brand-gradient);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    margin: 0 0 var(--space-1);
    padding-bottom: 0.08em;
    text-wrap: balance;
    max-width: 18ch;
}

.hero__subhead {
    font-size: clamp(0.9rem, 1.8vw, 1.05rem);
    font-weight: 500;
    color: var(--text);
    margin: 0;
    max-width: 44ch;
    text-wrap: balance;
    line-height: var(--lh-heading);
}


.home-grid {
    display: grid;
    gap: var(--space-2);
    max-width: var(--form-max);
    margin: var(--space-3) auto 0;
}

.hero-cta-separator {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin: var(--space-5) auto;
    max-width: 320px;
    width: 100%;
}
.hero-cta-separator__line {
    flex: 1;
    height: 1.5px;
    background: color-mix(in srgb, var(--muted) 65%, transparent);
}
.hero-cta-separator__chip {
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.divider-or {
    display: flex;
    align-items: center;
    gap: 12px;
    color: var(--muted-2);
    font-size: var(--text-xs);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    font-weight: 600;
    margin: var(--space-1) 0;
}
.divider-or::before, .divider-or::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--border);
}

.join-card {
    padding: var(--space-2-5);
}
.join-card h2 {
    font-size: var(--text-xs);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
    margin: 0 0 var(--space-2);
    font-weight: 700;
}

.join-card .field small {
    font-size: var(--text-xs);
    color: var(--muted-2);
    margin-top: 2px;
}
/* WCAG 1.4.11 — non-text contrast ≥3:1.
   In dark mode --border (#20202b) against --surface-alt (#111118) ≈1.6:1.
   Lift to ~3.5:1 by mixing in text. Light mode --border already meets 3:1. */
[data-theme="dark"] .join-card .field .input,
[data-theme="dark"] .join-card .field .pin-input {
    border-color: color-mix(in srgb, var(--text) 30%, var(--border));
}

/* Suppress the legacy Phase 19c card-level :focus-within outline.
   The prototype relies on field-level focus rings only — a constant
   accent outline around the form when any child is focused fights with
   the input's own :focus-visible ring. Field-level focus is set on
   .input via :focus-visible in the foundation block. */
.join-card:focus-within { outline: none; }

.aside-prompt {
    text-align: center;
    color: var(--muted);
    font-size: var(--text-sm);
    margin: var(--space-2) 0 0;
}
.aside-prompt a {
    color: var(--accent-text);
    font-weight: 600;
}

/* How-it-works — 3-up grid with icon medallions + numbered badge */
.how-it-works__heading {
    font-size: var(--fs-heading-2);
    font-weight: 700;
    color: var(--text);
    text-align: center;
    margin: 0 0 var(--space-3);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    grid-column: 1 / -1;
}
.how-it-works {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-2);
    max-width: var(--content-max);
    margin: var(--space-4) auto var(--space-3);
    list-style: none;
    padding: 0;
    /* reset legacy bleed-through: old block had background/border/box-shadow */
    background: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
}
.how-it-works__step {
    text-align: center;
    padding: var(--space-2-5) var(--space-1-5);
    background: color-mix(in srgb, var(--surface) 50%, transparent);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-md);
    /* reset legacy bleed-through: old block had flex:1 1 0, gap:.35rem */
    flex: unset;
    gap: unset;
}
.how-it-works__icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px;
    border-radius: 12px;
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    color: var(--accent-text);
    margin-bottom: var(--space-1-5);
    position: relative;
}
.how-it-works__icon::after {
    content: attr(data-n);
    position: absolute;
    top: -6px; right: -8px;
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--muted);
    font-size: 10px; font-weight: 700;
    width: 18px; height: 18px;
    border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
}
.how-it-works__title {
    font-weight: 700;
    margin: 0 0 4px;
    font-size: var(--text-sm);
    letter-spacing: -0.005em;
    color: var(--text);
}
.how-it-works__desc {
    color: var(--muted);
    font-size: var(--text-xs);
    margin: 0;
    line-height: 1.5;
}
@media (max-width: 640px) {
    .how-it-works { grid-template-columns: 1fr; }
}

/* ── landing compact How it works — 3-step horizontal row ───────────────── */
.landing-how-it-works {
    max-width: var(--content-max);
    margin: var(--space-4) auto var(--space-2);
    text-align: center;
    padding-inline: var(--gutter-x, 1rem);
}
.landing-how-it-works__heading {
    font-size: var(--fs-heading-2);
    font-weight: 700;
    color: var(--text);
    text-align: center;
    margin: 0 0 var(--space-3);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.landing-how-it-works__steps {
    display: flex;
    align-items: stretch;
    justify-content: center;
    gap: var(--space-2);
    list-style: none;
    padding: 0;
    margin: 0;
    flex-wrap: wrap;
}
.landing-how-it-works__step {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-1-5);
    padding: var(--space-2) var(--space-3);
    background: color-mix(in srgb, var(--surface) 50%, transparent);
    border: 1px solid color-mix(in srgb, var(--muted) 70%, var(--border));
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--text);
    flex: 1 1 0;
    min-width: 0;
}
.landing-how-it-works__step--arrow {
    flex: 0 0 auto;
    align-self: center;
    background: transparent;
    border: none;
    padding: var(--space-1);
    color: var(--muted);
}
.landing-how-it-works__step-num {
    font-size: 0.625rem;
    font-weight: 700;
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    color: var(--accent-text);
    width: 18px;
    height: 18px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.landing-how-it-works__step-label {
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}
@media (max-width: 640px) {
    .landing-how-it-works__steps {
        flex-direction: row;
        flex-wrap: nowrap;
        align-items: stretch;
        justify-content: center;
        gap: var(--space-1-5);
        padding-inline: 0;
    }
    .landing-how-it-works__step {
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: var(--space-1);
        padding: var(--space-1-5) var(--space-1-5);
        flex: 1 1 0;
        min-width: 0;
    }
    .landing-how-it-works__step--arrow {
        flex: 0 0 auto;
        align-self: center;
        padding: var(--space-1);
    }
}
@media (max-width: 380px) {
    /* iPhone SE / very small phones: arrows hidden to prevent overflow */
    .landing-how-it-works__step--arrow { display: none; }
    .landing-how-it-works__steps { gap: var(--space-3); }
}

/* ── landing inline use-cases line ("Perfect for weddings · …") ────────── */
.landing-use-cases-line {
    text-align: center;
    color: var(--muted);
    font-size: var(--fs-body);
    padding: var(--space-4) var(--space-3) var(--space-2);
    margin: 0 auto;
    max-width: var(--content-max);
    line-height: 1.6;
}
.landing-use-cases-line__items { white-space: normal; }

/* ── PerfectForSection — use-case tiles + expandable drawer ──────────────── */
/* (component deleted in v3-B; CSS kept for potential future reuse) */
.use-cases-section {
    margin: var(--space-4) auto 0;
    padding-block: calc(var(--section-py) * 1.25);
    max-width: var(--content-max);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
}

.use-cases-section__heading {
    font-size: var(--fs-heading-2);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
    margin: 0;
    text-align: center;
}

.use-cases-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-2);
    width: 100%;
}

.use-cases-grid--drawer {
    /* no extra overrides needed — inherits 3-col */
}

/* Tile card — F-UX-012: distinct warm bg tint vs how-it-works semi-transparent wash */
.use-case-tile {
    background: color-mix(in srgb, var(--accent) 4%, var(--surface));
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-md);
    padding: var(--card-padding) var(--card-padding);
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1);
    transition: border-color var(--duration-base);
}

.use-case-tile:hover {
    border-color: var(--border);
}
@media (prefers-reduced-motion: no-preference) {
    .use-case-tile {
        transition: border-color var(--duration-base), transform var(--duration-base), box-shadow var(--duration-base);
    }
    .use-case-tile:hover {
        transform: translateY(-2px);
        box-shadow: var(--shadow);
    }
}

.use-case-tile__icon {
    color: var(--accent-text);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    border-radius: var(--radius-sm);
    flex-shrink: 0;
    margin-bottom: var(--space-0-5);
}

.use-case-tile__title {
    font-size: var(--fs-heading-3);
    font-weight: 700;
    color: var(--text);
    margin: 0;
    line-height: 1.3;
}

.use-case-tile__hook {
    font-size: var(--text-xs);
    color: var(--muted);
    line-height: 1.5;
    margin: 0;
}

/* More events chip — pill-shaped secondary trigger */
.more-events-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-2-5);
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    color: var(--muted);
    font-size: var(--text-sm);
    font-weight: 500;
    cursor: pointer;
    transition: border-color var(--duration-base), color var(--duration-base), background var(--duration-base);
    min-height: 36px;
}

.more-events-chip:hover {
    border-color: var(--accent);
    color: var(--accent-text);
    background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.more-events-chip:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}

/* F-005 — PA-02 touch target floor: WCAG 2.5.5 requires ≥44×44px on coarse pointers */
@media (pointer: coarse), (max-width: 48rem) {
    .more-events-chip { min-height: var(--touch-target-min, 44px); }
}

.more-events-chip__chevron {
    transition: transform var(--duration-base);
    flex-shrink: 0;
}

.more-events-chip__chevron--up {
    transform: rotate(180deg);
}

/* Expandable drawer — max-height transition */
.more-events-drawer {
    width: 100%;
    overflow: hidden;
    max-height: 0;
    opacity: 0;
    visibility: hidden;
    transition: max-height var(--duration-slow) ease,
                opacity var(--duration-base) ease,
                visibility 0s var(--duration-slow);
}

.more-events-drawer--open {
    max-height: 600px; /* large enough to never clip; real height auto-sizes */
    opacity: 1;
    visibility: visible;
    transition-delay: 0s;
}

@media (prefers-reduced-motion: reduce) {
    .more-events-chip__chevron { transition: none; }
    .more-events-drawer { transition: none; }
}

/* Mobile: single-column tiles at ≤768px */
@media (max-width: 768px) {
    .use-cases-grid {
        grid-template-columns: 1fr;
    }
}

/* FAQ accordion */
.faq {
    margin: var(--space-4) auto 0;
    padding-block-start: var(--section-py);
    padding-block-end: var(--space-4);   /* was padding-block: var(--section-py) — FinalCtaSection removed in v3-B left orphan 48px bottom gap */
    max-width: var(--content-max);
}

/* Home page: page shell's 64px bottom padding stacks with FAQ's former 48px
   section-py end, creating the huge gap Irene flagged. Collapse to 0 here —
   FAQ's own padding-block-end (32px) + footer margin-top (24px) = ~56px total. */
.page:has(.home-page) {
    padding-block-end: 0;
}
.faq-h {
    font-size: var(--fs-heading-2);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
    margin: 0 0 var(--space-1-5);
    text-align: center;
}
.faq details {
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-sm);
    padding: var(--space-1-5) var(--space-2);
    background: var(--surface);
    margin-bottom: 6px;
    /* Reduced-motion: transition killed by global *, *::before, *::after guard (app.css:336) */
    transition: border-color var(--duration-base);
}
.faq details[open] { border-color: var(--border); }
.faq summary {
    cursor: pointer;
    font-weight: 600;
    font-size: var(--text-sm);
    list-style: none;
    display: flex; justify-content: space-between; align-items: center;
    gap: var(--space-2);
    color: var(--text);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
    content: "";
    width: 14px; height: 14px;
    border-right: 2px solid var(--muted);
    border-bottom: 2px solid var(--muted);
    transform: rotate(45deg);
    /* Reduced-motion: covered by *::after rule in global guard (app.css:336) */
    transition: transform var(--duration-base);
    flex: none;
}
.faq details[open] summary::after { transform: rotate(225deg); margin-top: 6px; }
.faq p {
    color: var(--muted);
    font-size: var(--text-sm);
    margin: var(--space-1-5) 0 0;
    line-height: 1.55;
}

/* ============================================================
   FinalCtaSection — Landing page bottom-of-funnel CTA.
   Sits between the FAQ accordion and the site footer.
   ============================================================ */

.final-cta-section {
    background: var(--surface);
    border-top: 2px solid var(--accent);
    padding: var(--section-py) var(--gutter-x, 1rem);
    text-align: center;
}

.final-cta-inner {
    max-width: 42rem;
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.25rem;
}

.final-cta__heading {
    font-size: clamp(1.5rem, 4vw, 2rem);
    font-weight: 700;
    color: var(--text);
    margin: 0;
}

.final-cta__button {
    min-width: 240px;
    min-height: 56px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding-inline: 2rem;
}

@media (max-width: 480px) {
    .final-cta__button {
        width: 100%;
        min-width: unset;
    }
}

/* ============================================================
   Frontend-handoff migration — Create a Drop page

   Two-column layout (form + live preview), step cards with numbered
   segmented duration chip row, custom-duration stepper, join-method
   cards, sticky smart-submit.
   The reveal-modal styles (.modal-scrim / .modal__*) are co-located
   here so the create-page surface is self-contained.

   Phase UX-5: removed two-column layout, live-preview pane, template
   picker, and empty "More options" accordion. Form is now single-column,
   above-the-fold on iPhone 14 Pro Max (393×844).
   Phase UX-5.1: .create-page no longer duplicates .page padding.
   ============================================================ */

.create-page {
    display: grid;
    gap: var(--space-3);
}

.create-page .back-link {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--muted);
    text-decoration: none;
    padding: 4px 8px;
    border-radius: var(--radius-sm);
    width: fit-content;
    transition: color var(--duration-base), background var(--duration-base);
}
.create-page .back-link:hover { color: var(--text); background: var(--surface-alt); }

.page-header { max-width: 100%; }
.page-header__title {
    font-size: clamp(1.6rem, 4vw, 2.2rem);
    font-weight: 800;
    letter-spacing: -0.03em;
    margin: 0;
    color: var(--text);
}
.page-header__sub {
    color: var(--muted);
    margin: 4px 0 0;
    font-size: var(--text-md);
    line-height: 1.5;
}

/* Phase UX-5 — single-screen smart-defaults form. Max-width constrains the
   form on wide viewports so the CTA stays at a thumb-comfortable width. */
.create-form {
    display: grid;
    gap: var(--space-2-5);
    min-width: 0;
    max-width: 540px;
    width: 100%;
}

/* Large autofocus input — used for the drop name field */
.input--lg {
    font-size: 1.1rem;
    padding: 12px 14px;
    height: auto;
}

/* Section wrapper for duration / share-method groups */
.create-section {
    display: grid;
    gap: var(--space-1-5);
}
.create-section__label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--text);
    margin: 0;
}
.create-section__label svg { color: var(--muted); flex: none; }






/* Segmented (pill) — handoff style.
   Scoped under .create-page so other future usages can opt-in
   without colliding with the existing duration-picker styles. */
.create-page .segmented {
    display: flex; flex-wrap: wrap; gap: 0;
    background: var(--surface-alt);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-pill);
    padding: 4px;
    grid-template-columns: none;
}
.create-page .segmented__option {
    flex: 1; min-width: max-content;
    text-align: center;
    font-size: var(--text-sm);
    font-weight: 500;
    padding: 9px 14px;
    border-radius: var(--radius-pill);
    cursor: pointer;
    color: var(--muted);
    white-space: nowrap;
    transition: background var(--duration-base), color var(--duration-base);
    position: relative;
    background: transparent;
}
.create-page .segmented__option:hover:not(.segmented__option--on) { color: var(--text); }
.create-page .segmented__option--on {
    background: color-mix(in srgb, var(--accent) 8%, var(--surface));
    color: var(--text);
    font-weight: 600;
    box-shadow: 0 0 0 2px var(--accent), var(--shadow-sm);
}

/* Custom-duration stepper */
.stepper {
    display: inline-flex; align-items: center; gap: 8px;
    margin-top: var(--space-1);
    flex-wrap: nowrap;
}
.stepper > button {
    /* F-20b-1: lift to 44×44 to meet WCAG 2.5.5 touch-target floor on all pointer types.
       Matches .duration-stepper__btn pattern from the FeedPage extend-duration control. */
    min-width: 44px;
    min-height: 44px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    font-size: var(--text-lg);
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    transition: background var(--duration-base), border-color var(--duration-base), color var(--duration-base);
    padding: 0;
}
.stepper > button:hover:not(:disabled) {
    border-color: var(--accent);
    background: var(--surface-hi);
    color: var(--accent-text);
}
.stepper > button:disabled { opacity: 0.45; cursor: not-allowed; }
.stepper > input {
    width: 100px;
    text-align: center;
    font-weight: 600;
    min-height: 44px;
    padding: 8px 12px;
}
.stepper__unit {
    color: var(--muted);
    font-size: var(--text-xs);
}
.stepper__range {
    color: var(--muted-2);
    font-size: var(--text-xs);
    display: block;
    margin-top: 6px;
}
.custom-duration__unit {
    min-height: 44px;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    font-size: var(--text-md);
    font-family: inherit;
    cursor: pointer;
    transition: border-color var(--duration-base);
}
.custom-duration__unit:hover { border-color: var(--accent); }
.custom-duration__unit:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 2px;
}

/* Expiry preview chip — sits under the duration control */
.expiry-preview {
    display: inline-flex; align-items: center; gap: 8px;
    margin-top: 10px;
    padding: 8px 12px;
    background: var(--surface-alt);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-sm);
    font-size: var(--text-xs);
    color: var(--muted);
    max-width: 100%;
    line-height: 1.5;
}
.expiry-preview strong { color: var(--text); font-weight: 600; }
.expiry-preview > svg { color: var(--accent-text); flex: none; }
.expiry-preview__sep { color: var(--muted-2); }

.duration-help {
    color: var(--muted-2);
    font-size: var(--text-xs);
    margin: 8px 0 0;
    line-height: 1.5;
}

/* Join-method cards — handoff styling.
   Distinct class from the legacy .join-card so other surfaces stay
   unchanged. */
.join-method-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--space-1-5);
}
.join-method-card {
    display: grid; gap: 6px;
    padding: var(--space-2);
    border: 1px solid var(--border);
    background: var(--surface);
    border-radius: var(--radius-md);
    cursor: pointer;
    position: relative;
    transition: background var(--duration-base);
    min-height: 0;
}
@media (hover: hover) and (pointer: fine) {
    .join-method-card:not(.join-method-card--on):hover {
        border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
    }
}
.join-method-card--on {
    border: 2px solid var(--accent);
    background: color-mix(in srgb, var(--accent) 6%, var(--surface));
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 40%, transparent);
}
.join-method-card__title {
    font-weight: 700;
    font-size: var(--text-sm);
    display: flex; gap: 10px; align-items: center;
    letter-spacing: -0.005em;
    padding-right: 26px;
    color: var(--text);
}
.join-method-card__title-icon {
    width: 30px; height: 30px;
    border-radius: 8px;
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    color: var(--accent-text);
    display: inline-flex; align-items: center; justify-content: center;
    flex: none;
}
.join-method-card__desc {
    color: var(--muted);
    font-size: var(--text-xs);
    line-height: 1.45;
    padding-left: 40px;
}
.join-method-card > input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.join-method-card__check {
    position: absolute; top: 12px; right: 12px;
    width: 24px; height: 24px;
    border-radius: 50%;
    border: 2px solid var(--border);
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--surface);
    transition: background var(--duration-base);
    color: var(--text-on-accent);
    flex: none;
}
.join-method-card__check svg {
    width: 14px; height: 14px;
}
.join-method-card--on .join-method-card__check {
    background: var(--accent);
    border-color: var(--accent);
}
.join-method-card:has(:focus-visible) {
    border-color: var(--accent);
    box-shadow: var(--focus-ring);
}
/* PIN "Required" card — always-on, non-interactive variant */
.join-method-card--required {
    cursor: default;
    border-color: color-mix(in srgb, var(--accent) 30%, var(--border));
    background: color-mix(in srgb, var(--accent) 4%, var(--surface));
}
.join-method-card__required-badge {
    position: absolute; top: 12px; right: 12px;
    font-size: 0.625rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--surface);
    color: var(--muted);
    border: 1px solid var(--border);
    white-space: nowrap;
    pointer-events: none;
    line-height: 1.5;
}

/* Smart submit row — sticky inside the form column on tall pages */
.create-submit {
    display: flex; flex-direction: column; gap: 6px;
    position: sticky;
    bottom: calc(var(--space-2) + env(safe-area-inset-bottom, 0px));
    background: var(--bg);
    padding: var(--space-1-5);
    border-radius: var(--radius-md);
    border: 1px solid var(--border-soft);
    z-index: 5;
    box-shadow: var(--shadow-sm);
}
/* B-002 (Phase 20a): at mobile widths the sticky submit sits at bottom:16px,
   which is inside the 72px MobileBottomNav zone (z:90). The "Create drop"
   primary CTA is obscured and untappable. Float above the nav bar by
   offsetting bottom by --m-bottomnav-total. z-index bump to 10 keeps it
   above form content while staying below the fixed nav (90) and modals (200). */
@media (max-width: 48rem) {
    .create-submit {
        bottom: calc(var(--m-bottomnav-total, 72px) + var(--space-2));
        z-index: 10;
    }
}
.create-submit__missing {
    display: flex; align-items: center; gap: 6px;
    font-size: var(--text-xs);
    color: var(--muted);
    padding: 0 4px;
}
.create-submit__missing strong { color: var(--text); }

/* Live preview panel */
.create-preview {
    position: sticky;
    top: calc(var(--navbar-h) + var(--space-2));
    display: grid;
    gap: var(--space-1-5);
}
.preview-card {
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-md);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
}
.preview-card__head {
    padding: var(--space-1-5) var(--space-2);
    border-bottom: 1px solid var(--border-soft);
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--space-1);
}
.preview-card__title {
    font-size: var(--text-xs);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
    margin: 0;
}
.preview-card__live {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: var(--text-micro);
    color: var(--success-text);
    font-weight: 600;
}
.preview-card__live::before {
    content: "";
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--success);
    box-shadow: 0 0 6px var(--success);
    animation: preview-pulse 2s ease-in-out infinite;
}
@keyframes preview-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: .5; }
}

.preview-navbar {
    background: var(--bg);
    border-bottom: 1px solid var(--border-soft);
    padding: 10px 12px;
    display: flex; align-items: center; gap: 8px;
    flex-wrap: wrap;
}
.preview-navbar--row2 {
    border-bottom: none;
    padding-top: 8px;
}
.preview-navbar__logo { flex: none; }
.preview-navbar__brand {
    font-weight: 800;
    font-size: 13px;
    background: var(--brand-gradient);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
}
.preview-navbar__drop {
    font-weight: 600;
    font-size: 12px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    min-width: 0; flex: 1;
    color: var(--text);
}
.preview-navbar__drop--muted { color: var(--muted-2); }
.preview-navbar__chip {
    display: inline-flex; align-items: center; gap: 4px;
    font-size: 10px;
    padding: 3px 8px;
    border-radius: var(--radius-pill);
    background: var(--surface-alt);
    border: 1px solid var(--border-soft);
    color: var(--muted);
}
.preview-navbar__pin-val {
    color: var(--accent-text);
    font-weight: 700;
    letter-spacing: 0.1em;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    margin-left: 4px;
}

.preview-summary {
    padding: var(--space-2);
    display: grid;
    gap: 10px;
}
.preview-summary__row {
    display: flex; justify-content: space-between; align-items: center;
    gap: var(--space-1);
    font-size: var(--text-xs);
}
.preview-summary__row--methods { align-items: flex-start; }
.preview-summary__label {
    color: var(--muted);
    display: flex; align-items: center; gap: 6px;
    flex: none;
}
.preview-summary__value {
    color: var(--text);
    font-weight: 600;
    text-align: right;
    min-width: 0;
    word-break: break-word;
}
.preview-summary__value--muted {
    color: var(--muted-2);
    font-weight: 500;
}

.preview-methods {
    display: flex; flex-wrap: wrap; gap: 4px;
    justify-content: flex-end;
}
.preview-methods__chip {
    display: inline-flex; align-items: center; gap: 4px;
    font-size: 10px;
    padding: 2px 8px;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    color: var(--accent-text);
    border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
}
.preview-methods__none { font-size: 10px; }

.preview-trust {
    display: flex; gap: 10px; align-items: flex-start;
    padding: var(--space-1-5);
    background: color-mix(in srgb, var(--info) 8%, transparent);
    border: 1px solid color-mix(in srgb, var(--info) 25%, transparent);
    border-radius: var(--radius-sm);
    font-size: var(--text-xs);
    color: var(--info-text);
    line-height: 1.5;
}
.preview-trust > svg {
    flex: none;
    margin-top: 2px;
    color: var(--info-text);
}
.preview-trust strong { color: var(--text); font-weight: 600; }

/* Reveal modal */
.modal-scrim {
    position: fixed;
    inset: 0;
    background: var(--scrim);
    display: grid;
    place-items: center;
    padding: var(--space-2);
    z-index: 200;
    backdrop-filter: blur(6px);
    animation: scrim-in 200ms var(--easing-emphasis);
}
@keyframes scrim-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}
.modal {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space-3);
    max-width: 520px;
    width: 100%;
    box-shadow: var(--shadow-lg);
    animation: modal-in 240ms var(--easing-emphasis);
    display: grid;
    gap: var(--space-1-5);
}
@keyframes modal-in {
    from { opacity: 0; transform: translateY(8px) scale(.98); }
    to   { opacity: 1; transform: translateY(0)   scale(1);   }
}
.modal > h2 {
    margin: 0;
    font-size: var(--text-xl);
    letter-spacing: -0.015em;
    color: var(--text);
}
.modal__sub {
    color: var(--muted);
    margin: 0;
    font-size: var(--text-sm);
    line-height: 1.5;
}
.modal__hint {
    color: var(--muted-2);
    margin: 0;
    font-size: var(--text-xs);
    text-align: center;
}

/* HOM-A1 — reveal modal: PIN display block (Proximity + Common Region) */
.modal__pin-display {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    background: var(--surface-alt);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-md);
    padding: var(--space-1-5) var(--space-2);
}
.modal__pin-label {
    font-size: var(--text-xs);
    color: var(--muted);
    letter-spacing: .06em;
    text-transform: uppercase;
    margin-bottom: 2px;
}
.modal__pin-value {
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: var(--text-xl);
    letter-spacing: .12em;
    color: var(--text);
}
/* Fitts: 44px touch-target floor, inline-flex for icon+text alignment */
.modal__copy-btn {
    min-height: 44px;
    padding: 0 12px;
    font-size: var(--text-sm);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    border: 1px solid color-mix(in srgb, var(--accent) 25%, var(--border));
    background: color-mix(in srgb, var(--accent) 3%, var(--surface));
}
.modal__copy-btn:hover {
    border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
    background: color-mix(in srgb, var(--accent) 6%, var(--surface));
}
/* PA-02 coarse-pointer guard — matches .more-events-chip pattern */
@media (pointer: coarse), (max-width: 48rem) {
    .modal__copy-btn { min-height: var(--touch-target-min, 44px); }
}
/* HOM-A1 — host link code block */
.modal__code {
    display: block;
    background: var(--surface-alt);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-sm);
    padding: 8px 10px;
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: var(--text-xs);
    color: var(--muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* HOM-A1 — security warning band */
.modal__warn {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    background: color-mix(in srgb, var(--warning) 8%, transparent);
    border: 1px solid color-mix(in srgb, var(--warning) 30%, transparent);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    margin: 0;
    font-size: var(--text-sm);
    color: var(--text);
    line-height: 1.5;
}
.modal__warn svg { color: var(--warning-text, var(--warning)); }
/* HOM-A1 reveal modal — flex column so .modal__body scrolls independently
   while .modal__footer (CTA) stays pinned at the bottom. Prevents the
   "I've saved it" button from being cut off on short viewports (iPhone SE
   375×667, iPhone 14 Pro Max 390×844 with Safari UI chrome). */
.modal.modal--reveal {
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 2 * var(--space-2));
    max-height: calc(100svh - 2 * var(--space-2));
    gap: 0;
}
.modal--reveal > h2 {
    flex-shrink: 0;
    margin-bottom: var(--space-1-5);
}
.modal--reveal .modal__body {
    flex: 1;
    min-height: 0;          /* required for flex children to shrink below content size */
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-1-5);
    padding-right: 2px;     /* prevent scrollbar overlap with content */
}
.modal--reveal .modal__footer {
    flex-shrink: 0;
    padding-top: var(--space-1-5);
    border-top: 1px solid var(--border-soft);
    margin-top: var(--space-1-5);
}
/* HOM-A1 host-link section divider — horizontal rule with centred label text
   that separates the guest-share area (PIN/QR/join-link) from the host-only
   access link so the host doesn't accidentally copy and share it with guests. */
.modal__host-divider {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--muted);
    font-size: var(--text-xs);
    letter-spacing: 0.05em;
    text-transform: uppercase;
}
.modal__host-divider::before,
.modal__host-divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--border-soft);
}
/* Fitts + Doherty: full-width copy button, inline-flex for icon+text */
.field > .btn-secondary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
}

/* ============================================================
   Frontend-handoff migration — Composer sidebar polish

   Refreshes the existing MomentComposerSidebar visuals to the
   handoff .composer card pattern WITHOUT restructuring the markup:

     - Sidebar section header → handoff .composer__header (medallion
       icon + bold h2-equivalent title)
     - Composer card frame → softer border, drop shadow, generous gap
     - Caption textarea → handoff .textarea (surface-alt fill,
       purple focus ring, accent border on hover, 11/13px padding)
     - Privacy row → handoff .switch (bold label + on/off pill +
       hint line + flat track-button)
     - Char counter → right-aligned, warn state when near 1000
     - Bottom action row → handoff .btn-primary submit, ghost-style
       attach buttons

   Functionality preserved: identical Razor markup; only visual rules
   are layered on top. The selectors target existing classes
   (.caploo-composer-sidebar*, .caploo-toggle-switch, etc.) and so
   take effect everywhere the composer renders (drop feed sidebar,
   mobile drawer, anywhere else FeedPage mounts the component).
   ============================================================ */

/* Composer card frame — visible only when nested inside the feed
   sidebar's SidebarSection. Scoped under [data-section="composer"]
   so it does not retouch the calendar / map sections. */
.caploo-sidebar-section[data-section="composer"] {
    border-color: var(--border-soft);
    box-shadow: var(--shadow-sm);
}
.caploo-sidebar-section[data-section="composer"] .caploo-sidebar-section__header {
    gap: 10px;
    padding: var(--space-1-5) var(--space-2);
    min-height: 0;
    background: transparent;
    border-bottom: 1px solid var(--border-soft);
}
.caploo-sidebar-section[data-section="composer"] .caploo-sidebar-section__icon {
    width: 28px; height: 28px;
    border-radius: 8px;
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    color: var(--accent-text);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
}
.caploo-sidebar-section[data-section="composer"] .caploo-sidebar-section__label {
    gap: 10px;
}
.caploo-sidebar-section[data-section="composer"] .caploo-sidebar-section__title {
    font-size: var(--text-md);
    font-weight: 700;
    letter-spacing: -0.005em;
    color: var(--text);
}
.caploo-sidebar-section[data-section="composer"] .caploo-sidebar-section__body {
    padding: var(--space-2);
}

/* Composer body — comfortable vertical rhythm matching the handoff. */
.caploo-composer-sidebar {
    gap: var(--space-1-5);
}

/* Caption textarea — handoff .textarea look. */
.caploo-composer-sidebar__caption {
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    padding: 11px 13px;
    font-size: max(16px, var(--text-md));
    line-height: 1.5;
    min-height: 92px;
    max-height: 240px;
    resize: vertical;
    transition: border-color var(--duration-base),
                box-shadow var(--duration-base),
                background var(--duration-base);
}
.caploo-composer-sidebar__caption::placeholder { color: var(--muted-2); }
.caploo-composer-sidebar__caption:hover {
    border-color: color-mix(in srgb, var(--accent) 30%, var(--border));
}
.caploo-composer-sidebar__caption:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: var(--focus-ring);
    background: var(--surface);
}

/* Character counter — right-aligned, warn state near the limit. */
.composer-char-counter {
    display: flex;
    justify-content: flex-end;
    font-size: var(--text-xs);
    color: var(--muted-2);
    margin-top: -2px;
    font-variant-numeric: tabular-nums;
}
.composer-char-counter.is-near-limit { color: var(--warning-text); font-weight: 600; }

/* Privacy row — handoff .switch pattern. Label is bold + carries an
   on/off pill; helper text sits below; track-button takes the right
   gutter with a flat pill. */
.caploo-composer-privacy {
    display: grid;
    grid-template-columns: 1fr auto;
    column-gap: var(--space-1-5);
    row-gap: 4px;
    align-items: center;
    padding: var(--space-1-5);
    border: 1px solid var(--border-soft);
    background: var(--surface-alt);
    border-radius: var(--radius-sm);
}
.caploo-composer-privacy__label {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-weight: 700;
    font-size: var(--text-sm);
    color: var(--text);
    letter-spacing: -0.005em;
    grid-row: 1;
    grid-column: 1;
}
.caploo-composer-privacy__label > svg { color: var(--accent-text); flex: none; }
.caploo-composer-privacy__hint {
    grid-row: 2;
    grid-column: 1 / -1;
    margin: 0;
    color: var(--muted);
    font-size: var(--text-xs);
    line-height: 1.45;
}
.caploo-composer-privacy .caploo-toggle-switch {
    grid-row: 1;
    grid-column: 2;
    margin: 0;
}

/* Toggle switch — refresh to the handoff flatter track. */
.caploo-toggle-switch {
    background: transparent;
    border: none;
    padding: 0;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    line-height: 1;
}
.caploo-toggle-switch__track {
    position: relative;
    display: inline-block;
    width: 38px;
    height: 22px;
    border-radius: var(--radius-pill);
    background: var(--border);
    transition: background var(--duration-base);
}
.caploo-toggle-switch__thumb {
    position: absolute;
    top: 2px; left: 2px;
    width: 18px; height: 18px;
    border-radius: 50%;
    background: var(--surface);
    box-shadow: var(--shadow-sm);
    transition: transform var(--duration-base), background var(--duration-base);
}
.caploo-toggle-switch--on .caploo-toggle-switch__track {
    background: var(--accent);
}
.caploo-toggle-switch--on .caploo-toggle-switch__thumb {
    transform: translateX(16px);
    background: var(--text-on-accent);
}
.caploo-toggle-switch:focus-visible .caploo-toggle-switch__track {
    box-shadow: var(--focus-ring);
}


/* Bottom action row — attach buttons read as ghost-style icons next to
   the primary submit. */
.caploo-composer-bottom-row {
    display: flex;
    align-items: center;
    gap: var(--space-1-5);
    flex-wrap: wrap;
    margin-top: 4px;
}
.caploo-composer-attach { display: inline-flex; gap: 6px; }
.caploo-composer-attach__btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 12px;
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: var(--text-sm);
    font-weight: 500;
    transition: color var(--duration-base),
                border-color var(--duration-base),
                background var(--duration-base);
    min-height: var(--touch-target-min);
}
.caploo-composer-attach__btn > svg { color: var(--accent-text); flex: none; }
.caploo-composer-attach__btn:hover:not(:disabled) {
    color: var(--text);
    border-color: var(--accent);
    background: var(--surface-alt);
}


/* Media chip — handoff-aligned tag-chip-style row. */
.caploo-composer-sidebar__media-chip {
    background: color-mix(in srgb, var(--accent) 8%, var(--surface-alt));
    border: 1px solid color-mix(in srgb, var(--accent) 25%, var(--border));
    border-radius: var(--radius-sm);
    padding: 8px 8px 8px 12px;
}

/* Disabled-hint banner — sit inside a soft card so it doesn't read as
   placeholder text. */
.caploo-composer-sidebar__disabled-hint {
    padding: var(--space-1-5);
    background: var(--surface-alt);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-sm);
    color: var(--muted);
    font-size: var(--text-sm);
}

/* CRE-A1: Mode strip — Text / Upload / Camera radio tabs above the textarea. */
.mode-strip {
    display: flex;
    gap: var(--space-1);
    margin-bottom: var(--space-2);
}
.mode-strip__radio {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}
.mode-strip__option {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--muted);
    background: transparent;
    cursor: pointer;
    user-select: none;
    transition: color var(--duration-base),
                border-color var(--duration-base),
                background var(--duration-base);
    min-height: var(--touch-target-min);
}
.mode-strip__option > svg { color: var(--accent-text); flex: none; }
.mode-strip__option:hover {
    color: var(--text);
    border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
    background: var(--surface-alt);
}
.mode-strip__option--on {
    color: var(--text-on-accent);
    background: var(--accent);
    border-color: transparent;
    box-shadow: 0 4px 12px var(--accent-glow);
}
.mode-strip__option--on > svg { color: var(--text-on-accent); }
.mode-strip__option--on:hover {
    color: var(--text-on-accent);
    background: var(--accent-dim);
    border-color: transparent;
}

/* CRE-A1: Upload dropzone — dashed block shown when Upload mode is active. */
.composer-dropzone {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-1);
    width: 100%;
    min-height: 96px;
    border: 2px dashed var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-alt);
    color: var(--muted);
    font-size: var(--text-sm);
    font-family: inherit;
    cursor: pointer;
    padding: var(--space-2);
    transition: border-color var(--duration-base),
                background var(--duration-base),
                color var(--duration-base);
    margin-bottom: var(--space-1);
    text-align: center;
}
.composer-dropzone > svg { color: var(--accent-text); }
.composer-dropzone:hover:not(:disabled) {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 5%, var(--surface-alt));
    color: var(--text);
}
.composer-dropzone:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
    border-color: var(--accent);
}
.composer-dropzone:disabled { opacity: 0.45; cursor: not-allowed; }
.composer-dropzone__label { font-weight: 600; color: var(--text); font-size: var(--text-sm); }
.composer-dropzone__hint  { font-size: var(--text-xs); color: var(--muted); }

/* CRE-A2: Full-width Post button row below the privacy toggle. */
.caploo-composer-sidebar__submit-row {
    margin-top: var(--space-2);
}

/* ============================================================
   Frontend-handoff migration — MomentCard + ReactionBar polish

   CSS-only overrides bringing the existing MomentCard (grid / list /
   large variants) and the per-moment ReactionBar to the handoff spec
   without touching markup or the rich behavior (multi-asset carousel,
   hover overlay, host actions, report flow, comment count badge,
   private chip, EditedByHost chip, picker popover, stacked reactor
   avatars, etc.).

   What changes:
     - Card frame: softer border (--border-soft), rounded radius-md,
       gentle hover lift with accent-tinted border + shadow-sm
     - Media wrap: handoff scale-on-hover for img, 4:3 aspect for
       large variant, 16:10 for "large" mode
     - Text-only hero: handoff brand-gradient panel with balanced
       wrap + accent-text shadow
     - Card meta / byline: handoff name + time stacking, accent tag
       chips, muted location chip
     - Reaction trigger: surface-alt pill, accent-text liked state,
       reactionPop animation on success
     - Reaction picker: pill-shaped popover with accent outline on
       active option, scale-on-hover

   Variants:
     - .moment-card--grid  → 1:1 media (unchanged), tighter padding
     - .moment-card--list  → horizontal 132px thumb, vertical meta
     - .moment-card--large → 16:10 media, full caption, large hero
   ============================================================ */

.moment-card {
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-md);
    box-shadow: none;
    transition: transform var(--duration-base) var(--easing-emphasis),
                border-color var(--duration-base),
                box-shadow var(--duration-base);
}
@media (prefers-reduced-motion: no-preference) {
    .moment-card:hover {
        transform: translateY(-2px);
        border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
        box-shadow: var(--shadow-sm);
    }
}
@media (prefers-reduced-motion: reduce) {
    .moment-card:hover {
        transform: none;
        border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
        box-shadow: var(--shadow-sm);
    }
}
.moment-card:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-color: var(--accent);
}

/* Media zoom on card hover — handoff signature affordance. */
.moment-media-wrap > img,
.moment-media-wrap > picture > img,
.moment-media-wrap > video {
    transition: transform 480ms var(--easing-emphasis);
    will-change: transform;
}
@media (prefers-reduced-motion: no-preference) {
    .moment-card:hover .moment-media-wrap > img,
    .moment-card:hover .moment-media-wrap > picture > img,
    .moment-card:hover .moment-media-wrap > video {
        transform: scale(1.03);
    }
}

/* Large variant — switch to 16:10 hero, more relaxed padding, full
   caption (no line clamp). */
.moment-card--large .moment-media-wrap { aspect-ratio: 16 / 10; }
.moment-card--large .moment-media-wrap--text { aspect-ratio: 21 / 9; }
.moment-card--large .moment-card-body { padding: var(--space-2); }
.moment-card--large .moment-card-caption .caption-text {
    -webkit-line-clamp: unset;
    font-size: var(--text-md);
    line-height: 1.55;
}

/* List variant — handoff horizontal layout (132px square thumb +
   stacked meta). Refines the existing 96px grid-template to match. */
.moment-card--list {
    display: flex;
    flex-direction: row;
    align-items: stretch;
    gap: 0;
    padding: 0;
    grid-template-columns: none;
}
.moment-card--list .moment-media-wrap {
    width: 132px;
    height: auto;
    min-height: 132px;
    aspect-ratio: 1 / 1;
    border-radius: 0;
    flex: none;
}
.moment-card--list .moment-card-body {
    flex: 1;
    min-width: 0;
    padding: var(--space-1-5);
    gap: 8px;
}
.moment-card--list .moment-card-caption .caption-text {
    -webkit-line-clamp: 2;
}

/* Text-only hero panel — handoff brand-gradient background with
   pretty wrap + balanced lines. */
.moment-media-wrap--text {
    display: grid;
    place-items: center;
    padding: var(--space-3) var(--space-2-5);
    background:
        radial-gradient(120% 80% at 20% 0%, rgba(255,255,255,0.12), transparent 60%),
        var(--brand-gradient);
    color: var(--text-on-accent);
}
.moment-text-hero__body {
    margin: 0;
    font-size: var(--text-lg);
    font-weight: 600;
    text-align: center;
    line-height: 1.3;
    letter-spacing: -0.01em;
    text-wrap: balance;
}
.moment-card--large .moment-text-hero__body {
    font-size: var(--text-xl);
}
.moment-card--list .moment-text-hero__body {
    font-size: var(--text-sm);
    text-align: left;
}

/* Byline — handoff: author bold + muted time on the same line. */
.moment-card-byline {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: var(--text-xs);
    color: var(--muted);
}
.moment-card-byline__author {
    color: var(--text);
    font-weight: 600;
    letter-spacing: -0.005em;
    font-size: var(--text-xs);
}
.moment-card-byline__time { color: var(--muted-2); }

/* Moment author name — handoff prototype line 1626. */
.moment-author__name {
    color: var(--text);
    font-weight: 600;
    font-size: var(--text-xs);
}

/* Caption — handoff: 14px text, pretty wrap, 2-line clamp on grid. */
.moment-card-caption {
    font-size: var(--text-sm);
    color: var(--text);
    line-height: 1.5;
    text-wrap: pretty;
}

/* Meta chips row — handoff: small accent-tinted tag chips +
   muted location chip with pin icon. */
.moment-card-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
    margin: 0;
    padding: 0;
}
.moment-card-meta__chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: var(--text-xs);
    padding: 2px 8px;
    border-radius: var(--radius-pill);
    background: var(--surface-alt);
    color: var(--muted);
    border: 1px solid var(--border-soft);
    line-height: 1.4;
}
.moment-card-meta__chip--tag {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    color: var(--accent-text);
    border-color: color-mix(in srgb, var(--accent) 30%, transparent);
}
.moment-card-meta__chip--location { color: var(--muted); }
.moment-card-meta__chip--location svg { color: var(--accent-text); flex: none; }
.moment-card-meta__chip--more { color: var(--muted-2); }

/* Comment count + private + carousel chips — handoff floating media
   badges. */
.media-badge,
.moment-carousel-count {
    position: absolute;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    padding: 4px 9px;
    border-radius: var(--radius-pill);
    font-size: var(--text-xs);
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    z-index: 2;
    pointer-events: none;
    user-select: none;
}
.media-badge--comments { top: 10px; left: 10px; }
.media-badge--private,
.moment-carousel-count { top: 10px; right: 10px; }

/* Bottom-gradient meta row (the hover-revealed author+time strip
   on grid cards) — soften to fit handoff aesthetic. */
.moment-card-meta {
    /* This rule already exists above for the chip-meta row; the
       overlay variant is handled by .moment-media-wrap > .moment-card-meta
       descendants below. */
}

/* Reaction bar — handoff pill trigger with accent-tinted liked state. */
.reaction-bar {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
.reaction-trigger {
    min-width: 44px;
    min-height: 36px;
    padding: 6px 12px;
    border-radius: var(--radius-pill);
    background: var(--surface-alt);
    border: 1px solid var(--border-soft);
    color: var(--muted);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
    transition: background var(--duration-base),
                color var(--duration-base),
                border-color var(--duration-base),
                transform var(--duration-fast);
    font-size: var(--text-xs);
    font-weight: 600;
    user-select: none;
}
@media (pointer: coarse) {
    .reaction-trigger { min-height: var(--touch-target-min); }
}
.reaction-trigger:hover {
    color: var(--text);
    border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
    background: var(--surface-hi);
}
.reaction-trigger--liked,
.reaction-trigger--active {
    background: color-mix(in srgb, var(--accent) 16%, transparent);
    border-color: color-mix(in srgb, var(--accent) 45%, transparent);
    color: var(--accent-text);
}
.reaction-trigger--success {
    animation: reaction-pop 360ms var(--easing-emphasis);
}
@keyframes reaction-pop {
    0%   { transform: scale(1); }
    40%  { transform: scale(1.18); }
    100% { transform: scale(1); }
}
.reaction-trigger__count { font-variant-numeric: tabular-nums; }

/* Stacked reactor avatars — slight overlap, surface border so they
   read as discs against any card background. */
.reaction-stack { display: inline-flex; }
.reaction-stack__avatar {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--surface-hi);
    border: 2px solid var(--surface);
    color: var(--accent-text);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 10px;
    font-weight: 700;
    margin-left: -7px;
}
.reaction-stack__avatar:first-child { margin-left: 0; }

/* Reaction picker popover — pill-shaped with accent outline on
   active. Position is owned by the existing rules; this only adjusts
   the visual treatment. */
.reaction-picker {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    padding: 6px;
    display: inline-flex;
    gap: 2px;
    box-shadow: var(--shadow-lg);
    animation: reaction-picker-in 200ms var(--easing-emphasis);
}
@keyframes reaction-picker-in {
    from { opacity: 0; transform: translateY(8px) scale(.92); }
    to   { opacity: 1; transform: translateY(0)   scale(1);   }
}
.reaction-picker__option {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: none;
    background: transparent;
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
    transition: transform var(--duration-fast), background var(--duration-base);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
@media (prefers-reduced-motion: no-preference) {
    .reaction-picker__option:hover {
        transform: translateY(-3px) scale(1.18);
        background: var(--surface-alt);
    }
}
.reaction-picker__option--active {
    background: color-mix(in srgb, var(--accent) 18%, transparent);
    outline: 2px solid var(--accent);
}

/* ============================================================
   Frontend-handoff migration — NavBar polish

   Refreshes the sticky top navbar to match the handoff:
     - Background: --bg with 72% mix + saturate(140%) blur for
       crisper glass effect on photos / brand-gradient under it
     - Bottom border: --border-soft hairline
     - Brand: 1.05rem, drop-shadow logo glow
     - PIN + countdown chips: handoff .chip pill (28px height,
       surface-alt + border-soft, 6px gap, accent-text on PIN value)
     - PIN-chip copy button: ghost link that flips to success-text
       on copied state
     - Countdown urgent state: warning-glow background

   Markup is unchanged — only visual rules are layered on. The 3-
   cluster IA (Phase 19b) + slot system (drop-info / meta / actions)
   still drives layout.
   ============================================================ */

.navbar {
    background: color-mix(in srgb, var(--bg) 72%, transparent);
    backdrop-filter: saturate(140%) blur(18px);
    -webkit-backdrop-filter: saturate(140%) blur(18px);
    border-bottom: 1px solid var(--border-soft);
    padding: 0 var(--gutter-x);
}

.navbar-brand {
    font-weight: 800;
    font-size: 1.05rem;
    letter-spacing: -0.02em;
    gap: 8px;
    padding: 4px 6px;
    border-radius: var(--radius-sm);
}
.navbar-brand svg {
    filter: drop-shadow(0 0 10px var(--accent-glow));
}
.navbar-brand:hover { filter: brightness(1.1); opacity: 1; }

/* Drop-name section in the navbar slot — match handoff title style */
.navbar-drop-name {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 28ch;
    letter-spacing: -0.005em;
}

/* PA-03: tap-to-expand interaction — ensure minimum touch target height
   and show pointer cursor so the affordance is discoverable. The
   data-cta-tier attribute is already present on the element. */
.navbar-drop-name[data-cta-tier] {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    cursor: pointer;
}

/* Countdown + PIN chips — handoff .chip pill (28px height). */
.navbar-countdown,
.navbar-pin-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 28px;
    padding: 0 10px;
    border-radius: var(--radius-pill);
    background: var(--surface-alt);
    border: 1px solid var(--border-soft);
    font-size: var(--text-xs);
    color: var(--muted);
    white-space: nowrap;
}
.navbar-countdown svg,
.navbar-pin-chip svg { flex: none; color: var(--muted); }

.navbar-countdown.countdown--warn,
.navbar-countdown.countdown--danger {
    background: var(--warning-glow);
    border-color: color-mix(in srgb, var(--warning) 35%, transparent);
    color: var(--warning-text);
}
.navbar-countdown.countdown--warn svg,
.navbar-countdown.countdown--danger svg { color: var(--warning-text); }

.navbar-pin-chip__label {
    color: var(--muted-2);
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.navbar-pin-chip__value {
    color: var(--accent-text);
    letter-spacing: 0.14em;
    font-weight: 700;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.navbar-pin-chip__copy {
    border: none;
    background: transparent;
    cursor: pointer;
    color: var(--muted-2);
    padding: 2px 4px;
    border-radius: var(--radius-sm);
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: var(--text-xs);
    font-weight: 500;
    transition: color var(--duration-base), background var(--duration-base);
}
.navbar-pin-chip__copy:hover {
    color: var(--text);
    background: transparent;
}
.navbar-pin-chip__copy.is-copied { color: var(--success-text); }

/* Cluster divider — softer + 18px tall to match handoff. */
.navbar-actions .nav-cluster + .nav-cluster::before {
    background: var(--border-soft);
    height: 18px;
}

/* Help-trigger button + Theme/Language buttons — handoff .icon-btn
   style. The existing buttons use various classes; we promote the
   shared visual treatment here. */
.navbar-actions .nav-cluster button:not(.btn-primary):not(.btn-secondary):not(.user-chip),
.navbar-actions .nav-cluster a:not(.btn-primary):not(.btn-secondary):not(.user-chip) {
    color: var(--muted);
    transition: background var(--duration-base), color var(--duration-base);
}
.navbar-actions .nav-cluster button:not(.btn-primary):not(.btn-secondary):not(.user-chip):hover,
.navbar-actions .nav-cluster a:not(.btn-primary):not(.btn-secondary):not(.user-chip):hover {
    color: var(--text);
    background: var(--surface-alt);
}
.navbar-actions .nav-cluster button[aria-expanded="true"] {
    background: var(--surface-alt);
    color: var(--accent-text);
}

/* ============================================================
   Frontend-handoff migration — NavOverflowSheet overrides + the
   generic .sheet / .sheet__* bottom-sheet primitives.

   NavOverflowSheet (mobile ≤480px ⋮ menu) directly consumes the
   .sheet-scrim + .sheet primitives at the Razor level; the per-
   surface overrides below only tweak its content layout (header,
   section labels, row density, destructive row tint).
   ============================================================ */

/* NavOverflowSheet now consumes .sheet-scrim + .sheet directly (Razor retrofit).
   Most of the polish rules previously in this block are superseded by the
   primitive; the remaining overrides below tighten its content layout
   against the .sheet defaults: bolder/larger title, denser header padding,
   bolder section labels, accent-tinted destructive row.

   The inner BEM elements (__header, __row, __section-label) supply their own
   horizontal padding, so neutralize the .sheet primitive's wrapper padding
   while preserving the safe-area-bottom inset that mobile sheets need. */
.sheet.nav-overflow-sheet {
    padding-top: 0;
    padding-inline: 0;
    padding-bottom: env(safe-area-inset-bottom, 0px);
}
.nav-overflow-sheet__header {
    border-bottom: 1px solid var(--border-soft);
    padding: var(--space-1) var(--space-2);
}
.nav-overflow-sheet__title {
    font-size: var(--text-md);
    font-weight: 700;
    color: var(--text);
    text-transform: none;
    letter-spacing: -0.01em;
}
.nav-overflow-sheet__section-label {
    padding: var(--space-1-5) var(--space-2) var(--space-1);
    font-size: var(--text-xs);
    font-weight: 700;
    color: var(--muted-2);
    letter-spacing: 0.06em;
}
.nav-overflow-sheet__row {
    padding: 12px var(--space-2);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
}
.nav-overflow-sheet__row-icon {
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    background: var(--surface-alt);
    color: var(--muted);
    flex: none;
}
.nav-overflow-sheet__row--destructive {
    color: var(--danger-text, var(--warning-text));
}
.nav-overflow-sheet__divider {
    border-top: 1px solid var(--border-soft);
    margin: 6px 0;
}

/* Generic .sheet primitives — handoff bottom-sheet pattern. Reusable
   by future surfaces (mobile MoreSheet, locale picker, theme picker)
   without reaching for ad-hoc CSS. */
.sheet-scrim {
    position: fixed;
    inset: 0;
    background: var(--scrim);
    z-index: 220;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    animation: scrim-in 200ms var(--easing-emphasis);
}
.sheet {
    /* FIX: position:fixed + z-index:221 required so the sheet renders ABOVE
       .sheet-scrim (z-index:220). Without this, .sheet has no position and
       renders in normal document flow, behind the fixed scrim. The scrim's
       backdrop-filter:blur(4px) then blurs the sheet content — visible but
       unreadable. bottom:0 + inset-inline:0 + margin-inline:auto replicates
       the centering the scrim's flex container was originally providing. */
    position: fixed;
    inset-block-end: 0;
    inset-inline: 0;
    margin-inline: auto;
    z-index: 221;
    background: var(--surface);
    width: 100%;
    max-width: 480px;
    border: 1px solid var(--border-soft);
    border-bottom: none;
    border-top-left-radius: var(--bottom-sheet-radius, 20px);
    border-top-right-radius: var(--bottom-sheet-radius, 20px);
    box-shadow: var(--shadow-lg);
    padding: var(--space-1) var(--space-2) calc(var(--space-2) + env(safe-area-inset-bottom, 0px));
    max-height: 86vh;
    overflow-y: auto;
    animation: sheet-in 280ms var(--easing-emphasis);
}
@keyframes sheet-in {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
}
.sheet__grabber {
    width: 36px;
    height: 4px;
    border-radius: var(--radius-pill);
    background: var(--border);
    margin: 6px auto 10px;
}
.sheet__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--space-2);
    padding: 0 4px;
}
.sheet__title {
    font-size: var(--text-md);
    font-weight: 700;
    margin: 0;
    letter-spacing: -0.01em;
}
.sheet__section {
    margin-bottom: var(--space-2);
    padding: 0 4px;
}
.sheet__section-label {
    font-size: var(--text-xs);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted-2);
    margin-bottom: 8px;
}
.sheet__row {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 12px 8px;
    background: transparent;
    border: none;
    border-radius: var(--radius-sm);
    color: var(--text);
    font-size: var(--text-sm);
    font-weight: 500;
    cursor: pointer;
    text-align: left;
    transition: background var(--duration-base);
    min-height: 48px;
}
.sheet__row:hover { background: var(--surface-alt); }
.sheet__row-icon {
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    background: var(--surface-alt);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    flex: none;
}
.sheet__row-label { flex: 1; }
.sheet__row-value {
    color: var(--muted);
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.04em;
}
.sheet__row--primary .sheet__row-icon {
    background: var(--accent);
    color: var(--text-on-accent);
}
.sheet__row--primary {
    color: var(--accent-text);
    font-weight: 600;
}
.sheet__row--on {
    background: color-mix(in srgb, var(--accent) 8%, transparent);
    color: var(--accent-text);
}

/* AUT-B1: NavOverflowSheet language section */
.sheet-locale-list {
    display: flex;
    flex-direction: column;
    gap: 0;
}
.sheet__row--locale {
    padding-block: 14px;
}
.sheet__row-icon--flag {
    width: 28px;
    text-align: center;
    font-size: 1.25rem;
    flex: none;
}
.nav-overflow-sheet__row-label {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}
.nav-overflow-sheet__row-english {
    font-size: var(--text-xs);
    color: var(--muted-2);
}
.nav-overflow-sheet__row-check {
    margin-inline-start: auto;
    color: var(--accent-text);
    display: inline-flex;
    align-items: center;
}
.lang-trigger__chevron { flex: 0 0 auto; color: var(--muted-2); }

/* UX-6B Item 4: language expand pattern */
.sheet-locale-expand {
    display: contents;
}
.sheet-locale-expand__summary {
    list-style: none;
    cursor: pointer;
}
.sheet-locale-expand__summary::-webkit-details-marker { display: none; }
.sheet-locale-expand[open] .sheet-locale-expand__summary .lucide-globe {
    color: var(--accent-text);
}

/* ============================================================
   Frontend-handoff migration — MobileBottomNav (CSS prep)

   Styling primitives for the handoff mobile bottom navigation.
   The Razor component is a separate follow-up; this block lets the
   eventual implementation drop in with handoff styling already in
   place.

   Token contract:
     --m-bottomnav-total : total height incl. safe-area-bottom

   Markup contract (handoff):
     <nav class="mobile-bottom-nav" aria-label="Primary">
       <button class="mbn__item [mbn__item--active|--disabled]">
         <svg>icon</svg><span class="mbn__label">…</span>
       </button>
       <button class="mbn__fab">…</button>
     </nav>
   ============================================================ */
:root {
    --m-bottomnav-h: 56px;
    --m-bottomnav-total: calc(var(--m-bottomnav-h) + env(safe-area-inset-bottom, 8px) + 8px);
}

.mobile-bottom-nav {
    display: none;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 90;
    height: var(--m-bottomnav-total);
    padding: 6px 8px env(safe-area-inset-bottom, 8px);
    background: color-mix(in srgb, var(--bg) 80%, transparent);
    backdrop-filter: saturate(160%) blur(18px);
    -webkit-backdrop-filter: saturate(160%) blur(18px);
    border-top: 1px solid var(--border-soft);
    grid-template-columns: repeat(5, 1fr);
    align-items: end;
    gap: 4px;
}
/* 48rem = --bp-md (Phase 16b token system) */
@media (max-width: 48rem) {
    .mobile-bottom-nav { display: grid; }
}
@media (max-width: 48rem) and (orientation: landscape) and (max-height: 500px) {
    .mobile-bottom-nav { display: none; }
}

.mbn__item {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    height: 56px;
    border: none;
    background: transparent;
    color: var(--muted-2);
    font-size: 11px; /* C-002 (Phase 20a): 10.5px below iOS 11px readability floor */
    font-weight: 600;
    letter-spacing: 0.01em;
    cursor: pointer;
    padding: 6px 4px;
    border-radius: var(--radius-sm);
    transition: color var(--duration-base);
}
.mbn__item:hover { color: var(--text); }
/* D-001 (Phase 20a): .mbn__item and .mbn__fab are excluded from the global
   button focus rule (not(.mbn__item):not(.mbn__fab)) but had no custom
   :focus-visible of their own — WCAG 2.4.11 violation. */
.mbn__item:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
    color: var(--text);
}
.mbn__item--active { color: var(--accent-text); }
.mbn__item--active svg { transform: scale(1.05); }
.mbn__item--disabled { opacity: .35; cursor: not-allowed; }
.mbn__label { line-height: 1; }

.mbn__fab {
    width: 52px;
    height: 52px;
    border-radius: 50%;
    background: var(--accent);
    color: var(--text-on-accent);
    border: 4px solid var(--bg);
    box-shadow: 0 8px 20px var(--accent-glow);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto;
    transition: transform var(--duration-fast), background var(--duration-base);
    align-self: center;
    transform: translateY(-12px);
}
.mbn__fab:hover { background: var(--accent-dim); }
.mbn__fab:active { transform: translateY(-10px) scale(.96); }
.mbn__fab:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--bg), var(--focus-ring);
}

/* ── E-001 Pull-to-refresh ───────────────────────────────────────────────── */
/* Container wrapper that receives the rubber-band translateY transform.      */
/* overscroll-behavior-y:contain is applied here (not on html/body) so the    */
/* feed container itself absorbs the gesture without triggering the browser's  */
/* built-in PTR on Android Chrome — the M-016 rule on html/body already does  */
/* the same at the page level; this ensures the component works even when the  */
/* page-level rule is absent (e.g. nested in a modal).                         */
.ptr-container {
    overscroll-behavior-y: contain;
    position: relative;
}

/* Indicator strip — appears above the top edge of the container.             */
.ptr-indicator {
    position: absolute;
    top: -48px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.8125rem;         /* 13px — matches helper-text sizing         */
    color: var(--color-text-secondary, #6b7280);
    opacity: 0;
    transition: opacity 150ms ease;
    pointer-events: none;
    z-index: 10;
    white-space: nowrap;
}

.ptr-indicator--visible {
    opacity: 1;
}

/* Spinner ring — inherits color from .ptr-indicator.                         */
.ptr-spinner {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: ptr-spin 0.6s linear infinite;
}

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

/* Accessibility — users who prefer reduced motion see a static ring.         */
@media (prefers-reduced-motion: reduce) {
    .ptr-indicator {
        transition: none;
    }
    .ptr-spinner {
        animation: none;
        border-top-color: currentColor;
        opacity: 0.6;
    }
}
/* ── /E-001 Pull-to-refresh ─────────────────────────────────────────────── */

/* ── E-003 Infinite scroll ───────────────────────────────────────────────── */

/* Zero-height sentinel — invisible trigger point for the IntersectionObserver.
   Must not affect layout; flex-shrink:0 prevents grids from collapsing it.   */
.infinite-scroll-sentinel {
    height: 1px;
    width: 100%;
    flex-shrink: 0;
    visibility: hidden;
    pointer-events: none;
}

/* Loading-more indicator — shown while an in-flight load-more is pending.
   Reuses the ptr-spin keyframe from E-001 to stay DRY.                       */
.is-loading-more {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 16px 0;
    color: var(--color-text-secondary, #6b7280);
    font-size: 0.875rem;
}

.is-loading-more__spinner {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: ptr-spin 0.6s linear infinite;
}

/* Desktop-only "Load more" fallback — hidden on mobile where infinite scroll
   handles pagination. Shown on wider viewports as an explicit trigger.        */
@media (max-width: 480px) {
    .btn-load-more--desktop-fallback {
        display: none;
    }
}

/* Accessibility — static ring for users who prefer reduced motion.            */
@media (prefers-reduced-motion: reduce) {
    .is-loading-more__spinner {
        animation: none;
        border-top-color: currentColor;
        opacity: 0.6;
    }
}

/* prefers-reduced-motion safety net — PA-02/Phase 1 baseline */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* ── /E-003 Infinite scroll ──────────────────────────────────────────────── */
