/* ═══════════════════════════════════════════════════════════════════════════
   FamilyTree — design tokens + global styles
   ───────────────────────────────────────────────────────────────────────────
   All colour/spacing decisions live here as --ft-* custom properties.
   MudBlazor writes  data-theme="dark"  on <html> when IsDarkMode is true —
   the [data-theme="dark"] block below overrides every token so every
   component that uses --ft-* vars gets dark mode for free.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Light mode (default) ─────────────────────────────────────────────────── */
:root {
    /* Greens */
    --ft-green-50: #f0faf4;
    --ft-green-100: #d6f2e0;
    --ft-green-200: #aee5c4;
    --ft-green-300: #7dcca3;
    --ft-green-400: #4db282;
    --ft-green-500: #2e9966;
    --ft-green-600: #1e7d50;
    --ft-green-700: #15623d;
    --ft-green-800: #0f472c;
    --ft-green-900: #082d1b;
    /* Grays */
    --ft-gray-50: #f8f9fa;
    --ft-gray-100: #f1f3f5;
    --ft-gray-200: #e9ecef;
    --ft-gray-300: #adb5bd;
    --ft-gray-400: #868e96;
    --ft-gray-500: #495057;
    --ft-gray-600: #343a40;
    --ft-gray-700: #212529;
    --ft-gray-800: #16191d;
    --ft-gray-900: #0d0f12;
    /* Surfaces */
    --ft-surface: #ffffff;
    --ft-surface-card: #f8faf9;
    --ft-surface-elevated: #ffffff;
    --ft-line-border: #dee2e6;
    /* Focus / selected node */
    --ft-focus-fill: var(--ft-green-100);
    --ft-focus-border: var(--ft-green-500);
    --ft-focus-text: var(--ft-green-800);
    --ft-focus-ring: var(--ft-green-400);
    /* Tree connector lines */
    --ft-line-couple: #B09870; /* U-arc between spouses */
    --ft-line-parent: #B09870; /* stem + drops to children */
    /* Generation band backgrounds */
    --ft-band-grandparents: var(--ft-green-50);
    --ft-band-parents: var(--ft-green-100);
    --ft-band-children: var(--ft-green-50);
    /* Tree node accent */
    --ft-leaf-mid: var(--ft-green-300);
    /* Typography */
    --ft-font-body: 'Inter', system-ui, sans-serif;
    --ft-font-display: 'Lora', Georgia, serif;
    /* Semantic colours */
    --ft-text-primary: var(--ft-gray-700);
    --ft-text-secondary: var(--ft-gray-500);
    --ft-text-muted: var(--ft-gray-300);
    /* Shadows */
    --ft-shadow-sm: 0 1px 3px rgba(0, 0, 0, .08);
    --ft-shadow-md: 0 4px 12px rgba(0, 0, 0, .10);
    /* Radii — referenced by ~13 pages/components (Dashboard, Admin tabs,
       About, FAQ, Stories, ...) via var(--ft-radius-card), but never
       actually defined here. They only existed in wwwroot/css/design-tokens.css,
       which is never <link>'d from App.razor and also carries a stale,
       conflicting color palette from an earlier design pass — so linking it
       to fix this would reintroduce wrong colors. Defining just the two
       radius tokens here (where every --ft-* var that's actually live
       already lives) fixes every one of those call sites at once without
       touching color. */
    --ft-radius-card: 12px;
    --ft-radius-sm: 8px;
}

/* ── Dark mode ────────────────────────────────────────────────────────────── */
/*
   MudBlazor sets data-theme="dark" on <html> when IsDarkMode = true.
   We also handle .dark-mode on <body> as a manual-toggle fallback.
*/
[data-theme="dark"],
.dark-mode {
    /* Greens — inverted (dark bg, lighter fg) */
    --ft-green-50: #0d2318;
    --ft-green-100: #143322;
    --ft-green-200: #1e4d33;
    --ft-green-300: #2e6b48;
    --ft-green-400: #3d8a5e;
    --ft-green-500: #4faa78;
    --ft-green-600: #72c299;
    --ft-green-700: #9dd6ba;
    --ft-green-800: #c8ebda;
    --ft-green-900: #e8f7f0;
    /* Grays — inverted */
    --ft-gray-50: #0d0f12;
    --ft-gray-100: #16191d;
    --ft-gray-200: #1e2227;
    --ft-gray-300: #6c757d;
    --ft-gray-400: #868e96;
    --ft-gray-500: #adb5bd;
    --ft-gray-600: #ced4da;
    --ft-gray-700: #dee2e6;
    --ft-gray-800: #f1f3f5;
    --ft-gray-900: #f8f9fa;
    /* Surfaces */
    --ft-surface: #121417;
    --ft-surface-card: #1a1d21;
    --ft-surface-elevated: #1e2227;
    --ft-line-border: #2e3238;
    /* Focus / selected node */
    --ft-focus-fill: #143322;
    --ft-focus-border: var(--ft-green-500);
    --ft-focus-text: var(--ft-green-800);
    --ft-focus-ring: var(--ft-green-500);
    /* Tree connector lines */
    --ft-line-couple: #C4956A; /* warmer twig for dark canvas */
    --ft-line-parent: #C4956A;
    /* Generation band backgrounds */
    --ft-band-grandparents: var(--ft-green-50);
    --ft-band-parents: var(--ft-green-100);
    --ft-band-children: var(--ft-green-50);
    /* Tree node accent */
    --ft-leaf-mid: var(--ft-green-400);
    /* Semantic */
    --ft-text-primary: var(--ft-gray-700);
    --ft-text-secondary: var(--ft-gray-500);
    --ft-text-muted: var(--ft-gray-300);
    /* Shadows */
    --ft-shadow-sm: 0 1px 3px rgba(0, 0, 0, .40);
    --ft-shadow-md: 0 4px 12px rgba(0, 0, 0, .50);
}

/* ── Reset & base ─────────────────────────────────────────────────────────── */

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

html, body {
    width: 100vw;
    /*height: 100vh;*/
    height: auto;
    margin: 0;
    padding: 0;
    /*overflow: hidden;*/
    overflow-x: hidden;
    overflow-y: auto;
    font-family: var(--ft-font-body);
    background: var(--ft-surface);
    color: var(--ft-text-primary);
    -webkit-font-smoothing: antialiased;
}

.custom-avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    cursor: pointer;
    background: var(--ft-green-600);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 500;
    margin-left: 6px;
    border: 2px solid var(--ft-green-400);
    overflow: hidden;
    flex-shrink: 0;
}

/* ── Page ──────────────────────────────────────────────────────── */

.page-header {
    align-items: center;
    justify-content: center;
    font-size: 32px;
    font-weight: 500;
    font-style: italic;
    font-family: var(--ft-font-display);
    color: var(--ft-green-800);
    margin-bottom: 28px;
    margin-top: 28px;
}

.custom-input-file {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    opacity: 0;
    cursor: pointer;
}

.hero-overlay {
    position: fixed;
    top: 72px;
    right: 24px;
    z-index: 1250;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
}

.hero-header {
    margin-bottom: 16px;
}

.hero-drag-handle {
    display: flex;
    justify-content: center;
    width: 100%;
    padding: 0 0 6px;
    cursor: grab;
    opacity: 0.35;
    transition: opacity 0.15s;
    user-select: none;
}
.hero-drag-handle:hover { opacity: 0.7; }
.hero-drag-handle:active { cursor: grabbing; }

.hero-glass {
    position: relative;
    isolation: isolate;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
    padding: 18px 24px;
    border-radius: 12px;
    background: rgba(245,245,245,0.3);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(12px);
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
    color: var(--ft-text-primary);
    font-family: var(--ft-font-display);
    transition: all 0.3s ease-in-out;
    animation: heroFadeIn 0.6s ease-out;
}

    .hero-glass::before {
        content: "";
        position: absolute;
        inset: 0;
        border-radius: inherit;
        background: radial-gradient(circle at top right, rgba(255,255,255,0.6) 0%, rgba(255,255,255,0.1) 70%);
        z-index: -1;
    }

    .hero-glass:hover {
        backdrop-filter: blur(24px);
        transition: all 0.25s ease;
        box-shadow: 0 12px 40px rgba(0, 0, 0, 0.18);
    }

    .hero-glass.expanded {
        width: 320px;
        background: rgba(255,255,255,0.65);
        box-shadow: 0 12px 40px rgba(0, 0, 0, 0.18);
    }

.hero-title {
    font-size: 22px;
    font-weight: 600;
    font-style: italic;
    color: var(--ft-green-800);
}

.hero-subtitle {
    font-size: 13px;
    color: var(--ft-text-muted);
}

.hero-actions {
    margin-top: 10px;
    margin-bottom: 4px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.hero-expanded {
    margin-top: 6px;
    font-size: 13px;
    color: var(--ft-text-secondary);
}

.hero-stat {
    margin-bottom: 4px;
}

.hero-quote {
    margin-top: 12px;
    font-style: italic;
    color: var(--ft-text-muted);
}

.hero-expanded-actions {
    margin-top: 12px;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
}
.hero-expanded-actions .mud-button-root {
    justify-content: flex-start;
}

/* Mini hero state */
.hero-glass.mini {
    padding: 8px 14px;
    gap: 0;
    width: auto;
    min-width: 0;
    transform: none !important;
}

.hero-mini-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    white-space: nowrap;
}

.hero-mini-name {
    font-size: 14px;
    font-weight: 500;
    font-style: italic;
    font-family: var(--ft-font-display);
    color: var(--ft-green-800);
}

/* Just an outline — no fill, no shadow. It should blend into the AppBar and
   read as a plain rounded rectangle with an icon and placeholder, not a
   separate card floating on top of the bar. */
.ft-search-wrap .mud-input-root {
    border-radius: var(--ft-radius-card) !important;
    background: transparent !important;
    box-shadow: none !important;
}
/* The visible outline lives on fieldset.mud-input-outlined-border, a SIBLING
   of .mud-input-root under the shared .mud-input wrapper — not a descendant
   of .mud-input-root. Every rule below previously targeted ".mud-input-root
   fieldset", which never matched anything, so the border silently stayed at
   MudBlazor's own default gray (#bdbdbd) regardless of what was written here. */
.ft-search-wrap .mud-input-outlined-border {
    border-radius: var(--ft-radius-card) !important;
    /* Neutral by default (matches common practice — GitHub, Linear, etc. all
       use a plain gray border at rest); the green accent is reserved for
       hover/focus below, not baked into the resting state. */
    border-color: var(--ft-line-border) !important;
}
.ft-search-wrap .mud-input:hover .mud-input-outlined-border {
    border-color: var(--ft-green-400) !important;
}
.ft-search-wrap .mud-input:focus-within .mud-input-outlined-border {
    border-color: var(--ft-green-600) !important;
    border-width: 1.5px !important;
}
[data-theme="dark"] .ft-search-wrap .mud-input-outlined-border {
    border-color: rgba(255, 255, 255, 0.16) !important;
}
[data-theme="dark"] .ft-search-wrap .mud-input-root:hover fieldset {
    border-color: var(--ft-green-400) !important;
}

[data-theme="dark"] .hero-glass {
    background: rgba(18, 20, 23, 0.35);
    border: 1px solid rgba(255, 255, 255, 0.08);
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}

.intro-overlay {
    position: fixed;
    inset: 0;
    backdrop-filter: blur(12px);
    background: rgba(100,100,100,0.22);
    display: flex;
    justify-content: center;
    z-index: 10001; /* above toolbar (1200) and hero (1250) */
    animation: introFadeIn 0.4s ease-out;
    padding: 16px;
    overflow-y: auto;
}

.intro-glass {
    position: relative;
    isolation: isolate;
    background: rgba(255,255,255,0.88);
    backdrop-filter: blur(22px) saturate(160%);
    -webkit-backdrop-filter: blur(22px) saturate(160%);
    border-radius: 20px;
    padding: 0px 36px 40px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.12);
    animation: introSlideUp 0.5s ease-out;
    /* Was centered via .intro-overlay's `align-items: center`, which — combined
       with `overflow-y: auto` on the overlay — clips the card with no way to
       scroll to the cut-off part whenever the card is taller than the viewport
       (a well-known flexbox gotcha, not something a breakpoint alone fixes).
       `margin: auto` centers when there's room but falls back to normal
       top-anchored, fully-scrollable flow the instant it doesn't fit. */
    margin: auto 0;
}

    .intro-glass::before {
        content: "";
        position: absolute;
        inset: 0;
        border-radius: inherit;
        background: radial-gradient(circle at top right, rgba(255,255,255,0.6) 0%, rgba(255,255,255,0.1) 70%);
        z-index: -1;
    }

.intro-title {
    font-size: 28px;
    font-weight: 600;
    font-style: italic;
    color: var(--ft-green-800);
    margin-bottom: 12px;
}

.intro-subtitle {
    font-size: 14px;
    color: var(--ft-gray-500);
}

.intro-btn {
    width: 100%;
    margin-top: 12px;
}

/* ── Auth card ────────────────────────────────────────────────────────────── */
.auth-card {
    /*margin-top: 30px;*/
    margin-top: 1rem;
    width: 100%;
    max-width: 440px;
    text-align: left;
}

.auth-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 6px;
}

.auth-brand-name {
    font-family: var(--ft-font-display);
    font-style: italic;
    font-size: 26px;
    font-weight: 600;
    color: var(--ft-green-800);
    letter-spacing: -0.01em;
}

.auth-google-btn {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 11px 16px;
    border: 1.5px solid #dadce0;
    border-radius: 8px;
    background: #fff;
    color: #3c4043;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.15s, box-shadow 0.15s;
    font-family: inherit;
}

.auth-google-btn:hover:not(:disabled) {
    background: #f8f9fa;
    box-shadow: 0 1px 4px rgba(0,0,0,0.12);
}

.auth-google-btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.auth-or-divider {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 18px 0 16px;
    color: var(--ft-gray-400);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.auth-or-divider::before,
.auth-or-divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--ft-line-border);
}

.auth-error {
    background: rgba(163,45,45,0.08);
    border: 1px solid rgba(163,45,45,0.2);
    color: #a32d2d;
    border-radius: 8px;
    padding: 10px 12px;
    font-size: 13px;
    margin-bottom: 12px;
}

.auth-forgot {
    text-align: right;
    margin-top: 6px;
}

.auth-forgot a {
    font-size: 12px;
    color: var(--ft-green-600);
    text-decoration: none;
}

.auth-forgot a:hover { text-decoration: underline; }

.auth-register-link {
    text-align: center;
    margin-top: 14px;
    font-size: 13px;
    color: var(--ft-gray-500);
}

.auth-register-link a {
    color: var(--ft-green-600);
    text-decoration: none;
    font-weight: 500;
}

.auth-register-link a:hover { text-decoration: underline; }

.auth-browse {
    margin-top: 18px;
    padding-top: 16px;
    border-top: 1px solid var(--ft-line-border);
    text-align: center;
}

.auth-browse button {
    background: none;
    border: none;
    font-size: 12px;
    color: var(--ft-gray-400);
    cursor: pointer;
    padding: 0;
    font-family: inherit;
    transition: color 0.15s;
}

.auth-browse button:hover { color: var(--ft-green-600); }

[data-theme="dark"] .intro-glass {
    background: rgba(18, 20, 23, 0.35);
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}

    [data-theme="dark"] .intro-glass::before {
        background: radial-gradient(circle at top right, rgba(255,255,255,0.08) 0%, rgba(255,255,255,0.02) 70%);
    }

[data-theme="dark"] .ft-auth-page::before {
    filter: blur(14px) brightness(0.28);
}

[data-theme="dark"] .auth-brand-name { color: var(--ft-green-400, #4faa78); }

[data-theme="dark"] .auth-google-btn {
    background: var(--ft-surface-card);
    border-color: var(--ft-line-border);
    color: var(--ft-text-primary);
}

[data-theme="dark"] .auth-google-btn:hover:not(:disabled) {
    background: var(--ft-surface);
}
/* ── Blazor error UI (unhandled circuit exception — small dismissible toast,
   distinct from the reconnect modal's full-screen takeover) ─────────────── */

#blazor-error-ui {
    display: none;
    position: fixed;
    top: 16px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 10500; /* above the reconnect modal's own content, below nothing else needs it */
    background: rgba(255, 251, 235, 0.92);
    backdrop-filter: blur(16px) saturate(160%);
    -webkit-backdrop-filter: blur(16px) saturate(160%);
    border: 1px solid rgba(245, 158, 11, 0.35);
    border-left: 4px solid #f59e0b;
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
    padding: 10px 16px;
    font-family: var(--ft-font-body);
    font-size: 13px;
    color: var(--ft-text-primary);
    display: flex;
    align-items: center;
    gap: 14px;
    max-width: min(420px, calc(100vw - 2rem));
}

[data-theme="dark"] #blazor-error-ui {
    background: rgba(40, 33, 14, 0.92);
    color: var(--ft-text-primary);
}

#blazor-error-ui .reload {
    color: var(--ft-green-600);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}

#blazor-error-ui .reload:hover { text-decoration: underline; }

#blazor-error-ui .dismiss {
    cursor: pointer;
    color: var(--ft-gray-400);
    font-size: 14px;
    line-height: 1;
    margin-left: auto;
}
/* ── Scrollbar (webkit) ───────────────────────────────────────────────────── */

::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background: var(--ft-line-border);
    border-radius: 3px;
}

    ::-webkit-scrollbar-thumb:hover {
        background: var(--ft-gray-300);
    }

/* ── Tree generation bands ────────────────────────────────────────────────── */

.ft-viewport {
    user-select: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    -webkit-touch-callout: none;
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    overflow: hidden;
    touch-action: none;
    background:
        radial-gradient(ellipse at 15% 28%, rgba(93, 202, 165, 0.18) 0%, transparent 48%),
        radial-gradient(ellipse at 85% 72%, rgba(29, 158, 117, 0.10) 0%, transparent 42%),
        radial-gradient(ellipse at 55% 5%,  rgba(159, 225, 203, 0.16) 0%, transparent 38%),
        linear-gradient(to bottom, var(--ft-green-50) 0%, var(--ft-green-200) 100%);
    z-index: 1;
    opacity: 0;
    transition: opacity 0.25s ease;
}

/*.ft-viewport {
    background: var(--ft-green-50);
}*/

.ft-main {
    padding: 0;
    margin: 0;
    background: none;
    min-height: 0;
}

/* ── Tree Canvas: Generation Bands ───────────────────────────────────────── */
.ft-band {
    position: absolute;
    left: 0;
    width: 100%;
    background: var(--ft-band-parents); /* or a decade-based token */
    opacity: 0.25; /* baseline; dynamic opacity still applied inline */
    pointer-events: none;
}

#ft-timeline {
    position: absolute;
    left: 0;
    top: 0;
    width: 56px;
    height: 100%;
    pointer-events: none;
    z-index: 2;
    overflow: hidden;
}

.ft-band-label {
    position: absolute;
    left: 10px;
    font-size: 10px;
    font-weight: 500;
    color: var(--ft-green-700);
    letter-spacing: 0.04em;
    font-family: var(--ft-font-body);
    pointer-events: none;
    white-space: nowrap;
    opacity: 0.65;
    line-height: 1;
}

[data-theme="dark"] .ft-band-label {
    color: var(--ft-green-300);
    opacity: 0.55;
}

.ft-band--grandparents {
    background: var(--ft-band-grandparents);
    opacity: 0.25;
}

.ft-band--parents {
    background: var(--ft-band-parents);
    opacity: 0.40;
}

.ft-band--children {
    background: var(--ft-band-children);
    opacity: 0.25;
}

.tree-viewport {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    /*background: var(--ft-surface);*/
    background: var(--ft-band-parents);
    overflow: auto;
    z-index: 0;
}

/* ── Tree Canvas: Transform Wrapper ───────────────────────────── */
#ft-transform {
    position: absolute;
    left: 0;
    top: 0;
    transform-origin: 0 0;
    will-change: transform;
    z-index: 3; /* above #ft-timeline (z-index:2) so nodes paint over year labels */
}

.ft-empty-message {
    position: absolute;
    top: 100px;
    left: 50%;
    transform: translateX(-50%);
    color: var(--ft-text-muted);
    font-size: 14px;
}

/* ── Tree Canvas: SVG Layer ───────────────────────────── */
.ft-svg {
    position: absolute;
    top: 0;
    left: 0;
    pointer-events: none;
    overflow: visible;
}


/* ── Person node ──────────────────────────────────────────────────────────── */

.person-node {
    position: absolute;
    border-radius: 50%;
    background: var(--ft-surface-card);
    border: 1.2px solid var(--ft-gray-400);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    overflow: hidden; /* clips backdrop-filter to the circular shape */
    box-shadow: var(--ft-shadow-sm);
    transition: transform .18s ease, box-shadow .18s ease;
}

/* Glassy — non-focus nodes are semi-transparent so year labels show through when overlapped */
.person-node:not(.person-node--focus) {
    backdrop-filter: blur(3px) saturate(1.2);
    -webkit-backdrop-filter: blur(3px) saturate(1.2);
}

    .person-node:hover {
        transform: scale(1.08) translateY(-2px);
        box-shadow: var(--ft-shadow-md), 0 0 0 1.5px var(--ft-green-200);
        z-index: 2;
    }

.person-node--focus {
    background: var(--ft-focus-fill);
    color: var(--ft-focus-text);
    border-color: var(--ft-focus-border);
    border-width: 2px;
    box-shadow: 0 0 0 4px var(--ft-focus-ring), 0 4px 18px rgba(29, 158, 117, 0.22);
    animation: ft-focus-pulse 2.2s ease-in-out infinite;
    z-index: 3;
}

.person-node__initials {
    font-weight: 600;
    font-size: 13px;
    letter-spacing: 0.03em;
    pointer-events: none;
    user-select: none;
}
/* ── Person label (name + year below each node) ───────────────────────────── */

.person-label {
    position: absolute;
    width: 88px;
    text-align: center;
    pointer-events: none;
    user-select: none;
    font-family: var(--ft-font-body);
}

.person-label--name {
    font-size: 11px;
    font-weight: 500;
    color: var(--ft-text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.person-label--year {
    font-size: 10px;
    color: var(--ft-text-muted);
    margin-top: 1px;
}
/* ── Toolbar ───────────────────────────────────────────────────────────── */
#toolbar-container {
    position: fixed;
    top: 12px;
    right: 12px;
    z-index: 10;
    cursor: move;
    transition: top 0.05s linear, left 0.05s linear;
}

    #toolbar-container.dragging {
        box-shadow: 0 4px 12px rgba(0,0,0,0.2);
    }

.mud-appbar.appbar-background,
.mud-appbar {
    background: rgba(245,245,245,0.4) !important;
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border-bottom: none;
    box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12);
    position: relative;
}

.mud-appbar::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 1.5px;
    background: linear-gradient(
        90deg,
        transparent 0%,
        var(--ft-green-200) 20%,
        var(--ft-green-400) 50%,
        var(--ft-green-200) 80%,
        transparent 100%
    );
    pointer-events: none;
}

/* Logo fade-in on load */
.mud-appbar img {
    animation: ft-logo-in 0.7s ease-out both;
}

@keyframes ft-logo-in {
    from { opacity: 0; transform: translateX(-8px); }
    to   { opacity: 1; transform: translateX(0); }
}

[data-theme="dark"] .mud-appbar {
    background: rgba(18, 20, 23, 0.35) !important;
    backdrop-filter: blur(24px);
    -webkit-backdrop-filter: blur(24px);
    border-bottom: 0.5px solid rgba(255, 255, 255, 0.08);
}

/* The light-green gradient underline reads as a subtle accent against the
   light-mode bg, but the same colors glow as a stray green blob against the
   near-black dark-mode bg — dim it so it stays a quiet accent in dark mode. */
[data-theme="dark"] .mud-appbar::after {
    opacity: 0.35;
}

/* ── Person detail drawer (glass, matches appbar/hero/toolbar recipe) ───────── */
.ft-person-drawer.mud-drawer {
    background: rgba(247,245,240,0.78) !important;
    backdrop-filter: blur(22px) saturate(160%);
    -webkit-backdrop-filter: blur(22px) saturate(160%);
    border-left: 0.5px solid rgba(255,255,255,0.3);
    box-shadow: -8px 0 32px rgba(0, 0, 0, 0.12);
}

[data-theme="dark"] .ft-person-drawer.mud-drawer {
    background: rgba(18, 20, 23, 0.82) !important;
    border-left: 0.5px solid rgba(255, 255, 255, 0.08);
    box-shadow: -8px 0 32px rgba(0, 0, 0, 0.4);
}

/* ── Auth pages (Register, Login, Forgot password) ───────────────────────────── */

.ft-auth-page {
    position: relative;
    min-height: 100vh;
    display: flex;
    justify-content: center;
    padding: 32px 16px;
    overflow-x: hidden; /* clips the intentionally-oversized blurred ::before background */
    overflow-y: auto;   /* was `overflow: hidden` — combined with align-items:center this
                            clipped the card with NO way to scroll to the cut-off part
                            whenever it was taller than the viewport (worse than
                            .intro-overlay's version of the same bug, which at least had
                            overflow-y:auto). Vertical centering now comes from
                            .intro-glass's own `margin: auto 0`, which degrades to normal
                            top-anchored scrollable flow instead of clipping. */
}

.ft-auth-page::before {
    content: '';
    position: absolute;
    inset: -60px;
    background: url('/images/tree-canvas-bg.svg') center / cover no-repeat;
    filter: blur(14px);
    transform: scale(1.06);
    z-index: 0;
}

.ft-auth-page > * {
    position: relative;
    z-index: 1;
}

.auth-password-hint {
    font-size: 11px;
    color: var(--ft-gray-400);
    margin-top: 4px;
    margin-left: 2px;
}

.auth-terms {
    font-size: 11px;
    color: var(--ft-gray-400);
    text-align: center;
    margin-top: 12px;
    line-height: 1.5;
}

.auth-terms a {
    color: var(--ft-green-600);
    text-decoration: none;
}

.auth-terms a:hover { text-decoration: underline; }

/* ── Form pages (Add / Edit person) ──────────────────────────────────────────── */

.ft-form-page {
    min-height: 100vh;
    background: linear-gradient(180deg, var(--ft-green-50) 0%, var(--ft-surface) 80%);
    padding-bottom: 60px;
    /* pull up to cover the MudMainContent appbar-padding gap */
    margin-top: -64px;
    padding-top: 64px;
}

.ft-field-label {
    font-size: 12px;
    font-weight: 600;
    color: var(--ft-text-secondary);
    margin-bottom: 4px;
    line-height: 1.4;
    letter-spacing: 0.02em;
}

.ft-required {
    color: var(--mud-palette-error, #f44336);
}

/* ── Person List Page ───────────────────────────────────────────────────────────── */

.people-table .mud-table-row:hover {
    background: var(--ft-surface-card);
    box-shadow: var(--ft-shadow-md);
    transition: all 0.25s ease;
}

.people-table .mud-table-cell {
    padding: 12px 16px;
}

.search-field {
    min-width: 220px;
    border-radius: 8px;
    background: rgba(245,245,245,0.4);
    backdrop-filter: blur(8px);
}


/* ── Animations ───────────────────────────────────────────────────────────── */

@keyframes ft-focus-pulse {
    0%, 100% {
        box-shadow: 0 0 0 4px var(--ft-focus-ring), 0 4px 18px rgba(29, 158, 117, 0.22);
    }
    50% {
        box-shadow: 0 0 0 10px color-mix(in srgb, var(--ft-focus-ring) 30%, transparent),
                    0 6px 28px rgba(29, 158, 117, 0.38);
    }
}

/* ── SVG connector polish ────────────────────────────────────────────────── */
.ft-svg path {
    stroke-width: 1.5px;
}

/* ── Person label stagger-fade on load ───────────────────────────────────── */
.person-label {
    animation: ft-label-in 0.45s ease-out both;
}

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

/* ── Family Facts Banner ─────────────────────────────────────────────────── */
.facts-banner {
    position: fixed;
    top: 72px;
    left: 0;
    right: 0;
    z-index: 1100;
    height: 34px;
    display: flex;
    align-items: center;
    padding: 0 20px;
    /* Fading the tint out toward the bottom (rather than a flat fill ending
       in a hard border) lets the canvas's own green gradient show through
       gradually — a flat edge here reads as a visible seam where two
       differently-toned translucent panels butt up against each other. */
    background: linear-gradient(to bottom, rgba(212, 240, 228, 0.45) 0%, rgba(212, 240, 228, 0.45) 60%, rgba(212, 240, 228, 0.1) 100%);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border-bottom: none;
    animation: ft-banner-in 0.45s cubic-bezier(0.22, 1, 0.36, 1) both;
}

.facts-banner--leave {
    animation: ft-banner-out 0.3s ease-in forwards !important;
}

@keyframes ft-banner-in {
    from { transform: translateY(-100%); opacity: 0; }
    to   { transform: translateY(0);     opacity: 1; }
}

@keyframes ft-banner-out {
    from { transform: translateY(0);     opacity: 1; max-height: 34px; }
    to   { transform: translateY(-100%); opacity: 0; max-height: 0; }
}

.facts-banner-inner {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    max-width: 1000px;
    margin: 0 auto;
}

.facts-banner-text {
    flex: 1;
    font-size: 12.5px;
    color: var(--ft-green-800);
    font-family: var(--ft-font-body);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: opacity 0.32s ease;
}

.facts-banner-text--fading {
    opacity: 0;
}

.facts-banner-close {
    flex-shrink: 0;
    background: none;
    border: none;
    cursor: pointer;
    color: var(--ft-green-600);
    font-size: 18px;
    line-height: 1;
    padding: 0 2px;
    opacity: 0.55;
    transition: opacity 0.15s ease;
}
.facts-banner-close:hover { opacity: 1; }

[data-theme="dark"] .facts-banner {
    background: linear-gradient(to bottom, rgba(18, 40, 28, 0.72) 0%, rgba(18, 40, 28, 0.72) 60%, rgba(18, 40, 28, 0.15) 100%);
}
[data-theme="dark"] .facts-banner-text {
    color: var(--ft-green-200);
}

@keyframes ft-dash-spin {
    to {
        stroke-dashoffset: -48;
    }
}

@keyframes fadeInHero {
    to {
        opacity: 1;
    }
}

@keyframes heroFadeIn {
    from {
        opacity: 0;
        transform: translateY(-6px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes introFadeIn {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

@keyframes introSlideUp {
    from {
        opacity: 0;
        transform: translateY(12px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}
/* ── Media upload zone ────────────────────────────────────────────────────── */

.media-upload-zone {
    transition: border-color .15s ease, background .15s ease, box-shadow .15s ease;
}

.media-upload-zone--active {
    box-shadow: 0 0 0 3px var(--ft-green-200);
}
/* ── Draggable panels ─────────────────────────────────────────────────────── */

.ft-dragging {
    opacity: 0.92;
    box-shadow: 0 12px 40px rgba(0,0,0,0.22) !important;
}

/* Applied to <html> while any drag is active — kills all text selection */
html.ft-no-select,
html.ft-no-select * {
    user-select: none !important;
    -webkit-user-select: none !important;
    cursor: grabbing !important;
}

/* AppBar floating (detached from top) */
.ft-appbar-floating.mud-appbar {
    border-radius: 12px !important;
    border: 0.5px solid rgba(255,255,255,0.3) !important;
    box-shadow: 0 8px 32px rgba(0,0,0,0.18) !important;
}


/* ── Toolbar mini pill (collapsed state) ─────────────────────────────────── */
.ft-toolbar-mini {
    display: flex;
    align-items: center;
    gap: 2px;
    padding: 4px 6px;
    background: rgba(245, 245, 245, 0.35);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid rgba(255, 255, 255, 0.20);
    border-radius: 12px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.10);
    transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Tactile press feedback — the pill is the trigger for the nav/toolbar
   expansion, so it should feel like something is about to pop open. */
.ft-toolbar-mini:active { transform: scale(0.90); }

/* Reads as a small caret affordance rather than a full icon button — same
   touch-target size (44px on mobile via #ft-toolbar .mud-icon-button) but a
   visually smaller glyph, distinct from the pill's primary Center action. */
.ft-toolbar-caret .mud-icon-root {
    font-size: 18px;
}

/* Shrink the full toolbar on short viewports (landscape mobile) so it never
   needs more vertical room than is available. display:none here would be
   worse than clipping — the mini pill only exists in the DOM when the
   component's _collapsed flag is true, so hiding .ft-toolbar-full leaves
   nothing in its place at all. */
@media (max-height: 520px) {
    #ft-toolbar { bottom: 8px; left: 8px; }
    .ft-toolbar-full { padding: 4px !important; gap: 2px; }
    .ft-toolbar-full .mud-button-root { min-height: 32px; padding: 2px 10px; }
}

/* Toolbar drag handle */
.ft-toolbar-grip {
    display: flex;
    align-items: center;
    padding: 0 2px;
    cursor: grab;
    opacity: 0.35;
    transition: opacity 0.15s;
    touch-action: none; /* let Pointer Events own this element */
}

.ft-toolbar-grip:hover { opacity: 0.7; }
.ft-toolbar-grip:active { cursor: grabbing; }

/* MudBlazor default popover/tooltip z-index (~1300+) naturally sits above hero-overlay (1250) */

/* ── MudBlazor palette overrides ──────────────────────────────────────────── */
/*
   Forward our tokens into MudBlazor's own palette vars so MudBlazor
   components (Snackbar, Dialog, Drawer, etc.) also respond to dark mode.
   These live under :root — no [data-theme] block needed because they
   reference --ft-* vars which already switch on theme change.
*/

:root {
    --mud-palette-surface: var(--ft-surface);
    --mud-palette-background: var(--ft-surface);
    --mud-palette-background-grey: var(--ft-surface-card);
    --mud-palette-table-lines: var(--ft-line-border);
    --mud-palette-divider: var(--ft-line-border);
    --mud-palette-text-primary: var(--ft-text-primary);
    --mud-palette-text-secondary: var(--ft-text-secondary);
    --mud-palette-primary: var(--ft-green-600);
    --mud-palette-primary-darken: var(--ft-green-700);
    --mud-palette-primary-lighten: var(--ft-green-400);
    --mud-palette-primary-text: #ffffff;
    --mud-palette-success: var(--ft-green-600);
    --mud-palette-overlay-dark: rgba(0, 0, 0, .5);
    --mud-palette-drawer-background: var(--ft-surface-card);
    --mud-palette-appbar-background: var(--ft-surface-card);
}

/* ═══════════════════════════════════════════════════════════════════════════
   CUSTOM TOAST NOTIFICATIONS
   ═══════════════════════════════════════════════════════════════════════════ */

.ft-toast-container {
    position: fixed;
    bottom: 1.5rem;
    right: 1.5rem;
    z-index: 9999;
    display: flex;
    flex-direction: column-reverse;
    gap: 0.5rem;
    width: min(380px, calc(100vw - 2rem));
    pointer-events: none;
}

.ft-toast {
    pointer-events: all;
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 13px 14px 13px 0;
    background: var(--ft-surface-elevated);
    border: 1px solid var(--ft-line-border);
    border-radius: 12px;
    box-shadow: 0 8px 24px rgba(0,0,0,.1), 0 2px 6px rgba(0,0,0,.06);
    position: relative;
    overflow: hidden;
    animation: ft-toast-in 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

.ft-toast--out {
    animation: ft-toast-out 0.26s ease-in both !important;
}

@keyframes ft-toast-in {
    from { transform: translateX(calc(100% + 2rem)); opacity: 0; }
    to   { transform: translateX(0);                  opacity: 1; }
}

@keyframes ft-toast-out {
    from { transform: translateX(0);                  opacity: 1; }
    to   { transform: translateX(calc(100% + 2rem)); opacity: 0; }
}

/* ── Blazor reconnect modal ───────────────────────────────────────────────── */
#components-reconnect-modal {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 10000;
    background: rgba(20, 30, 24, 0.45);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    align-items: center;
    justify-content: center;
    font-family: var(--ft-font-body);
}

#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected {
    display: flex;
    animation: introFadeIn 0.3s ease-out;
}

.ft-reconnect-card {
    position: relative;
    isolation: isolate;
    background: rgba(255, 255, 255, 0.9);
    backdrop-filter: blur(22px) saturate(160%);
    -webkit-backdrop-filter: blur(22px) saturate(160%);
    border-radius: 20px;
    padding: 36px 40px 40px;
    text-align: center;
    box-shadow: 0 16px 48px rgba(0, 0, 0, 0.22);
    min-width: 280px;
    animation: introSlideUp 0.35s ease-out;
}

[data-theme="dark"] .ft-reconnect-card {
    background: rgba(18, 20, 23, 0.85);
    box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
}

.ft-reconnect-logo {
    display: flex;
    justify-content: center;
    margin-bottom: 16px;
    user-select: none;
}

.ft-reconnect-logo img {
    width: 140px;
    height: auto;
}

.ft-reconnect-icon {
    font-size: 32px;
    line-height: 1;
}

.ft-reconnect-body {
    display: none;
    flex-direction: column;
    align-items: center;
    gap: 10px;
}

#components-reconnect-modal.components-reconnect-show     .ft-reconnect-body--reconnecting,
#components-reconnect-modal.components-reconnect-failed   .ft-reconnect-body--failed,
#components-reconnect-modal.components-reconnect-rejected .ft-reconnect-body--rejected {
    display: flex;
}

.ft-reconnect-body p {
    margin: 0;
    color: var(--ft-text-primary);
    font-size: 16px;
    font-weight: 600;
    font-family: var(--ft-font-display);
}

.ft-reconnect-sub {
    font-size: 13px !important;
    font-weight: 400 !important;
    font-family: var(--ft-font-body) !important;
    color: var(--ft-text-muted) !important;
    margin-top: -4px !important;
}

.ft-reconnect-btn {
    margin-top: 6px;
    padding: 9px 22px;
    border: none;
    border-radius: 8px;
    background: var(--ft-green-600);
    color: #fff;
    font-family: var(--ft-font-body);
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.15s;
}

.ft-reconnect-btn:hover { background: var(--ft-green-700); }

.ft-reconnect-btn--ghost {
    background: transparent;
    color: var(--ft-text-muted);
    border: 1px solid var(--ft-line-border);
    margin-top: 0;
}

.ft-reconnect-btn--ghost:hover {
    background: rgba(0, 0, 0, 0.04);
    color: var(--ft-text-secondary);
}

.ft-reconnect-spinner {
    width: 32px;
    height: 32px;
    border: 3px solid var(--ft-green-100);
    border-top-color: var(--ft-green-600);
    border-radius: 50%;
    animation: ft-spin 0.8s linear infinite;
}

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

/* ── Shared error-state card (ErrorStateCard.razor) — /Error page,
   NotFound/NotAuthorized router fallbacks ───────────────────────────────── */
.ft-error-card {
    max-width: 420px;
    text-align: center;
}

.ft-error-card-logo {
    display: flex;
    justify-content: center;
    margin-bottom: 12px;
    user-select: none;
}

.ft-error-card-logo img {
    width: 55%;
    max-width: 160px;
    height: auto;
}

.ft-error-card .ft-error-card-icon {
    font-size: 40px !important;
    width: 40px;
    height: 40px;
    color: var(--ft-green-500);
    margin-bottom: 4px;
}

.ft-error-card-title {
    font-size: 20px;
    font-weight: 600;
    font-family: var(--ft-font-display);
    color: var(--ft-text-primary);
    margin-bottom: 8px;
}

.ft-error-card-message {
    font-size: 13px;
    color: var(--ft-text-muted);
    line-height: 1.6;
}

/* Centers ErrorStateCard within MainLayout's normal content flow (NotFound /
   NotAuthorized) rather than as a full-page takeover like .ft-auth-page. */
.ft-inline-error-wrap {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: calc(100vh - 64px); /* viewport minus the AppBar */
    padding: 32px 16px;
}

.ft-toast--success { border-left: 4px solid #22c55e; padding-left: 14px; }
.ft-toast--error   { border-left: 4px solid #ef4444; padding-left: 14px; }
.ft-toast--warning { border-left: 4px solid #f59e0b; padding-left: 14px; }
.ft-toast--info    { border-left: 4px solid #3b82f6; padding-left: 14px; }

.ft-toast-icon {
    flex-shrink: 0;
    width: 18px;
    height: 18px;
    margin-top: 2px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.ft-toast--success .ft-toast-icon { color: #22c55e; }
.ft-toast--error   .ft-toast-icon { color: #ef4444; }
.ft-toast--warning .ft-toast-icon { color: #f59e0b; }
.ft-toast--info    .ft-toast-icon { color: #3b82f6; }

.ft-toast-body { flex: 1; min-width: 0; }

.ft-toast-title {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--ft-text-primary);
    line-height: 1.3;
    margin-bottom: 2px;
}

.ft-toast-msg {
    font-size: 0.8125rem;
    color: var(--ft-text-secondary);
    line-height: 1.45;
}

.ft-toast-msg--solo {
    font-size: 0.875rem;
    color: var(--ft-text-primary);
}

/* ── AppBar logo ──────────────────────────────────────────────────────────── */
.ft-appbar-logo {
    flex-shrink: 0;
    cursor: pointer;
    user-select: none;
    display: flex;
    align-items: center;
    margin-right: 12px;
}

.ft-appbar-logo img {
    height: 52px;
    display: block;
}

/* The logo artwork has a tree+roots flourish above/below the wordmark, so the
   readable "ArborKin" text is only ~28% of the image's own height — sizing by
   height alone leaves the text looking tiny. The dark, near-black wordmark
   also has almost no contrast against the dark-mode appbar, so recolor the
   whole mark to a clean white silhouette there instead of trying to keep the
   green (inverting would shift green to an off-brand purple). */
[data-theme="dark"] .ft-appbar-logo img {
    filter: brightness(0) invert(1);
    opacity: 0.92;
}

/* ── AppBar search (desktop only) ────────────────────────────────────────── */
.ft-search-wrap {
    flex: 1;
    max-width: 420px;
}

/* Placeholder and input text color (pill background/border are set by the
   .mud-input-root rules above, which are theme-aware — don't duplicate here) */
.ft-search-wrap .mud-input-slot input::placeholder { color: var(--ft-text-secondary); }
.ft-search-wrap .mud-input-slot input { color: var(--ft-text-primary); }

/* ── AppBar family title (mobile only — replaces search) ─────────────────── */
.ft-appbar-family-title {
    display: none;
    flex: 1;
    justify-content: center;
    align-items: center;
    min-width: 0;
    padding: 0 8px;
    cursor: pointer;
}

.ft-appbar-user-id {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.ft-family-title-name {
    font-size: 15px;
    font-weight: 600;
    color: var(--ft-text-primary);
    letter-spacing: 0.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    /* Flex items default to min-width:auto (their own content width), which
       blocks shrinking below the full name — the parent .ft-appbar-user-id
       already has min-width:0, but that's wasted unless this item can shrink
       too, so text-overflow:ellipsis never actually triggers and the name
       just overflows past the AppBar's centered identity block on mobile. */
    min-width: 0;
    flex: 1 1 auto;
}

/* Signals the identity block is tappable (opens MobileControlPanel) — same
   small-caret treatment as the toolbar's own expand trigger. */
.ft-appbar-caret {
    font-size: 18px !important;
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    color: var(--ft-gray-400);
}

/* ── Mobile control panel (person info + zoom/center/reset; drops below
   AppBar when the identity block or the canvas trigger is tapped) ────────── */
.ft-mobile-panel {
    display: none; /* shown only on mobile via media query */
    position: fixed;
    top: 56px;
    left: 0;
    right: 0;
    background: rgba(236, 246, 236, 0.88);
    backdrop-filter: blur(24px) saturate(1.6);
    -webkit-backdrop-filter: blur(24px) saturate(1.6);
    border-bottom: 0.5px solid rgba(255, 255, 255, 0.45);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.10);
    padding: 16px 20px 18px;
    z-index: 1299;
    transform-origin: top center;
    animation: ft-nav-drop 0.38s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

[data-theme="dark"] .ft-mobile-panel {
    background: rgba(14, 24, 14, 0.88);
    border-bottom-color: rgba(255, 255, 255, 0.08);
}

.ft-mobile-panel-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 1298;
    animation: introFadeIn 0.2s ease-out;
}

.ft-mobile-panel-name {
    font-size: 16px;
    font-weight: 600;
    color: var(--ft-green-800);
    font-family: var(--ft-font-display);
    margin-bottom: 4px;
}

[data-theme="dark"] .ft-mobile-panel-name { color: var(--ft-green-200); }

.ft-mobile-panel-dates {
    font-size: 12px;
    color: var(--ft-gray-400);
    margin-bottom: 10px;
}

.ft-mobile-panel-stats {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--ft-text-secondary);
    margin-bottom: 14px;
}

.ft-mobile-panel-dot { color: var(--ft-gray-300); }

.ft-mobile-panel-actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}

.ft-mobile-panel-btn {
    position: relative;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    background: linear-gradient(135deg, var(--ft-green-500) 0%, var(--ft-green-700) 100%);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 10px;
    padding: 10px 12px;
    min-height: 44px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    box-shadow: 0 3px 10px rgba(15, 71, 44, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.25);
    transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s;
}

/* Glossy top sheen so the gradient reads as glass rather than flat paint. */
.ft-mobile-panel-btn::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.32) 0%, rgba(255, 255, 255, 0) 55%);
    pointer-events: none;
}

/* Each action gets a slightly different hue instead of four identical flat-
   green tiles — still clearly one family, just not visually monotonous. */
.ft-mobile-panel-actions .ft-mobile-panel-btn:nth-child(1) { background: linear-gradient(135deg, var(--ft-green-400) 0%, var(--ft-green-600) 100%); }
.ft-mobile-panel-actions .ft-mobile-panel-btn:nth-child(2) { background: linear-gradient(135deg, var(--ft-green-500) 0%, var(--ft-green-700) 100%); }
.ft-mobile-panel-actions .ft-mobile-panel-btn:nth-child(3) { background: linear-gradient(135deg, var(--ft-green-600) 0%, var(--ft-green-800) 100%); }
.ft-mobile-panel-actions .ft-mobile-panel-btn:nth-child(4) { background: linear-gradient(135deg, var(--ft-green-500) 0%, var(--ft-green-800) 100%); }

.ft-mobile-panel-btn:active {
    transform: scale(0.95);
    box-shadow: 0 1px 4px rgba(15, 71, 44, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.15);
}

/* Secondary treatment for the app-level toggle below the canvas-action grid —
   frosted glass rather than the actions' solid gradient, so "this is a
   setting, not a canvas command" reads at a glance. Text uses the theme-aware
   --ft-text-primary token (not a manually-swapped green shade) specifically
   because this codebase's green-NNN tokens invert their light/dark meaning
   between themes — --ft-green-200 is a pale tint in the light palette but a
   dark accent in the dark palette, so hand-picking it for dark-mode text
   produced illegible dark-on-dark text. --ft-text-primary already resolves
   correctly in both themes. */
.ft-mobile-panel-btn--wide {
    width: 100%;
    margin-top: 8px;
    background: var(--ft-surface-card);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    color: var(--ft-text-primary);
    border: 1px solid var(--ft-line-border);
    box-shadow: none;
}

.ft-mobile-panel-btn--wide::before { display: none; }

.ft-mobile-panel-btn--wide:active {
    transform: scale(0.98);
    background: var(--ft-green-50);
}

[data-theme="dark"] .ft-mobile-panel-btn--wide:active { background: rgba(255, 255, 255, 0.06); }

/* ── Mobile nav panel (custom overlay, no MudDrawer) ─────────────────────────
   Drops down from below the AppBar and covers roughly the top half of the
   screen — the common mobile hamburger pattern — rather than a full-height
   side drawer, which reads more like a desktop-app off-canvas panel than a
   mobile one. ─────────────────────────────────────────────────────────────── */
.ft-mobile-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(20, 30, 24, 0.4);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    z-index: 19999;
    animation: introFadeIn 0.2s ease-out;
}

.ft-mobile-nav {
    position: fixed;
    isolation: isolate;
    top: 56px;
    left: 0;
    right: 0;
    max-height: 55vh;
    background: rgba(255, 255, 255, 0.58);
    backdrop-filter: blur(29px) saturate(190%);
    -webkit-backdrop-filter: blur(29px) saturate(190%);
    border-bottom: 0.5px solid rgba(255, 255, 255, 0.5);
    border-radius: 0 0 20px 20px;
    z-index: 20000;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    box-shadow: 0 16px 40px rgba(0, 0, 0, 0.18);
    transform-origin: top center;
    animation: ft-nav-drop 0.38s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

.ft-mobile-nav::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    /* Fading fully to 100% (rather than plateauing at a flat 0.08 past the
       70% mark) removes the visible ring where the gradient's steep early
       falloff meets its flat tail — that boundary was landing mid-list and
       reading as a banding seam across whichever row happened to sit there. */
    background: radial-gradient(circle at top left, rgba(255, 255, 255, 0.32) 0%, rgba(255, 255, 255, 0.04) 100%);
}

[data-theme="dark"] .ft-mobile-nav {
    background: rgba(18, 20, 23, 0.52);
    border-bottom-color: rgba(255, 255, 255, 0.1);
}

[data-theme="dark"] .ft-mobile-nav::before {
    background: radial-gradient(circle at top left, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.02) 100%);
}

/* Slight overshoot-then-settle "unfold" rather than a flat slide — same
   spring bezier as the toast-in animation, reused here for a consistent
   "delight" feel across the app's pop-in surfaces. */
@keyframes ft-nav-drop {
    0%   { opacity: 0; transform: translateY(-16px) scaleY(0.92); }
    60%  { opacity: 1; transform: translateY(2px) scaleY(1.01); }
    100% { opacity: 1; transform: translateY(0) scaleY(1); }
}

/* Drawer/panel items themselves cascade in slightly after the shell settles,
   rather than all popping in at once with their container. */
@keyframes ft-item-in {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ── Mobile nav drawer ────────────────────────────────────────────────────── */
.ft-drawer-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    padding: 14px 16px 8px;
}

.ft-drawer-username {
    font-size: 13px;
    font-weight: 600;
    color: var(--ft-text-primary);
    line-height: 1.3;
}

.ft-drawer-role {
    font-size: 11px;
    color: var(--ft-gray-400);
    margin-top: 2px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.ft-drawer-close {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 18px;
    color: var(--ft-gray-500);
    line-height: 1;
    padding: 4px 6px;
    border-radius: 6px;
    flex-shrink: 0;
    margin-left: 8px;
}

.ft-drawer-close:hover { background: var(--ft-green-50); }

.ft-drawer-divider {
    height: 0.5px;
    background: var(--ft-line-border);
    margin: 0;
}

.ft-drawer-nav {
    padding: 2px 0;
}

.ft-drawer-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 9px 20px;
    font-size: 14px;
    color: var(--ft-text-primary);
    cursor: pointer;
    user-select: none;
    transition: background 0.1s;
    animation: ft-item-in 0.3s ease-out both;
}

/* Cascade each row in slightly after the last — a plain instant list read
   as static; each .ft-drawer-nav group restarts its own 1-5 count so the
   header/theme-toggle rows in the second nav block stagger the same way. */
.ft-drawer-item:nth-child(1) { animation-delay: 0.06s; }
.ft-drawer-item:nth-child(2) { animation-delay: 0.10s; }
.ft-drawer-item:nth-child(3) { animation-delay: 0.14s; }
.ft-drawer-item:nth-child(4) { animation-delay: 0.18s; }
.ft-drawer-item:nth-child(5) { animation-delay: 0.22s; }

.ft-drawer-item:hover  { background: var(--ft-green-50); }
.ft-drawer-item:active { background: var(--ft-green-100); }

.ft-drawer-icon { font-size: 18px !important; color: var(--ft-gray-400); flex-shrink: 0; }

.ft-drawer-item--signout { color: var(--ft-gray-500); }

/* ── AppBar responsive nav ────────────────────────────────────────────────── */
.ft-nav-desktop {
    display: flex;
    align-items: center;
}

.ft-nav-mobile {
    display: none;
    align-items: center;
}

/* Rotating the button itself (rather than just swapping the glyph) reads as
   a hamburger-morphing-to-X even though it's actually two distinct Material
   icons underneath — the rotation sells the transition. */
.ft-hamburger-btn { transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1); }
.ft-hamburger-btn--open { transform: rotate(90deg); }

/* Tree scroll lock is now injected by TreeLayout.razor via <HeadContent>. */

/* ── Admin nav rail (desktop) ─────────────────────────────────────────────── */
/* MudTabs Position="Left" gives a vertical tab list for free, but its default
   tab styling (centered, fixed-width-100%) is built for a horizontal strip —
   left-align the icon+label and give the rail a fixed column width instead of
   stretching to fill whatever space MudTabs hands it. */
.ft-admin-tabs .mud-tabs-tabbar { width: 200px; flex-shrink: 0; }
.ft-admin-tabs .mud-tab { justify-content: flex-start; text-align: left; }
.ft-admin-nav-row { cursor: pointer; border-radius: 8px; transition: background-color 0.1s; }
.ft-admin-nav-row:hover { background: var(--ft-green-50); }

/* ── Mobile baseline (≤ 768px) ────────────────────────────────────────────── */
@media (max-width: 768px) {

    /* Kill horizontal overflow caused by off-screen drag positions */
    html, body { overflow-x: hidden; }

    /* Hero overlay not yet adapted for mobile */
    .hero-overlay { display: none !important; }
    .facts-banner { display: none !important; }

    /* Canvas fills the true visible viewport (accounts for mobile address bar) */
    .ft-viewport {
        height: 100dvh;
        width: 100dvw;
    }

    /* Drawer goes full-width on small screens. MudBlazor's closed-state offset
       is calculated as `right: -1 * var(--mud-drawer-width)`, so overriding the
       rendered `width` alone leaves that calc using the original 300px — only
       shifting the drawer off-screen by 300px while it's actually 100% wide,
       leaving the remainder visible as a blank box. Override the variable
       MudBlazor itself reads so open and closed stay in sync. */
    .ft-person-drawer.mud-drawer { --mud-drawer-width: 100vw !important; }

    /* Swap desktop nav for hamburger */
    .ft-nav-desktop { display: none; }
    .ft-nav-mobile  { display: flex; }

    /* Logo flush to the left edge — the toolbar's own left gutter is removed
       below, so the logo needs no extra offset on top of that (a stacked
       negative margin here previously pushed it past the edge and clipped it). */
    .mud-toolbar,
    .mud-toolbar-appbar,
    .mud-toolbar-gutters { padding-left: 0 !important; }
    .mud-appbar { padding-left: 0 !important; }
    .ft-appbar-logo { margin: 0 12px 0 8px; padding: 0; }

    /* Search is desktop-only; show user identity instead */
    .ft-search-wrap         { display: none; }
    .ft-appbar-family-title { display: flex; }

    /* This MudSpacer normally pushes desktop nav icons to the far right; on
       mobile it also has flex:1, so it eats half the space meant for centering
       .ft-appbar-family-title — splitting the bar in two instead of centering
       across the whole logo-to-hamburger span. Remove it from the flex flow. */
    .ft-appbar-spacer { display: none; }

    /* Force the toolbar's remaining 3 visible children (logo, identity block,
       hamburger — search/spacer/desktop-nav are all display:none above) into
       a strict 3-column grid instead of relying on flex:1 balance between just
       one of the two side elements. Flex-shrink edge cases (an unbounded
       min-content width on the identity block's name text) could otherwise
       still push the hamburger off-screen or leave the name flush to one side
       instead of centered — grid's auto/1fr/auto columns can't overflow the
       container the way flex items can, so this is deterministic regardless
       of name length or viewport width. */
    #ft-appbar .mud-toolbar {
        display: grid;
        grid-template-columns: auto 1fr auto;
        align-items: center;
        column-gap: 8px;
    }

    .ft-appbar-family-title { min-width: 0; }

    /* Mobile control panel and its tap-away backdrop */
    .ft-mobile-panel         { display: block; }
    .ft-mobile-panel-backdrop { display: block; }

    /* Toolbar: anchor at bottom-left, touch-friendly button sizes. On mobile
       the toolbar only ever renders as the collapsed mini pill (tapping its
       expand arrow opens .ft-mobile-panel instead of the inline full toolbar
       — see CustomToolbar's ForceCollapsed handling), so top/right are forced
       to auto here too: a stray inline position from a previously-restored
       desktop drag position must never leak through the async viewport-width
       JS round trip that drives ForceCollapsed. */
    #ft-toolbar {
        bottom: 16px !important;
        left: 12px !important;
        top: auto !important;
        right: auto !important;
    }
    #ft-toolbar .mud-icon-button { min-width: 44px; min-height: 44px; }

    /* The mini pill's desktop styling (near-white glass at 35% opacity, no
       border weight) reads fine against a MudPaper page background, but on
       mobile it sits directly on the pale-green canvas — nearly the same
       luminosity, so the pill all but disappears. Raise contrast here only. */
    .ft-toolbar-mini {
        padding: 6px 8px;
        background: rgba(255, 255, 255, 0.85);
        border: 1px solid rgba(0, 0, 0, 0.08);
        box-shadow: 0 4px 16px rgba(0, 0, 0, 0.22);
    }

    [data-theme="dark"] .ft-toolbar-mini {
        background: rgba(28, 32, 28, 0.9);
        border-color: rgba(255, 255, 255, 0.12);
    }

    /* Admin nav rail: revert to the original horizontal scrolling tab strip at
       mobile width rather than introducing a second off-canvas drawer pattern
       alongside the main AppBar's hamburger — this is the same layout the page
       already used (and still works) before the desktop rail was added. */
    .ft-admin-tabs .mud-tabs-vertical { flex-direction: column !important; }
    .ft-admin-tabs .mud-tabs-tabbar { width: 100% !important; }
    .ft-admin-tabs .mud-tabs-tabbar-inner { flex-direction: row !important; }
    .ft-admin-tabs .mud-tab { justify-content: center; text-align: center; }

    /* ── Auth pages/overlay (Login, Register, Forgot/Reset password, Story respond) ──
       Vertical centering via .intro-glass's `margin: auto 0` works well on desktop,
       where the card reliably fits with room to spare — but on phones the card is a
       much larger fraction of viewport height, so centering flips unpredictably
       between "genuinely centered" and "flush against the top" depending on exact
       content length, which reads as broken rather than responsive. Mobile drops
       centering entirely in favor of one consistent top-anchored, scrollable layout —
       also just the standard mobile auth-page pattern — plus a smaller header so the
       card is short enough to rarely need that scroll in the first place. */
    .intro-overlay,
    .ft-auth-page {
        align-items: flex-start;
        padding-top: 24px;
    }

    .intro-glass {
        margin: 0;
        padding-left: 20px;
        padding-right: 20px;
    }

    .intro-glass img[alt="ArborKin"] {
        width: 55% !important;
        max-width: 160px !important;
    }

    .intro-subtitle {
        margin-bottom: 12px !important;
    }
}

.ft-toast-x {
    flex-shrink: 0;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    color: var(--ft-text-muted);
    transition: background 0.15s, color 0.15s;
    margin: -2px 2px 0 0;
    padding: 0;
}

.ft-toast-x:hover { background: var(--ft-gray-100); color: var(--ft-text-primary); }
[data-theme="dark"] .ft-toast-x:hover { background: var(--ft-gray-700); }

.ft-toast-bar {
    position: absolute;
    bottom: 0;
    left: 0;
    height: 2px;
    width: 100%;
    transform-origin: left center;
    animation: ft-deplete linear forwards;
}

@keyframes ft-deplete {
    from { transform: scaleX(1); }
    to   { transform: scaleX(0); }
}

.ft-toast--success .ft-toast-bar { background: #22c55e; }
.ft-toast--error   .ft-toast-bar { background: #ef4444; }
.ft-toast--warning .ft-toast-bar { background: #f59e0b; }
.ft-toast--info    .ft-toast-bar { background: #3b82f6; }