/* ============================================================================
   family.css — the post-wedding site.

   The page tells one story from top to bottom: it opens at night, fireworks
   reveal the wedding photo, and as the visitor scrolls the sky turns to day and
   the family tree grows out of that moment. The night → day transition is
   driven by a single custom property, --day, set from scroll position in
   family.js; everything else keys off it.
   ========================================================================= */

/* ── Tokens ───────────────────────────────────────────────────────────────── */
:root {
    --night-0:      #04060d;
    --night-1:      #0a1020;
    --night-2:      #08202c;

    --gold:         #C9A84C;
    --gold-light:   #E2C97E;
    --gold-pale:    #F3E5B8;

    --aqua-deep:    #0077A8;
    --aqua:         #00ACC1;
    --aqua-mid:     #4DD0E1;
    --aqua-light:   #80DEEA;
    --aqua-pale:    #B2EBF2;
    --aqua-whisper: #E0F7FA;

    --cream:        #F8FDFF;
    --white:        #FFFFFF;
    --ink:          #22333F;
    --ink-soft:     #5A7684;

    --font-script:  'Great Vibes', cursive;
    --font-serif:   'Cormorant Garamond', Georgia, serif;
    --font-sans:    'Josefin Sans', system-ui, sans-serif;

    --shadow-soft:  0 10px 40px rgba(6, 60, 74, .09);
    --shadow-card:  0 4px 20px rgba(6, 60, 74, .08);
    --shadow-lift:  0 18px 50px rgba(6, 60, 74, .16);

    --radius:       20px;
    --day:          0;      /* 0 = night, 1 = full daylight */
    --maxw:         1160px;
}

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

html {
    scroll-behavior: smooth;
    -webkit-text-size-adjust: 100%;
}

body.fam {
    font-family: var(--font-sans);
    color: var(--ink);
    background: var(--night-0);
    min-height: 100dvh;
    overflow-x: hidden;
}

/* The intro owns the viewport until the reveal finishes. */
body.fam.is-locked { overflow: hidden; height: 100dvh; }

/* So does the member panel, for as long as it is open. Kept apart from
   .is-locked because the lightbox can open on top of the panel and clears its
   own lock on close. */
body.fam.is-modal { overflow: hidden; }

::selection { background: var(--aqua-pale); color: var(--aqua-deep); }

img { max-width: 100%; display: block; }

button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

/* The hidden attribute only sets display:none at low priority, so any element
   with an explicit display — the round control buttons, the skip link — would
   otherwise ignore it. */
[hidden] { display: none !important; }

:focus-visible {
    outline: 2px solid var(--aqua-mid);
    outline-offset: 3px;
    border-radius: 6px;
}

/* ── Backgrounds ──────────────────────────────────────────────────────────── */
.sky {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
}

.sky-night {
    background:
        radial-gradient(ellipse 120% 80% at 50% 110%, rgba(0, 90, 118, .40) 0%, transparent 62%),
        radial-gradient(ellipse 80% 60% at 20% 8%,   rgba(28, 44, 96, .32) 0%, transparent 60%),
        linear-gradient(178deg, var(--night-0) 0%, var(--night-1) 46%, var(--night-2) 100%);
}

/* Star field — three layers of different sizes, each drifting at its own pace. */
.stars {
    position: absolute;
    inset: -10%;
    background-repeat: repeat;
    opacity: calc(1 - var(--day));
    transition: opacity .5s linear;
}
.stars-1 {
    background-image:
        radial-gradient(1.4px 1.4px at 12% 18%,  rgba(255,255,255,.9), transparent),
        radial-gradient(1.2px 1.2px at 68% 32%,  rgba(255,255,255,.75), transparent),
        radial-gradient(1.6px 1.6px at 34% 66%,  rgba(226,201,126,.85), transparent),
        radial-gradient(1.1px 1.1px at 88% 74%,  rgba(255,255,255,.7), transparent),
        radial-gradient(1.3px 1.3px at 52% 12%,  rgba(178,235,242,.8), transparent);
    background-size: 340px 300px;
    animation: starDrift 200s linear infinite, twinkleA 5.5s ease-in-out infinite alternate;
}
.stars-2 {
    background-image:
        radial-gradient(1px 1px at 22% 44%, rgba(255,255,255,.6), transparent),
        radial-gradient(1px 1px at 76% 16%, rgba(255,255,255,.5), transparent),
        radial-gradient(1px 1px at 44% 88%, rgba(255,255,255,.55), transparent);
    background-size: 220px 210px;
    animation: starDrift 320s linear infinite reverse, twinkleB 7.5s ease-in-out infinite alternate;
}
.stars-3 {
    background-image:
        radial-gradient(2.2px 2.2px at 62% 26%, rgba(243,229,184,.75), transparent),
        radial-gradient(2px 2px   at 16% 72%, rgba(178,235,242,.6), transparent);
    background-size: 560px 520px;
    animation: twinkleA 9s ease-in-out infinite alternate;
}

@keyframes starDrift { to { transform: translate3d(-340px, 60px, 0); } }
@keyframes twinkleA  { from { opacity: .55; } to { opacity: 1; } }
@keyframes twinkleB  { from { opacity: .85; } to { opacity: .4; } }

/* Fixed daylight wash behind the content. It follows the scroll so the sky
   *feels* like it turns, but it is decoration only — the content below carries
   its own opaque background, so the page is readable even if JS never runs. */
.sky-day {
    opacity: var(--day);
    background:
        radial-gradient(ellipse 900px 700px at 18% 12%, rgba(0, 172, 193, .13) 0%, transparent 66%),
        radial-gradient(ellipse 800px 800px at 84% 26%, rgba(77, 208, 225, .11) 0%, transparent 66%),
        linear-gradient(180deg, var(--cream) 0%, #EEF9FC 55%, var(--cream) 100%);
}

/* Fireworks canvas — full screen, fades out once the story moves on.
   width/height are mandatory here: <canvas> is a replaced element, so inset: 0
   alone leaves it at its intrinsic 300x150 instead of stretching it. */
.fw-canvas {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    height: 100dvh;
    z-index: 1;
    pointer-events: none;
    opacity: calc(1 - var(--day) * 1.35);
    transition: opacity .4s linear;
}

/* ── Reveal stage ─────────────────────────────────────────────────────────── */
/* The photo is pinned near the top and the shell detonates at the centre of the
   viewport, so its light front travels upward and across the picture. */
.reveal {
    position: relative;
    z-index: 3;
    min-height: 100dvh;
    display: grid;
    grid-template-rows: auto 1fr auto;
    justify-items: center;
    padding: clamp(1.2rem, 4vh, 2.6rem) 1.2rem clamp(4rem, 9vh, 6rem);
    text-align: center;
}

.reveal-top {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: clamp(.7rem, 2vh, 1.4rem);
    max-width: min(760px, 92vw);
}

.reveal-bottom {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: clamp(.5rem, 1.6vh, 1.1rem);
    align-self: end;
    max-width: min(760px, 92vw);
}

/* Faint outline of the frame while the sky is still dark, so the eye knows
   where to look before the first shell opens. */
.frame-ghost {
    position: absolute;
    inset: 0;
    border: 1px solid rgba(226, 201, 126, .12);
    border-radius: 4px;
    opacity: 0;
    animation: ghostIn 2.4s ease-out .4s forwards;
    pointer-events: none;
}
.fam[data-stage="fire"]  .frame-ghost,
.fam[data-stage="photo"] .frame-ghost,
.fam[data-stage="done"]  .frame-ghost { animation: ghostOut .8s ease-out forwards; }

@keyframes ghostIn  { to { opacity: 1; } }
@keyframes ghostOut { to { opacity: 0; } }

.reveal-eyebrow {
    font-family: var(--font-serif);
    font-size: clamp(.72rem, 1.9vw, .92rem);
    letter-spacing: .48em;
    text-transform: uppercase;
    color: var(--gold-light);
    opacity: 0;
    transform: translateY(14px);
}

.reveal-title {
    font-family: var(--font-script);
    font-size: clamp(2.6rem, 8.5vw, 5.6rem);
    line-height: 1.05;
    font-weight: 400;
    color: var(--gold-pale);
    text-shadow: 0 0 42px rgba(226, 201, 126, .38), 0 2px 2px rgba(0, 0, 0, .35);
    opacity: 0;
    transform: translateY(20px);
}

.reveal-subtitle {
    font-family: var(--font-serif);
    font-style: italic;
    font-size: clamp(1rem, 2.8vw, 1.35rem);
    letter-spacing: .18em;
    color: var(--aqua-light);
    opacity: 0;
    transform: translateY(16px);
}

/* Ornamental divider */
.reveal-rule {
    display: flex;
    align-items: center;
    gap: .9rem;
    opacity: 0;
}
.reveal-rule i {
    display: block;
    width: clamp(40px, 12vw, 90px);
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--gold), transparent);
}
.reveal-rule b {
    width: 7px; height: 7px;
    background: var(--gold);
    transform: rotate(45deg);
    box-shadow: 0 0 14px rgba(201, 168, 76, .7);
}

/* ── The photo frame ──────────────────────────────────────────────────────── */
.hero-frame {
    position: relative;
    width: min(560px, 88vw);
    aspect-ratio: 4 / 3;
}

/* On landscape screens the width is not the tight constraint — the height is,
   because the burst has to happen *below* the photo. Driving the frame from its
   height there keeps the 4:3 ratio intact instead of letting max-height crop it. */
@media (min-aspect-ratio: 1/1) {
    .hero-frame {
        width: auto;
        height: min(44dvh, 420px);
        max-width: 88vw;
    }
}

.hero-frame::after {
    /* Warm bloom behind the photo, as if lit by the fireworks. */
    content: '';
    position: absolute;
    inset: -14%;
    z-index: -1;
    background: radial-gradient(ellipse at center, rgba(226, 201, 126, .30) 0%, transparent 68%);
    opacity: 0;
    transition: opacity 1.6s ease-out;
}

/* The photo and the reveal canvas occupy exactly the same box. The <img> starts
   invisible; the canvas paints it into existence from the firework light, then
   fades out and lets the real image take over. */
.hero-photo,
.hero-reveal {
    position: absolute;
    inset: 14px;
    width: calc(100% - 28px);
    height: calc(100% - 28px);
    border-radius: 4px;
}

.hero-photo {
    object-fit: cover;
    box-shadow: 0 24px 70px rgba(0, 0, 0, .55);
    opacity: 0;
    transition: opacity .9s ease-out;
}
.hero-photo.is-revealed { opacity: 1; }

.hero-reveal {
    z-index: 2;
    opacity: 1;
    transition: opacity .85s ease-out;
    pointer-events: none;
}
.hero-reveal.is-faded { opacity: 0; }

.hero-photo-missing {
    position: absolute;
    inset: 14px;
    display: grid;
    place-items: center;
    gap: .6rem;
    background: linear-gradient(150deg, rgba(255,255,255,.06), rgba(255,255,255,.02));
    border: 1px dashed rgba(226, 201, 126, .38);
    border-radius: 4px;
    color: var(--gold-light);
    font-family: var(--font-serif);
    font-size: .92rem;
    text-align: center;
    padding: 1.5rem;
    line-height: 1.6;
}
.hero-photo-missing span { font-size: 2rem; }

/* The gold border draws itself with a stroke-dashoffset sweep. */
.hero-frame svg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
    pointer-events: none;
}
.frame-line {
    fill: none;
    stroke: var(--gold);
    stroke-width: 1.5;
    vector-effect: non-scaling-stroke;
    stroke-dasharray: var(--len, 2000);
    stroke-dashoffset: var(--len, 2000);
    filter: drop-shadow(0 0 6px rgba(226, 201, 126, .5));
}
.frame-line.inner { stroke-width: .8; opacity: .55; }

.frame-corner {
    fill: none;
    stroke: var(--gold-light);
    stroke-width: 1.2;
    vector-effect: non-scaling-stroke;
    opacity: 0;
}

/* ── Reveal choreography ──────────────────────────────────────────────────── */
/* family.js walks through the stages dark → gather → fire → photo → done on a
   timeline; CSS owns every bit of the motion. The photo itself is *not*
   animated here — the fireworks paint it onto .hero-reveal. */

/* The date appears first, alone in the dark, while the first rocket climbs. */
.reveal-eyebrow {
    animation: riseIn 1.6s cubic-bezier(.16,.84,.44,1) .5s forwards;
}

/* At the moment the shell breaks, the frame is struck into place and its gold
   line is drawn by the passing light. */
.fam[data-stage="fire"]  .hero-frame,
.fam[data-stage="photo"] .hero-frame,
.fam[data-stage="done"]  .hero-frame {
    animation: frameIn 1.1s cubic-bezier(.16,.84,.44,1) forwards;
}
.fam[data-stage="fire"]  .frame-line,
.fam[data-stage="photo"] .frame-line,
.fam[data-stage="done"]  .frame-line {
    animation: drawFrame 1.5s cubic-bezier(.4,0,.15,1) forwards;
}
.fam[data-stage="fire"]  .frame-line.inner,
.fam[data-stage="photo"] .frame-line.inner,
.fam[data-stage="done"]  .frame-line.inner { animation-delay: .25s; }

.fam[data-stage="photo"] .hero-frame::after,
.fam[data-stage="done"]  .hero-frame::after { opacity: 1; }
.fam[data-stage="photo"] .frame-corner,
.fam[data-stage="done"]  .frame-corner {
    animation: fadeIn .9s ease-out .3s forwards;
}

/* Names arrive once the picture is legible. */
.fam[data-stage="photo"] .reveal-title,
.fam[data-stage="done"]  .reveal-title {
    animation: riseIn 1.5s cubic-bezier(.16,.84,.44,1) .35s forwards;
}
.fam[data-stage="photo"] .reveal-rule,
.fam[data-stage="done"]  .reveal-rule {
    animation: fadeIn 1.1s ease-out .8s forwards;
}
.fam[data-stage="photo"] .reveal-subtitle,
.fam[data-stage="done"]  .reveal-subtitle {
    animation: riseIn 1.4s cubic-bezier(.16,.84,.44,1) .95s forwards;
}

@keyframes riseIn   { to { opacity: 1; transform: translateY(0); } }
@keyframes fadeIn   { to { opacity: 1; } }
/* Deliberately transform-only: the box is visible from the start (it holds the
   ghost outline and the reveal canvas), it just settles when the shell hits. */
@keyframes frameIn  { from { transform: scale(1.04); } to { transform: none; } }
@keyframes drawFrame{ to { stroke-dashoffset: 0; } }

/* ── Reveal controls ──────────────────────────────────────────────────────── */
.reveal-controls {
    position: fixed;
    right: clamp(.8rem, 2.5vw, 1.6rem);
    bottom: clamp(.8rem, 2.5vw, 1.6rem);
    z-index: 20;
    display: flex;
    gap: .5rem;
    opacity: calc(1 - var(--day) * 1.6);
    transition: opacity .3s;
}

.ctl {
    width: 42px; height: 42px;
    display: grid; place-items: center;
    border-radius: 50%;
    background: rgba(255, 255, 255, .07);
    border: 1px solid rgba(226, 201, 126, .32);
    color: var(--gold-pale);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    font-size: 1rem;
    transition: transform .25s, background .25s, border-color .25s;
}
.ctl:hover { transform: translateY(-2px); background: rgba(255,255,255,.14); border-color: var(--gold-light); }
.ctl.is-on { background: rgba(226,201,126,.2); border-color: var(--gold-light); }

.skip-btn {
    position: fixed;
    left: 50%;
    bottom: clamp(1rem, 3vh, 2rem);
    transform: translateX(-50%);
    z-index: 20;
    font-family: var(--font-serif);
    font-size: .82rem;
    letter-spacing: .28em;
    text-transform: uppercase;
    color: rgba(243, 229, 184, .68);
    padding: .5rem 1.2rem;
    border-bottom: 1px solid rgba(243, 229, 184, .25);
    transition: color .3s, border-color .3s, opacity .4s;
}
.skip-btn:hover { color: var(--gold-pale); border-color: var(--gold-light); }

/* Scroll invitation, shown once the reveal has landed. */
.scroll-cue {
    position: absolute;
    left: 50%;
    bottom: clamp(1rem, 3vh, 2.2rem);
    transform: translateX(-50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .5rem;
    opacity: 0;
    pointer-events: none;
    transition: opacity .8s ease-out;
}
.fam[data-stage="done"] .scroll-cue { opacity: calc(1 - var(--day) * 2); pointer-events: auto; }

.scroll-cue span {
    font-family: var(--font-serif);
    font-size: .74rem;
    letter-spacing: .34em;
    text-transform: uppercase;
    color: rgba(178, 235, 242, .7);
}
.scroll-cue i {
    width: 1px; height: 44px;
    background: linear-gradient(180deg, rgba(226,201,126,.75), transparent);
    animation: cueDrop 2.2s ease-in-out infinite;
}
@keyframes cueDrop {
    0%, 100% { transform: scaleY(.35); transform-origin: top; opacity: .4; }
    50%      { transform: scaleY(1);   transform-origin: top; opacity: 1; }
}

/* ── Content sections ─────────────────────────────────────────────────────── */
/* Daylight starts here and is baked into the element, not driven by script: the
   first 260px fade the night out, everything below is solid cream. */
.content {
    position: relative;
    z-index: 3;
    background:
        radial-gradient(ellipse 1100px 700px at 12% 6%,  rgba(0, 172, 193, .10) 0%, transparent 62%),
        radial-gradient(ellipse 900px 900px at 88% 34%,  rgba(77, 208, 225, .09) 0%, transparent 62%),
        radial-gradient(ellipse 1200px 800px at 50% 92%, rgba(201, 168, 76, .09) 0%, transparent 66%),
        linear-gradient(180deg,
            rgba(248, 253, 255, 0)   0px,
            rgba(248, 253, 255, .55) 110px,
            rgba(248, 253, 255, .94) 220px,
            var(--cream)             300px,
            #EEF9FC                  60%,
            var(--cream)             100%);
}

.section {
    max-width: var(--maxw);
    margin: 0 auto;
    padding: clamp(3.5rem, 10vh, 7rem) clamp(1rem, 4vw, 2.5rem);
}

.section-head { text-align: center; margin-bottom: clamp(2rem, 5vh, 3.4rem); }

.section-eyebrow {
    font-family: var(--font-serif);
    font-size: .74rem;
    letter-spacing: .42em;
    text-transform: uppercase;
    color: var(--gold);
    margin-bottom: .7rem;
}

.section-title {
    font-family: var(--font-script);
    font-size: clamp(2rem, 5.5vw, 3.4rem);
    font-weight: 400;
    line-height: 1.15;
    color: var(--aqua-deep);
}

.section-intro {
    font-family: var(--font-serif);
    font-size: clamp(1rem, 2.2vw, 1.16rem);
    line-height: 1.75;
    color: var(--ink-soft);
    max-width: 62ch;
    margin: .9rem auto 0;
}

/* Reveal-on-scroll for content blocks. */
.rise {
    opacity: 0;
    transform: translateY(28px);
    transition: opacity .9s cubic-bezier(.16,.84,.44,1), transform .9s cubic-bezier(.16,.84,.44,1);
}
.rise.is-in { opacity: 1; transform: none; }

/* ── Story ────────────────────────────────────────────────────────────────── */
.story-card {
    background: rgba(255, 255, 255, .72);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border: 1px solid rgba(0, 137, 123, .1);
    border-radius: var(--radius);
    padding: clamp(1.6rem, 4vw, 3rem);
    box-shadow: var(--shadow-soft);
    max-width: 780px;
    margin: 0 auto;
    position: relative;
}
.story-card::before,
.story-card::after {
    content: '';
    position: absolute;
    width: 26px; height: 26px;
    border: 1px solid var(--gold);
    opacity: .5;
}
.story-card::before { top: 12px; left: 12px; border-right: 0; border-bottom: 0; border-radius: 6px 0 0 0; }
.story-card::after  { bottom: 12px; right: 12px; border-left: 0; border-top: 0; border-radius: 0 0 6px 0; }

.rich-text {
    font-family: var(--font-serif);
    font-size: clamp(1.02rem, 2.3vw, 1.2rem);
    line-height: 1.85;
    color: var(--ink);
}
.rich-text p + p { margin-top: 1rem; }
.rich-text h3, .rich-text h4 {
    font-family: var(--font-sans);
    font-weight: 400;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--aqua-deep);
    margin: 1.4rem 0 .6rem;
    font-size: .92rem;
}
.rich-text ul, .rich-text ol { margin: .8rem 0 .8rem 1.4rem; }
.rich-text li { margin-bottom: .35rem; }
.rich-text blockquote {
    margin: 1.2rem 0;
    padding-left: 1.1rem;
    border-left: 2px solid var(--gold);
    font-style: italic;
    color: var(--ink-soft);
}
.rich-text hr { border: 0; height: 1px; background: linear-gradient(90deg, transparent, var(--gold), transparent); margin: 1.6rem 0; }

/* ── Family tree ──────────────────────────────────────────────────────────── */
.tree-shell {
    position: relative;
    border-radius: var(--radius);
    background:
        radial-gradient(ellipse 70% 50% at 50% 0%, rgba(0, 172, 193, .07), transparent 70%),
        rgba(255, 255, 255, .58);
    border: 1px solid rgba(0, 137, 123, .1);
    box-shadow: var(--shadow-soft);
    overflow: hidden;
}

.tree-toolbar {
    position: absolute;
    top: 12px; right: 12px;
    z-index: 5;
    display: flex;
    gap: .35rem;
    padding: .3rem;
    background: rgba(255, 255, 255, .84);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid rgba(0, 137, 123, .12);
    border-radius: 999px;
    box-shadow: var(--shadow-card);
}

.tb {
    width: 34px; height: 34px;
    display: grid; place-items: center;
    border-radius: 50%;
    color: var(--aqua-deep);
    font-size: .95rem;
    transition: background .2s, transform .2s;
}
.tb:hover { background: var(--aqua-whisper); transform: translateY(-1px); }

.tree-search {
    position: absolute;
    top: 12px; left: 12px;
    z-index: 5;
    width: min(230px, 46vw);
}
.tree-search input {
    width: 100%;
    padding: .5rem .9rem .5rem 2.1rem;
    font-family: var(--font-sans);
    font-size: .86rem;
    color: var(--ink);
    background: rgba(255, 255, 255, .86) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234DD0E1' stroke-width='2'><circle cx='11' cy='11' r='7'/><path d='M20 20l-3.5-3.5'/></svg>") no-repeat .6rem center / 15px;
    border: 1px solid rgba(0, 137, 123, .16);
    border-radius: 999px;
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    outline: none;
    transition: border-color .2s;
}
.tree-search input:focus { border-color: var(--aqua-mid); }

.tree-results {
    margin-top: .35rem;
    background: rgba(255,255,255,.96);
    border: 1px solid rgba(0,137,123,.14);
    border-radius: 12px;
    box-shadow: var(--shadow-card);
    overflow: hidden;
    max-height: 220px;
    overflow-y: auto;
}
.tree-results:empty { display: none; }
.tree-results button {
    display: block;
    width: 100%;
    text-align: left;
    padding: .5rem .8rem;
    font-size: .85rem;
    border-bottom: 1px solid rgba(0,137,123,.07);
    transition: background .15s;
}
.tree-results button:last-child { border-bottom: 0; }
.tree-results button:hover { background: var(--aqua-whisper); }
.tree-results small { display: block; color: var(--ink-soft); font-size: .72rem; }

.ft-root {
    position: relative;
    height: clamp(430px, 66vh, 760px);
    overflow: hidden;
    touch-action: none;
    cursor: grab;
}
.ft-root.is-panning { cursor: grabbing; }

.ft-viewport {
    position: absolute;
    top: 0; left: 0;
    transform-origin: 0 0;
    will-change: transform;
}

.ft-links { position: absolute; top: 0; left: 0; overflow: visible; }

.ft-link {
    fill: none;
    stroke: rgba(0, 137, 123, .3);
    stroke-width: 1.6;
    stroke-linecap: round;
}
.ft-link-couple { stroke: var(--gold); stroke-width: 2; opacity: .75; }
.ft-knot { fill: var(--gold); stroke: rgba(255,255,255,.85); stroke-width: 1.5; }

.ft-nodes { position: absolute; top: 0; left: 0; }

.ft-card {
    --ft-accent: var(--aqua-mid);
    position: absolute;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .6rem;
    padding: 1rem .75rem .95rem;
    text-align: center;
    background: rgba(255, 255, 255, .94);
    border: 1px solid rgba(0, 137, 123, .12);
    border-radius: 16px;
    box-shadow: var(--shadow-card);
    transition: transform .25s cubic-bezier(.2,.7,.3,1), box-shadow .25s, border-color .25s;
}
.ft-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-lift); border-color: var(--ft-accent); }
.ft-card.is-active { border-color: var(--ft-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ft-accent) 24%, transparent), var(--shadow-lift); }
.ft-card.is-draft { opacity: .55; border-style: dashed; }
.ft-card.is-root { border-color: var(--gold); background: linear-gradient(170deg, #FFFDF6, rgba(255,255,255,.94)); }
.ft-card.is-root::before {
    content: '💍';
    position: absolute;
    top: -13px; left: 50%;
    transform: translateX(-50%);
    font-size: .95rem;
    background: var(--white);
    border: 1px solid var(--gold);
    border-radius: 999px;
    width: 26px; height: 26px;
    display: grid; place-items: center;
    box-shadow: 0 2px 8px rgba(201,168,76,.3);
}
.ft-card.gender-female { --ft-accent: #E8A0BF; }
.ft-card.gender-male   { --ft-accent: #4DD0E1; }

.ft-photo {
    position: relative;
    width: 92px; height: 92px;
    border-radius: 50%;
    overflow: hidden;
    background: linear-gradient(145deg, var(--aqua-whisper), #fff);
    border: 2px solid var(--ft-accent);
    display: grid;
    place-items: center;
    flex: none;
}
.ft-photo img { width: 100%; height: 100%; object-fit: cover; }

.ft-monogram {
    font-family: var(--font-serif);
    font-size: 1.9rem;
    font-weight: 600;
    color: var(--aqua-deep);
    opacity: .55;
    letter-spacing: 1px;
}

.ft-photo-count {
    position: absolute;
    bottom: 0; left: 0; right: 0;
    background: rgba(0, 60, 74, .62);
    color: #fff;
    font-size: .6rem;
    letter-spacing: .5px;
    padding: 1px 0 2px;
}

.ft-body { display: flex; flex-direction: column; gap: .16rem; width: 100%; }

.ft-name {
    font-family: var(--font-serif);
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.25;
    color: var(--ink);
    overflow-wrap: break-word;
}
.ft-role {
    font-size: .66rem;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--ft-accent);
    filter: brightness(.78);
}
.ft-years {
    font-family: var(--font-serif);
    font-size: .78rem;
    color: var(--ink-soft);
    font-style: italic;
}

.ft-toggle {
    position: absolute;
    width: 30px; height: 26px;
    display: grid; place-items: center;
    background: var(--white);
    border: 1px solid rgba(0, 137, 123, .22);
    border-radius: 999px;
    font-size: .78rem;
    line-height: 1;
    color: var(--aqua-deep);
    box-shadow: 0 2px 8px rgba(6,60,74,.12);
    transition: background .2s, transform .2s;
    z-index: 2;
}
.ft-toggle:hover { background: var(--aqua-whisper); transform: scale(1.08); }
.ft-toggle.is-collapsed { background: var(--gold); border-color: var(--gold); color: #fff; font-size: .68rem; }

.ft-empty {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    padding: 2rem;
    text-align: center;
    font-family: var(--font-serif);
    font-size: 1.05rem;
    color: var(--ink-soft);
}

.tree-hint {
    text-align: center;
    font-family: var(--font-serif);
    font-size: .84rem;
    color: var(--ink-soft);
    margin-top: .9rem;
    opacity: .8;
}

/* ── Timeline ─────────────────────────────────────────────────────────────── */
.timeline {
    position: relative;
    max-width: 900px;
    margin: 0 auto;
    padding: .5rem 0;
}
.timeline::before {
    content: '';
    position: absolute;
    left: 50%;
    top: 0; bottom: 0;
    width: 1px;
    transform: translateX(-.5px);
    background: linear-gradient(180deg, transparent, rgba(201,168,76,.5) 8%, rgba(201,168,76,.5) 92%, transparent);
}

.tl-item {
    position: relative;
    width: 50%;
    padding: 0 clamp(1.4rem, 4vw, 3rem) clamp(2rem, 5vh, 3.2rem) 0;
}
.tl-item:nth-child(even) {
    margin-left: 50%;
    padding: 0 0 clamp(2rem, 5vh, 3.2rem) clamp(1.4rem, 4vw, 3rem);
    text-align: left;
}
.tl-item:nth-child(odd) { text-align: right; }

.tl-dot {
    position: absolute;
    top: 6px;
    width: 34px; height: 34px;
    display: grid; place-items: center;
    border-radius: 50%;
    background: var(--white);
    border: 1px solid rgba(201, 168, 76, .5);
    box-shadow: 0 3px 12px rgba(6, 60, 74, .12);
    font-size: .95rem;
    z-index: 2;
}
.tl-item:nth-child(odd)  .tl-dot { right: -17px; }
.tl-item:nth-child(even) .tl-dot { left: -17px; }
.tl-item.is-highlight .tl-dot {
    background: linear-gradient(145deg, var(--gold-light), var(--gold));
    border-color: var(--gold);
    box-shadow: 0 0 0 5px rgba(201, 168, 76, .16), 0 4px 16px rgba(201,168,76,.35);
}

.tl-card {
    display: inline-block;
    text-align: left;
    background: rgba(255, 255, 255, .8);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border: 1px solid rgba(0, 137, 123, .1);
    border-radius: var(--radius);
    box-shadow: var(--shadow-card);
    overflow: hidden;
    max-width: 420px;
    width: 100%;
    transition: transform .3s, box-shadow .3s;
}

/* Briefly ringed after jumping here from a moment listed in a member modal. */
.tl-item.is-flash .tl-card {
    box-shadow: 0 0 0 2px var(--gold), var(--shadow-lift);
}
.tl-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lift); }

.tl-cover {
    width: 100%;
    aspect-ratio: 16 / 10;
    object-fit: cover;
    cursor: zoom-in;
}

.tl-text { padding: 1.1rem 1.3rem 1.3rem; }

.tl-date {
    font-family: var(--font-sans);
    font-size: .7rem;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--gold);
    margin-bottom: .35rem;
}
.tl-title {
    font-family: var(--font-serif);
    font-size: 1.28rem;
    font-weight: 600;
    color: var(--ink);
    line-height: 1.3;
}
.tl-subtitle {
    font-family: var(--font-serif);
    font-style: italic;
    font-size: .95rem;
    color: var(--aqua);
    margin-top: .2rem;
}
.tl-body {
    font-family: var(--font-serif);
    font-size: .98rem;
    line-height: 1.7;
    color: var(--ink-soft);
    margin-top: .6rem;
}
.tl-body p + p { margin-top: .6rem; }

.tl-thumbs { display: flex; gap: .35rem; flex-wrap: wrap; margin-top: .8rem; }
.tl-thumbs img {
    width: 52px; height: 52px;
    object-fit: cover;
    border-radius: 8px;
    cursor: zoom-in;
    transition: transform .2s;
}
.tl-thumbs img:hover { transform: scale(1.08); }

.tl-member {
    display: inline-block;
    margin-top: .7rem;
    font-size: .74rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--aqua-deep);
    background: var(--aqua-whisper);
    border-radius: 999px;
    padding: .22rem .7rem;
}

/* ── Gallery ──────────────────────────────────────────────────────────────── */
.gallery-filters {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: .45rem;
    margin-bottom: 1.8rem;
}
.gf {
    font-family: var(--font-sans);
    font-size: .76rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    padding: .45rem 1.1rem;
    border-radius: 999px;
    border: 1px solid rgba(0, 137, 123, .18);
    color: var(--ink-soft);
    background: rgba(255,255,255,.6);
    transition: all .22s;
}
.gf:hover { border-color: var(--aqua-mid); color: var(--aqua-deep); }
.gf.is-active {
    background: linear-gradient(135deg, var(--aqua-deep), var(--aqua));
    border-color: transparent;
    color: #fff;
    box-shadow: 0 6px 18px rgba(0, 137, 123, .28);
}

.gallery-grid {
    columns: 4 220px;
    column-gap: 14px;
}
.g-item {
    break-inside: avoid;
    margin-bottom: 14px;
    border-radius: 14px;
    overflow: hidden;
    position: relative;
    box-shadow: var(--shadow-card);
    cursor: zoom-in;
    background: var(--aqua-whisper);
    display: block;
    width: 100%;
}
.g-item img {
    width: 100%;
    transition: transform .55s cubic-bezier(.2,.7,.3,1), filter .4s;
}
.g-item:hover img { transform: scale(1.05); }

.g-cap {
    position: absolute;
    inset: auto 0 0 0;
    padding: 1.6rem .9rem .7rem;
    background: linear-gradient(transparent, rgba(4, 32, 42, .78));
    color: #fff;
    font-family: var(--font-serif);
    font-size: .88rem;
    line-height: 1.4;
    opacity: 0;
    transform: translateY(8px);
    transition: opacity .3s, transform .3s;
    text-align: left;
}
.g-item:hover .g-cap { opacity: 1; transform: none; }

.gallery-empty {
    text-align: center;
    font-family: var(--font-serif);
    font-size: 1.02rem;
    color: var(--ink-soft);
    padding: 2.5rem 1rem;
}

/* ── Member detail panel ──────────────────────────────────────────────────── */
/* Stacking: below the lightbox (90), which opens on top of the panel. */
.mm-backdrop {
    position: fixed;
    inset: 0;
    z-index: 82;
    background: rgba(4, 26, 34, .55);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    opacity: 0;
    pointer-events: none;
    transition: opacity .35s;
}
.mm-backdrop.is-open { opacity: 1; pointer-events: auto; }

/* Anchored to the right edge; the panel itself slides in from off-screen. */
.mm-wrap {
    position: fixed;
    inset: 0 0 0 auto;
    z-index: 83;
    /* Percentages, not vw: 100vw counts the scrollbar while the fixed-position
       edges do not, which slides the panel out past the right edge. */
    width: min(460px, 100%);
    display: flex;
    pointer-events: none;
}
.mm-wrap.is-open { pointer-events: auto; }

.mm {
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100dvh;
    overflow: hidden;
    background: var(--cream);
    box-shadow: -20px 0 60px rgba(4, 40, 50, .22);
    transform: translateX(100%);
    transition: transform .45s cubic-bezier(.22, .61, .36, 1);
}
.mm-wrap.is-open .mm { transform: none; }

.mm-close {
    position: absolute;
    top: 14px; right: 14px;
    z-index: 4;
    width: 38px; height: 38px;
    border-radius: 50%;
    display: grid; place-items: center;
    background: rgba(255, 255, 255, .92);
    box-shadow: var(--shadow-card);
    font-size: 1rem;
    color: var(--ink);
    transition: transform .2s, background .2s;
}
.mm-close:hover { transform: rotate(90deg); background: #fff; }

/* ── Portrait ── */
.mm-media {
    position: relative;
    flex: none;
    aspect-ratio: 4 / 3;
    background: linear-gradient(150deg, var(--aqua-pale), var(--aqua-whisper));
    display: grid;
    place-items: center;
    overflow: hidden;
}
.mm-media img { width: 100%; height: 100%; object-fit: cover; }
.mm-monogram {
    font-family: var(--font-serif);
    font-size: clamp(2.8rem, 14vw, 4.2rem);
    letter-spacing: .06em;
    color: var(--aqua-deep);
    opacity: .55;
}
/* A hairline of gold where the portrait meets the story. */
.mm-media::after {
    content: '';
    position: absolute;
    inset: auto 0 0 0;
    height: 2px;
    background: linear-gradient(90deg, transparent, var(--gold), transparent);
}

.mm-scroll {
    flex: 1;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}
.mm-body { padding: clamp(1.4rem, 5vw, 2rem); }

/* ── Header ── */
.mm-name {
    font-family: var(--font-script);
    font-size: clamp(2.1rem, 8vw, 2.8rem);
    color: var(--aqua-deep);
    line-height: 1.05;
}
.mm-role {
    font-family: var(--font-sans);
    font-size: .72rem;
    letter-spacing: .24em;
    text-transform: uppercase;
    color: var(--gold);
    margin-top: .35rem;
}
.mm-nick {
    font-family: var(--font-serif);
    font-style: italic;
    color: var(--ink-soft);
    font-size: .98rem;
    margin-top: .3rem;
}
.mm-rule {
    display: flex;
    align-items: center;
    gap: .7rem;
    margin-top: 1.1rem;
}
.mm-rule i { flex: 1; height: 1px; background: linear-gradient(90deg, var(--gold), transparent); }
.mm-rule b { width: 6px; height: 6px; background: var(--gold); transform: rotate(45deg); flex: none; }

.mm-quote {
    font-family: var(--font-serif);
    font-style: italic;
    font-size: 1.04rem;
    color: var(--aqua);
    margin-top: 1.2rem;
    padding-left: .9rem;
    border-left: 2px solid var(--gold);
    line-height: 1.6;
}

/* ── Facts ── */
.mm-facts {
    display: grid;
    gap: .55rem;
    margin-top: 1.4rem;
    padding-top: 1.3rem;
    border-top: 1px solid rgba(0, 137, 123, .12);
}
.fact { display: flex; gap: .7rem; align-items: baseline; font-size: .92rem; }
.fact dt {
    flex: none;
    width: 6.4rem;
    font-size: .68rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--ink-soft);
}
.fact dd { font-family: var(--font-serif); color: var(--ink); }

.mm-bio { margin-top: 1.4rem; font-size: 1rem; }

.mm-section-title {
    font-family: var(--font-sans);
    font-size: .7rem;
    letter-spacing: .24em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin: 1.8rem 0 .7rem;
}

/* ── Relatives ── */
.mm-relations { display: flex; flex-wrap: wrap; gap: .4rem; }
.rel-chip {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .3rem .8rem .3rem .3rem;
    background: rgba(255, 255, 255, .85);
    border: 1px solid rgba(0, 137, 123, .14);
    border-radius: 999px;
    font-size: .82rem;
    color: var(--ink);
    transition: border-color .2s, transform .2s;
}
.rel-chip:hover { border-color: var(--aqua-mid); transform: translateY(-1px); }
.rel-chip img, .rel-chip span.mono {
    width: 26px; height: 26px;
    border-radius: 50%;
    object-fit: cover;
    background: var(--aqua-whisper);
    display: grid; place-items: center;
    font-size: .66rem;
    color: var(--aqua-deep);
    flex: none;
}

/* ── Moments this person appears in ── */
.mm-moments { display: grid; gap: .45rem; }
.mm-moment {
    display: flex;
    align-items: center;
    gap: .75rem;
    width: 100%;
    text-align: left;
    padding: .6rem .8rem;
    background: rgba(255, 255, 255, .85);
    border: 1px solid rgba(0, 137, 123, .12);
    border-radius: 14px;
    transition: border-color .2s, transform .2s;
}
.mm-moment:hover { border-color: var(--aqua-mid); transform: translateX(2px); }
.mm-moment-icon { font-size: 1.15rem; flex: none; }
.mm-moment-text { min-width: 0; }
.mm-moment-title {
    font-family: var(--font-serif);
    font-weight: 600;
    font-size: .98rem;
    color: var(--ink);
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.mm-moment-date {
    font-size: .68rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

/* ── Photos ── */
.mm-gallery { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.mm-gallery img {
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    border-radius: 8px;
    cursor: zoom-in;
    transition: transform .25s;
}
.mm-gallery img:hover { transform: scale(1.04); }

@media (max-width: 620px) {
    .mm-wrap { width: 100%; }
    .mm-media { aspect-ratio: 16 / 10; }
    .fact dt { width: 5.4rem; }
}

/* ── Lightbox ─────────────────────────────────────────────────────────────── */
.lightbox {
    position: fixed;
    inset: 0;
    z-index: 90;
    background: rgba(3, 14, 20, .94);
    display: grid;
    grid-template-rows: 1fr auto;
    opacity: 0;
    pointer-events: none;
    transition: opacity .3s;
}
.lightbox.is-open { opacity: 1; pointer-events: auto; }

.lb-stage { display: grid; place-items: center; padding: 3rem 1rem 1rem; min-height: 0; }
.lb-stage img {
    max-width: min(94vw, 1300px);
    max-height: calc(100dvh - 9rem);
    object-fit: contain;
    border-radius: 6px;
    box-shadow: 0 30px 90px rgba(0,0,0,.6);
    animation: lbIn .4s cubic-bezier(.16,.84,.44,1);
}
@keyframes lbIn { from { opacity: 0; transform: scale(.96); } to { opacity: 1; transform: none; } }

.lb-bar {
    padding: .9rem 1.2rem 1.6rem;
    text-align: center;
    color: rgba(255,255,255,.82);
    font-family: var(--font-serif);
    font-size: .95rem;
    line-height: 1.5;
}
.lb-count { display: block; font-size: .72rem; letter-spacing: .2em; color: rgba(226,201,126,.75); margin-top: .3rem; }

.lb-btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 48px; height: 48px;
    border-radius: 50%;
    display: grid; place-items: center;
    background: rgba(255,255,255,.1);
    border: 1px solid rgba(255,255,255,.18);
    color: #fff;
    font-size: 1.2rem;
    transition: background .2s, transform .2s;
}
.lb-btn:hover { background: rgba(255,255,255,.2); }
.lb-prev { left: clamp(.6rem, 2vw, 1.6rem); }
.lb-next { right: clamp(.6rem, 2vw, 1.6rem); }
.lb-close { top: clamp(.8rem, 2vw, 1.4rem); right: clamp(.8rem, 2vw, 1.4rem); transform: none; }
.lb-close:hover { transform: rotate(90deg); }

/* ── Footer ───────────────────────────────────────────────────────────────── */
.fam-footer {
    position: relative;
    z-index: 3;
    background: var(--cream);
    text-align: center;
    padding: clamp(2.5rem, 7vh, 4.5rem) 1.2rem clamp(2rem, 5vh, 3rem);
    border-top: 1px solid rgba(0, 137, 123, .1);
}
.fam-footer-mark {
    font-family: var(--font-script);
    font-size: clamp(1.7rem, 5vw, 2.4rem);
    color: var(--aqua);
    margin-bottom: .4rem;
}
.fam-footer-text {
    font-family: var(--font-serif);
    font-size: .92rem;
    letter-spacing: .1em;
    color: var(--ink-soft);
}
.fam-footer-heart { color: var(--gold); display: inline-block; animation: beat 1.8s ease-in-out infinite; }
@keyframes beat { 0%,100%{transform:scale(1)} 14%{transform:scale(1.28)} 28%{transform:scale(1)} 42%{transform:scale(1.28)} 56%{transform:scale(1)} }

/* ── Responsive ───────────────────────────────────────────────────────────── */
@media (max-width: 860px) {
    .gallery-grid { columns: 2 150px; }
}

@media (max-width: 720px) {
    .timeline::before { left: 18px; }
    .tl-item,
    .tl-item:nth-child(even),
    .tl-item:nth-child(odd) {
        width: 100%;
        margin-left: 0;
        padding: 0 0 2.2rem 3rem;
        text-align: left;
    }
    .tl-item:nth-child(odd)  .tl-dot,
    .tl-item:nth-child(even) .tl-dot { left: 1px; right: auto; }
    .tl-card { max-width: none; }

    .ft-root { height: clamp(400px, 60vh, 560px); }
    .tree-search { width: min(160px, 40vw); }
}

@media (max-width: 460px) {
    .gallery-grid { columns: 2 120px; column-gap: 8px; }
    .g-item { margin-bottom: 8px; border-radius: 10px; }
    .tree-toolbar { top: auto; bottom: 12px; right: 12px; }
    .mm-gallery { grid-template-columns: repeat(3, 1fr); gap: 5px; }
}

/* ── Motion preferences ───────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }

    .stars, .scroll-cue i, .fam-footer-heart { animation: none !important; }

    .rise { opacity: 1; transform: none; transition: none; }

    /* Keep the reveal, drop the choreography — everything lands immediately. */
    .reveal-eyebrow, .reveal-title, .reveal-subtitle, .reveal-rule { opacity: 1; transform: none; animation: none !important; }
    .hero-frame { transform: none; animation: none !important; }
    .hero-photo { opacity: 1; animation: none !important; }
    .hero-reveal { display: none; }
    .frame-ghost { display: none; }
    .frame-line { stroke-dashoffset: 0; animation: none !important; }
    .frame-corner { opacity: 1; animation: none !important; }
    .ft-card, .tl-card, .g-item img { transition: none; }
    .mm { transition: none; }
}

@media print {
    .sky, .fw-canvas, .reveal-controls, .skip-btn, .tree-toolbar, .tree-search { display: none !important; }
    body.fam { background: #fff; }
}
