/* ===================================================================
   qub creator — style-guide-aligned stylesheet
   Reference: docs/QUB-STYLE-GUIDE.md
   =================================================================== */

/* === Font Faces === */
@font-face {
    font-family: 'Satoshi';
    src: url('/fonts/Satoshi-Regular.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Satoshi';
    src: url('/fonts/Satoshi-Medium.woff2') format('woff2');
    font-weight: 500;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Satoshi';
    src: url('/fonts/Satoshi-Bold.woff2') format('woff2');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'JetBrains Mono';
    src: url('/fonts/JetBrainsMono-Regular.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

/* Fraunces — display/editorial serif (style guide v2.0 §3.1). Variable
 * font with a 400–700 weight axis in a single file per style. Used
 * for .text-display-* classes on marketing and ceremonial surfaces;
 * app chrome continues to use Satoshi. Latin subset only for v1 — add
 * latin-ext in a follow-up if locale coverage demands it. */
@font-face {
    font-family: 'Fraunces';
    src: url('/fonts/Fraunces-Variable.woff2') format('woff2');
    font-weight: 400 700;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Fraunces';
    src: url('/fonts/Fraunces-Italic.woff2') format('woff2');
    font-weight: 400 700;
    font-style: italic;
    font-display: swap;
}

/* === Design Tokens === */
:root {
    /* --- Primary: Lime scale --- */
    --lime-50: #F7FEE7;
    --lime-100: #ECFCCB;
    --lime-200: #D9F99D;
    --lime-300: #BEF264;
    --lime-400: #A3E635;
    --lime-500: #84CC16;
    --lime-600: #78B813;
    --lime-700: #65A30D;
    --lime-800: #4A7A0B;
    --lime-900: #3F6212;
    --lime-950: #365314;

    /* --- Neutrals: green-tinted charcoal --- */
    --neutral-0: #FFFFFF;
    --neutral-50: #F4F5F2;
    --neutral-100: #E8EBE4;
    --neutral-150: #E0E5DC;
    --neutral-200: #D4DAD0;
    --neutral-300: #B0B8A8;
    --neutral-400: #8A9282;
    --neutral-500: #5F675A;
    --neutral-600: #4B5245;
    --neutral-700: #3D4437;
    --neutral-800: #2E3329;
    --neutral-850: #222620;
    --neutral-900: #1A1D18;
    --neutral-950: #0C0F0A;

    /* --- Semantic colours (not lime-tinted) --- */
    --red-500: #EF4444;
    --red-600: #DC2626;
    --red-700: #B91C1C;
    --red-100: #FEE2E2;
    --red-950: #450A0A;
    --amber-500: #F59E0B;
    --amber-100: #FEF3C7;
    --amber-700: #B45309;
    --blue-500: #3B82F6;
    --blue-100: #DBEAFE;
    --blue-400: #60A5FA;
    --blue-700: #1D4ED8;
    --blue-950: #172554;
    --green-500: var(--lime-500);

    /* --- Secondary: Wax (ceremonial accent) — v2.0 §2.2 ---
     * Used only at ceremonial moments (seal-success, reveal transition,
     * permanence callouts, final-hour countdown). Never in UI chrome.
     * Wax and lime never appear as peers in the same composition. */
    --wax-100: #FFEDD5;
    --wax-400: #EA580C;
    --wax-500: #C2410C;
    --wax-700: #9A3412;

    /* --- Paper: warm ceremonial surfaces — v2.0 §2.4 ---
     * Parallel light-surface palette for marketing, seal modal,
     * reveal screen, seal-success card. App chrome stays on the
     * cooler neutral-0/50/100 tier. */
    --paper-0: #FAF9F5;
    --paper-50: #F2F1EC;
    --paper-100: #E8E6DE;

    /* --- Font stacks --- */
    --font-display: 'Fraunces', Georgia, 'Times New Roman', serif;
    --font-sans: 'Satoshi', system-ui, -apple-system, sans-serif;
    --font-mono: 'JetBrains Mono', 'SF Mono', 'Fira Code', monospace;

    /* --- Spacing (4px base) --- */
    --space-0: 0px;
    --space-1: 4px;
    --space-2: 8px;
    --space-2-5: 10px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 20px;
    --space-6: 24px;
    --space-8: 32px;
    --space-10: 40px;
    --space-12: 48px;
    --space-16: 64px;
    --space-20: 80px;
    --space-24: 96px;

    /* --- Border radius --- */
    --radius-sm: 4px;
    --radius-md: 8px;
    --radius-lg: 12px;
    --radius-xl: 16px;
    --radius-full: 9999px;

    /* --- Motion --- */
    --ease-default: cubic-bezier(0.25, 0.1, 0.25, 1.0);
    --ease-out: cubic-bezier(0.0, 0.0, 0.2, 1.0);
    --ease-in: cubic-bezier(0.4, 0.0, 1.0, 1.0);
    --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1.0);
    /* Seal sweep only — v2.0 §7.2. Slow start, accelerating middle,
     * deliberate landing. Never reuse for generic transitions. */
    --ease-seal: cubic-bezier(0.6, 0.0, 0.2, 1.0);

    --duration-fast: 100ms;
    --duration-default: 150ms;
    --duration-medium: 200ms;
    --duration-slow: 300ms;
    --duration-slower: 400ms;
    /* Signature motion only — v2.0 §7.3. The one earned exception to
     * the 400ms generic ceiling. Reserved for the seal sweep (§7.6). */
    --duration-seal: 800ms;
    --duration-breath: 2000ms;

    /* --- Touch --- */
    --touch-min: 44px;

    /* --- Semantic aliases (dark mode default — avoids FOUC) --- */
    --color-bg-page: var(--neutral-950);
    --color-bg-surface: var(--neutral-900);
    --color-bg-elevated: var(--neutral-850);
    --color-bg-overlay: var(--neutral-800);
    --color-border-primary: var(--neutral-800);
    --color-border-secondary: var(--neutral-700);
    --color-text-heading: var(--neutral-100);
    --color-text-body: var(--neutral-400);
    --color-text-muted: var(--neutral-500);
    --color-text-on-primary: var(--neutral-950);
    --color-primary: var(--lime-500);
    --color-primary-hover: var(--lime-700);
    --color-primary-text: var(--lime-400);

    /* Ceremonial aliases — paper collapses to neutral-900 in dark mode
     * per v2.0 §2.7. Wax text uses wax-400 on dark; app chrome never
     * pulls these tokens. */
    --color-bg-ceremonial: var(--neutral-900);
    --color-bg-ceremonial-inset: var(--neutral-850);
    --color-border-ceremonial: var(--neutral-800);
    --color-accent-wax: var(--wax-500);
    --color-accent-wax-subtle: var(--wax-700);
    --color-text-wax: var(--wax-400);
}

/* --- Light mode overrides --- */
@media (prefers-color-scheme: light) {
    :root {
        --color-bg-page: var(--neutral-0);
        --color-bg-surface: var(--neutral-50);
        --color-bg-elevated: var(--neutral-100);
        --color-bg-overlay: var(--neutral-200);
        --color-border-primary: var(--neutral-200);
        --color-border-secondary: var(--neutral-150);
        --color-text-heading: var(--neutral-900);
        --color-text-body: var(--neutral-600);
        --color-text-muted: var(--neutral-400);
        --color-text-on-primary: var(--neutral-950);
        --color-primary: var(--lime-500);
        --color-primary-hover: var(--lime-700);
        --color-primary-text: var(--lime-800);

        /* Ceremonial aliases use paper in light mode. */
        --color-bg-ceremonial: var(--paper-0);
        --color-bg-ceremonial-inset: var(--paper-50);
        --color-border-ceremonial: var(--paper-100);
        --color-accent-wax: var(--wax-500);
        --color-accent-wax-subtle: var(--wax-100);
        --color-text-wax: var(--wax-700);
    }
}

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

html {
    font-family: var(--font-sans);
    font-size: 1rem;
    line-height: 1.6;
    color: var(--color-text-body);
    background: var(--color-bg-page);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

body {
    min-height: 100dvh;
    padding:
        env(safe-area-inset-top)
        env(safe-area-inset-right)
        env(safe-area-inset-bottom)
        env(safe-area-inset-left);
    -webkit-tap-highlight-color: transparent;
}

@supports not (min-height: 100dvh) {
    body { min-height: 100vh; }
}

/* === Screen reader only === */
.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;
}

/* === Focus === */
*:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

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

/* === Links (Style Guide §3.3) === */
a {
    color: var(--blue-500);
    text-decoration: none;
    transition: border-color var(--duration-fast) var(--ease-default);
    border-bottom: 1px solid transparent;
}

a:hover {
    border-bottom-color: currentColor;
}

@media (prefers-color-scheme: light) {
    a { color: var(--blue-500); }
}
@media (prefers-color-scheme: dark) {
    a { color: #93C5FD; }
}

/* === Typography Utilities === */
/* Display tier inherits Satoshi from html. Fraunces remains loaded
 * as a latent @font-face declaration for future opt-in but no
 * currently-live surface consumes it. */
.text-display-xl {
    font-size: 3rem;
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: -0.03em;
    color: var(--color-text-heading);
}

.text-display-lg {
    font-size: 2.25rem;
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -0.025em;
    color: var(--color-text-heading);
}

.text-display-md {
    font-size: 1.75rem;
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: -0.02em;
    color: var(--color-text-heading);
}

.text-heading-lg {
    font-size: 1.5rem;
    font-weight: 500;
    line-height: 1.3;
    letter-spacing: -0.015em;
    color: var(--color-text-heading);
}

.text-heading-md {
    font-size: 1.25rem;
    font-weight: 500;
    line-height: 1.35;
    letter-spacing: -0.01em;
    color: var(--color-text-heading);
}

.text-heading-sm {
    font-size: 1.125rem;
    font-weight: 500;
    line-height: 1.4;
    letter-spacing: -0.005em;
    color: var(--color-text-heading);
}

.text-body-lg {
    font-size: 1.0625rem;
    font-weight: 400;
    line-height: 1.6;
}

.text-body {
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.6;
}

.text-body-sm {
    font-size: 0.875rem;
    font-weight: 400;
    line-height: 1.5;
    letter-spacing: 0.005em;
}

.text-caption {
    font-size: 0.8125rem;
    font-weight: 400;
    line-height: 1.4;
    letter-spacing: 0.01em;
}

.text-overline {
    font-size: 0.75rem;
    font-weight: 500;
    line-height: 1.3;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.text-mono {
    font-family: var(--font-mono);
    font-size: 0.875rem;
    font-weight: 400;
    line-height: 1.5;
}

/* === App Layout === */
.app-layout {
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
}

.app-header {
    display: flex;
    align-items: center;
    padding: var(--space-4) var(--space-6);
    border-block-end: 1px solid var(--color-border-primary);
    background: var(--color-bg-page);
}

.app-header__wordmark {
    block-size: 28px;
    inline-size: auto;
}

.app-main {
    flex: 1;
    padding: var(--space-6);
    max-inline-size: 640px;
    inline-size: 100%;
    margin-inline: auto;
}

/* Pact compose needs a wider container than the default 640px — the
   structured form carries more density (fields, inline rows, role
   pills) than the single-textarea Message view. */
.app-main:has(.pact-compose) {
    max-inline-size: 760px;
}

/* === Buttons === */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-block-size: var(--touch-min);
    padding: var(--space-3) var(--space-6);
    border: none;
    border-radius: var(--radius-md);
    font-family: var(--font-sans);
    font-size: 1rem;
    font-weight: 500;
    cursor: pointer;
    transition:
        background-color var(--duration-fast) var(--ease-default),
        border-color var(--duration-fast) var(--ease-default),
        box-shadow var(--duration-fast) var(--ease-default),
        transform var(--duration-fast) var(--ease-default);
    touch-action: manipulation;
    text-decoration: none;
}

.btn:active:not(:disabled) {
    transform: scale(0.97);
}

.btn:disabled {
    /* The HTML `disabled` attribute already blocks click activation,
     * so we don't need pointer-events:none — we want the cursor hint
     * (not-allowed) AND the dimmed opacity to land for users that
     * hover the button before they've written anything. */
    cursor: not-allowed;
    opacity: 0.5;
}

/* Primary (lime) */
.btn--primary {
    background: var(--color-primary);
    color: var(--color-text-on-primary);
    /* Tactile raised treatment at rest — inner top-edge highlight
     * (simulates a light source above) + subtle outer shadow gives
     * the primary CTA a physical "button" feel without being showy.
     * Hover amplifies, active flattens — consistent tactile arc
     * across idle → hover → press. Learnings §7: cards barely
     * shadowed, popovers stronger, primary CTAs get the tactile
     * raised feel since they're the signature action. */
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.20),
        0 1px 2px rgba(0, 0, 0, 0.15);
}

.btn--primary:hover:not(:disabled) {
    /* Lift to a brighter lime + a hint of scale and a soft glow so
     * the active CTA feels alive, not flat. The scale stays small
     * (1.02) so it doesn't shift surrounding layout perceptibly.
     * The rest-state inner highlight is preserved inside the
     * amplified shadow stack. */
    background: var(--lime-400);
    transform: scale(1.02);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.25),
        0 4px 14px rgba(132, 204, 22, 0.25);
}

.btn--primary:active:not(:disabled) {
    background: var(--lime-800);
    /* Active state overrides the hover scale to feel like a press.
     * Shadow drops entirely so the button reads as "pushed down"
     * relative to its raised rest and lifted hover. */
    transform: scale(0.97);
    box-shadow: none;
}

.btn--primary:disabled {
    background: var(--neutral-800);
    color: var(--neutral-600);
    /* Drop the tactile shadow stack on disabled so the button reads
     * as inert rather than pressable. Hover/active states already
     * exclude :disabled via :not(:disabled), so the rest-state
     * shadow is the only stack that reaches the disabled button. */
    box-shadow: none;
}

@media (prefers-color-scheme: light) {
    .btn--primary:disabled {
        background: var(--neutral-200);
        color: var(--neutral-400);
    }
}

/* Seal button — primary CTA on the seal-warning modal (v2.0 §6.1,
 * §10.2). Wax focus ring is the single permitted deviation from the
 * lime ring convention; the button is the commit action on the most
 * irreversible moment in the product. The seal-sweep animation that
 * fires on press (§7.6) lives in the component that owns the sweep,
 * not here — this rule only covers the focus treatment. */
.btn--seal:focus-visible {
    outline-color: var(--wax-500);
}

/* Primary button on a paper surface — lime-500 against paper-0 is
 * only ~1.9:1, below WCAG 1.4.11's 3:1 UI-component threshold (v2.0
 * §10.1). An inset 1px lime-800 ring brings the button boundary to
 * ~4.9:1 against paper without affecting layout. Scoped to light
 * mode because ceremonial surfaces in dark mode collapse to
 * neutral-900, where lime-500 already reaches ~9.8:1. */
@media (prefers-color-scheme: light) {
    .btn--primary.on-paper {
        box-shadow:
            inset 0 0 0 1px var(--lime-800),
            inset 0 1px 0 rgba(255, 255, 255, 0.20),
            0 1px 2px rgba(0, 0, 0, 0.15);
    }
}

/* In-button spinner — renders alongside the button label when an
 * async action is in flight (seal, upload, magic-link send). The
 * primary CTA's "morph into a spinner" moment from the Phase 3
 * signature-motion set: the button stays in place, its label
 * changes ("Seal my prediction" → "Sealing..."), and a 16px
 * spinner joins the row so the in-flight state reads as the
 * button doing work — not as a blank disabled button. */
.btn__spinner {
    width: 16px;
    height: 16px;
    border: 2px solid transparent;
    border-top-color: currentColor;
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
    flex-shrink: 0;
    /* .btn is already `display: inline-flex; gap: var(--space-2)`
     * so the spinner and label space themselves correctly as flex
     * siblings — no margin needed. */
}

@media (prefers-reduced-motion: reduce) {
    .btn__spinner {
        /* Global rule collapses animation-duration; add a slower
         * visible rotation so the spinner still communicates
         * "working" without the rapid motion. */
        animation-duration: 3s;
    }
}

/* Secondary (transparent + border) */
.btn--secondary {
    background: transparent;
    color: var(--color-text-heading);
    border: 1.5px solid var(--color-border-primary);
}

.btn--secondary:hover:not(:disabled) {
    background: var(--color-bg-surface);
}

.btn--secondary:active:not(:disabled) {
    background: var(--color-bg-elevated);
}

/* Ghost (inline actions) */
.btn--ghost {
    background: transparent;
    color: var(--color-text-muted);
}

.btn--ghost:hover:not(:disabled) {
    background: var(--color-bg-surface);
    color: var(--color-text-body);
}

/* Destructive (delete) */
.btn--destructive {
    background: var(--red-500);
    color: #FFFFFF;
}

.btn--destructive:hover:not(:disabled) {
    background: var(--red-600);
}

/* === Inputs === */
.input {
    display: block;
    inline-size: 100%;
    min-block-size: var(--touch-min);
    padding: var(--space-3) var(--space-4);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    font-family: var(--font-sans);
    font-size: 1rem;
    line-height: 1.6;
    color: var(--color-text-heading);
    transition:
        border-color var(--duration-fast) var(--ease-default),
        box-shadow var(--duration-fast) var(--ease-default);
}

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

.input:focus {
    outline: none;
    border-color: var(--lime-500);
    box-shadow: 0 0 0 1px var(--lime-500);
}

.input--error {
    border-color: var(--red-500);
}

.input--error:focus {
    border-color: var(--red-500);
    box-shadow: 0 0 0 1px var(--red-500);
}

.input__error-text {
    font-size: 0.8125rem;
    line-height: 1.4;
    letter-spacing: 0.01em;
    color: var(--red-500);
    margin-block-start: var(--space-1);
}

/* === Textarea === */
.textarea {
    display: block;
    inline-size: 100%;
    min-block-size: 160px;
    max-block-size: 400px;
    padding: var(--space-3) var(--space-4);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    font-family: var(--font-sans);
    font-size: 1rem;
    line-height: 1.6;
    color: var(--color-text-heading);
    resize: vertical;
    transition:
        border-color var(--duration-fast) var(--ease-default),
        box-shadow var(--duration-fast) var(--ease-default);
}

.textarea::placeholder {
    color: var(--color-text-muted);
}

.textarea:focus {
    outline: none;
    border-color: var(--lime-500);
    /* Tight 1px ring for crispness, plus a soft 4px brand-tinted glow
     * to make the active writing state feel deliberate. The rgba uses
     * the lime-500 base; if the brand colour shifts, update both. */
    box-shadow:
        0 0 0 1px var(--lime-500),
        0 0 0 4px rgba(132, 204, 22, 0.18);
}

/* === Cards === */
.card {
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-secondary);
    border-radius: var(--radius-lg);
    padding: var(--space-6);
    transition:
        border-color var(--duration-medium) var(--ease-default),
        transform var(--duration-medium) var(--ease-default);
}

.card:hover {
    border-color: var(--color-border-primary);
}

/* Ceremonial card — paper surface tier (v2.0 §4.4, §6.3). Softer
 * corners (radius-xl vs radius-lg on app cards) and more breathing
 * room (space-8 vs space-6). Used on the seal-success card, reveal
 * moment, and featured marketing cards. Not for app chrome. */
.card--ceremonial {
    background: var(--color-bg-ceremonial);
    border: 1px solid var(--color-border-ceremonial);
    border-radius: var(--radius-xl);
    padding: var(--space-8);
}

/* Wax callout — the ceremonial surface's single punctuation, a 3px
 * wax-500 left border on an inset card (v2.0 §6.3, §8.3). Used for
 * the permanence paragraph in the seal modal, the delivery-link
 * panel on the seal-success card, and the "revealed X ago" meta on
 * the reveal moment. Maximum one per ceremonial surface. */
.wax-callout {
    border-left: 3px solid var(--color-accent-wax);
    background: var(--color-bg-ceremonial-inset);
    border-radius: var(--radius-md);
    padding: var(--space-4);
    color: var(--color-text-wax);
}

/* Paper grain — 0.5%-opacity SVG noise overlay for ceremonial paper
 * surfaces (v2.0 §2.4, §8.3). Scoped to light mode — paper surfaces
 * collapse to neutral-900 in dark mode, where grain would read as
 * visual noise on an already-weighty surface. Compose with
 * .card--ceremonial or .modal-dialog--ceremonial. */
@media (prefers-color-scheme: light) {
    .paper-grain {
        background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.5 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
        background-size: 200px 200px;
    }
}

/* === Draft Card === */
.draft-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-secondary);
    border-radius: var(--radius-lg);
    padding: var(--space-5);
    cursor: pointer;
    transition:
        border-color var(--duration-medium) var(--ease-default),
        transform var(--duration-medium) var(--ease-default);
}

.draft-card:hover {
    border-color: var(--color-border-primary);
    transform: translateY(-1px);
}

/* Single-row layout matching .history-card__row (5d09af9):
 *   [status] [preview ...] [updated] [unlock] [delete-slot]
 * Preview grows; siblings stay intrinsic. Wraps on narrow
 * viewports so the row rolls onto a second line rather than
 * squashing. */
.draft-card__row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
    width: 100%;
}

.draft-card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

.draft-card__preview {
    flex: 1 1 240px;
    min-width: 0;
    margin: 0;
    font-size: 0.875rem;
    line-height: 1.5;
    letter-spacing: 0.005em;
    color: var(--color-text-body);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.draft-card__meta {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    font-size: 0.8125rem;
    line-height: 1.4;
    letter-spacing: 0.01em;
    color: var(--color-text-muted);
    flex-shrink: 0;
}

.draft-card__unlock {
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--color-primary-text);
    flex-shrink: 0;
}

.draft-card__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-shrink: 0;
}

/* === Status Badges === */
.badge {
    display: inline-flex;
    align-items: center;
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
    font-size: 0.75rem;
    font-weight: 500;
    line-height: 1.3;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    white-space: nowrap;
}

/* Composing — neutral */
.badge--composing {
    background: var(--neutral-800);
    color: var(--neutral-400);
}

@media (prefers-color-scheme: light) {
    .badge--composing {
        background: var(--neutral-200);
        color: var(--neutral-600);
    }
}

/* Sealed — lime */
.badge--sealed {
    background: var(--lime-950);
    color: var(--lime-400);
}

@media (prefers-color-scheme: light) {
    .badge--sealed {
        background: var(--lime-100);
        color: var(--lime-800);
    }
}

/* Uploading — blue */
.badge--uploading {
    background: var(--blue-950);
    color: var(--blue-400);
}

@media (prefers-color-scheme: light) {
    .badge--uploading {
        background: var(--blue-100);
        color: var(--blue-700);
    }
}

/* Failed — red */
.badge--failed {
    background: var(--red-950);
    color: var(--red-500);
}

@media (prefers-color-scheme: light) {
    .badge--failed {
        background: var(--red-100);
        color: var(--red-700);
    }
}

/* === Compose Form === */
.compose-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.compose-form__field {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.compose-form__label {
    font-size: 0.75rem;
    font-weight: 500;
    line-height: 1.3;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.compose-form__hint {
    font-size: 0.8125rem;
    line-height: 1.4;
    letter-spacing: 0.01em;
    color: var(--color-text-muted);
}

.compose-form__footer {
    display: flex;
    /* Mobile: stack with the primary action first (the seal inline
     * bar), secondary "Back to edit" below. `column-reverse` keeps
     * the DOM order tab-friendly (back before seal) while visually
     * placing the CTA on top so the thumb lands on it first. */
    flex-direction: column-reverse;
    align-items: stretch;
    gap: var(--space-3);
}

@media (min-width: 640px) {
    .compose-form__footer {
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-4);
    }
}

/* Seal inline bar spans the footer width on mobile so the CTA has a
 * full-width target; desktop lets it collapse back to intrinsic
 * width alongside the back button. */
.compose-form__footer > .seal-inline-bar {
    width: 100%;
}

@media (min-width: 640px) {
    .compose-form__footer > .seal-inline-bar {
        width: auto;
    }
}

/* "Back to edit" on mobile: quieter, centered, no oversized pill so
 * it reads as "undo" rather than competing with the CTA above. */
.compose-form__footer > .btn-ghost {
    align-self: center;
}

/* === Hero prompt (rotating heading above the textarea) === */
/* Style Guide §3.1 — heading-lg on mobile, display-sm on desktop.
 * The only animations are the JS-driven typewriter reveal and the
 * caret blink below — no shimmer, no fade-in, no transform.
 */
.hero-prompt {
    margin: 0 0 var(--space-4) 0;
    padding-block-end: var(--space-2);
    text-align: center;
    font-family: var(--font-sans);
    font-size: 1.5rem; /* heading-lg / 24px on mobile */
    font-weight: 500;
    /* Heading-lg spec (§3.2): lh 1.3, ls -0.015em. Tightened from the
     * prior mobile compromise so the hero prompt reads at the same
     * optical rhythm as other heading-lg surfaces. */
    line-height: 1.3;
    letter-spacing: -0.015em;
    color: var(--color-text-heading);
    /* Balance line lengths when the prompt wraps onto two lines on
     * narrow viewports. Applied at the block level because text-wrap:
     * balance only takes effect on block boxes, not inline spans. */
    text-wrap: balance;
    /* Decorative — keyboard/clicks must reach the textarea below it. */
    pointer-events: none;
    /* Ease opacity on intent swap and focus transitions so the
     * tagline cross-fades rather than cut-swaps. */
    opacity: 1;
    transition: opacity var(--duration-medium) var(--ease-default);
}

@media (min-width: 640px) {
    .hero-prompt {
        font-size: 2rem; /* display-sm / 32px on tablet+ */
        /* Tighten for the larger display-scale size — interpolates
         * between heading-lg (-0.015em) and display-md (-0.02em). */
        line-height: 1.2;
        letter-spacing: -0.02em;
    }
}

/* User is typing or the field has content — hide the hero entirely
 * so only the typewriter and caret remain when visible. */
.hero-prompt--hidden {
    opacity: 0;
}

/* Tagline span. `pre-wrap` preserves intentional leading/trailing
 * whitespace in the i18n prompt; `overflow-wrap: anywhere` is the
 * safety net for a single unbreakable word near the edge. Line
 * balancing lives on the parent h1 (text-wrap: balance only applies
 * to block boxes). */
.hero-prompt__text {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* === Intent selector pills === */
.intent-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    overflow-x: auto;
    /* Vertical padding gives us a 44px tap target around 36px pills. */
    padding-block: var(--space-1);
    padding-inline: var(--space-1);
    margin-block-end: var(--space-4);
    /* Hide scrollbar on Webkit/Blink and Firefox. */
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.intent-row::-webkit-scrollbar {
    display: none;
}

.intent-pill {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    flex-shrink: 0;
    /* 36px visual height; padding lifts tap area to ≥44px. */
    min-height: 36px;
    padding-block: var(--space-2);
    padding-inline: var(--space-4);
    border-radius: var(--radius-full);
    background: var(--color-bg-surface);
    color: var(--color-text-body);
    border: 1px solid var(--color-border-primary);
    font-family: var(--font-sans);
    font-size: 0.875rem; /* body-sm / 14px */
    font-weight: 500;
    line-height: 1.2;
    cursor: pointer;
    transition:
        background-color var(--duration-default) var(--ease-default),
        border-color var(--duration-default) var(--ease-default),
        color var(--duration-default) var(--ease-default);
}

.intent-pill:hover {
    border-color: var(--color-text-muted);
}

.intent-pill:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.intent-pill--selected {
    background: var(--lime-100);
    color: var(--color-primary-text);
    border-color: var(--lime-500);
}

@media (prefers-color-scheme: dark) {
    .intent-pill--selected {
        background: var(--lime-950);
    }
}

.intent-pill__icon {
    flex-shrink: 0;
    width: 16px;
    height: 16px;
    /* Morph stroke-width on selection transition — learnings §28 +
     * uplift-plan priority #22. The selected pill's icon thickens
     * from 2px to 2.5px over 150ms so the state change reads as
     * "depth without colour alone" rather than a colour-only swap. */
    transition: stroke-width var(--duration-default) var(--ease-default);
}

.intent-pill--selected .intent-pill__icon {
    /* CSS stroke-width overrides the SVG presentation attribute
     * per CSS SVG spec. Applied to the icon inside the selected
     * pill so every intent icon in the set picks up the heavier
     * variant without per-icon changes. */
    stroke-width: 2.5;
}

/* === Compose seed chips ===
 * Per-intent starter prompts shown below the textarea when empty.
 * Not permanent chrome — hidden once the user types anything,
 * re-shown only if they clear the field. Closes learnings §31 +
 * uplift-plan priority #5 (blank-canvas activation problem). */
.compose-seeds {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-block-start: var(--space-3);
}

.compose-seeds__label {
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin: 0;
}

.compose-seeds__row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.compose-seeds__chip {
    display: inline-flex;
    align-items: center;
    min-height: 36px;
    padding: var(--space-2) var(--space-3);
    border: 1px dashed var(--color-border-primary);
    border-radius: var(--radius-full);
    background: transparent;
    color: var(--color-text-body);
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    font-weight: 400;
    cursor: pointer;
    transition:
        background-color var(--duration-default) var(--ease-default),
        border-color var(--duration-default) var(--ease-default),
        color var(--duration-default) var(--ease-default);
}

.compose-seeds__chip:hover {
    background: var(--color-bg-elevated);
    border-color: var(--color-primary);
    border-style: solid;
    color: var(--color-text-heading);
}

.compose-seeds__chip:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* === Byte Counter === */
.byte-counter {
    font-size: 0.8125rem;
    line-height: 1.4;
    letter-spacing: 0.01em;
    /* Tabular figures prevent the live byte count from jittering left
     * and right as the user types — "123" and "987" stay the same
     * width. */
    font-variant-numeric: tabular-nums;
    color: var(--color-text-muted);
    text-align: end;
}

.byte-counter--warning {
    color: var(--amber-500);
}

.byte-counter--danger {
    color: var(--red-500);
}

/* === Saved Indicator === */
.saved-indicator {
    font-size: 0.8125rem;
    line-height: 1.4;
    color: var(--color-primary-text);
    opacity: 0;
    transition: opacity var(--duration-default) var(--ease-default);
}

.saved-indicator--visible {
    opacity: 1;
}

/* === Toast === */
.toast {
    position: fixed;
    inset-block-end: calc(var(--space-16) + env(safe-area-inset-bottom));
    inset-inline-start: 50%;
    transform: translateX(-50%);
    padding: var(--space-3) var(--space-6);
    background: var(--color-bg-elevated);
    color: var(--color-text-heading);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    font-size: 0.875rem;
    font-weight: 500;
    z-index: 1000;
    animation: toast-enter var(--duration-slow) var(--ease-out);
}

@keyframes toast-enter {
    from {
        opacity: 0;
        transform: translateX(-50%) translateY(8px);
    }
    to {
        opacity: 1;
        transform: translateX(-50%) translateY(0);
    }
}

/* === Empty State === */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-4);
    padding: var(--space-12) var(--space-6);
    text-align: center;
}

.empty-state__title {
    font-size: 1.25rem;
    font-weight: 500;
    line-height: 1.35;
    letter-spacing: -0.01em;
    color: var(--color-text-heading);
}

.empty-state__body {
    font-size: 0.875rem;
    line-height: 1.5;
    color: var(--color-text-muted);
    max-inline-size: 30ch;
}

.empty-state__cta {
    margin-block-start: var(--space-4);
}

/* Hero art slot above the empty-state title. Sized to 96px in a
 * comfortable 120px square (viewBox padding built into the art).
 * Coloured via the brand-art rule below; the lime tint signals
 * "this is the product" without a full-brightness button-y feel. */
.empty-state__art {
    display: block;
    width: 96px;
    height: 96px;
    margin-block-end: var(--space-2);
    /* Subtle drop below the hero so sighted users get a soft
     * grounding shadow, not a hard edge. */
    filter: drop-shadow(0 6px 18px rgba(132, 204, 22, 0.10));
}

/* Muted variant for transient/secondary empty states (e.g.
 * /drafts — the plan's Phase 5 §26 calls for "a clear, short copy
 * + CTA back to compose; don't over-design; drafts is a transient
 * surface"). Smaller footprint, neutral colour, no glow — reads as
 * consistent brand-native shape without claiming equal visual
 * weight to first-impression surfaces like /history. */
.empty-state__art--muted {
    width: 64px;
    height: 64px;
    filter: none;
    opacity: 0.55;
    color: var(--color-text-muted);
}

/* About-page hero slot (above the markdown body). Sits centred,
 * 128px square — larger than the empty-state mark so first-time
 * /about visitors land on the product primitive before any copy.
 * Same lime drop-shadow as the empty-state hero so the language
 * carries across surfaces. */
.about-hero {
    display: flex;
    justify-content: center;
    width: 100%;
    max-width: 640px;
    margin: 0 auto;
    padding-block-start: var(--space-8);
    padding-block-end: var(--space-2);
}

.about-hero .brand-art {
    width: 128px;
    height: 128px;
    filter: drop-shadow(0 8px 24px rgba(132, 204, 22, 0.12));
}

/* Add-to-calendar link on /sealed. Secondary weight — sits below
 * the primary delivery URL + share row; the core action is share,
 * the calendar invite is a retention nudge for creators who'll
 * leave the tab. Muted by default, primary-text on hover so the
 * affordance reads as present-but-not-shouting. */
.calendar-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--touch-min);
    padding: var(--space-2) var(--space-4);
    margin-block-start: var(--space-2);
    color: var(--color-text-muted);
    font-size: 0.875rem;
    font-weight: 500;
    text-decoration: none;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    transition:
        color var(--duration-default) var(--ease-default),
        border-color var(--duration-default) var(--ease-default);
}

.calendar-link:hover {
    color: var(--color-primary-text);
    border-color: var(--color-border-primary);
}

.calendar-link:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* === Viewer first-visit intro ===
 * Dismissable one-sentence framing above the countdown / reveal
 * on /c/:tx_id. Shown once per device (localStorage-gated via
 * qub:viewer-intro-seen). First-time viewers get context before
 * the countdown dominates attention; returning viewers skip it. */
.viewer-intro {
    display: flex;
    gap: var(--space-4);
    align-items: center;
    justify-content: space-between;
    width: 100%;
    max-width: 640px;
    margin-inline: auto;
    margin-block-end: var(--space-6);
    padding: var(--space-4) var(--space-5);
    background: color-mix(in srgb, var(--lime-950) 30%, transparent);
    border: 1px solid var(--lime-800);
    border-radius: var(--radius-lg);
}

@media (prefers-color-scheme: light) {
    .viewer-intro {
        background: color-mix(in srgb, var(--lime-50) 70%, transparent);
        border-color: var(--lime-200);
    }
}

.viewer-intro__art {
    flex-shrink: 0;
    width: 32px;
    height: 32px;
    color: var(--color-primary);
    display: flex;
    align-items: center;
    justify-content: center;
}

.viewer-intro__body {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    min-width: 0;
}

.viewer-intro__title {
    font-family: var(--font-sans);
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--color-text-heading);
    margin: 0;
}

.viewer-intro__text {
    font-size: 0.8125rem;
    line-height: 1.4;
    color: var(--color-text-body);
    margin: 0;
}

.viewer-intro__dismiss {
    flex-shrink: 0;
    min-height: var(--touch-min);
    padding: var(--space-2) var(--space-4);
    background: transparent;
    color: var(--color-primary-text);
    border: 1px solid var(--color-primary);
    border-radius: var(--radius-md);
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    font-weight: 500;
    cursor: pointer;
    transition: background-color var(--duration-default) var(--ease-default);
}

.viewer-intro__dismiss:hover {
    background: color-mix(in srgb, var(--color-primary) 12%, transparent);
}

.viewer-intro__dismiss:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* === PWA install banner ===
 * Rendered at the bottom of /sealed after the first successful
 * seal. Two-action layout (dismiss ghost + install primary) with
 * a title + body stacked left of the actions. Elevated surface so
 * the banner reads as a distinct affordance below the share and
 * calendar rows — not competing with the primary share actions
 * above, but available for the user who wants the home-screen
 * install without having to discover the browser's native menu.
 * No-ops on browsers without beforeinstallprompt support (the
 * component gates on the PwaInstallAvailable signal which stays
 * false on Firefox/Safari). */
.pwa-install-banner {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    align-items: center;
    justify-content: space-between;
    width: 100%;
    max-width: 640px;
    margin-inline: auto;
    margin-block-start: var(--space-8);
    padding: var(--space-4) var(--space-5);
    background: var(--color-bg-elevated);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-lg);
}

.pwa-install-banner__body {
    flex: 1 1 240px;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.pwa-install-banner__title {
    font-family: var(--font-sans);
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--color-text-heading);
    margin: 0;
}

.pwa-install-banner__text {
    font-size: 0.8125rem;
    line-height: 1.4;
    color: var(--color-text-muted);
    margin: 0;
}

.pwa-install-banner__actions {
    display: flex;
    gap: var(--space-2);
    flex-shrink: 0;
}

/* === Brand art (Phase 5 signature visuals) ===
 * Geometric primitives derived from qub's own product surfaces.
 * Size comes from the parent slot (e.g. .empty-state__art); colour
 * comes from the parent's `color` token via the SVG's `currentColor`
 * stroke. Rules here set the default tint (lime) and guarantee the
 * SVG fills its slot. */
.brand-art {
    width: 100%;
    height: 100%;
    color: var(--color-primary);
}

/* Reduced-motion keeps brand art static — no entrance animation
 * defined here, but the rule is in place so future motion on the
 * mark (pulsing glow, slow rotation) stays opt-out for users who
 * have requested reduced motion. */
@media (prefers-reduced-motion: reduce) {
    .brand-art {
        animation: none !important;
    }
}

/* === Inline Confirm === */
.inline-confirm {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) 0;
    font-size: 0.875rem;
    color: var(--color-text-body);
}

/* === Draft List === */
.draft-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

/* === Navigation (bottom tabs) === */
.nav-tabs {
    display: flex;
    border-block-start: 1px solid var(--color-border-primary);
    background: var(--color-bg-page);
    padding-block-end: env(safe-area-inset-bottom);
}

.nav-tab {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-block-size: var(--touch-min);
    padding: var(--space-1) var(--space-2);
    border: none;
    background: none;
    color: var(--color-text-muted);
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    font-weight: 500;
    cursor: pointer;
    touch-action: manipulation;
    text-decoration: none;
    transition: color var(--duration-fast) var(--ease-default);
}

.nav-tab:hover {
    color: var(--color-text-body);
}

.nav-tab--active {
    color: var(--color-primary);
}

/* === Page Stubs (for remaining stub pages) === */
.page-stub {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-4);
    padding: var(--space-12);
    color: var(--color-text-muted);
    text-align: center;
}

.page-stub__title {
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1.3;
    letter-spacing: -0.015em;
    color: var(--color-text-heading);
}

/* === Markdown Pages (legal, etc.) === */
.markdown-page {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: var(--space-8) var(--space-4);
    width: 100%;
}

.markdown-page__title {
    font-size: 1.75rem;
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: -0.02em;
    color: var(--color-text-heading);
    margin-bottom: var(--space-6);
}

.markdown-page .content-body {
    width: 100%;
    max-width: 65ch;
}

/* === Legal Directory Page === */
.legal-page {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: var(--space-12) var(--space-4);
    gap: var(--space-4);
}

.legal-page__title {
    font-size: 1.75rem;
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: -0.02em;
    color: var(--color-text-heading);
}

.legal-nav {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    width: 100%;
    max-width: 28rem;
    margin-top: var(--space-4);
}

.legal-nav__item {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-4);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    text-decoration: none;
    transition: border-color 0.15s ease;
    min-height: var(--touch-min);
}

.legal-nav__item:hover {
    border-color: var(--color-primary);
}

.legal-nav__label {
    font-size: 1rem;
    font-weight: 600;
    color: var(--color-primary-text);
}

.legal-nav__desc {
    font-size: 0.875rem;
    color: var(--color-text-muted);
}

/* === Contact page === */
.contact-page {
    max-width: 640px;
    margin-inline: auto;
    padding-block: var(--space-12);
    padding-inline: var(--space-4);
    font-family: var(--font-sans);
}

.contact-page__title {
    font-size: 1.75rem;
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: -0.02em;
    color: var(--color-text-heading);
    margin: 0;
}

.contact-page__subtitle {
    margin-block: var(--space-3) var(--space-8);
    color: var(--color-text-body);
    font-size: 1rem;
    line-height: 1.6;
}

.contact-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-3);
}

@media (min-width: 480px) {
    .contact-grid {
        grid-template-columns: 1fr 1fr;
        gap: var(--space-4);
    }
}

.contact-tile {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-height: 44px;
    padding: var(--space-5);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-lg);
    text-decoration: none;
    color: inherit;
    transition:
        background-color var(--duration-default) var(--ease-default),
        border-color var(--duration-default) var(--ease-default),
        transform var(--duration-medium) var(--ease-default);
}

.contact-tile:hover {
    background: var(--color-bg-elevated);
    border-color: var(--color-primary);
}

.contact-tile:active {
    background: var(--color-bg-overlay);
}

.contact-tile:focus-visible {
    outline: 2px solid var(--lime-500);
    outline-offset: 2px;
}

.contact-tile__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 32px;
    block-size: 32px;
    color: var(--color-primary-text);
    margin-block-end: var(--space-1);
}

.contact-tile__heading {
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.35;
    color: var(--color-text-heading);
}

.contact-tile__description {
    font-size: 0.875rem;
    line-height: 1.5;
    color: var(--color-text-muted);
}

@media (prefers-reduced-motion: reduce) {
    .contact-tile {
        transition: none;
    }
}

/* === Loading State === */
.loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-4);
    padding: var(--space-12) var(--space-4);
    color: var(--color-text-muted);
    text-align: center;
}

/* === Auth Verify Page === */
.auth-verify-page {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-5);
    max-width: 520px;
    min-height: 60vh;
    margin-inline: auto;
    padding: var(--space-8) var(--space-4);
    text-align: center;
    font-family: var(--font-sans);
}

.auth-verify-page__spinner {
    width: 32px;
    height: 32px;
    border: 2px solid var(--color-border-primary);
    border-top-color: var(--color-primary);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}

.auth-verify-page__icon {
    font-size: 2.5rem;
    line-height: 1;
}

.auth-verify-page__heading {
    font-size: 1.75rem;
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: -0.02em;
    color: var(--color-text-heading);
    margin: 0;
}

.auth-verify-page__body {
    max-width: 42ch;
    margin: 0;
    font-size: 1rem;
    line-height: 1.6;
    color: var(--color-text-body);
}

.auth-verify-page__actions {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-3);
    width: 100%;
    max-width: 320px;
    margin-top: var(--space-2);
}

.auth-verify-page__actions .btn {
    width: 100%;
}

/* === Modal (Style Guide §6.4) === */
.modal-overlay {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: max(var(--space-4), env(safe-area-inset-top))
             max(var(--space-4), env(safe-area-inset-right))
             max(var(--space-4), env(safe-area-inset-bottom))
             max(var(--space-4), env(safe-area-inset-left));
    background: rgba(12, 15, 10, 0.50);
    animation: modal-fade-in var(--duration-default) var(--ease-default);
}

@media (prefers-color-scheme: dark) {
    .modal-overlay {
        background: rgba(12, 15, 10, 0.70);
    }
}

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

@keyframes modal-scale-in {
    from { opacity: 0; transform: scale(0.95); }
    to   { opacity: 1; transform: scale(1); }
}

.modal-dialog {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    width: 100%;
    max-width: 420px;
    /* On ~800px-tall laptops the unconstrained modal clips the CTA.
     * Cap at viewport height minus a 32px safe margin (or 720px,
     * whichever is smaller) and let the body region scroll so the
     * confirm button is always reachable. */
    max-height: min(100vh - 2rem, 720px);
    overflow-y: auto;
    padding: var(--space-6);
    background: var(--neutral-0);
    border-radius: var(--radius-xl);
    animation: modal-scale-in var(--duration-default) var(--ease-default);
}

@media (prefers-color-scheme: dark) {
    .modal-dialog {
        /* Overlay-level surface per style guide §4.4 — one step lighter
         * than .card (surface level, neutral-900) so the dialog reads
         * as elevated above any cards behind the scrim. Border dropped:
         * the previous neutral-800 border duplicated this very bg hex,
         * which was visual noise per the "don't notice the border"
         * principle for dark-mode elevation. */
        background: var(--neutral-800);
    }
}

/* Ceremonial modal — the seal-warning modal (v2.0 §6.4) and any
 * future ceremonial dialog. Paper surface in light mode, neutral-900
 * in dark (heavier than the default modal's neutral-800 — the
 * permanence moment should feel weighted, not hovering). Compose
 * with .paper-grain on the same element for the grain overlay. */
.modal-dialog--ceremonial {
    background: var(--color-bg-ceremonial);
    border: 1px solid var(--color-border-ceremonial);
}

@media (prefers-color-scheme: dark) {
    .modal-dialog--ceremonial {
        background: var(--color-bg-ceremonial);
    }
}

.modal-dialog__icon {
    width: 32px;
    height: 32px;
    color: var(--amber-500);
    flex-shrink: 0;
}

.modal-dialog__title {
    font-family: var(--font-sans);
    font-size: 1.5rem;
    font-weight: 500;
    line-height: 1.3;
    letter-spacing: -0.015em;
    color: var(--color-text-heading);
    margin: 0;
}

.modal-dialog__body {
    font-family: var(--font-sans);
    font-size: 1rem;
    line-height: 1.5;
    color: var(--color-text-body);
    margin: 0;
}

.modal-dialog__body--pre {
    white-space: pre-line;
}

.modal-dialog__checkbox-label {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    font-size: 0.875rem;
    line-height: 1.5;
    color: var(--color-text-body);
    cursor: pointer;
    min-block-size: var(--touch-min);
    padding: var(--space-2) 0;
}

.modal-dialog__checkbox {
    flex-shrink: 0;
    width: 20px;
    height: 20px;
    margin-top: 2px;
    accent-color: var(--color-primary);
    cursor: pointer;
}

.modal-dialog__terms-link {
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.modal-dialog__terms-link:hover {
    color: var(--color-text-body);
}

.modal-dialog__actions {
    display: flex;
    flex-direction: row;
    justify-content: flex-end;
    gap: var(--space-3);
    margin-top: var(--space-2);
}

/* Inline error row inside SealWarningModal. Shown when the seal
 * operation fails in a way the user can correct without closing
 * the modal — notably "unlock time passed" when the clock drifts
 * past the Immediate preset's 60-second floor while the user is
 * reading the confirmation. Amber so it reads as "try again",
 * not "system broke". */
.modal-dialog__error-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    background: rgba(245, 158, 11, 0.08);
    border: 1px solid rgba(245, 158, 11, 0.35);
    border-radius: var(--radius-md);
    color: var(--amber-500);
    font-size: 0.875rem;
    line-height: 1.4;
}

/* === Verify Gate Modal (Phase G) === */
/* Layers on .modal-overlay + .modal-dialog + .modal-dialog__title
 * + .modal-dialog__body. Only the form layout, privacy footnote,
 * error line, and close-X button need gate-specific rules. */
.verify-gate__title {
    /* Leave room for the absolutely-positioned close button in the
     * top-right so long heading strings don't collide with it. */
    padding-inline-end: var(--touch-min);
}

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

.verify-gate__privacy {
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--color-text-muted);
    margin: 0;
}

.verify-gate__error {
    font-family: var(--font-sans);
    font-size: 0.875rem;
    line-height: 1.4;
    color: var(--red-500);
    margin: 0;
}

.verify-gate__close {
    position: absolute;
    top: var(--space-3);
    inset-inline-end: var(--space-3);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--touch-min);
    height: var(--touch-min);
    padding: 0;
    background: transparent;
    border: none;
    border-radius: var(--radius-md);
    color: var(--color-text-muted);
    cursor: pointer;
    touch-action: manipulation;
    transition:
        background var(--duration-fast) var(--ease-default),
        color var(--duration-fast) var(--ease-default);
}

.verify-gate__close:hover {
    background: var(--color-bg-surface);
    color: var(--color-text-heading);
}

.verify-gate__close:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: -2px;
}

.verify-gate__close svg {
    width: 20px;
    height: 20px;
}

/* === Seal Sweep Ceremony (Style Guide v2.0 §7.6) ===
 * Single 800ms signature motion that fires when the user commits on
 * the seal warning modal. Phase animations scope to .modal-dialog.sweeping
 * so the modal's CSS stays clean when the ceremony isn't active.
 *
 * Choreography:
 *   0–150ms   | Phase 1: primary button bg lime-500 → lime-800
 *   100–650ms | Phase 2: bowl reveals clockwise, wax-500 trace chases
 *   650–800ms | Phase 3: descender + q-tail draws, mark pulses once
 *   800ms     | Phase 4: navigation to /sealed (coordinated in compose.rs
 *               via MIN_CEREMONY_MS floor so the animation never truncates).
 */
.seal-ceremony {
    /* Flow-based layout — renders as a sibling above the action row
     * when the ceremony is active, pushing the row down slightly.
     * The layout shift happens once at seal time and is part of
     * the ceremony's weight. */
    width: 96px;
    height: 118px;
    margin: var(--space-2) auto 0 auto;
    pointer-events: none;
    color: var(--lime-500);
}

.seal-ceremony__svg {
    width: 100%;
    height: 100%;
    display: block;
    transform-origin: center center;
}

.seal-ceremony__audio {
    display: none;
}

/* Phase 1 — button colour transition. */
.modal-dialog.sweeping .btn--primary {
    background: var(--lime-800);
    transition: background var(--duration-default) var(--ease-default);
}

/* Phase 2 — bowl clockwise reveal + wax-500 chaser. */
.modal-dialog.sweeping .seal-sweep__circle {
    animation: seal-circle-draw 550ms var(--ease-seal) 100ms forwards;
}
.modal-dialog.sweeping .seal-sweep__trace {
    animation: seal-trace-run 550ms var(--ease-seal) 100ms forwards;
}

/* Phase 3 — descender + hook, brief pulse. */
.modal-dialog.sweeping .seal-sweep__stem {
    animation: seal-stem-draw 150ms var(--ease-seal) 650ms forwards;
}
.modal-dialog.sweeping .seal-ceremony__svg {
    animation: seal-pulse 150ms var(--ease-spring) 650ms;
}

@keyframes seal-circle-draw {
    from { stroke-dashoffset: 100; }
    to   { stroke-dashoffset: 0; }
}

/* wax-500 dash (length 3) chases ahead of the lime reveal and drifts
 * past the end so no trace residue remains when the mark settles. */
@keyframes seal-trace-run {
    from { stroke-dashoffset: 100; }
    to   { stroke-dashoffset: -3; }
}

@keyframes seal-stem-draw {
    from { stroke-dashoffset: 100; }
    to   { stroke-dashoffset: 0; }
}

@keyframes seal-pulse {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.04); }
}

@media (prefers-reduced-motion: reduce) {
    /* Collapse the ceremony to its end state instantly. The
     * MIN_CEREMONY_MS floor in compose.rs still holds the
     * navigation for 800ms so the user sees the settled mark
     * briefly before routing. */
    .modal-dialog.sweeping .seal-sweep__circle,
    .modal-dialog.sweeping .seal-sweep__stem,
    .modal-dialog.sweeping .seal-ceremony__svg {
        animation: none;
    }
    .modal-dialog.sweeping .seal-sweep__circle,
    .modal-dialog.sweeping .seal-sweep__stem {
        stroke-dashoffset: 0;
    }
    .modal-dialog.sweeping .seal-sweep__trace {
        animation: none;
        stroke-dashoffset: -3;
    }
    .modal-dialog.sweeping .btn--primary {
        transition: none;
    }
}

/* === Seal State UI === */
.seal-progress {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-text-muted);
}

.seal-progress::before {
    content: '';
    width: 16px;
    height: 16px;
    border: 2px solid var(--color-text-muted);
    border-top-color: transparent;
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}

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

.seal-success {
    color: var(--lime-600);
    font-weight: 500;
}

.seal-error {
    color: var(--red-500);
}

/* === Sealed Page === */
.sealed-page {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-6);
    padding: var(--space-6) var(--space-4);
    text-align: center;
    /* Phase-3 signature-moment entrance — the sealed page is the
     * creator's "I did the thing" moment, so the arrival gets the
     * same spring-settle treatment as the reveal page. Matches the
     * sealed-success-icon's own spring-pulse inside; outer
     * translateY + opacity composes cleanly with the inner scale
     * animation. */
    animation: reveal-fade-in var(--duration-slower) var(--ease-spring);
}

.sealed-success-icon {
    position: relative;
    width: 64px;
    height: 64px;
    color: var(--lime-500);
    /* Pointer-driven tilt — --tilt-x / --tilt-y are written by the
     * pointermove handler in sealed.rs (set_tilt_from_pointer),
     * default 0deg at rest + after pointerleave. Perspective keeps
     * the rotation visually 3D without over-foreshortening.
     * transform-style: preserve-3d on the parent context isn't
     * needed since we're only rotating a single flat plane. */
    --tilt-x: 0deg;
    --tilt-y: 0deg;
    transform: perspective(600px) rotateX(var(--tilt-x)) rotateY(var(--tilt-y));
    transform-origin: center center;
    /* 120ms delay so the icon pulse plays AFTER the page settles,
     * not simultaneously with it. Three-beat cadence: page springs
     * up → icon punches in → share row is already in place, ready
     * to tap. `both` fill mode holds the 0% keyframe (scale 0.9,
     * opacity 0.7) until the delay elapses so the icon doesn't
     * flash at final state before its own animation starts. */
    animation: sealed-pulse 300ms var(--ease-spring) 120ms both;
    /* Transition smooths the pointer-driven rotation so the icon
     * follows the cursor at a calm cadence rather than jittering
     * 60fps. Filter transition lights up the drop-shadow on hover. */
    transition:
        filter var(--duration-medium) var(--ease-default),
        transform var(--duration-default) var(--ease-default);
}

@media (prefers-reduced-motion: reduce) {
    .sealed-success-icon {
        /* Freeze the tilt for users who opted out of motion. The
         * inline style still writes to the custom properties, but
         * the transform no longer reads them so the icon stays
         * flat. */
        transform: none;
        transition: none;
    }
}

/* Phase 8 craft moment — learnings §36 "holographic tilt". A
 * subtle conic-gradient shimmer appears on hover over the sealed-
 * qub success icon, signalling that the sealed artefact is
 * something worth lingering over. Full-bleed pointer-driven tilt
 * would need a JS pointermove listener + @property rotation; this
 * is the CSS-only approximation — hover-triggered rotation of a
 * translucent conic gradient overlay + slight chroma lift. Reads
 * as a quiet craft moment without claiming interaction weight
 * (the icon isn't clickable; the shimmer invites the eye, not a
 * tap). */
.sealed-success-icon::before {
    content: "";
    position: absolute;
    inset: -8px;
    border-radius: 50%;
    background: conic-gradient(
        from 0deg,
        transparent 0deg,
        rgba(132, 204, 22, 0.18) 60deg,
        rgba(163, 230, 53, 0.12) 140deg,
        transparent 200deg,
        rgba(132, 204, 22, 0.14) 280deg,
        transparent 360deg
    );
    opacity: 0;
    transition: opacity var(--duration-slow) var(--ease-default);
    pointer-events: none;
    z-index: -1;
}

.sealed-success-icon:hover::before {
    opacity: 1;
    animation: sealed-shimmer-rotate 6s linear infinite;
}

.sealed-success-icon:hover {
    filter: drop-shadow(0 0 12px rgba(132, 204, 22, 0.35));
}

@keyframes sealed-shimmer-rotate {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .sealed-success-icon:hover::before {
        /* Static reveal; no rotation for users who opted out. */
        animation: none;
    }
}

/* Hero group: intent-aware headline + subhead + prominent reveal
 * date. Tighter gap than the page default so the three lines read
 * as one block of meaning, not three separate rows. */
.sealed-hero {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    max-width: 480px;
}

.sealed-hero__subhead {
    font-family: var(--font-sans);
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.4;
    color: var(--color-text-muted);
    margin: 0;
}

/* Reveal date is the *meaning* of the seal — elevate it to the
 * headline neighbourhood instead of burying it as a small muted
 * row below the metadata. heading-md per Style Guide §3.1. */
.sealed-hero__reveal {
    font-family: var(--font-sans);
    font-size: 1.25rem;
    font-weight: 500;
    line-height: 1.3;
    color: var(--color-text-heading);
    margin: var(--space-2) 0 0 0;
    /* When the full date wraps to two lines on narrow viewports,
     * balance the break so "at 9:24 AM (UTC+10:00)" doesn't sit as
     * a short orphan trailing the first line. No effect on single-
     * line desktop renders. */
    text-wrap: balance;
}

@media (min-width: 640px) {
    .sealed-hero__reveal {
        font-size: 1.5rem;
    }
}

@keyframes sealed-pulse {
    0% { transform: scale(0.9); opacity: 0.7; }
    50% { transform: scale(1.05); }
    100% { transform: scale(1); opacity: 1; }
}

.delivery-url-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    max-width: 480px;
    padding: var(--space-5);
    border-radius: var(--radius-md);
    border: 1px solid var(--lime-200);
    background: color-mix(in srgb, var(--lime-50) 40%, transparent);
}

@media (prefers-color-scheme: dark) {
    .delivery-url-card {
        border-color: var(--lime-800);
        background: color-mix(in srgb, var(--lime-950) 40%, transparent);
    }
}

.delivery-url-text {
    font-family: var(--font-mono);
    font-size: 0.875rem;
    line-height: 1.4;
    color: var(--color-text-body);
    word-break: break-all;
    user-select: all;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    background: var(--neutral-50);
    width: 100%;
    text-align: center;
}

@media (prefers-color-scheme: dark) {
    .delivery-url-text {
        background: var(--neutral-900);
    }
}

.delivery-url-card .btn-row {
    display: flex;
    gap: var(--space-2);
}

.copy-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    font-weight: 500;
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    background: transparent;
    color: var(--color-text-body);
    cursor: pointer;
    min-height: var(--touch-min);
    transition: background var(--duration-fast) var(--ease-default),
                border-color var(--duration-fast) var(--ease-default);
}

.copy-btn:hover {
    background: var(--neutral-100);
}

@media (prefers-color-scheme: dark) {
    .copy-btn:hover {
        background: var(--neutral-800);
    }
}

.copy-btn--copied {
    color: var(--lime-600);
    border-color: var(--lime-400);
}

.share-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 600;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-sm);
    border: none;
    background: var(--lime-500);
    color: var(--neutral-950);
    cursor: pointer;
    min-height: 44px;
    transition: background var(--duration-fast) var(--ease-default);
}

.share-btn:hover {
    background: var(--lime-400);
}

.share-btn:active {
    transform: scale(0.97);
}

.sealed-details {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    width: 100%;
    max-width: 480px;
    text-align: left;
}

.sealed-details__row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-3);
    padding: var(--space-1) 0;
}

.sealed-details__label {
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    flex-shrink: 0;
}

.sealed-details__value {
    font-family: var(--font-sans);
    font-size: 0.875rem;
    color: var(--color-text-body);
    text-align: right;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sealed-actions {
    display: flex;
    gap: var(--space-3);
    justify-content: center;
    width: 100%;
    max-width: 480px;
}

/* Collapsible technical-details group. The qub ID and upload
 * status are power-user concerns — hide behind a <details>
 * disclosure so the celebratory hero isn't cluttered with hex. */
.sealed-details-disclosure {
    width: 100%;
    max-width: 480px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: var(--space-2) var(--space-3);
    background: transparent;
}

.sealed-details-disclosure__summary {
    cursor: pointer;
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--color-text-muted);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    list-style: none;
    padding: var(--space-2) 0;
    min-height: var(--touch-min);
    display: flex;
    align-items: center;
}

.sealed-details-disclosure__summary::-webkit-details-marker {
    display: none;
}

.sealed-details-disclosure__summary::before {
    content: "+";
    display: inline-block;
    width: 1em;
    margin-inline-end: var(--space-2);
    font-weight: 700;
    transition: transform var(--duration-fast) var(--ease-default);
}

.sealed-details-disclosure[open] .sealed-details-disclosure__summary::before {
    content: "\2212"; /* minus sign */
}

/* === Phase E share enhancements (Post to X + QR code) === */

/* Share actions row that holds Copy / Share / Post-to-X side by
   side. Wraps to a second line on narrow screens (<375px). */
.share-actions-row {
    /* 2-column grid on mobile so the four share targets (Post to X /
     * Threads / Bluesky / Share) line up in an even 2×2 with uniform
     * width — previously `flex-wrap` produced a ragged layout because
     * each button sized to its label. Copy link renders alongside at
     * the same grid width. */
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
    width: 100%;
    max-width: 480px;
}

@media (min-width: 640px) {
    .share-actions-row {
        /* Wide-screen: let them spread along a single row when the
         * container has the space. Grid with auto-flow keeps them
         * aligned; no wrap needed above the breakpoint. */
        grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    }
}

/* Uniform width so the two columns line up regardless of label
 * length. Center the contents inside each cell. */
.share-actions-row > .share-button,
.share-actions-row > .share-btn,
.share-actions-row > .copy-btn {
    width: 100%;
    justify-content: center;
    min-width: 0;
}

.share-button {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    /* 44px tap target via min-height. */
    min-height: 44px;
    padding-block: var(--space-2);
    padding-inline: var(--space-4);
    background: transparent;
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    color: var(--color-text-body);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    transition:
        border-color var(--duration-default) var(--ease-default),
        color var(--duration-default) var(--ease-default);
}

.share-button:hover {
    border-color: var(--color-text-muted);
    color: var(--color-text-heading);
}

/* Primary share-button variant — lime fill, heading text. Used on
 * the sealed page to push Post-to-X as the headline share action
 * (drives the viral loop harder than Copy/Web-Share). */
.share-button--primary {
    background: var(--lime-500);
    border-color: var(--lime-500);
    color: var(--neutral-950);
    font-weight: 600;
}

.share-button--primary:hover {
    background: var(--lime-400);
    border-color: var(--lime-400);
    color: var(--neutral-950);
}

@media (prefers-color-scheme: light) {
    .share-button--primary {
        background: var(--lime-600);
        border-color: var(--lime-600);
        color: var(--neutral-50);
    }
    .share-button--primary:hover {
        background: var(--lime-700);
        border-color: var(--lime-700);
        color: var(--neutral-50);
    }
}

.share-button:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* Threads variant — purple brand accent on hover border.
 * Secondary visual weight; colour identifies the platform
 * without competing with the primary Post-to-X CTA. */
.share-button--threads:hover {
    border-color: #8b5cf6;
    color: #a78bfa;
}

/* Bluesky variant — sky-blue brand accent on hover. Same
 * secondary weight as Threads. */
.share-button--bluesky:hover {
    border-color: #1d9bf0;
    color: #60a5fa;
}

.share-button__icon {
    flex-shrink: 0;
    width: 20px;
    height: 20px;
}

/* Compact QR code shown below the share row. */
.qr-code {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    /* The QR is interactive — tap to expand. */
    background: transparent;
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    padding: var(--space-3);
    cursor: pointer;
    transition: border-color var(--duration-default) var(--ease-default);
}

.qr-code:hover {
    border-color: var(--color-text-muted);
}

.qr-code:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.qr-code__wrapper {
    display: inline-block;
}

.qr-code__inner {
    /* Default QR is 200px square — scannable at arm's length from
     * a phone held by a friend, without requiring the tap-to-expand
     * interaction most visitors would never discover. The SVG inside
     * scales to fill. */
    width: 200px;
    height: 200px;
    /* Force the inline SVG to render in the foreground colour
       so the QR matrix inverts cleanly between light and dark. */
    color: var(--color-text-heading);
}

.qr-code__inner svg {
    width: 100%;
    height: 100%;
    display: block;
}

/* Centred lock glyph injected into the QR SVG itself
   (`share::qr_svg_for` → `inject_lock_overlay`). Brand mark in the
   matrix's quiet zone; EcLevel::H carries enough error correction
   to absorb the ~5% module occlusion without scanner failures. */
.qr-lock-overlay__plate {
    fill: var(--color-bg-page);
    stroke: var(--color-primary);
    stroke-width: 1.25;
}
.qr-lock-overlay__glyph {
    color: var(--color-primary);
}

.qr-code__label {
    font-size: 0.75rem;
    color: var(--color-text-muted);
    text-align: center;
}

/* Expanded QR modal — overlay + centred panel. */
.qr-code-overlay {
    position: fixed;
    inset: 0;
    background: rgba(10, 10, 10, 0.7);
    z-index: 110;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
    /* Fade-in via opacity. The global reduced-motion media query
       collapses the transition to instant. */
    animation: qr-overlay-fade var(--duration-default) var(--ease-default);
}

@keyframes qr-overlay-fade {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

.qr-code-expanded {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-6);
    background: var(--color-bg-page);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-lg);
    max-width: 360px;
}

.qr-code-expanded .qr-code__inner {
    /* Expanded QR is 280px so it fills a phone screen comfortably
       and remains scannable from a couple of metres away. */
    width: 280px;
    height: 280px;
}

.qr-code-expanded__url {
    font-family: var(--font-mono);
    font-size: 0.75rem;
    color: var(--color-text-muted);
    word-break: break-all;
    text-align: center;
    max-width: 280px;
}

.qr-code-expanded__done {
    /* Use the existing primary button styles via class composition
       in the view! macro. This rule is just a hook for any
       overrides specific to the modal context. */
}

/* === Offline Banner === */
.offline-banner {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    background: var(--amber-100);
    color: var(--amber-700);
    font-size: 0.875rem;
    font-weight: 500;
    border-radius: var(--radius-md);
    margin-block-end: var(--space-4);
}

@media (prefers-color-scheme: dark) {
    .offline-banner {
        background: rgba(245, 158, 11, 0.15);
        color: var(--amber-500);
    }
}

/* === Purchase Page === */
.purchase-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
    padding: var(--space-6) 0;
}

.purchase-tier-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: var(--space-6);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-secondary);
    border-radius: var(--radius-lg);
}

.purchase-tier-badge {
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--color-primary-text);
}

.purchase-price {
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--color-text-heading);
}

/* === Success Page === */
.success-page {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-6);
    padding: var(--space-12) 0;
    text-align: center;
}

.success-icon {
    font-size: 3rem;
}

/* === App Toolbar ===
 * Three-column strip: empty spacer (balances the drawer trigger on
 * the right), centred home logo, drawer trigger. The spacer is a
 * sibling element so the logo stays optically centred regardless of
 * the trigger's intrinsic width.
 */
.app-toolbar {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    padding: var(--space-3) var(--space-4) 0;
}

.app-toolbar__spacer {
    /* Matches the drawer trigger's 40px touch target so the logo is
     * perfectly centred relative to the usable toolbar width. */
    inline-size: 40px;
    block-size: 40px;
}

.app-toolbar > .home-logo {
    justify-self: center;
}

.app-toolbar > .drawer-trigger {
    justify-self: end;
}

/* === Home logo (clickable wordmark in every top chrome) === */
.home-logo {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Tap target scales with the wordmark — comfortably above the
       44px minimum so the larger logo stays easy to hit on mobile. */
    min-block-size: 56px;
    min-inline-size: 56px;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-md);
    text-decoration: none;
    color: inherit;
    transition: opacity var(--duration-fast) var(--ease-default);
}

.home-logo:hover,
.home-logo:focus-visible {
    opacity: 0.8;
}

.home-logo:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.home-logo__wordmark {
    /* Style Guide §1.3 — 48px minimum digital width. Sized by
       block-size (height) so the wordmark's natural aspect ratio
       gives us ~64px wide on mobile and ~80px wide on tablet+. */
    block-size: 40px;
    inline-size: auto;
    display: block;
}

@media (min-width: 640px) {
    .home-logo__wordmark {
        block-size: 48px;
    }
}

/* === Draft Badge === */
.draft-badge {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-md);
    background: transparent;
    border: none;
    color: var(--color-text-muted);
    cursor: pointer;
    transition: color var(--duration-fast) var(--ease-default),
                background var(--duration-fast) var(--ease-default);
}

.draft-badge:hover {
    background: var(--color-bg-surface);
    color: var(--color-text-body);
}

.draft-badge__count {
    position: absolute;
    top: 4px;
    right: 4px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--color-primary);
    color: var(--neutral-900);
    font-size: 0.625rem;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
}

/* === Account chip (top-left toolbar) ===
 * Mirrors .drawer-trigger's shape (44×44 square, radius-md,
 * transparent at rest) so the two toolbar ends visually balance
 * the centred home logo. Interior renders either an identity
 * initial (lime-tinted) when the device is linked or a muted
 * person glyph when anonymous. */
.account-chip {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    border-radius: var(--radius-md);
    background: transparent;
    border: none;
    color: var(--color-text-muted);
    cursor: pointer;
    transition:
        color var(--duration-fast) var(--ease-default),
        background var(--duration-fast) var(--ease-default);
}

.account-chip:hover {
    background: var(--color-bg-surface);
    color: var(--color-text-heading);
}

.account-chip:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.account-chip__initial {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-full);
    background: var(--lime-500);
    color: var(--neutral-950);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 700;
    line-height: 1;
}

/* === Drawer trigger (top-right menu icon, replaces draft badge) === */
.drawer-trigger {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 44px tap target — universal minimum. */
    min-width: 44px;
    min-height: 44px;
    border-radius: var(--radius-md);
    background: transparent;
    border: none;
    color: var(--color-text-muted);
    cursor: pointer;
    transition:
        color var(--duration-fast) var(--ease-default),
        background var(--duration-fast) var(--ease-default);
}

.drawer-trigger:hover {
    background: var(--color-bg-surface);
    color: var(--color-text-heading);
}

.drawer-trigger:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.drawer-trigger__badge {
    position: absolute;
    top: 6px;
    right: 6px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--color-primary);
    color: var(--neutral-900);
    font-size: 0.625rem;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
    pointer-events: none;
}

/* === Account popover (floating identity panel) ===
 * Anchored to the AccountChip on the top-left of the toolbar.
 * Identity / tier / quota / manage-signature / sign-out live here
 * — distinct from the drawer on the right which is the utility
 * menu. Scrim catches outside clicks; panel is a card-shaped
 * floating surface on desktop and a top-anchored sheet on mobile.
 */
.account-popover__scrim {
    position: fixed;
    inset: 0;
    z-index: 85;
    /* Transparent — the popover is a focused affordance, not a
     * modal take-over. Clicks on the scrim close it. */
    background: transparent;
}

.account-popover__panel {
    position: absolute;
    /* Anchor under the AccountChip (top-left toolbar). The chip
     * sits at ~space-3 from the viewport edge; the panel hangs
     * ~52 px below it (chip height + gap). */
    top: calc(var(--space-3) + 44px);
    left: var(--space-3);
    width: min(320px, calc(100vw - var(--space-6)));
    padding: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-lg);
    box-shadow:
        0 4px 12px rgba(0, 0, 0, 0.08),
        0 12px 24px rgba(0, 0, 0, 0.06);
    animation: account-popover-in var(--duration-default) var(--ease-default);
}

@media (prefers-color-scheme: dark) {
    .account-popover__panel {
        background: var(--neutral-900);
        border-color: var(--neutral-800);
        box-shadow:
            0 4px 12px rgba(0, 0, 0, 0.4),
            0 12px 24px rgba(0, 0, 0, 0.3);
    }
}

@keyframes account-popover-in {
    from {
        opacity: 0;
        transform: translateY(-4px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.account-popover__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}

.account-popover__heading {
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin: 0;
}

.account-popover__close {
    background: transparent;
    border: none;
    padding: var(--space-1);
    margin: calc(var(--space-1) * -1);
    color: var(--color-text-muted);
    cursor: pointer;
    border-radius: var(--radius-sm);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: color var(--duration-fast) var(--ease-default);
}

.account-popover__close:hover {
    color: var(--color-text-heading);
}

.account-popover__close:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.account-popover__section {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.account-popover__email {
    font-size: 0.9375rem;
    color: var(--color-text-heading);
    margin: 0;
    word-break: break-word;
}

.account-popover__verified-label {
    display: inline-flex;
    align-items: center;
    padding: 2px var(--space-2);
    background: var(--lime-100, rgba(163, 230, 53, 0.15));
    color: var(--color-primary-text);
    font-size: 0.6875rem;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    border-radius: var(--radius-sm);
    margin-right: var(--space-1);
}

.account-popover__tier {
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    margin: 0;
}

.account-popover__quota {
    font-size: 0.8125rem;
    color: var(--color-text-body);
    margin: 0;
}

.account-popover__actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-top: var(--space-2);
}

.account-popover__primary-btn {
    padding: var(--space-2) var(--space-3);
    background: var(--color-primary);
    color: var(--neutral-950);
    border: none;
    border-radius: var(--radius-md);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
    min-height: var(--touch-min);
    transition: background var(--duration-fast) var(--ease-default);
}

.account-popover__primary-btn:hover {
    background: var(--lime-400);
}

.account-popover__link-btn {
    padding: var(--space-2) var(--space-3);
    background: transparent;
    color: var(--color-text-body);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 500;
    text-decoration: none;
    text-align: center;
    cursor: pointer;
    min-height: var(--touch-min);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition:
        border-color var(--duration-fast) var(--ease-default),
        color var(--duration-fast) var(--ease-default);
}

.account-popover__link-btn:hover {
    border-color: var(--color-text-muted);
    color: var(--color-text-heading);
}

.account-popover__signout {
    padding: var(--space-2) var(--space-3);
    background: transparent;
    color: var(--color-text-muted);
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    min-height: var(--touch-min);
    transition:
        border-color var(--duration-fast) var(--ease-default),
        color var(--duration-fast) var(--ease-default);
}

.account-popover__signout:hover {
    border-color: var(--red-400, #f87171);
    color: var(--red-500, #ef4444);
}

/* Mobile — panel spans the viewport width below the chip. Still
 * anchored to the top-left so visual continuity with the chip is
 * maintained; pinning to the bottom would read as a separate
 * surface. */
@media (max-width: 640px) {
    .account-popover__panel {
        left: var(--space-3);
        right: var(--space-3);
        width: auto;
    }
}

/* === Drawer (utility tray slide-in) === */
.drawer-overlay {
    position: fixed;
    inset: 0;
    background: rgba(10, 10, 10, 0.5);
    z-index: 90;
    /* Fade with the panel slide. */
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--duration-slow) var(--ease-default);
}

@media (prefers-color-scheme: dark) {
    .drawer-overlay {
        background: rgba(10, 10, 10, 0.7);
    }
}

.drawer-overlay--visible {
    opacity: 1;
    pointer-events: auto;
}

.drawer-panel {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    width: 80vw;
    max-width: 320px;
    background: var(--color-bg-page);
    border-inline-end: 1px solid var(--color-border-primary);
    z-index: 100;
    display: flex;
    flex-direction: column;
    transform: translateX(-100%);
    transition: transform var(--duration-slow) var(--ease-default);
    box-shadow: 4px 0 24px rgba(0, 0, 0, 0.2);
}

@media (min-width: 1024px) {
    .drawer-panel {
        width: 320px;
    }
}

.drawer-panel--open {
    transform: translateX(0);
}

.drawer__header {
    /* Generous top padding so the wordmark sits clear of the device
     * status bar / safe area, with a comfortable gap to the identity
     * block below. */
    padding: var(--space-8) var(--space-6) var(--space-5);
    border-block-end: 1px solid var(--color-border-primary);
}

.drawer__header-wordmark {
    display: block;
    block-size: 32px;
    inline-size: auto;
    margin: 0;
}

.drawer__identity {
    /* Stub area — Phase G fills this in with the linked email or
       sign-in row. Reserve the layout slot now so adding content
       later is a content swap, not a layout surgery. Extra block
       padding gives the identity block breathing room above the
       nav list and a clear visual divider via border. */
    padding: var(--space-5) var(--space-6);
    min-height: 24px;
    color: var(--color-text-muted);
    font-size: 0.8125rem;
    border-block-end: 1px solid var(--color-border-primary);
}

.drawer__identity--anon,
.drawer__identity--linked {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.drawer__identity--anon .drawer__identity-tier,
.drawer__identity--linked .drawer__identity-tier {
    /* Section heading — bumped above the default muted text so the
     * anonymous prompt / verified label stand out as the title of
     * the identity block. */
    font-family: var(--font-sans);
    font-size: 0.9375rem;
    font-weight: 700;
    line-height: 1.4;
    color: var(--color-text-heading);
    margin: 0;
}

.drawer__identity-email {
    font-family: var(--font-sans);
    font-size: 0.875rem;
    line-height: 1.4;
    color: var(--color-text-body);
    margin: 0;
    word-break: break-word;
}

.drawer__identity-email .drawer__identity-tier {
    /* The "Verified" label sits inline with the email address, so
     * reset the heading treatment inherited above and show it as a
     * small muted prefix. Uses the muted text colour (not lime) so
     * the drawer's account-status label doesn't glow the same colour
     * as the viewer's "Integrity verified" chip — lime stays
     * reserved for the product's core trust promise, per
     * tasks/ui-ux-review.md cross-flow polish guidance. */
    display: inline;
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--color-text-muted);
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

.drawer__identity-quota {
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--color-text-muted);
    margin: 0;
}

.drawer__identity-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-block-start: var(--space-3);
}

.drawer__identity-link-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-block-size: var(--touch-min);
    padding: var(--space-2) var(--space-4);
    background: var(--color-primary);
    color: var(--color-text-on-primary);
    border: none;
    border-radius: var(--radius-md);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    touch-action: manipulation;
    transition:
        background var(--duration-fast) var(--ease-default),
        transform var(--duration-fast) var(--ease-default),
        box-shadow var(--duration-fast) var(--ease-default);
}

.drawer__identity-link-btn:hover {
    background: var(--lime-400);
    transform: scale(1.02);
    box-shadow: 0 4px 14px rgba(132, 204, 22, 0.25);
}

.drawer__identity-link-btn:active {
    background: var(--lime-800);
    transform: scale(0.97);
    box-shadow: none;
}

.drawer__identity-link-btn:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.drawer__identity-signout {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-block-size: var(--touch-min);
    padding: var(--space-2) var(--space-4);
    background: transparent;
    color: var(--color-text-muted);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    touch-action: manipulation;
    transition:
        background var(--duration-fast) var(--ease-default),
        color var(--duration-fast) var(--ease-default);
}

.drawer__identity-signout:hover {
    background: var(--color-bg-surface);
    color: var(--color-text-body);
}

.drawer__identity-signout:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.drawer__list {
    flex: 1;
    display: flex;
    flex-direction: column;
    /* More vertical breathing room above the nav items so they don't
     * feel crowded against the identity block. */
    padding: var(--space-4) 0;
    gap: var(--space-1);
    /* Allow content to scroll if it grows past the viewport. */
    overflow-y: auto;
}

/* Collapsible drawer section (Messages, Pacts).
 * Uses native `<details>` so keyboard + screen readers get
 * disclosure behaviour for free. The summary disc is removed so we
 * can render our own chevron on the heading. */
.drawer__section {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.drawer__section + .drawer__section {
    margin-top: var(--space-2);
}

.drawer__section-heading {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-4);
    font-family: var(--font-sans);
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-text-muted);
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.drawer__section-heading::-webkit-details-marker {
    display: none;
}

/* Disclosure chevron — points right when collapsed, rotates 90° on
 * open. CSS-only via the `details[open]` selector. Rendered as a
 * thin border triangle so we don't need an extra SVG asset. */
.drawer__section-heading::before {
    content: "";
    width: 0;
    height: 0;
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    border-left: 5px solid currentColor;
    transition: transform var(--duration-fast) var(--ease-default);
}

.drawer__section[open] > .drawer__section-heading::before {
    transform: rotate(90deg);
}

/* Nested folder links sit slightly inset so the visual hierarchy
 * (section heading → folder link) reads at a glance. */
.drawer__section .drawer__item {
    padding-left: var(--space-6);
}

.drawer__item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    /* 48px height meets the 44px tap target with internal padding. */
    min-height: 48px;
    padding-block: var(--space-2);
    padding-inline: var(--space-6);
    background: transparent;
    border: none;
    border-inline-start: 3px solid transparent;
    color: var(--color-text-body);
    font-family: var(--font-sans);
    font-size: 0.9375rem;
    text-decoration: none;
    cursor: pointer;
    transition:
        background var(--duration-fast) var(--ease-default),
        color var(--duration-fast) var(--ease-default);
}

.drawer__item:hover {
    background: var(--color-bg-surface);
}

.drawer__item:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: -2px;
}

.drawer__item--active {
    border-inline-start-color: var(--color-primary);
    color: var(--color-primary-text);
}

.drawer__item-icon {
    flex-shrink: 0;
    width: 20px;
    height: 20px;
}

.drawer__item-label {
    flex: 1;
}

.drawer__item-badge {
    flex-shrink: 0;
    color: var(--color-text-muted);
    font-size: 0.8125rem;
}

.drawer__footer {
    /* Slightly looser top padding so the divider sits clear of the
     * last nav item, and a wider gap between the About / Legal links
     * for easier tapping. */
    padding: var(--space-5) var(--space-6) var(--space-6);
    margin-block-start: var(--space-2);
    border-block-start: 1px solid var(--color-border-primary);
    display: flex;
    gap: var(--space-5);
}

.drawer__footer-link {
    font-size: 0.75rem;
    color: var(--color-text-muted);
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition:
        color var(--duration-fast) var(--ease-default),
        border-color var(--duration-fast) var(--ease-default);
}

.drawer__footer-link:hover {
    color: var(--color-text-body);
    border-bottom-color: var(--color-text-body);
}

/* === Sealed history page === */
.history-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.history-page__list {
    display: flex;
    flex-direction: column;
    /* Tight list-mode rhythm — smaller gap between rows so dense
     * folders (Messages → Sealed/Revealed, Pacts → Issued) read as
     * an inbox rather than a stack of cards. Pairs with the reduced
     * .history-card padding below. */
    gap: var(--space-2);
}

.history-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    /* Compact vertical rhythm. Vertical padding tightened from
     * --space-4 (16px) to --space-2 (8px) so each row is closer to
     * a single line of text + a touch of breathing room. Horizontal
     * padding stays roomy so preview text doesn't kiss the border. */
    padding: var(--space-2) var(--space-4);
    background: var(--color-bg-surface);
    /* Softer secondary border to match .draft-card and keep list rows
     * in the "barely-there" elevation band — primary would have read
     * as too assertive for dense list surfaces (ui-ux-review.md
     * shadow guidance + learnings §7). Hover still promotes. */
    border: 1px solid var(--color-border-secondary);
    border-radius: var(--radius-md);
    transition: border-color var(--duration-fast) var(--ease-default);
}

.history-card:hover {
    border-color: var(--color-border-primary);
}

/* Single-row layout — learnings §18 + UX review:
 *   [preview ...............] [state-icon] [date] [txid] [Copy]
 * Preview grows; siblings stay intrinsic width. Wraps on narrow
 * viewports so state/date/txid/Copy roll onto a second line
 * rather than squashing. */
.history-card__row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    width: 100%;
}

.history-card__preview {
    flex: 1 1 240px;
    min-width: 0;
    font-family: var(--font-sans);
    /* Compact list-mode font size — half a step down from the
     * previous 0.9375rem so denser rows still feel airy. */
    font-size: 0.875rem;
    color: var(--color-text-heading);
    line-height: 1.35;
    margin: 0;
    /* Single-line clamp inside the row; overflows to ellipsis so
     * a long preview doesn't force-wrap the row. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.history-card__date {
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    flex-shrink: 0;
}

/* tx-id suffix in mono, muted. Enough to disambiguate adjacent
 * rows without crowding the row with the full hex. */
.history-card__txid {
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    flex-shrink: 0;
}

/* State chip is now icon-only — label comes via aria-label on the
 * parent <span>. Sized to match body line-height. */
.history-card__status {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: var(--radius-sm);
    flex-shrink: 0;
}

.history-card__status--sealed {
    background: var(--color-bg-elevated);
    color: var(--color-text-body);
}

.history-card__status--revealed {
    background: var(--lime-100);
    color: var(--color-primary-text);
}

@media (prefers-color-scheme: dark) {
    .history-card__status--revealed {
        background: var(--lime-950);
    }
}

.history-card__actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
}

/* === Footer === */
.app-footer {
    display: flex;
    justify-content: center;
    gap: var(--space-4);
    padding: var(--space-6) var(--space-4) var(--space-4);
    margin-top: auto;
}

.app-footer__link {
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: color var(--duration-fast) var(--ease-default),
                border-color var(--duration-fast) var(--ease-default);
}

.app-footer__link:hover {
    color: var(--color-text-body);
    border-bottom-color: currentColor;
}

/* === Date Picker Step === */
.date-picker {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    padding: var(--space-4) 0;
}

.date-picker__preview {
    max-height: 6lh;
    overflow: hidden;
    padding: var(--space-4);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-secondary);
    border-radius: var(--radius-md);
    font-size: 0.875rem;
    line-height: 1.6;
    color: var(--color-text-body);
    white-space: pre-wrap;
    word-break: break-word;
}

.date-picker__presets {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.date-picker__preset-btn {
    min-height: 36px;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--color-text-body);
    font-size: 0.875rem;
    cursor: pointer;
    transition: all var(--duration-fast) var(--ease-default);
}

.date-picker__preset-btn:hover {
    border-color: var(--color-primary);
    color: var(--color-primary-text);
}

/* Multi-option pill selected state — aligned with .intent-pill--selected
 * so the two tinted-pill patterns read identically across compose and
 * date-picker surfaces. The segmented-control Message/Agreement toggle
 * (.compose-mode-active) intentionally uses filled-lime instead, since
 * it's a binary mode switch where full emphasis reads correctly. */
.date-picker__preset-btn--active {
    border-color: var(--lime-500);
    background: var(--lime-100);
    color: var(--color-primary-text);
    font-weight: 500;
}

@media (prefers-color-scheme: dark) {
    .date-picker__preset-btn--active {
        background: var(--lime-950);
    }
}

.date-picker__resolved {
    font-size: 0.9375rem;
    font-weight: 500;
    color: var(--color-text-heading);
}

.date-picker__custom {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    background: var(--color-bg-surface);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
    border: 1px solid var(--color-border-primary);
    /* Slide-down animation when expanded. The transition-duration
       override in the global prefers-reduced-motion media query
       will collapse this to instant. */
    animation: date-picker-expand var(--duration-slow) var(--ease-default);
    transform-origin: top center;
}

@keyframes date-picker-expand {
    from {
        opacity: 0;
        transform: scaleY(0.95);
    }
    to {
        opacity: 1;
        transform: scaleY(1);
    }
}

.date-picker__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding-block: var(--space-1);
}

.date-picker__nav-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 44px tap target — meets the universal minimum. */
    min-width: 44px;
    min-height: 44px;
    padding: var(--space-2);
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    color: var(--color-text-body);
    cursor: pointer;
    transition: background-color var(--duration-default) var(--ease-default);
}

.date-picker__nav-btn:hover {
    background: var(--color-bg-elevated);
}

.date-picker__nav-btn:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.date-picker__month-label {
    flex: 1;
    text-align: center;
    font-family: var(--font-sans);
    font-size: 1rem;
    font-weight: 500;
    color: var(--color-text-heading);
}

.date-picker__weekday-header {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: var(--space-1);
    padding-block: var(--space-1);
}

.date-picker__weekday-header > span {
    text-align: center;
    font-size: 0.75rem;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-text-muted);
}

.date-picker__grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: var(--space-1);
}

.date-picker__day {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 40px visual cell with surrounding gap brings the tap area
       to 44px. Empty padding cells render with the same size so
       the grid stays aligned. */
    min-width: 40px;
    min-height: 40px;
    padding: 0;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-full);
    color: var(--color-text-body);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 400;
    cursor: pointer;
    transition:
        background-color var(--duration-default) var(--ease-default),
        color var(--duration-default) var(--ease-default),
        border-color var(--duration-default) var(--ease-default);
}

.date-picker__day:hover:not(.date-picker__day--disabled):not(.date-picker__day--padding):not(.date-picker__day--selected) {
    background: var(--color-bg-elevated);
}

.date-picker__day:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.date-picker__day--today {
    border-color: var(--color-border-primary);
    color: var(--color-text-heading);
}

.date-picker__day--selected {
    background: var(--color-primary);
    color: var(--color-text-on-primary);
    border-color: var(--color-primary);
}

.date-picker__day--disabled {
    color: var(--color-text-muted);
    opacity: 0.4;
    cursor: default;
    pointer-events: none;
}

.date-picker__day--padding {
    cursor: default;
    pointer-events: none;
    background: transparent;
    border-color: transparent;
}

.date-picker__time {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding-block: var(--space-2);
}

.date-picker__time-label {
    font-size: 0.75rem;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-text-muted);
}

.date-picker__time-select {
    /* 44px tap target. Reuses the same height as nav buttons. */
    min-height: 44px;
    /* Strip native chrome so the select reads as an app-styled
     * pill rather than browser-default. A custom chevron is
     * rendered via `background-image` below; extra right-padding
     * keeps the value from colliding with it. */
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding-inline: var(--space-3);
    padding-inline-end: calc(var(--space-3) + 16px);
    padding-block: var(--space-2);
    background-color: var(--color-bg-elevated);
    /* Chevron-down SVG (currentColor via CSS mask is better-supported
     * but adds complexity; background-image with a url() is fine
     * because the chevron colour is fixed against the elevated
     * surface behind it and dark mode already flips that surface). */
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236B7A6A' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
    background-repeat: no-repeat;
    background-position: right var(--space-2) center;
    background-size: 12px;
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    color: var(--color-text-heading);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    transition: border-color var(--duration-default) var(--ease-default);
}

.date-picker__time-select:hover {
    border-color: var(--color-text-muted);
}

.date-picker__time-select:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.date-picker__ampm {
    display: inline-flex;
    align-items: center;
    gap: 0;
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    overflow: hidden;
}

.date-picker__ampm-btn {
    min-width: 44px;
    min-height: 44px;
    padding-inline: var(--space-3);
    background: transparent;
    border: none;
    color: var(--color-text-body);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    transition: background-color var(--duration-default) var(--ease-default);
}

.date-picker__ampm-btn:hover {
    background: var(--color-bg-elevated);
}

.date-picker__ampm-btn--active {
    background: var(--color-primary);
    color: var(--color-text-on-primary);
}

.date-picker__error {
    /* Soft amber helper, not loud red — the condition is "pick a
     * later time" not "something broke". Stays on its own line
     * directly under the time row so the guidance is anchored to
     * the field the user needs to change. */
    font-size: 0.8125rem;
    color: var(--amber-500);
    padding-block: var(--space-1);
}

/* === Sealing Screen === */
.sealing-screen {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-6);
    min-height: 60vh;
    text-align: center;
    /* Entrance matches .reveal-page and .sealed-page so the three
     * creator/viewer "arrival" moments share one motion vocabulary.
     * The seal-pulse on the icon runs its own infinite loop; the
     * container's one-shot settle composes cleanly beneath it. */
    animation: reveal-fade-in var(--duration-slower) var(--ease-spring);
}

.sealing-screen__icon {
    width: 64px;
    height: 64px;
    color: var(--color-primary);
    animation: seal-pulse 2s ease-in-out infinite;
}

@keyframes seal-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: 0.7; transform: scale(1.05); }
}

.sealing-screen__text {
    font-size: 1.125rem;
    font-weight: 500;
    color: var(--color-text-heading);
}

.sealing-screen__timeout {
    font-size: 0.875rem;
    color: var(--color-text-muted);
    animation: fade-in-subtle 0.3s ease-out;
}

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

/* Sealing beats — per-stage ceremony (OCTALYSIS.md §5.3 Item 3).
 * Four-row card stack staged via animation-delay cascade, mirroring
 * the .reveal-page sequential-reveal motion vocabulary. Each beat
 * surfaces a real protocol artefact (qub id short, drand round,
 * Arweave tx) as it becomes available — pending values pulse until
 * they arrive. The total cascade ends at ~1050ms; the post-upload
 * MIN_CEREMONY_MS floor in compose.rs holds the screen long enough
 * for all beats to settle. */
.sealing-screen__beats {
    list-style: none;
    margin: 0;
    padding: 0;
    width: min(28rem, 100%);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    text-align: left;
    font-family: var(--font-mono);
    font-size: 0.875rem;
}

.sealing-screen__beat {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--color-border-secondary);
    border-radius: var(--radius-sm);
    background: var(--color-bg-surface);
    animation: reveal-fade-in var(--duration-slow) var(--ease-out) backwards;
}

.sealing-screen__beats > .sealing-screen__beat:nth-child(1) { animation-delay: 0ms; }
.sealing-screen__beats > .sealing-screen__beat:nth-child(2) { animation-delay: 250ms; }
.sealing-screen__beats > .sealing-screen__beat:nth-child(3) { animation-delay: 500ms; }
.sealing-screen__beats > .sealing-screen__beat:nth-child(4) { animation-delay: 750ms; }

.sealing-screen__beat-label {
    color: var(--color-text-heading);
    font-weight: 500;
    font-family: var(--font-sans);
}

.sealing-screen__beat-value {
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}

.sealing-screen__beat-value--pending {
    animation: seal-pulse 1.4s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
    .sealing-screen__beat {
        animation: none;
    }
    .sealing-screen__beat-value--pending {
        animation: none;
    }
}

/* === Seal Inline Bar (Progressive Trust, UX-FLOW §2.3) === */
.seal-inline-bar {
    display: flex;
    /* Mobile: stack explainer text + review link + CTA vertically so
     * the CTA gets a full-width tap target instead of sharing the
     * row with 120px of prose. Desktop goes back to inline. */
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
}

@media (min-width: 640px) {
    .seal-inline-bar {
        flex-direction: row;
        align-items: center;
        gap: var(--space-3);
        flex-wrap: wrap;
    }
}

/* On mobile the primary CTA inside the bar should span the card
 * edge-to-edge; on desktop it sits at the right of the row. */
.seal-inline-bar > .btn-primary {
    width: 100%;
    justify-content: center;
}

@media (min-width: 640px) {
    .seal-inline-bar > .btn-primary {
        width: auto;
    }
}

.seal-inline-bar__text {
    font-size: 0.875rem;
    color: var(--color-text-body);
    flex: 1 1 auto;
}

.seal-inline-bar__link {
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    cursor: pointer;
    background: none;
    border: none;
    border-bottom: 1px solid transparent;
    padding: 0;
    transition: color var(--duration-fast) var(--ease-default),
                border-color var(--duration-fast) var(--ease-default);
}

.seal-inline-bar__link:hover {
    color: var(--color-text-body);
    border-bottom-color: currentColor;
}

/* === Modal Preview === */
.modal-dialog__preview {
    max-height: 5lh;
    overflow: hidden;
    padding: var(--space-3);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-secondary);
    border-radius: var(--radius-sm);
    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--color-text-body);
    white-space: pre-wrap;
    word-break: break-word;
}

/* === Viewer === */

/* === Viewer Layout === */
.viewer-layout {
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
    max-width: 640px;
    margin: 0 auto;
    padding: 0 var(--space-4);
}

.viewer-header {
    padding: var(--space-4) 0;
    text-align: center;
}

.viewer-header__wordmark {
    font-family: var(--font-sans);
    font-weight: 700;
    font-size: 1.25rem;
    color: var(--color-primary);
    letter-spacing: 0.05em;
}

.viewer-main {
    flex: 1;
    display: flex;
    flex-direction: column;
}

/* === Loading (Viewer) === */
.loading__text {
    color: var(--color-text-muted);
    font-size: 0.875rem;
}

.loading__spinner {
    width: 32px;
    height: 32px;
    border: 2px solid var(--color-border-primary);
    border-top-color: var(--color-primary);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}

/* === Viewer skeleton (initial /c/:tx_id load) ===
 * Pre-reserves the countdown-page vertical rhythm so the transition
 * to Countdown or Revealed cross-fades instead of popping layout.
 * The loading_text remains as an aria-live accessible label; sighted
 * users see quiet pulsing blocks instead of a lone spinner. Aligns
 * with the "render a skeleton with the countdown block shape" fix
 * in tasks/ui-ux-review.md. */
.viewer-skeleton {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-4);
    padding: var(--space-16) var(--space-4);
}

.viewer-skeleton__a11y {
    /* Visually hidden but announced to assistive tech. */
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.viewer-skeleton__block {
    background: var(--color-bg-elevated);
    border-radius: var(--radius-md);
    animation: viewer-skeleton-pulse 1500ms ease-in-out infinite;
}

.viewer-skeleton__block--sm {
    inline-size: 140px;
    block-size: 16px;
}

.viewer-skeleton__block--md {
    inline-size: 240px;
    block-size: 20px;
}

.viewer-skeleton__block--countdown {
    inline-size: min(480px, 80vw);
    block-size: 140px;
    border-radius: var(--radius-lg);
}

@keyframes viewer-skeleton-pulse {
    0%, 100% { opacity: 0.55; }
    50% { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .viewer-skeleton__block {
        animation: none;
        opacity: 0.7;
    }
}

/* === Countdown Page === */
.countdown-page {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-8);
    padding: var(--space-16) 0;
    text-align: center;
    /* Same spring-settle entrance as the other "arrival" surfaces
     * (reveal-page, sealed-page, sealing-screen). The viewer
     * skeleton upstream pulses quietly then cross-fades into this
     * spring landing, so the transition between Loading → Countdown
     * reads as a settle rather than a snap. */
    animation: reveal-fade-in var(--duration-slower) var(--ease-spring);
}

.countdown-page__title {
    font-size: 1.25rem;
    font-weight: 500;
    line-height: 1.35;
    letter-spacing: -0.01em;
    color: var(--color-text-heading);
}

/* Intent lead-in caption above the countdown title — gives first-time
 * visitors narrative context ("A prediction is locked in.") before
 * they see raw digits. Uppercase eyebrow treatment keeps it quiet
 * but recognisable. */
.countdown-page__intent {
    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--color-primary);
    margin: 0 0 var(--space-2) 0;
}

/* Prediction-intent badge. Distinct visual treatment on prediction
 * qubs (DISTRIBUTION-STRATEGY.md §12 D3) — frames viewer
 * expectations so they return at reveal for the "called it / was
 * wrong" moment. Non-prediction intents rely on the
 * .countdown-page__intent caption only. */
.countdown-page__prediction-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-text-inverse);
    background: var(--color-primary);
    padding: 0.25rem 0.5rem;
    border-radius: var(--radius-sm);
    margin: 0 0 var(--space-3) 0;
}

/* Author line — "Sealed by {name}" under the title. Pre-decryption
 * attribution sourced from the envelope's ML-DSA-65 pubkey. Sits
 * between the countdown title and the timer so a cold viewer sees
 * "who" before "when". */
.countdown-page__author {
    font-size: 0.9375rem;
    font-weight: 500;
    color: var(--color-text-body);
    margin: 0;
    max-width: 440px;
    word-break: break-word;
}

/* === Countdown Timer (§6.5 + §2.4) === */
.countdown {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-8) var(--space-6);
    border-radius: var(--radius-lg);
    transition: background var(--duration-slow) var(--ease-out),
                color var(--duration-slow) var(--ease-out);
}

/* Counting state (dark default) */
.countdown--counting {
    background: var(--neutral-900);
    box-shadow: 0 0 24px 0 rgba(132, 204, 22, 0.15);
}

.countdown--counting .countdown__number {
    color: var(--lime-400);
}

/* Final hour (amber) */
.countdown--final-hour {
    background: var(--neutral-900);
    box-shadow: 0 0 24px 0 rgba(245, 158, 11, 0.15);
}

.countdown--final-hour .countdown__number {
    color: var(--amber-200);
}

/* Final-minute tempo cue (Octalysis item #8). Layers a subtle 1s
 * scale pulse on the numerals on top of the final-hour amber palette.
 * Cycle matches the second-tick cadence so the visual rhythm aligns
 * with the clock ticking down. prefers-reduced-motion suppresses
 * the animation; the amber palette alone communicates "final minute"
 * without the pulse. */
.countdown--final-minute .countdown__number {
    animation: countdown-final-minute-pulse 1s var(--ease-out) infinite;
}

@keyframes countdown-final-minute-pulse {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.04); }
}

/* Final-seconds state (remaining <= 10s) — bumps the numerals'
 * type scale on top of the final-minute pulse so the digits
 * visibly grow as unlock approaches. font-size composes cleanly
 * with the transform-based pulse animation; using transform here
 * would conflict. Octalysis item #8 deferred-portion. */
.countdown--final-seconds .countdown__number {
    font-size: 1.25em;
    transition: font-size var(--duration-slow) var(--ease-out);
}

@media (prefers-reduced-motion: reduce) {
    .countdown--final-minute .countdown__number {
        animation: none;
    }
    .countdown--final-seconds .countdown__number {
        transition: none;
    }
}

/* Unlocked */
.countdown--unlocked {
    background: #1A2E05;
    box-shadow: 0 0 24px 0 rgba(132, 204, 22, 0.20);
}

.countdown--unlocked .countdown__number {
    color: var(--lime-400);
}

/* Light mode countdown overrides */
@media (prefers-color-scheme: light) {
    .countdown--counting {
        background: var(--lime-50);
        box-shadow: 0 0 24px 0 rgba(132, 204, 22, 0.10);
    }
    .countdown--counting .countdown__number {
        color: var(--lime-950);
    }
    .countdown--final-hour {
        background: var(--amber-100);
        box-shadow: 0 0 24px 0 rgba(245, 158, 11, 0.10);
    }
    .countdown--final-hour .countdown__number {
        color: var(--amber-700);
    }
    .countdown--unlocked {
        background: var(--lime-100);
    }
    .countdown--unlocked .countdown__number {
        color: var(--lime-950);
    }
}

.countdown__unit {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 56px;
}

.countdown__number {
    font-family: var(--font-sans);
    font-weight: 700;
    font-size: 3rem;
    line-height: 1.1;
    /* Display-xl spacing (style guide §3.2) keeps the digits optically
     * tight at 48px so the countdown reads as a unified block, not a
     * row of loose glyphs. */
    letter-spacing: -0.03em;
    /* Tabular figures keep each digit in a fixed-width slot so the
     * seconds column doesn't reflow on tick (Satoshi is proportional
     * by default and "1" is narrower than "8"). Without this the
     * whole digit group shifts every second. */
    font-variant-numeric: tabular-nums;
    transition: color var(--duration-default) var(--ease-out);
}

.countdown__label {
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.08em;
    text-transform: lowercase;
    color: var(--color-text-muted);
    margin-top: var(--space-1);
}

.countdown__separator {
    font-size: 2rem;
    font-weight: 700;
    line-height: 1.15;
    /* Match the tightening applied to the adjacent digits so the
     * colon / dot sits visually on the same optical rhythm. */
    letter-spacing: -0.02em;
    color: var(--neutral-700);
    align-self: flex-start;
    padding-top: 0.5rem;
}

@media (prefers-color-scheme: light) {
    .countdown__separator { color: var(--neutral-300); }
}

/* Number cross-fade animation (§6.5) */
@keyframes number-fade {
    0% { opacity: 0.6; }
    100% { opacity: 1; }
}

.countdown__number {
    animation: number-fade var(--duration-default) var(--ease-out);
}

/* === Countdown Reveal Date ===
 * The reveal date is the meaning of the seal — promote it to heading
 * weight so it reads as the headline below the countdown, not muted
 * footer fine print. Days-hours-minutes-seconds is the texture; the
 * date is the destination. */
.countdown-page__reveal-date {
    font-size: 1.25rem;
    font-weight: 500;
    line-height: 1.35;
    letter-spacing: -0.01em;
    color: var(--color-text-heading);
    text-align: center;
    margin-top: var(--space-4);
    max-width: 480px;
}

@media (min-width: 640px) {
    .countdown-page__reveal-date {
        /* Promoted to heading-lg at tablet+ — tighter spacing matches
         * the larger display size per style guide §3.2. */
        font-size: 1.5rem;
        line-height: 1.3;
        letter-spacing: -0.015em;
    }
}

/* Phase F: "This qub was sealed on …" caption. Rendered below the
 * notify row as a footer trust signal — the commitment proof is
 * secondary to the "join the story" primary action. */
.countdown-page__sealed-on {
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    text-align: center;
    margin-block-start: var(--space-4);
}

/* Viral-loop CTA below the notify form — a filled-green primary
 * button that matches the conversion weight the review requested.
 * Previously a plain text link; promoted here so it outweighs the
 * sibling "Embed on your site" disclosure. Carries the intent over
 * via the `?from=` query param. */
.countdown-page__viral-cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: 44px;
    padding: var(--space-2) var(--space-5);
    margin-top: var(--space-2);
    background: var(--color-primary);
    color: var(--color-text-on-primary);
    border: 1px solid var(--color-primary);
    border-radius: var(--radius-md);
    font-family: var(--font-sans);
    font-size: 0.9375rem;
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    transition:
        background-color var(--duration-fast) var(--ease-default),
        border-color var(--duration-fast) var(--ease-default);
}

.countdown-page__viral-cta:hover {
    background: var(--color-primary-hover);
    border-color: var(--color-primary-hover);
}

.countdown-page__viral-cta:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* === Phase F: viewer engagement (watching count + view count) === */
.viewer-engagement {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    margin-block: var(--space-2);
}

.viewer-engagement__icon {
    flex-shrink: 0;
    width: 16px;
    height: 16px;
}

.viewer-engagement__count {
    /* No special styling — inherits from the parent. The class
       exists so future tweaks (e.g. tabular-nums) have a hook. */
    font-variant-numeric: tabular-nums;
}

/* === Phase F: notify-me email row === */
.notify-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    width: 100%;
    max-width: 400px;
    margin-block: var(--space-3);
}

.notify-row__input {
    flex: 1;
    min-width: 180px;
    min-height: 44px;
    padding-block: var(--space-2);
    padding-inline: var(--space-3);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    color: var(--color-text-heading);
    font-family: var(--font-sans);
    font-size: 0.875rem;
}

.notify-row__input:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
    border-color: var(--color-primary);
}

/* Filled primary button — Notify me is a real commitment from the
 * viewer (they're handing over an email), so it needs real button
 * presence. Transparent + thin border read as an unfinished state
 * against the dark countdown page. */
.notify-row__button {
    min-height: 44px;
    padding-inline: var(--space-5);
    background: var(--lime-500);
    border: 1px solid var(--lime-500);
    border-radius: var(--radius-md);
    color: var(--neutral-950);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
    transition:
        background var(--duration-default) var(--ease-default),
        border-color var(--duration-default) var(--ease-default);
}

.notify-row__button:hover {
    background: var(--lime-400);
    border-color: var(--lime-400);
}

.notify-row__button:active {
    transform: scale(0.97);
}

.notify-row__button:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.notify-row__button:focus-visible {
    outline: 2px solid var(--color-text-heading);
    outline-offset: 2px;
}

@media (prefers-color-scheme: light) {
    .notify-row__button {
        background: var(--lime-600);
        border-color: var(--lime-600);
        color: var(--neutral-50);
    }
    .notify-row__button:hover {
        background: var(--lime-700);
        border-color: var(--lime-700);
    }
}

.notify-row__success {
    /* Replaces the input + button after a successful submission. */
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-primary-text);
    font-size: 0.875rem;
}

.notify-row__privacy {
    font-size: 0.75rem;
    color: var(--color-text-muted);
    margin: 0;
    width: 100%;
}

.notify-row__error {
    font-size: 0.8125rem;
    color: var(--red-500);
    margin: 0;
    width: 100%;
}

/* === Push subscribe — companion to the email notify row === */
.push-subscribe {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: var(--space-2) 0 0;
    font-size: 0.875rem;
}

.push-subscribe__button {
    background: transparent;
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    padding: var(--space-2) var(--space-3);
    font-size: 0.875rem;
    color: var(--color-text-body);
    cursor: pointer;
    min-height: var(--touch-min);
    transition: border-color 0.15s ease, color 0.15s ease;
}

.push-subscribe__button:hover:not(:disabled) {
    border-color: var(--color-primary);
    color: var(--color-text-heading);
}

.push-subscribe__button:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.push-subscribe__button:disabled {
    opacity: 0.6;
    cursor: default;
}

.push-subscribe__success {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    color: var(--color-primary-text);
    font-size: 0.875rem;
}

.push-subscribe__denied {
    margin: 0;
    color: var(--color-text-muted);
    font-size: 0.8125rem;
}

/* === Phase F: viewer trust explainer (in proof bar details) === */
.viewer-trust {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    /* Match the inline padding of .details-section__row so the
     * "HOW THIS WORKS" heading and the key-value rows below align
     * at the same left edge. The vertical border between this
     * block and the first row is provided by `.details-section__row`'s
     * own `border-top` — no bottom border here, otherwise the two
     * dividers stack into a visible "step". */
    padding: var(--space-3) var(--space-4);
}

.viewer-trust__heading {
    font-size: 0.8125rem;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-text-muted);
    margin: 0;
}

.viewer-trust__body {
    font-size: 0.875rem;
    line-height: 1.5;
    color: var(--color-text-body);
    margin: 0;
}

.viewer-trust__link {
    color: var(--color-primary-text);
    font-size: 0.875rem;
    text-decoration: none;
    border-bottom: 1px solid currentColor;
    align-self: flex-start;
}

/* === Sealed reactions (F2 / DISTRIBUTION-STRATEGY.md §1.2) === */
/* "How did it play out?" block on the reveal page, before the
 * technical details. Two-button ask closes the feedback loop
 * that feeds the results-card percentage in F1. */
.reveal-reactions {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3) 0;
    border-block-start: 1px solid var(--color-border-primary);
    border-block-end: 1px solid var(--color-border-primary);
}

.reveal-reactions__row {
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.reveal-reactions__button {
    flex: 1 1 auto;
    min-height: var(--touch-min);
    padding: var(--space-2) var(--space-4);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--color-text-body);
    font-family: inherit;
    font-size: 0.9375rem;
    font-weight: 500;
    cursor: pointer;
    transition: all var(--duration-fast) var(--ease-out);
}

.reveal-reactions__button:hover {
    border-color: var(--color-primary);
}

.reveal-reactions__button:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.reveal-reactions__button--called-it:hover {
    color: var(--color-primary);
}

.reveal-reactions__percent {
    font-size: 0.875rem;
    color: var(--color-text-muted);
    margin: 0;
}

/* Creator-only reveal share row (F8). Visually distinct from the
 * sealed-reactions block above — the creator's tweet is a
 * broadcast action, not a feedback signal, so the two share
 * buttons use the primary variant (lime fill). */
.creator-reveal-share {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3) 0;
}

.creator-reveal-share__row {
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.creator-reveal-share__row .share-button {
    flex: 1 1 auto;
}

/* Viewer-side reveal share row — the decrypt-moment action row that
 * points to X / Threads / Bluesky / Copy. Pairs with the creator-
 * reveal-share row above but uses the neutral share-button styling
 * (secondary visual weight) because the broadcast here is a viewer
 * forwarding the reveal, not the creator announcing their result. */
.reveal-share {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3) 0;
}

.reveal-share__buttons {
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
}

/* Buttons on this row mirror the sealed-page share-button chrome
 * (44px tap target, neutral border, hover-darkens). Rules here
 * exist so the class name tracks the semantic context; token
 * values come from the same palette as `.share-button`. */
.reveal-share__button {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: 44px;
    padding-block: var(--space-2);
    padding-inline: var(--space-4);
    background: transparent;
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    color: var(--color-text-body);
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    transition:
        border-color var(--duration-default) var(--ease-default),
        color var(--duration-default) var(--ease-default);
}

.reveal-share__button:hover {
    border-color: var(--color-text-muted);
    color: var(--color-text-heading);
}

.reveal-share__button:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* Reply CTA — sits below the share row on every reveal. Green
 * outlined ghost so it reads as an on-brand secondary action, not
 * a default-styled anchor. Visually related to .reveal-share__button
 * but tinted to signal "this is a qub action" vs. a generic share. */
.reveal-reply {
    display: flex;
    justify-content: center;
    padding: var(--space-2) 0;
}

.reveal-reply__button {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: 44px;
    padding-block: var(--space-2);
    padding-inline: var(--space-5);
    background: transparent;
    border: 1px solid var(--color-primary);
    border-radius: var(--radius-md);
    color: var(--color-primary-text);
    font-family: var(--font-sans);
    font-size: 0.9375rem;
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    transition:
        background-color var(--duration-default) var(--ease-default),
        border-color var(--duration-default) var(--ease-default);
}

.reveal-reply__button:hover {
    background: rgba(132, 204, 22, 0.1);
    border-color: var(--color-primary-hover);
}

.reveal-reply__button:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* Return-visitor nudge on countdown page (M5) — muted caption
 * above the notify form, visible only on 2nd+ visit. */
.countdown-page__return-nudge {
    font-size: 0.875rem;
    color: var(--color-primary);
    font-weight: 500;
    margin: 0 0 var(--space-2) 0;
}

/* === Reveal Page === */
.reveal-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
    padding: var(--space-4) 0 var(--space-16);
    /* Entrance animation — spring-settle from 12px below, fading in.
     * The countdown → reveal state swap is the viral payoff moment
     * for every shared qub; the reveal shouldn't pop. Uses the
     * spring easing token so the arrival has just enough overshoot
     * to feel alive without wobbling. Reduced-motion is handled by
     * the global *::before/*::after rule that collapses animation-
     * duration to 0.01ms. */
    animation: reveal-fade-in var(--duration-slower) var(--ease-spring);
}

/* Per-child stagger (Octalysis item #7). Reveal surfaces cascade in
 * with a 100ms step so each element lands as a distinct beat —
 * Drive 7 (curiosity: each beat a dopamine event) and Drive 5 (for
 * pacts: bilateral signing legible as separate arrivals). Reuses
 * the reveal-fade-in keyframe so the shape matches the outer-frame
 * spring-settle; the stagger layers on top (page spring-fades as a
 * unit, children each fade-in at their own delay). Cap at child 7+
 * so long reveals still stop staggering by ~600ms. */
.reveal-page > * {
    animation: reveal-fade-in var(--duration-slow) var(--ease-out) backwards;
}
.reveal-page > *:nth-child(1) { animation-delay: 0ms; }
.reveal-page > *:nth-child(2) { animation-delay: 100ms; }
.reveal-page > *:nth-child(3) { animation-delay: 200ms; }
.reveal-page > *:nth-child(4) { animation-delay: 300ms; }
.reveal-page > *:nth-child(5) { animation-delay: 400ms; }
.reveal-page > *:nth-child(6) { animation-delay: 500ms; }
.reveal-page > *:nth-child(n+7) { animation-delay: 600ms; }

@media (prefers-reduced-motion: reduce) {
    .reveal-page > * {
        animation: none;
    }
}

/* === Verification Badge === */
.verification-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-4);
    background: rgba(132, 204, 22, 0.1);
    border: 1px solid rgba(132, 204, 22, 0.2);
    border-radius: var(--radius-full);
    width: fit-content;
}

.verification-badge__icon {
    color: var(--lime-500);
    font-size: 1rem;
    font-weight: 700;
}

.verification-badge__text {
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--color-primary-text);
}

/* === Content Body (Markdown output) === */
.content-body {
    font-size: 1rem;
    line-height: 1.7;
    color: var(--color-text-heading);
    word-break: break-word;
}

/* Markdown body headings — size/weight match the display/heading scale
 * from style guide §3.2. Weight 600 on h3/h4 is a deliberate step-up
 * (vs spec 500) so inline markdown headings stay scannable against
 * body-lg copy; line-height and letter-spacing stay spec-exact. */
.content-body h1 { font-size: 1.75rem; font-weight: 700; line-height: 1.2; letter-spacing: -0.02em; margin: var(--space-6) 0 var(--space-3); color: var(--color-text-heading); }
.content-body h2 { font-size: 1.5rem; font-weight: 700; line-height: 1.3; letter-spacing: -0.015em; margin: var(--space-6) 0 var(--space-3); color: var(--color-text-heading); }
.content-body h3 { font-size: 1.25rem; font-weight: 600; line-height: 1.35; letter-spacing: -0.01em; margin: var(--space-4) 0 var(--space-2); color: var(--color-text-heading); }
.content-body h4 { font-size: 1.1rem; font-weight: 600; line-height: 1.4; letter-spacing: -0.005em; margin: var(--space-4) 0 var(--space-2); color: var(--color-text-heading); }
.content-body strong { font-weight: 700; color: var(--color-text-heading); }

.content-body p { margin-bottom: var(--space-4); }

.content-body ul,
.content-body ol {
    padding-left: var(--space-6);
    margin-bottom: var(--space-4);
}

.content-body li { margin-bottom: var(--space-1); }

.content-body blockquote {
    border-left: 3px solid var(--color-primary);
    padding-left: var(--space-4);
    margin: var(--space-4) 0;
    color: var(--color-text-muted);
    font-style: italic;
}

.content-body code {
    font-family: var(--font-mono);
    font-size: 0.875em;
    background: var(--color-bg-surface);
    padding: 0.15em 0.4em;
    border-radius: var(--radius-sm);
}

.content-body pre {
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    padding: var(--space-4);
    overflow-x: auto;
    margin: var(--space-4) 0;
}

.content-body pre code {
    background: transparent;
    padding: 0;
    border-radius: 0;
    font-size: 0.875rem;
    line-height: 1.5;
}

.content-body hr {
    border: none;
    border-top: 1px solid var(--color-border-primary);
    margin: var(--space-6) 0;
}

.content-body del { color: var(--color-text-muted); }

/* === Markdown tables (trusted content only) === */
.markdown-table-wrap {
    margin: var(--space-5) 0;
    overflow-x: auto;
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    background: var(--color-bg-surface);
}

.markdown-table {
    border-collapse: collapse;
    width: 100%;
    font-size: 0.9rem;
    line-height: 1.5;
}

.markdown-table thead {
    background: var(--color-bg-raised, var(--color-bg-surface));
}

.markdown-table th,
.markdown-table td {
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--color-border-primary);
    vertical-align: top;
    text-align: left;
}

.markdown-table th {
    font-weight: 600;
    color: var(--color-text-heading);
    white-space: nowrap;
}

.markdown-table tbody tr:last-child th,
.markdown-table tbody tr:last-child td {
    border-bottom: none;
}

.markdown-table__cell--left { text-align: left; }
.markdown-table__cell--center { text-align: center; }
.markdown-table__cell--right { text-align: right; }

.markdown-table p {
    margin: 0;
}

/* === Content body max-width === */
.content-body {
    max-width: 65ch;
}

/* Let tables use the full content column even when wider than the
   prose max-width. The wrapper keeps overflow bounded. */
.content-body .markdown-table-wrap {
    max-width: 100%;
}

/* === Content Metadata === */
.content-meta {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-4);
    background: var(--color-bg-surface);
    border-radius: var(--radius-md);
    border: 1px solid var(--color-border-primary);
}

.content-meta__row {
    display: flex;
    gap: var(--space-2);
}

.content-meta__label {
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    min-width: 80px;
}

.content-meta__value {
    font-size: 0.8125rem;
    color: var(--color-text-body);
}

/* === Unsigned Notice ===
 * Rendered as a <details> so the short notice line is the summary
 * and the trust explainer ("Signed qubs are cryptographically tied
 * …") expands on click. Matches the disclosure pattern used by
 * embed-snippet + reveal-details so the widget reads as an in-app
 * convention, not a bespoke control. */
.unsigned-notice {
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    font-style: italic;
    text-align: center;
}

.unsigned-notice__summary {
    cursor: pointer;
    list-style: none;
    min-height: var(--touch-min);
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    justify-content: center;
    padding: var(--space-2);
}

.unsigned-notice__summary::-webkit-details-marker {
    display: none;
}

.unsigned-notice__more {
    /* Small inline "(More)" affordance — signals tap-to-expand without
     * pulling visual weight from the main "Unsigned qub …" line. */
    font-style: normal;
    color: var(--color-primary-text);
    font-size: 0.75rem;
}

.unsigned-notice__explainer {
    /* One-sentence explainer revealed on click. Non-italic so the
     * revealed copy reads as content (the italic summary is the
     * metadata flag). */
    margin: var(--space-2) 0 0;
    font-style: normal;
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    max-width: 42ch;
    margin-inline: auto;
    text-align: center;
}

/* === Identity Author Line (IDENTITY.md §5) ===
 * Sits inside the verified signature badge. The fingerprint form is
 * the initial render; on a successful attestation fetch the span
 * text upgrades to email / social while keeping the same visual
 * weight, so the upgrade is seamless rather than jumpy. */
.identity-author-line {
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    color: var(--color-text-body);
}

/* Staleness annotation (IDENTITY.md §5.4) — applied when an
 * attestation is older than 180 days. The author line is muted so
 * viewers can still parse it but get a visual cue that the handle
 * may not be current. */
.identity-stale {
    color: var(--color-text-muted);
}

/* === Proof Bar === */
.proof-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-secondary);
    border-radius: var(--radius-md);
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    line-height: 1.5;
}

.proof-bar__verified {
    color: var(--color-primary-text);
    font-weight: 500;
}

/* === Report Section === */
.report-section {
    padding: var(--space-4) 0;
    border-top: 1px solid var(--color-border-primary);
}

.report-toggle {
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    background: none;
    border: none;
    cursor: pointer;
    padding: var(--space-2) 0;
    min-height: var(--touch-min);
    font-family: var(--font-sans);
}

.report-toggle:hover { color: var(--color-text-body); }

.report-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.report-form__reason {
    border: none;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.report-form__reason-option {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
    min-height: var(--touch-min);
}

.report-form__reason-radio {
    accent-color: var(--color-primary);
}

.report-form__reason-label {
    font-size: 0.875rem;
    color: var(--color-text-body);
}

.report-form__text {
    font-family: var(--font-sans);
    font-size: 0.875rem;
    padding: var(--space-3);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    background: var(--color-bg-surface);
    color: var(--color-text-heading);
    resize: vertical;
    min-height: 80px;
}

.report-form__text::placeholder { color: var(--color-text-muted); }

.report-form__submit {
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 500;
    padding: var(--space-2) var(--space-4);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    background: var(--color-bg-surface);
    color: var(--color-text-body);
    cursor: pointer;
    min-height: var(--touch-min);
    width: fit-content;
    transition: border-color var(--duration-fast) var(--ease-default);
}

.report-form__submit:hover:not(:disabled) {
    border-color: var(--color-text-body);
}

.report-form__submit:disabled {
    opacity: 0.5;
    cursor: default;
}

.report-form__submitted {
    font-size: 0.875rem;
    color: var(--color-primary-text);
    padding: var(--space-2) 0;
}

/* === Embed snippet disclosure ===
 * Reusable F4-n component shown on sealed / reveal / countdown pages so any
 * visitor (creator or publisher) can grab the iframe snippet for their own
 * blog / Notion / Substack. Closed by default to keep the page uncluttered;
 * opens to a monospaced code block + copy button. */
/* Embed-snippet disclosure. Closed state is intentionally quiet
 * (no border, muted body colour) so the filled-green "Seal a qub"
 * CTA directly above it reads as the primary viewer action and the
 * embed flow is a power-user side option. Opened, the card regains
 * its surface+border so the textarea and copy button have clear
 * visual boundaries. */
.embed-snippet {
    margin-top: var(--space-4);
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    background: transparent;
    transition:
        border-color var(--duration-default) var(--ease-default),
        background-color var(--duration-default) var(--ease-default);
}

.embed-snippet[open] {
    border-color: var(--color-border-primary);
    background: var(--color-bg-elevated);
}

.embed-snippet__summary {
    list-style: none;
    cursor: pointer;
    padding: var(--space-2) var(--space-3);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    min-height: var(--touch-min);
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--color-text-muted);
    user-select: none;
    border-radius: var(--radius-md);
}

.embed-snippet[open] .embed-snippet__summary {
    padding: var(--space-3) var(--space-4);
    font-size: 0.9375rem;
    color: var(--color-text-heading);
}

.embed-snippet__summary::-webkit-details-marker {
    display: none;
}

.embed-snippet__summary::after {
    content: "+";
    font-family: var(--font-mono);
    font-size: 1.25rem;
    line-height: 1;
    color: var(--color-text-muted);
    transition: transform var(--duration-default) var(--ease-out);
}

.embed-snippet[open] .embed-snippet__summary::after {
    content: "\2212"; /* minus */
}

.embed-snippet__summary:hover {
    color: var(--color-primary-text);
}

.embed-snippet__summary:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.embed-snippet__body {
    padding: 0 var(--space-4) var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.embed-snippet__description {
    font-size: 0.875rem;
    color: var(--color-text-body);
    margin: 0;
}

.embed-snippet__code {
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    line-height: 1.55;
    padding: var(--space-3);
    border-radius: var(--radius-sm);
    background: var(--neutral-950);
    color: var(--neutral-100);
    overflow-x: auto;
    white-space: pre;
    margin: 0;
    border: 1px solid var(--neutral-800);
}

@media (prefers-color-scheme: light) {
    .embed-snippet__code {
        background: var(--neutral-900);
        color: var(--neutral-50);
    }
}

.embed-snippet__actions {
    display: flex;
    justify-content: flex-end;
}

/* === CTA Section === */
.cta-section {
    text-align: center;
    padding: var(--space-12) 0 var(--space-8);
    border-top: 1px solid var(--color-border-primary);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-4);
}

.cta-section__title {
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1.3;
    letter-spacing: -0.015em;
    color: var(--color-text-heading);
}

.cta-section__body {
    font-size: 1rem;
    color: var(--color-text-body);
    max-width: 360px;
}

/* === Error Page === */
.error-page {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-4);
    padding: var(--space-16) 0;
    text-align: center;
}

.error-page__icon {
    width: 48px;
    height: 48px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--color-text-muted);
}

.error-page__title {
    font-size: 1.125rem;
    font-weight: 500;
    color: var(--color-text-heading);
    max-width: 320px;
}

.error-page__body {
    font-size: 0.875rem;
    color: var(--color-text-muted);
    max-width: 320px;
}

/* === Expandable Details === */
.details-section {
    border: 1px solid var(--color-border-secondary);
    border-radius: var(--radius-md);
    overflow: hidden;
}

.details-section__summary {
    padding: var(--space-3) var(--space-4);
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--color-text-muted);
    cursor: pointer;
    list-style: none;
    transition: color var(--duration-fast) var(--ease-default);
}

.details-section__summary::-webkit-details-marker { display: none; }
.details-section__summary::marker { display: none; }

.details-section__summary:hover {
    color: var(--color-text-body);
}

.details-section__row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-4);
    border-top: 1px solid var(--color-border-secondary);
}

.details-section__row:last-child {
    padding-bottom: var(--space-3);
}

.details-section__label {
    font-size: 0.75rem;
    color: var(--color-text-muted);
    white-space: nowrap;
}

.details-section__value {
    font-size: 0.75rem;
    font-family: var(--font-mono);
    color: var(--color-text-body);
    word-break: break-all;
    text-align: right;
}

/* Copyable identifier chip — replaces a raw hash value in a
 * details row with a truncated tap-to-copy button. Keeps the same
 * monospace treatment as .details-section__value so the visual
 * weight inside the row is unchanged, plus an interactive affordance
 * via hover + focus-visible. */
.copyable-hash {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: 32px;
    padding: var(--space-1) var(--space-2);
    margin: calc(var(--space-1) * -1) calc(var(--space-2) * -1);
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    font-family: var(--font-mono);
    font-size: 0.75rem;
    color: var(--color-text-body);
    cursor: pointer;
    transition:
        border-color var(--duration-fast) var(--ease-default),
        color var(--duration-fast) var(--ease-default);
}

.copyable-hash:hover {
    border-color: var(--color-border-primary);
    color: var(--color-text-heading);
}

.copyable-hash:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.copyable-hash--copied {
    color: var(--color-primary-text);
    border-color: var(--color-primary);
}

/* === Viewer Footer === */
.viewer-footer {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-6) var(--space-4) var(--space-4);
    margin-top: auto;
}

.viewer-footer__link {
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    text-decoration: none;
    transition: color var(--duration-fast) var(--ease-default);
}

.viewer-footer__link:hover {
    color: var(--color-text-body);
}

.viewer-footer__cta {
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--color-primary-text);
    text-decoration: none;
    transition: opacity var(--duration-fast) var(--ease-default);
}

.viewer-footer__cta:hover {
    opacity: 0.8;
}

/* === Reveal Transitions === */
@keyframes dissolve-out {
    from { opacity: 1; }
    to { opacity: 0; }
}

@keyframes reveal-fade-in {
    from { opacity: 0; transform: translateY(12px); }
    to { opacity: 1; transform: translateY(0); }
}

/* Integrity-verified chip ("... ✓") — staggered 200ms after the
 * reveal-page entrance so the attestation signal lands as a
 * deliberate beat rather than arriving simultaneously with the
 * rest of the page. Learnings §25: decompose signature-moment
 * motion into named sub-animations; this is the attestation
 * sub-animation. Animation keyframe is reused from reveal-fade-in
 * to keep the entire reveal surface on one motion vocabulary. */
.proof-bar__verified {
    animation: reveal-fade-in var(--duration-medium) var(--ease-out) 200ms both;
}

.dissolve-out {
    animation: dissolve-out 0.4s ease-out forwards;
}

.fade-in {
    animation: reveal-fade-in 0.4s ease-out;
}

/* === Developer Page === */

.developer-page {
    max-width: 640px;
    margin: 0 auto;
    padding: var(--space-8) var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-10);
}

.developer-hero {
    text-align: center;
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.developer-section {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

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

.developer-capability {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    color: var(--color-text-body);
    font-family: var(--font-sans);
    font-size: 0.9375rem;
    line-height: 1.5;
}

.developer-capability::before {
    content: '\2713';
    color: var(--color-primary);
    font-weight: 700;
    flex-shrink: 0;
}

.developer-tier-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.875rem;
    line-height: 1.4;
}

.developer-tier-table th,
.developer-tier-table td {
    padding: var(--space-2) var(--space-3);
    text-align: left;
    border-bottom: 1px solid var(--color-border-primary);
}

.developer-tier-table th {
    color: var(--color-text-heading);
    font-weight: 500;
}

.developer-tier-table td {
    color: var(--color-text-body);
}

.developer-tier-table th:first-child,
.developer-tier-table td:first-child {
    color: var(--color-text-muted);
    font-weight: 400;
}

.developer-checkout {
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-lg);
    padding: var(--space-6);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.developer-checkout__form {
    display: flex;
    gap: var(--space-3);
    align-items: flex-end;
}

.developer-checkout__form .input {
    flex: 1;
}

.developer-code-block {
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    padding: var(--space-4);
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    line-height: 1.6;
    color: var(--color-text-body);
    overflow-x: auto;
    white-space: pre;
}

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

.developer-link {
    color: var(--color-primary-text);
    text-decoration: none;
    font-size: 0.9375rem;
}

.developer-link:hover {
    text-decoration: underline;
}

.developer-key-display {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    align-items: center;
    text-align: center;
}

.developer-key-value {
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    padding: var(--space-3) var(--space-4);
    font-family: var(--font-mono);
    font-size: 0.875rem;
    color: var(--color-primary-text);
    word-break: break-all;
    width: 100%;
    text-align: center;
    user-select: all;
}

.developer-key-warning {
    color: var(--amber-500);
    font-size: 0.875rem;
    font-weight: 500;
}

.developer-cancelled {
    text-align: center;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    align-items: center;
    padding: var(--space-10) var(--space-4);
}

/* Developer page — showcase sections (auth / quick start / endpoints / MCP) */

.developer-section__subtitle {
    color: var(--color-text-muted);
    font-size: 0.875rem;
    line-height: 1.5;
    margin-top: calc(var(--space-1) * -1);
}

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

.developer-auth-item {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-4);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
}

.developer-auth-item__name {
    font-family: var(--font-sans);
    font-weight: 500;
    color: var(--color-text-heading);
    font-size: 0.9375rem;
}

.developer-auth-item__desc {
    color: var(--color-text-body);
    font-size: 0.875rem;
    line-height: 1.5;
}

.developer-auth-item__example {
    margin-top: var(--space-2);
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    color: var(--color-primary-text);
    padding: var(--space-2) var(--space-3);
    background: rgba(132, 204, 22, 0.08);
    border-radius: var(--radius-sm);
    overflow-x: auto;
    white-space: pre;
}

.developer-code-example {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.developer-code-example__label {
    font-family: var(--font-sans);
    font-weight: 500;
    color: var(--color-text-heading);
    font-size: 0.875rem;
}

.developer-endpoint-group {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-bottom: var(--space-4);
}

.developer-endpoint-group__title {
    font-family: var(--font-sans);
    font-weight: 500;
    color: var(--color-text-heading);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.developer-endpoint-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.developer-endpoint-item {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
}

.developer-endpoint-item__head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.developer-endpoint-method {
    font-family: var(--font-mono);
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    padding: 2px var(--space-2);
    border-radius: var(--radius-sm);
    text-transform: uppercase;
    min-width: 48px;
    text-align: center;
}

.developer-endpoint-method--get {
    background: rgba(132, 204, 22, 0.12);
    color: var(--color-primary-text);
}

.developer-endpoint-method--post {
    background: rgba(132, 204, 22, 0.22);
    color: var(--color-primary-text);
}

.developer-endpoint-method--delete {
    background: rgba(239, 68, 68, 0.16);
    color: #fca5a5;
}

.developer-endpoint-path {
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    color: var(--color-text-body);
    word-break: break-all;
    flex: 1;
}

.developer-endpoint-item__desc {
    color: var(--color-text-muted);
    font-size: 0.8125rem;
    line-height: 1.5;
}

.developer-auth-badge {
    font-family: var(--font-mono);
    font-size: 0.6875rem;
    padding: 2px var(--space-2);
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border-primary);
    color: var(--color-text-muted);
    white-space: nowrap;
    letter-spacing: 0.02em;
}

.developer-auth-badge--none {
    border-color: var(--color-border-primary);
    color: var(--color-text-muted);
}

.developer-auth-badge--turnstile {
    border-color: rgba(14, 165, 233, 0.4);
    color: #93c5fd;
}

.developer-auth-badge--apikey {
    border-color: rgba(132, 204, 22, 0.5);
    color: var(--color-primary-text);
}

.developer-auth-badge--admin {
    border-color: rgba(239, 68, 68, 0.4);
    color: #fca5a5;
}

.developer-mcp-section {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.developer-mcp-tools {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.developer-mcp-tool {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-3) var(--space-4);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
}

.developer-mcp-tool__name {
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    color: var(--color-primary-text);
    font-weight: 700;
}

.developer-mcp-tool__desc {
    color: var(--color-text-body);
    font-size: 0.8125rem;
    line-height: 1.5;
}

.developer-mcp-env-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.developer-mcp-env-item {
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    color: var(--color-text-body);
    line-height: 1.5;
}

/* =================================================================
   Drawer — signature item subtitle
   ================================================================= */

.drawer__item-subtitle {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--color-text-muted);
    font-size: 0.75rem;
    line-height: 1;
    margin-inline-start: auto;
    flex-shrink: 0;
}

.drawer__item-subtitle-icon {
    flex-shrink: 0;
    width: 12px;
    height: 12px;
}

/* =================================================================
   Shared page header — back link above left-aligned heading.
   Used by /signature, /history, /drafts, /sealed.
   ================================================================= */

.page-header {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
    text-align: left;
    align-self: stretch;
}

/* =================================================================
   Signature management page (/signature)
   IDENTITY.md §3 + §6, PROTOCOL.md §9
   ================================================================= */

.signature-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
    max-width: 520px;
    margin-inline: auto;
    padding: var(--space-6) var(--space-4);
    width: 100%;
}

/* "Your signature is live" beat — OCTALYSIS.md §5.3 Item 2.
 * One-shot card that surfaces the moment of email-attestation
 * verification. Fades in via spring; auto-dismisses after 6s in
 * Rust. Lime accent border anchors this as a positive moment in the
 * identity stack without competing with the verified email card
 * itself, which renders below. */
.signature-live-beat {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-4) var(--space-5);
    background: rgba(132, 204, 22, 0.08);
    border: 1px solid var(--color-primary);
    border-radius: var(--radius-lg);
    animation: reveal-fade-in var(--duration-slower) var(--ease-spring);
}

.signature-live-beat__title {
    color: var(--color-text-heading);
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.3;
}

.signature-live-beat__body {
    color: var(--color-text-body);
    font-size: 0.875rem;
    line-height: 1.5;
}

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

.signature-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
    padding: var(--space-5) var(--space-4);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-lg);
}

.signature-card__body {
    color: var(--color-text-body);
    font-size: 0.9375rem;
    line-height: 1.6;
}

.signature-card__note {
    color: var(--color-text-muted);
    font-size: 0.75rem;
    line-height: 1.5;
}

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

.signature-section__heading {
    font-family: var(--font-sans);
    font-weight: 500;
    font-size: 1.0625rem;
    color: var(--color-text-heading);
    margin: 0;
}

.signature-key__label {
    color: var(--color-text-muted);
    font-size: 0.8125rem;
    margin: 0;
}

.signature-key__fingerprint {
    font-family: var(--font-mono);
    font-size: 0.875rem;
    color: var(--color-text-heading);
    word-break: break-all;
    margin: 0;
}

.signature-key__status {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-text-body);
    font-size: 0.8125rem;
    margin: 0;
}

.signature-key__active-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--color-primary);
    flex-shrink: 0;
}

.signature-email-row {
    display: flex;
    gap: var(--space-2);
}

.signature-email-input {
    flex: 1;
    min-height: var(--touch-min);
    padding: var(--space-2) var(--space-3);
    font-family: var(--font-sans);
    font-size: 0.9375rem;
    color: var(--color-text-body);
    background: var(--color-bg-app);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    /* Outline intentionally not overridden — the global *:focus-visible
     * rule (2px lime, 2px offset) applies for keyboard focus, matching
     * every other focusable element. The border-color shift below
     * provides additional emphasis on both keyboard and mouse focus. */
    transition: border-color var(--duration-fast) var(--ease-default);
}

.signature-email-input:focus {
    border-color: var(--color-primary);
}

.signature-code-input {
    min-height: var(--touch-min);
    width: 100%;
    max-width: 180px;
    padding: var(--space-2) var(--space-3);
    font-family: var(--font-mono);
    font-size: 1.25rem;
    text-align: center;
    letter-spacing: 0.3em;
    color: var(--color-text-heading);
    background: var(--color-bg-app);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    /* Same reasoning as .signature-email-input — global focus ring
     * applies on keyboard focus; border-color adds on-focus emphasis. */
    transition: border-color var(--duration-fast) var(--ease-default);
}

.signature-code-input:focus {
    border-color: var(--color-primary);
}

.signature-code-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
}

.signature-email-verified {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: 0.9375rem;
    color: var(--color-text-heading);
}

.signature-email-verified__icon {
    color: var(--color-primary);
    flex-shrink: 0;
}

.signature-error {
    color: var(--color-error);
    font-size: 0.8125rem;
    margin: 0;
}

.signature-success {
    color: var(--color-primary-text);
    font-size: 0.8125rem;
    margin: 0;
}

.signature-danger-zone {
    padding-block-start: var(--space-4);
    border-block-start: 1px solid var(--color-border-primary);
}

.signature-danger-zone__actions {
    display: flex;
    gap: var(--space-2);
    align-items: center;
}

/* --------------------------------------------------------------------------
   Identity-card — three-card layout for /signature (email / handle / key).
   Each card is a self-contained block with an icon-led header. The key
   card is collapsible; the chevron pivots via a CSS rotation tied to
   the [aria-expanded] state on the toggle button.
   -------------------------------------------------------------------------- */

.signature-page__subtitle {
    color: var(--color-text-muted);
    font-size: 0.9375rem;
    line-height: 1.5;
    margin: var(--space-1) 0 0;
}

.identity-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: var(--space-4) var(--space-4);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-lg);
}

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

.identity-card__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--color-bg-elevated);
    color: var(--color-text-heading);
    flex-shrink: 0;
}

.identity-card__title {
    font-family: var(--font-sans);
    font-weight: 500;
    font-size: 1rem;
    color: var(--color-text-heading);
    margin: 0;
    flex: 1;
}

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

.identity-card__disclosure {
    appearance: none;
    background: transparent;
    border: 0;
    padding: var(--space-1) var(--space-2);
    color: var(--color-text-muted);
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    cursor: pointer;
    border-radius: var(--radius-sm);
    min-height: var(--touch-min);
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

.identity-card__disclosure:hover {
    color: var(--color-text-heading);
}

.identity-card__divider {
    border: 0;
    border-top: 1px solid var(--color-border-primary);
    margin: var(--space-2) 0;
}

/* --------------------------------------------------------------------------
   Cryptographic key card — fingerprint copy chip + algorithm note +
   danger row.
   -------------------------------------------------------------------------- */

.signature-key__fingerprint-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.signature-key__fingerprint-row .signature-key__fingerprint {
    flex: 1;
    min-width: 0;
}

.signature-key__copy-btn {
    appearance: none;
    background: var(--color-bg-elevated);
    border: 1px solid var(--color-border-primary);
    color: var(--color-text-body);
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-md);
    cursor: pointer;
    min-height: 32px;
    transition: background var(--duration-fast) var(--ease-default),
                color var(--duration-fast) var(--ease-default),
                border-color var(--duration-fast) var(--ease-default);
}

.signature-key__copy-btn:hover {
    border-color: var(--color-primary);
    color: var(--color-text-heading);
}

.signature-key__copy-btn--copied {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: var(--color-text-on-primary);
}

.signature-key__algo-note {
    color: var(--color-text-muted);
    font-size: 0.8125rem;
    line-height: 1.5;
    margin: 0;
}

.signature-danger-row {
    display: flex;
    justify-content: flex-end;
}

/* --------------------------------------------------------------------------
   Handle section — the file referenced these classes from styles.rs but
   never shipped CSS, so the buttons fell back to browser defaults. The
   rules below give the section the same visual register as the other
   identity cards.
   -------------------------------------------------------------------------- */

.signature-handle-section {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.signature-handle-display {
    font-family: var(--font-mono);
    font-size: 1.125rem;
    color: var(--color-text-heading);
    word-break: break-all;
    margin: 0;
}

.signature-handle-display--auto {
    color: var(--color-text-body);
}

.signature-handle-input {
    min-height: var(--touch-min);
    width: 100%;
    padding: var(--space-2) var(--space-3);
    font-family: var(--font-mono);
    font-size: 1rem;
    color: var(--color-text-heading);
    background: var(--color-bg-app);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    transition: border-color var(--duration-fast) var(--ease-default);
}

.signature-handle-input:focus {
    border-color: var(--color-primary);
}

.signature-handle-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
}

.signature-handle-locked {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-text-muted);
    font-size: 0.8125rem;
    line-height: 1.5;
    margin: 0;
}

.signature-handle-locked-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px var(--space-2);
    border-radius: var(--radius-full);
    background: var(--color-bg-elevated);
    color: var(--color-text-muted);
    font-family: var(--font-sans);
    font-size: 0.75rem;
    font-weight: 500;
    flex-shrink: 0;
}

.signature-handle-cooldown {
    color: var(--color-text-muted);
    font-size: 0.8125rem;
    line-height: 1.5;
    margin: 0;
}

.signature-handle-error {
    color: var(--color-error);
    font-size: 0.8125rem;
    line-height: 1.5;
    margin: 0;
}

.signature-handle-progress {
    color: var(--color-text-muted);
    font-size: 0.8125rem;
    margin: 0;
}

/* ==========================================================================
   Pact — Compose mode selector
   ========================================================================== */

.compose-mode-selector {
    display: flex;
    gap: var(--space-1);
    background: var(--color-bg-elevated);
    border-radius: var(--radius-full);
    padding: var(--space-1);
    width: fit-content;
    margin: 0 auto var(--space-6);
}

.compose-mode-option {
    padding: var(--space-2) var(--space-5);
    border-radius: var(--radius-full);
    border: none;
    background: transparent;
    color: var(--color-text-body);
    font-family: var(--font-sans);
    font-size: 1rem;
    font-weight: 500;
    cursor: pointer;
    min-height: 44px;
    transition: background var(--duration-fast) var(--ease-default),
                color var(--duration-fast) var(--ease-default);
}

.compose-mode-active {
    background: var(--color-primary);
    color: var(--color-text-on-primary);
}

/* ==========================================================================
   Pact — Card / terms renderer
   ========================================================================== */

.pact-card {
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-lg);
    padding: var(--space-6);
}

.pact-title {
    margin: 0 0 var(--space-5) 0;
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.pact-terms-table {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    margin-bottom: var(--space-5);
    background: var(--color-bg-page);
    border-radius: var(--radius-md);
    padding: var(--space-4);
}

.pact-term-row {
    display: flex;
    gap: var(--space-4);
}

.pact-term-key {
    flex: 0 0 35%;
    font-size: 0.875rem;
    color: var(--color-text-muted);
    font-weight: 500;
}

.pact-term-value {
    flex: 1;
    color: var(--color-text-heading);
}

.pact-parties {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-bottom: var(--space-4);
    font-size: 0.875rem;
}

.pact-party {
    color: var(--color-text-body);
}

.pact-notes {
    font-size: 0.875rem;
    color: var(--color-text-body);
    font-style: italic;
    margin-top: var(--space-4);
    padding-top: var(--space-4);
    border-top: 1px solid var(--color-border-secondary);
}

.pact-encumbrance-note {
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    margin-top: var(--space-2);
    padding-left: var(--space-6);
}

/* ==========================================================================
   Pact — Signature status
   ========================================================================== */

.pact-sig-status {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding-top: var(--space-5);
    border-top: 1px solid var(--color-border-primary);
    margin-top: var(--space-5);
}

.pact-sig-verified {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-text-body);
}

.pact-sig-verified svg {
    color: var(--lime-500);
}

.pact-sig-pending {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-text-muted);
}

.pact-sig-failed {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--red-500);
}

.pact-bilateral-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-md);
    font-size: 0.875rem;
    font-weight: 500;
    background: var(--lime-100);
    color: var(--lime-800);
    margin-top: var(--space-3);
}

@media (prefers-color-scheme: dark) {
    .pact-bilateral-badge {
        background: var(--lime-950);
        color: var(--lime-400);
    }
}

/* ==========================================================================
   Pact — Compose form
   ========================================================================== */

.pact-compose {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

.pact-compose-term {
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-secondary);
    border-radius: var(--radius-md);
    padding: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    position: relative;
}

.pact-compose-remove {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    background: transparent;
    border: none;
    color: var(--color-text-muted);
    cursor: pointer;
    padding: var(--space-2);
    min-width: 44px;
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.pact-compose-remove:hover {
    color: var(--red-500);
}

.pact-compose-add {
    background: transparent;
    border: none;
    color: var(--color-text-muted);
    cursor: pointer;
    font-family: var(--font-sans);
    font-size: 0.875rem;
    padding: var(--space-3);
    min-height: 44px;
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.pact-compose-add:hover {
    color: var(--color-primary-text);
}

.pact-compose-parties {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

.pact-compose-party-group {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

/* ==========================================================================
   Pact — Read-only viewer sections (staging / cosign pages)

   Distinct from .pact-step (the stepped composer) because the viewer
   wants a denser layout: label/value pairs on one row, moderate gap
   between pairs, clear break between sections. Reusing .pact-step's
   `gap: space-6` + paragraph margins gives an airy composer feel
   that reads as empty space in a read-only legal document.
   ========================================================================== */

.pact-view-section {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding-block: var(--space-4);
}

.pact-view-section + .pact-view-section {
    border-block-start: 1px solid var(--color-border-secondary);
}

.pact-view-section__heading {
    margin: 0 0 var(--space-1) 0;
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--color-text-heading);
}

.pact-view-row {
    display: grid;
    grid-template-columns: minmax(140px, 35%) 1fr;
    gap: var(--space-3);
    align-items: baseline;
}

@media (max-width: 520px) {
    .pact-view-row {
        grid-template-columns: 1fr;
        gap: var(--space-1);
    }
}

.pact-view-row__label {
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--color-text-muted);
}

.pact-view-row__value {
    color: var(--color-text-heading);
    margin: 0;
    word-break: break-word;
}

/* ==========================================================================
   Pact — Stepped composer
   ========================================================================== */

.pact-step {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
    width: 100%;
    margin: 0 auto;
    padding: var(--space-6) 0;
}

.pact-step__progress {
    display: flex;
    gap: var(--space-2);
    justify-content: center;
    margin-bottom: var(--space-4);
}

.pact-step__dot {
    width: var(--space-2);
    height: var(--space-2);
    border-radius: var(--radius-full);
    background: var(--color-border-primary);
    transition: background var(--duration-default) var(--ease-default),
        width var(--duration-default) var(--ease-default);
}

.pact-step__dot--active {
    width: var(--space-6);
    background: var(--lime-500);
}

.pact-step__heading {
    margin: 0 0 var(--space-2) 0;
}

.pact-step__nav {
    display: flex;
    justify-content: space-between;
    gap: var(--space-3);
    margin-top: var(--space-6);
    padding-top: var(--space-4);
    border-top: 1px solid var(--color-border-secondary);
}

@media (max-width: 480px) {
    .pact-step__nav {
        flex-direction: column-reverse;
    }
}

.pact-segmented {
    display: inline-flex;
    padding: var(--space-1);
    gap: var(--space-1);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
}

.pact-segmented__option {
    min-height: var(--touch-min);
    padding: var(--space-2) var(--space-4);
    border: none;
    border-radius: calc(var(--radius-md) - var(--space-1));
    background: transparent;
    color: var(--color-text-body);
    font-family: var(--font-sans);
    font-weight: 500;
    cursor: pointer;
    transition: background var(--duration-fast) var(--ease-default),
        color var(--duration-fast) var(--ease-default);
}

.pact-segmented__option:hover:not(.pact-segmented__option--active) {
    background: var(--color-bg-elevated);
}

.pact-segmented__option:focus-visible {
    outline: 2px solid var(--lime-500);
    outline-offset: 2px;
}

.pact-segmented__option--active {
    background: var(--color-primary);
    color: var(--color-text-on-primary);
}

.pact-inline-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-3);
}

@media (max-width: 520px) {
    .pact-inline-row {
        grid-template-columns: 1fr;
    }
}

.pact-field-note {
    color: var(--color-text-muted);
    margin-top: var(--space-1);
}

.pact-ack-row {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-4);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-secondary);
    border-radius: var(--radius-md);
}

.pact-ack-row input[type="checkbox"] {
    flex-shrink: 0;
    margin-top: 2px;
    min-width: var(--space-6);
    min-height: var(--space-6);
    accent-color: var(--lime-500);
    cursor: pointer;
}

.pact-ack-row input[type="checkbox"]:focus-visible {
    outline: 2px solid var(--lime-500);
    outline-offset: 2px;
}

.pact-ack-row label {
    cursor: pointer;
}

/* Pill group used for pact type (Goods/Services) and role selections,
   mirroring the Message tab's intent pills. */
.pact-pill-group {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    padding-block: var(--space-1);
}

/* ==========================================================================
   Pact — Staging page
   ========================================================================== */

.pact-staging {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    max-width: 640px;
    margin: 0 auto;
    padding: var(--space-6) var(--space-4);
}

/* Cosign lock-in moment (Octalysis item #6). Rendered for ~1.8s
 * when the pact transitions to Sealed state; a spring-scale
 * entrance on the whole block, then redirect to the viewer page.
 * Both cosigner (just signed) and author (polled into Sealed) land
 * here. prefers-reduced-motion suppresses the animation; the text
 * stays visible for the same 1.8s so the navigation still lands
 * after a deliberate pause. */
.pact-lock-in {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-10) var(--space-4);
    text-align: center;
    animation: pact-lock-in-enter var(--duration-slow) var(--ease-spring) both;
}

.pact-lock-in__icon {
    width: 56px;
    height: 56px;
    color: var(--lime-500);
    margin-bottom: var(--space-2);
}

.pact-lock-in__title {
    font-family: var(--font-sans);
    font-size: 1.5rem;
    font-weight: 500;
    line-height: 1.3;
    letter-spacing: -0.015em;
    color: var(--color-text-heading);
    margin: 0;
}

.pact-lock-in__subtitle {
    font-family: var(--font-sans);
    font-size: 1rem;
    line-height: 1.5;
    color: var(--color-text-body);
    margin: 0;
}

.pact-lock-in__redirecting {
    font-size: 0.8125rem;
    line-height: 1.4;
    letter-spacing: 0.01em;
    color: var(--color-text-muted);
    margin-top: var(--space-4);
}

@keyframes pact-lock-in-enter {
    from { opacity: 0; transform: scale(0.94); }
    to   { opacity: 1; transform: scale(1); }
}

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

.pact-staging-status {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    font-size: 0.875rem;
}

.pact-shield-icon {
    color: var(--lime-500);
    margin: 0 auto;
}

.btn--block {
    width: 100%;
    text-align: center;
}

.pact-retract-btn {
    color: var(--red-500);
}

.pact-staging-share {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    background: var(--color-bg-elevated);
    border-radius: var(--radius-md);
    padding: var(--space-3) var(--space-4);
    font-family: var(--font-mono);
    font-size: 0.875rem;
    overflow: hidden;
    word-break: break-all;
}

/* Pact reveal hero — shown above the terms on a fully sealed pact
 * viewer. Surfaces the bilateral identity as the meaning of the
 * artifact; the seal date + "both verified" badge land as muted
 * metadata below the heading. */
.pact-reveal-hero {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    text-align: center;
    max-width: 520px;
    margin: 0 auto var(--space-4) auto;
}

.pact-reveal-hero__title {
    margin: 0;
}

.pact-reveal-hero__meta {
    font-family: var(--font-sans);
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--color-text-muted);
    margin: 0;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    justify-content: center;
}

/* Pact hero group — warm headline + supporting subhead shared
 * across pact_staged share screen and pact_staging waiting views.
 * Mirrors the sealed-page hero structure so both feel coherent. */
.pact-hero {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    text-align: center;
    max-width: 520px;
    margin: 0 auto;
}

.pact-hero__title {
    margin: 0;
}

.pact-hero__subhead {
    font-family: var(--font-sans);
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.4;
    color: var(--color-text-muted);
    margin: 0;
    max-width: 440px;
}

/* Counterparty identity block — avatar-initial disc + name + email.
 * Replaces the "Share this link with {name}" muted caption with a
 * proper identity row so the person the agreement is *for* reads as
 * the primary context. */
.pact-counterparty {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    background: var(--color-bg-elevated);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    max-width: 480px;
    margin: 0 auto;
    width: 100%;
}

.pact-counterparty__initial {
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-full);
    background: var(--lime-500);
    color: var(--neutral-950);
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-sans);
    font-size: 1rem;
    font-weight: 700;
    text-transform: uppercase;
}

.pact-counterparty__info {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    flex: 1;
}

.pact-counterparty__name {
    font-family: var(--font-sans);
    font-size: 0.9375rem;
    font-weight: 500;
    color: var(--color-text-heading);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.pact-counterparty__email {
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* What-happens-next three-beat box on the cosigner view — manages
 * expectations about the cryptographic commitment in plain language
 * so signing doesn't feel like clicking a legal trap. */
.pact-next-steps {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-4);
    background: var(--color-bg-elevated);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    max-width: 480px;
    margin: 0 auto;
    width: 100%;
}

.pact-next-steps__heading {
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin: 0 0 var(--space-1) 0;
}

.pact-next-steps__list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    list-style: none;
    padding: 0;
    margin: 0;
    counter-reset: pact-steps;
}

.pact-next-steps__item {
    position: relative;
    padding-inline-start: var(--space-6);
    font-family: var(--font-sans);
    font-size: 0.9375rem;
    line-height: 1.5;
    color: var(--color-text-body);
    counter-increment: pact-steps;
}

.pact-next-steps__item::before {
    content: counter(pact-steps);
    position: absolute;
    inset-inline-start: 0;
    top: 0;
    width: 20px;
    height: 20px;
    border-radius: var(--radius-full);
    background: var(--lime-500);
    color: var(--neutral-950);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.75rem;
    font-weight: 700;
}

/* Retract disclosure — <details> wrapping the destructive retract
 * button so it's accessible to power users but removed from the
 * primary visual band. Destructive actions shouldn't share weight
 * with share actions. */
.pact-retract-disclosure {
    max-width: 480px;
    margin: var(--space-4) auto 0 auto;
    width: 100%;
}

.pact-retract-disclosure__summary {
    cursor: pointer;
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--color-text-muted);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    list-style: none;
    padding: var(--space-2) 0;
    min-height: var(--touch-min);
    display: flex;
    align-items: center;
    text-align: center;
    justify-content: center;
}

.pact-retract-disclosure__summary::-webkit-details-marker {
    display: none;
}

.pact-retract-disclosure__summary::before {
    content: "+";
    display: inline-block;
    width: 1em;
    margin-inline-end: var(--space-2);
    font-weight: 700;
    transition: transform var(--duration-fast) var(--ease-default);
}

.pact-retract-disclosure[open] .pact-retract-disclosure__summary::before {
    content: "\2212"; /* minus sign */
}

/* Reduce motion */
@media (prefers-reduced-motion: reduce) {
    .compose-mode-option {
        transition: none;
    }
}

/* -----------------------------------------------------------------
 * Creator lifecycle opt-in (P0-10 / DISTRIBUTION-STRATEGY.md §12.1)
 *
 * Rendered on the Confirming step, above the seal-warning modal.
 * Collapsed state: a single-line checkbox affordance.
 * Expanded state (checkbox on): description + email input (or
 * read-only verified badge) + consent footnote.
 *
 * Lives on the main compose surface, not inside the alertdialog —
 * see `pages/compose.rs` Confirming branch. Honours touch-min and
 * prefers-reduced-motion per the style guide.
 * ----------------------------------------------------------------- */

.lifecycle-optin {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    margin: var(--space-3) 0;
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
}

.lifecycle-optin__label {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    font-size: 0.9375rem;
    line-height: 1.5;
    color: var(--color-text-body);
    cursor: pointer;
    min-block-size: var(--touch-min);
    user-select: none;
}

.lifecycle-optin__checkbox {
    flex-shrink: 0;
    width: 20px;
    height: 20px;
    accent-color: var(--color-primary);
    cursor: pointer;
}

.lifecycle-optin__details {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding-inline-start: calc(20px + var(--space-3));
}

.lifecycle-optin__description {
    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--color-text-muted);
    margin: 0;
}

.lifecycle-optin__email-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
}

.lifecycle-optin__email-input {
    flex: 1 1 200px;
    min-block-size: var(--touch-min);
    padding: var(--space-2) var(--space-3);
    font-size: 0.9375rem;
    font-family: var(--font-sans);
    color: var(--color-text-heading);
    background: var(--color-bg-page);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-sm);
}

.lifecycle-optin__email-input:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
    border-color: var(--color-primary);
}

.lifecycle-optin__email-input:disabled,
.lifecycle-optin__email-input[readonly] {
    background: var(--color-bg-elevated);
    color: var(--color-text-muted);
    cursor: default;
}

.lifecycle-optin__verified-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-2);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--color-primary-text);
    background: var(--color-bg-elevated);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-sm);
    white-space: nowrap;
}

.lifecycle-optin__verified-badge::before {
    content: "\2713"; /* checkmark */
    font-weight: 700;
}

.lifecycle-optin__error {
    font-size: 0.8125rem;
    color: var(--red-500);
    margin: 0;
}

.lifecycle-optin__consent {
    font-size: 0.75rem;
    line-height: 1.5;
    color: var(--color-text-muted);
    margin: 0;
}

@media (prefers-reduced-motion: reduce) {
    .lifecycle-optin__details {
        /* No transitions to disable today — the disclosure is
         * conditional render, not a height animation. If a future
         * transition is added, kill it here. */
    }
}

/* ==========================================================================
   /u/:handle profile page
   ========================================================================== */

.profile-page {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    max-width: 640px;
    margin-inline: auto;
    padding: var(--space-6) var(--space-4);
    width: 100%;
}

.profile__header-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-5) var(--space-4);
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-lg);
    position: relative;
}

.profile__heading {
    font-family: var(--font-sans);
    font-weight: 600;
    font-size: 1.5rem;
    line-height: 1.2;
    color: var(--color-text-heading);
    margin: 0;
    /* Long display names break gracefully rather than overflow. */
    overflow-wrap: anywhere;
}

.profile__handle-line {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    font-size: 0.875rem;
    color: var(--color-text-muted);
}

.profile__handle {
    font-family: var(--font-mono);
    font-size: 0.9375rem;
    color: var(--color-text-body);
}

.profile__email-pill {
    display: inline-flex;
    align-items: center;
    padding: 2px var(--space-2);
    border-radius: var(--radius-full);
    background: var(--lime-100);
    color: var(--color-primary-text);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

@media (prefers-color-scheme: dark) {
    .profile__email-pill {
        background: var(--lime-950);
    }
}

.profile__fingerprint {
    font-family: var(--font-mono);
    font-size: 0.75rem;
    color: var(--color-text-muted);
}

.profile__url-row {
    margin: 0;
    font-size: 0.9375rem;
}

.profile__url-link {
    color: var(--color-primary-text);
    text-decoration: none;
    font-family: var(--font-sans);
    word-break: break-all;
}

.profile__url-link:hover {
    text-decoration: underline;
}

.profile__edit-cta {
    appearance: none;
    align-self: flex-start;
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--color-text-body);
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    text-decoration: none;
    cursor: pointer;
    transition: border-color var(--duration-fast) var(--ease-default),
                color var(--duration-fast) var(--ease-default);
}

.profile__edit-cta:hover {
    border-color: var(--color-primary);
    color: var(--color-text-heading);
}

.profile__stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-3);
    padding: var(--space-4);
    background: var(--color-bg-elevated);
    border-radius: var(--radius-lg);
}

.profile__stat {
    text-align: center;
    font-size: 0.8125rem;
    color: var(--color-text-body);
    line-height: 1.4;
}

@media (max-width: 480px) {
    .profile__stats {
        /* Stack stats vertically on narrow viewports rather than
         * crushing the three columns. */
        grid-template-columns: 1fr;
        gap: var(--space-2);
    }

    .profile__stat {
        text-align: left;
    }
}

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

.profile__timeline-heading {
    font-family: var(--font-sans);
    font-weight: 500;
    font-size: 1rem;
    color: var(--color-text-heading);
    margin: 0;
}

.profile__intent-badge {
    display: inline-flex;
    align-items: center;
    padding: 2px var(--space-2);
    border-radius: var(--radius-full);
    background: var(--color-bg-elevated);
    color: var(--color-text-body);
    font-family: var(--font-sans);
    font-size: 0.6875rem;
    font-weight: 500;
    letter-spacing: 0.02em;
    text-transform: capitalize;
    flex-shrink: 0;
}

.profile__empty {
    color: var(--color-text-muted);
    font-size: 0.875rem;
    line-height: 1.5;
    margin: 0;
}

.profile__cta {
    align-self: center;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-md);
    background: var(--color-bg-elevated);
    color: var(--color-text-body);
    font-family: var(--font-sans);
    font-size: 0.9375rem;
    text-decoration: none;
    transition: background var(--duration-fast) var(--ease-default);
}

.profile__cta:hover {
    background: var(--color-primary);
    color: var(--color-text-on-primary);
}

/* Subtle hover affordance for `@handle` references inside viewer
 * pages (countdown, reveal). The text colour stays inherited; only
 * the underline appears on hover so the byline reads as identity at
 * rest. Keyboard focus reveals the underline + a focus ring (the
 * global `:focus-visible` rule already handles the ring). */
.profile-handle-link {
    color: inherit;
    text-decoration: none;
    cursor: pointer;
}

.profile-handle-link:hover {
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* ==========================================================================
   /signature → Profile card form
   ========================================================================== */

.signature-profile__field {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.signature-profile__label {
    font-family: var(--font-sans);
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--color-text-body);
}

.signature-profile__input {
    min-height: var(--touch-min);
    padding: var(--space-2) var(--space-3);
    font-family: var(--font-sans);
    font-size: 0.9375rem;
    color: var(--color-text-body);
    background: var(--color-bg-app);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--radius-md);
    transition: border-color var(--duration-fast) var(--ease-default);
}

.signature-profile__input:focus {
    border-color: var(--color-primary);
}

.signature-profile__helper {
    font-size: 0.75rem;
    color: var(--color-text-muted);
    line-height: 1.4;
    margin: 0;
}

.signature-profile__actions {
    display: flex;
    justify-content: flex-end;
}
