/*
 * Shared /join hero styles. Used by two places, so it lives in one file:
 *   - the standalone /join page
 *   - the full-screen intro overlay on the homepage (index.html)
 * Everything is scoped under .join-* so it can't affect the rest of
 * the site's styles when loaded on the homepage.
 */

    /* ── /join hero ──
       Vertical gradient bars rising from the bottom edge, in Linea's gold
       rather than the reference's orange. Everything here is scoped under
       .join-* so it can't leak into the shared styles used by the rest of
       the site. */
    .join-hero {
        position: relative;
        /* svh first as the fallback: the small viewport height is the size
           with the browser bars showing, so the hero always fits rather
           than hiding the form behind Safari's bottom bar. dvh then wins
           where supported -- it tracks the currently visible area, so the
           content stays optically centred whether the bars are showing or
           hidden. Sizing to svh alone left the hero short of the visible
           area (and the content sitting high) once the bars retracted. */
        min-height: 100svh;
        min-height: 100dvh;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        overflow: hidden;
        padding: 32px 24px max(24px, env(safe-area-inset-bottom));
        text-align: center;
        background: #000;
    }

    .join-content {
        position: relative;
        z-index: 2;
        width: 100%;
        max-width: 620px;
    }

    /* Social proof, kept intentionally quiet so it supports the headline
       instead of reading like a notification badge. */
    .join-pill {
        display: inline-flex;
        align-items: center;
        gap: 11px;
        padding: 6px 16px 6px 8px;
        border-radius: 9999px;
        background: rgba(255, 255, 255, 0.045);
        box-shadow: inset 0 0 0 1px rgba(216, 185, 111, 0.16),
                    0 10px 30px rgba(0, 0, 0, 0.22);
        font-size: 14px;
        color: rgba(245, 243, 238, 0.66);
        margin-bottom: 18px;
        opacity: 0;
        transform: translateY(12px);
    }
    .join-pill.is-in { animation: joinRise .7s cubic-bezier(.22,.61,.36,1) forwards; }
    .join-pill strong { color: var(--text-primary); font-weight: 500; }

    /* Abstract gold discs rather than photos of people -- stock portraits
       beside a waitlist claim would read as real members. */
    .join-faces { display: flex; }
    .join-face {
        width: 26px; height: 26px;
        border-radius: 50%;
        border: 2px solid #0a0a0a;
        margin-right: -9px;
        background: linear-gradient(140deg, #E6C373, #A67B3E);
    }
    .join-face:nth-child(2) { background: linear-gradient(140deg, #D8B56A, #916A36); }
    .join-face:nth-child(3) { background: linear-gradient(140deg, #C9A85E, #7E6232); }
    .join-face:nth-child(4) { background: linear-gradient(140deg, #F0D59A, #B98E4C); margin-right: 0; }

    /* Each disc slides in from the right and settles onto the stack, one
       after the next -- coins being stacked. Later siblings paint above
       earlier ones, so each genuinely lands on top of the one before it.
       `backwards` holds the offset start through the delay, so a disc
       doesn't flash in place before its turn. */
    .join-pill.is-in .join-face {
        animation: joinCoin .5s cubic-bezier(.34,1.2,.5,1) backwards;
    }
    .join-pill.is-in .join-face:nth-child(1) { animation-delay: .26s; }
    .join-pill.is-in .join-face:nth-child(2) { animation-delay: .34s; }
    .join-pill.is-in .join-face:nth-child(3) { animation-delay: .42s; }
    .join-pill.is-in .join-face:nth-child(4) { animation-delay: .50s; }

    @keyframes joinCoin {
        from { transform: translateX(22px); opacity: 0; }
        to   { transform: translateX(0);    opacity: 1; }
    }

    .join-title {
        font-size: clamp(30px, 7.4vw, 52px);
        font-weight: 300;
        line-height: 1.12;
        letter-spacing: -0.025em;
        margin-bottom: 16px;
        opacity: 0;
        transform: translateY(16px);
    }
    .join-title.is-in { animation: joinRise .8s cubic-bezier(.22,.61,.36,1) .06s forwards; }
    /* The <em> carries the gold accent only -- upright, not italic. */
    .join-title em {
        font-style: normal;
        color: #D8B96F;
    }

    .join-media {
        /* Narrower than the landscape version was: cropping to the middle
           50% made this portrait, so the same width would make it much
           taller and crowd the form below it. */
        max-width: 230px;
        margin: 0 auto 18px;
        border-radius: 16px;
        overflow: hidden;
        box-shadow: 0 0 0 1px rgba(214, 178, 94, 0.22),
                    0 18px 44px rgba(0, 0, 0, 0.55);
        opacity: 0;
        transform: translateY(16px);
    }
    .join-media.is-in { animation: joinRise .8s cubic-bezier(.22,.61,.36,1) .1s forwards; }
    .join-video {
        display: block;
        width: 100%;
        height: auto;
    }

    .join-sub {
        font-size: 17px;
        font-weight: 400;
        line-height: 1.62;
        color: rgba(245, 243, 238, 0.68);
        max-width: 430px;
        margin: 0 auto 34px;
        opacity: 0;
        transform: translateY(16px);
    }
    .join-sub.is-in { animation: joinRise .8s cubic-bezier(.22,.61,.36,1) .12s forwards; }

    .join-form-wrap {
        opacity: 0;
        transform: translateY(16px);
    }
    .join-form-wrap.is-in { animation: joinRise .8s cubic-bezier(.22,.61,.36,1) .18s forwards; }

    /* Exit motion when they join: the form lifts and dissolves. */
    .join-form-wrap.is-out {
        animation: joinExit .45s cubic-bezier(.4,0,.2,1) forwards;
    }

    @keyframes joinRise {
        to { opacity: 1; transform: translateY(0); }
    }
    @keyframes joinExit {
        to { opacity: 0; transform: translateY(-14px) scale(.97); }
    }

    /* Pill-shaped field + full-width CTA, matching the reference's stacked
       layout rather than the inline pill used elsewhere on the site. */
    /* One capsule containing both field and button, rather than the
       reference's stacked full-width pair -- takes roughly half the
       vertical space, which is what lets the hero fit on a phone. */
    /* Class, not #joinForm: the shared module prefixes every id per
       instance (the homepage overlay renders #ovjoinForm), so id-based
       rules matched only the standalone page and the overlay's button fell
       back to browser defaults. */
    .join-form {
        display: flex;
        align-items: center;
        max-width: 430px;
        margin: 0 auto;
        padding: 4px 4px 4px 18px;
        border-radius: 9999px;
        background: rgba(255, 255, 255, 0.055);
        box-shadow: inset 0 0 0 1px rgba(216, 185, 111, 0.22),
                    0 14px 42px rgba(0, 0, 0, 0.36);
        transition: box-shadow .25s, background .25s;
    }
    .join-form:focus-within {
        background: rgba(255, 255, 255, 0.075);
        box-shadow: inset 0 0 0 1px rgba(216, 185, 111, 0.7),
                    0 14px 46px rgba(0, 0, 0, 0.42),
                    0 0 0 4px rgba(216, 185, 111, 0.06);
    }

    .join-form input[type="email"] {
        /* flex:1 + min-width:0 so the button always gets its full width and
           the field absorbs whatever's left -- never the reverse. */
        flex: 1 1 auto;
        min-width: 0;
        padding: 9px 0;
        border: none;
        background: transparent;
        color: var(--text-primary);
        font-family: 'General Sans', -apple-system, BlinkMacSystemFont, sans-serif;
        /* 16px minimum -- anything smaller makes iOS Safari zoom the
           viewport in when the field takes focus. */
        font-size: 16px;
        outline: none;
    }
    .join-form input[type="email"]::placeholder { color: rgba(255,255,255,0.42); }

    .join-form button {
        flex: 0 0 auto;
        white-space: nowrap;
        padding: 10px 19px;
        border-radius: 9999px;
        border: none;
        background: linear-gradient(135deg, #E5C878, #CDA454);
        color: #17130C;
        font-family: 'General Sans', -apple-system, BlinkMacSystemFont, sans-serif;
        font-size: 14px;
        font-weight: 600;
        letter-spacing: -0.01em;
        cursor: pointer;
        box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.32),
                    0 8px 22px rgba(205, 164, 84, 0.16);
        transition: transform .18s cubic-bezier(.22,.61,.36,1), filter .2s, opacity .2s;
    }
    .join-form button:hover:not(:disabled) { filter: brightness(1.08); }
    .join-form button:active:not(:disabled) { transform: scale(.98); }
    .join-form button:disabled { opacity: .6; cursor: not-allowed; }

    .join-note {
        margin-top: 12px;
        font-size: 12px;
        font-weight: 400;
        letter-spacing: 0.025em;
        color: rgba(245, 243, 238, 0.42);
    }

    .join-error {
        margin-top: 10px;
        font-size: 14px;
        color: var(--warning);
        min-height: 20px;
    }

    /* Confetti surface. Overlays the form area, ignores pointer events so
       it can never intercept a tap on the field or button, and is wider than
       the pill so particles can arc outward before falling out of frame. */
    .join-confetti {
        position: absolute;
        left: 50%;
        top: 50%;
        width: min(560px, 100vw);
        height: 340px;
        transform: translate(-50%, -50%);
        pointer-events: none;
        z-index: 5;
    }

    /* ── Success state ──
       Occupies the same grid cell as the form so the swap doesn't shift
       anything around it. */
    .join-swap { display: grid; position: relative; }
    .join-swap > * { grid-area: 1 / 1; }

    .join-success {
        opacity: 0;
        transform: translateY(18px) scale(.96);
        pointer-events: none;
        display: flex;
        flex-direction: column;
        align-items: center;
    }
    .join-success.is-in {
        animation: joinRise .6s cubic-bezier(.22,.61,.36,1) .1s forwards;
    }

    .join-check {
        width: 62px; height: 62px;
        border-radius: 50%;
        display: grid; place-items: center;
        background: rgba(214, 178, 94, 0.14);
        box-shadow: inset 0 0 0 1px rgba(214, 178, 94, 0.5);
        margin-bottom: 20px;
    }
    .join-check svg { width: 30px; height: 30px; display: block; }
    .join-check path {
        stroke: #D6B25E;
        stroke-width: 2.5;
        stroke-linecap: round;
        stroke-linejoin: round;
        fill: none;
        /* Drawn on, rather than appearing at once. */
        stroke-dasharray: 32;
        stroke-dashoffset: 32;
    }
    .join-success.is-in .join-check path {
        animation: joinDraw .5s cubic-bezier(.65,0,.35,1) .3s forwards;
    }
    .join-success.is-in .join-check {
        animation: joinPulse .7s cubic-bezier(.22,.61,.36,1) .25s;
    }
    @keyframes joinDraw { to { stroke-dashoffset: 0; } }
    @keyframes joinPulse {
        0%   { box-shadow: inset 0 0 0 1px rgba(214,178,94,.5), 0 0 0 0 rgba(214,178,94,.4); }
        100% { box-shadow: inset 0 0 0 1px rgba(214,178,94,.5), 0 0 0 26px rgba(214,178,94,0); }
    }

    .join-success-title {
        font-size: 24px;
        font-weight: 300;
        margin-bottom: 8px;
    }
    .join-success-sub {
        font-size: 15px;
        color: var(--text-secondary);
        line-height: 1.6;
    }

    /* Thin progress line that visibly fills over the redirect delay, so the
       hand-off to checkout reads as intentional rather than abrupt. */
    .join-progress {
        margin-top: 24px;
        width: 132px; height: 2px;
        border-radius: 2px;
        background: rgba(255,255,255,0.12);
        overflow: hidden;
    }
    .join-progress span {
        display: block; height: 100%; width: 0;
        background: linear-gradient(to right, #D6B25E, #F0C070);
        border-radius: 2px;
    }
    .join-success.is-in .join-progress span {
        animation: joinFill var(--redirect-ms, 2600ms) linear .35s forwards;
    }
    @keyframes joinFill { to { width: 100%; } }


    /* Short viewports (most phones): scale the stack down further so the
       form and its button never fall below the fold. The video gives up the
       most room since it's supporting material, not the call to action. */
    @media (max-height: 780px) {
        .join-pill { margin-bottom: 12px; font-size: 13px; padding: 6px 15px 6px 8px; }
        .join-face { width: 22px; height: 22px; }
        .join-title { margin-bottom: 10px; }
        .join-media { max-width: 168px; margin-bottom: 14px; }
        .join-sub { font-size: 15px; margin-bottom: 30px; }
        .join-note { margin-top: 10px; font-size: 12px; }
        .join-error { min-height: 0; }
    }

    @media (max-height: 660px) {
        .join-media { max-width: 132px; }
        .join-sub { margin-bottom: 22px; }
    }

    /* Honour the OS-level motion preference: keep the layout and the state
       changes, drop the movement. */
    @media (prefers-reduced-motion: reduce) {
        .join-pill, .join-title, .join-media, .join-sub, .join-form-wrap,
        .join-success, .join-check path, .join-check, .join-progress span,
        .join-pill.is-in .join-face {
            animation: none !important;
            opacity: 1;
            transform: none;
        }
        .join-check path { stroke-dashoffset: 0; }
    }

/* ══════════════════════════════════════════════════════════════════
   Homepage intro modal
   Everything below is scoped under .join-overlay, so the standalone
   /join page keeps its original full-bleed hero untouched. The same
   markup is reused; only its presentation changes in here.
   ══════════════════════════════════════════════════════════════════ */

.join-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    /* Flex column + `margin: auto` on the card, NOT grid/place-items:center.
       Centring a scroll container's child that way makes overflow spill
       *upward* into space that can never be scrolled back to -- so when the
       taller success state swapped in on a short viewport (an in-app
       browser), the top of the modal was cut off with no way to reach it.
       Auto margins centre identically while keeping overflow reachable. */
    display: flex;
    flex-direction: column;
    /* Generous viewport margins, plus safe-area on notched devices. */
    padding: max(28px, env(safe-area-inset-top)) 28px max(28px, env(safe-area-inset-bottom));
    /* The site stays visible behind, softly blurred rather than hidden. */
    background: rgba(4, 4, 5, 0.62);
    -webkit-backdrop-filter: blur(20px) saturate(115%);
    backdrop-filter: blur(20px) saturate(115%);
    overflow-y: auto;
    opacity: 0;
    transition: opacity .4s cubic-bezier(.22,.61,.36,1);
}
.join-overlay.is-open { opacity: 1; }
.join-overlay.is-closing { opacity: 0; pointer-events: none; }

/* `display: grid` above beats the UA stylesheet's `[hidden] { display: none }`,
   so a closed overlay stayed in the layout as a full-viewport layer and went
   on swallowing every click on the page behind it -- which is why the navbar
   email field couldn't be focused after dismissing the modal. */
.join-overlay[hidden] { display: none; }

/* position:fixed, not just overflow:hidden -- iOS Safari ignores
   overflow:hidden on <body>, so focusing the email field (which opens the
   keyboard) scrolled the page behind the modal. Dismissing then left the
   reader stranded partway down. The offsetting `top` is written from JS at
   open time and the scroll position is restored on close. */
body.join-overlay-open {
    position: fixed;
    left: 0;
    right: 0;
    width: 100%;
    overflow: hidden;
}

/* ── The card ── */
.join-modal {
    position: relative;
    /* Auto margins do the centring -- see .join-overlay above. */
    margin: auto;
    width: 100%;
    min-width: 0;
    max-width: 1120px;
    /* Never taller than the viewport; the body scrolls internally instead. */
    max-height: calc(100svh - 56px);
    max-height: calc(100dvh - 56px);
    display: flex;
    background: #08080A;
    border: 1px solid rgba(214, 178, 94, 0.26);
    border-radius: 22px;
    overflow: hidden;
    box-shadow:
        0 48px 130px rgba(0, 0, 0, 0.72),
        0 12px 36px rgba(0, 0, 0, 0.5);
    transform: translateY(14px) scale(.986);
    transition: transform .5s cubic-bezier(.22,.61,.36,1);
}
.join-overlay.is-open .join-modal { transform: none; }

/* Focused programmatically on open in some layouts; it's a wrapper, not a
   control, so it must never paint a focus ring. Kept out of the mobile
   block so it applies wherever the container takes focus. */
.join-modal:focus { outline: none; }

.join-modal-body {
    flex: 1 1 auto;
    min-width: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

.join-close {
    position: absolute;
    top: 16px;
    right: 16px;
    z-index: 10;
    width: 36px;
    height: 36px;
    display: grid;
    place-items: center;
    border: none;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.06);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
    color: rgba(245, 245, 243, 0.7);
    cursor: pointer;
    transition: background .2s, color .2s;
}
.join-close:hover { background: rgba(255, 255, 255, 0.12); color: #F5F5F3; }
.join-close:focus-visible { outline: 2px solid #D6B25E; outline-offset: 2px; }
.join-close svg { width: 15px; height: 15px; display: block; }

/* ── Hero, restyled for the card ──
   Strips the full-screen framing so the hero becomes a panel. */
.join-overlay .join-hero {
    min-height: 0;
    padding: 0;
    background: transparent;
    display: block;
    overflow: visible;
}

/* Two columns: copy left, media right. The media spans every row so it
   fills the full height of the card whatever the copy does. Left-hand
   children carry their own margins rather than the grid carrying padding,
   so the media can still bleed to the card's edge. */
.join-overlay .join-content {
    max-width: none;
    min-width: 0;
    display: grid;
    grid-template-columns: 1.02fr 0.98fr;
    /* Explicit rows, one per copy element (pill, title, sub, form). Without
       them every row is implicit, and `grid-row: 1 / -1` on the media
       resolves against the *explicit* grid -- which was empty, so the media
       spanned a single row instead of the full height. */
    grid-template-rows: auto auto auto auto;
    align-items: start;
    text-align: left;
    z-index: auto;
}

/* justify-self:start, or the pill stretches: it is `inline-flex`, but a grid
   item is blockified to `flex`, so without this it fills the whole copy
   column and its contents sit clustered at the left with a wide empty gap
   on the right -- reading as badly centred. `start` makes it hug its
   contents and sit on the copy column's left edge, with the headline. */
.join-overlay .join-pill   { grid-column: 1; justify-self: start; margin: 56px 56px 20px; }
/* Equal gaps above and below the sub-line so it sits optically centred in
   the band between headline and form. Was 16px above / 30px below, which
   read as the line hugging the headline -- more noticeable once the second
   sentence was removed and the block shrank to one line. */
.join-overlay .join-title  { grid-column: 1; margin: 0 56px 23px; }
.join-overlay .join-sub    { grid-column: 1; margin: 0 56px 23px; max-width: 430px; }
.join-overlay .join-swap   { grid-column: 1; margin: 0 56px 56px; }

.join-overlay .join-media {
    grid-column: 2;
    grid-row: 1 / -1;
    align-self: stretch;
    position: relative;
    max-width: none;
    width: 100%;
    height: auto;
    margin: 0;
    border-radius: 0;
    box-shadow: none;
    overflow: hidden;
}
/* Absolute, so the video contributes no intrinsic height. In flow its
   natural 540x608 ratio inflated every row it spanned, stretching the card
   to its max-height, spreading the copy apart and clipping the footnote.
   Out of flow, the grid is sized purely by the copy column and this panel
   simply fills whatever height that produces. */
.join-overlay .join-video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* ── Typography ── */
.join-overlay .join-title {
    font-size: clamp(30px, 2.5vw, 40px);
    line-height: 1.14;
    color: #F5F5F3;
}
.join-overlay .join-sub {
    font-size: 15.5px;
    line-height: 1.6;
    color: rgba(245, 245, 243, 0.62);
}
.join-overlay .join-pill { font-size: 13px; }
.join-overlay .join-note {
    margin-top: 14px;
    text-align: center;
    color: rgba(245, 245, 243, 0.4);
}
.join-overlay .join-error { text-align: center; }

/* ── Form: stacked, full-width, unmistakable ── */
.join-overlay .join-form {
    display: block;
    max-width: none;
    margin: 0;
    padding: 0;
    background: none;
    box-shadow: none;
    border-radius: 0;
}
.join-overlay .join-form:focus-within { background: none; box-shadow: none; }

.join-overlay .join-form input[type="email"] {
    display: block;
    width: 100%;
    height: 52px;
    padding: 0 20px;
    border-radius: 12px;
    background: #0E0E11;
    box-shadow: inset 0 0 0 1px rgba(245, 245, 243, 0.14);
    color: #F5F5F3;
    font-size: 16px;
    transition: box-shadow .2s, background .2s;
}
.join-overlay .join-form input[type="email"]::placeholder {
    color: rgba(245, 245, 243, 0.36);
}
.join-overlay .join-form input[type="email"]:focus {
    background: #121216;
    box-shadow: inset 0 0 0 1px rgba(214, 178, 94, 0.7);
}

.join-overlay .join-form button {
    display: block;
    width: 100%;
    height: 52px;
    margin-top: 10px;
    padding: 0 24px;
    border-radius: 12px;
    background: #D6B25E;
    color: #0A0A0A;
    font-size: 15px;
    font-weight: 500;
    letter-spacing: .01em;
    transition: background .2s, opacity .2s;
}
.join-overlay .join-form button:hover:not(:disabled) { background: #E0BE70; }
.join-overlay .join-form button:active:not(:disabled) { transform: none; background: #C9A552; }
.join-overlay .join-form button:focus-visible { outline: 2px solid #F5F5F3; outline-offset: 2px; }

/* The success block shares a grid cell with the form so the two can swap
   in place. It's taller than the form, though, and an opacity:0 element
   still reserves its height -- which left a band of dead space under the
   CTA. display:none removes it from layout until it's actually needed; the
   .is-in class both reveals it and triggers its entrance animation. */
.join-overlay .join-success { display: none; }
.join-overlay .join-success.is-in { display: flex; }

/* Confetti is anchored to the copy column, where the button is. */
.join-overlay .join-confetti { left: 26%; width: min(460px, 90%); }

/* Narrow desktop windows keep the two-column orientation, so pull in the
   gutters and headline rather than letting the copy column crush. */
@media (max-width: 820px) and (hover: hover) and (pointer: fine) {
    .join-overlay .join-pill  { margin: 32px 32px 14px; }
    .join-overlay .join-title { margin: 0 32px 12px; font-size: clamp(22px, 3.2vw, 30px); }
    .join-overlay .join-sub   { margin: 0 32px 20px; font-size: 14px; }
    .join-overlay .join-swap  { margin: 0 32px 32px; }
}

/* ══════════════════════════════════════════════════════════════════
   Mobile: not a card at all
   The modal drops its border, radius, shadow and backdrop blur and
   becomes a full-bleed black surface -- a seamless continuation of the
   site rather than a panel floating over it. Desktop above is untouched.
   ══════════════════════════════════════════════════════════════════ */
/* Gated on a coarse pointer, not width alone: a desktop window dragged
   narrow was flipping to the stacked phone layout. Desktop browsers keep
   the side-by-side orientation at every window size; only touch devices
   get the single-column card. */
@media (max-width: 900px) and (hover: none) and (pointer: coarse) {
    .join-overlay {
        /* ~20px of breathing room around the card on every side. */
        padding: max(20px, env(safe-area-inset-top)) 20px max(20px, env(safe-area-inset-bottom));
        background: rgba(0, 0, 0, 0.82);
        -webkit-backdrop-filter: blur(12px);
        backdrop-filter: blur(12px);
    }

    .join-modal {
        /* Single source of truth for the content grid: every left-aligned
           block and every centred block derives from this one value, so the
           edges cannot drift apart as rules are edited. */
        --join-gutter: 24px;
        margin: auto;
        width: 100%;
        max-width: none;
        /* Sized to its content rather than the viewport, so it reads as a
           card floating on the page instead of a takeover. */
        height: auto;
        max-height: calc(100svh - 40px);
        max-height: calc(100dvh - 40px);
        background: #000;
        /* Charcoal hairline + diffused shadow: enough to separate the card
           from the backdrop without the gold ring drawing attention. */
        border: 1px solid #24242A;
        border-radius: 20px;
        box-shadow: 0 24px 70px rgba(0, 0, 0, 0.72);
        /* Clips the hero image to the rounded corners. */
        overflow: hidden;
    }

    /* A plain glyph -- no circle, border or fill. Out of flow (absolute),
       so it takes no part in the grid and cannot shift the pill. */
    .join-close {
        position: absolute;
        top: 12px;
        right: 12px;
        width: 26px;
        height: 26px;
        background: none;
        box-shadow: none;
        color: #8A8A8F;
    }
    .join-close:hover { background: none; color: #C9C9CE; }
    .join-close svg { width: 14px; height: 14px; stroke-width: 1.5; }

    /* Social-proof pill, scaled for the narrower card. The right margin
       clears the close button (26px wide, 12px from the edge) so the two
       can never collide; the pill is inline-flex, so on a very narrow
       screen it shrinks rather than sliding underneath. */
    /* justify-self centres the pill in its grid cell, so it is centred on
       the card itself rather than on whatever margins happen to flank it.
       Equal left/right padding, and align-items:center on the base rule
       keeps the coins and text on a shared centre line. */
    .join-overlay .join-pill {
        display: inline-flex;
        /* Centred in its own grid cell, which spans the full card width --
           so this is centred on the card, not on whatever flanks it. */
        justify-self: center;
        /* Coins and text share one centre line. */
        align-items: center;
        margin: 24px var(--join-gutter) 18px;
        /* Optical, not geometric: the four overlapping gold coins carry far
           more visual weight than the text, so with equal 12/12 padding the
           contents read as sitting right of centre even though the box is
           centred to a hundredth of a pixel. Shifting 2px from the left to
           the right pulls the perceived centre of mass back. Total padding
           is unchanged (24px), so the pill's width -- and therefore its own
           mathematical centring -- is untouched. */
        padding: 4px 14px 4px 10px;
        font-size: 11.5px;
        gap: 8px;
    }
    .join-overlay .join-face { width: 16px; height: 16px; margin-right: -6px; }
    .join-overlay .join-face:nth-child(4) { margin-right: 0; }

    .join-modal-body { display: flex; flex-direction: column; }
    .join-overlay .join-hero {
        flex: 1 1 auto;
        display: flex;
        flex-direction: column;
        min-height: 0;
    }
    .join-overlay .join-content {
        grid-template-columns: 1fr;
        /* All rows content-sized: the card hugs its contents.
           pill / title / media / copy / form. */
        grid-template-rows: auto auto auto auto auto;
        flex: 1 1 auto;
        min-height: 0;
    }

    /* Homepage type: .section-title's 28px/300 and .hero-sub's 17px/1.7. */
    .join-overlay .join-title {
        margin: 0 var(--join-gutter) 0;
        font-size: 28px;
        font-weight: 300;
        line-height: 1.3;
        letter-spacing: normal;
    }
    .join-overlay .join-sub {
        /* Narrower gutters than the 24px grid, which is safe *because* this
           text is centred -- it has no left edge to align to the headline
           and form. Buys the width the sentence needs at 14px. */
        /* 12px top / 20px bottom + the swap's 22px top margin gives the
           line 44px of clear space on each side -- deliberate breathing
           room, and still symmetric. */
        margin: 12px 18px 20px;
        max-width: none;
        font-size: 14px;
        text-align: center;
        /* Measured: at 14px the first sentence needs 317.3px. Widening to
           an 18px gutter gives 314px, and this much tracking brings it to
           ~307px -- one line, with real margin, without the crushed look of
           the -0.03em it would take to fit the original 300px column. */
        letter-spacing: -0.012em;
        /* The card is narrower than the viewport, so the first sentence
           wraps; `pretty` prevents the last word being left alone on its
           own line. `balance` was tried first and narrowed both lines
           instead, which read worse. */
        text-wrap: pretty;
        font-weight: 400;
        line-height: 1.55;
        color: var(--text-secondary);
    }

    /* Edge to edge, generous, and the visual anchor of the screen. */
    /* Full width of the card, clipped by its rounded corners. ~40% taller
       than before so the photography carries the screen. */
    .join-overlay .join-media {
        grid-column: 1;
        grid-row: auto;
        height: clamp(258px, 45vh, 380px);
        min-height: 0;
        /* Full bleed: no gutters, so it meets both card edges exactly. */
        width: 100%;
        /* Bottom margin here is the whitespace above the copy block; it's
           matched to the 32px below it (20px on the copy + 12px on the form)
           so the copy sits centred between the image and the field. */
        margin: 22px 0 32px;
    }
    .join-overlay .join-video { object-position: 50% 38%; }

    /* The form column: same gutters as the copy above it, so the field's
       left edge sits on the headline's left edge exactly. */
    .join-overlay .join-swap {
        /* 10px here + the copy's own 20px bottom margin lands the gap below
           the copy at the same 32px measured above it, so the line sits
           optically centred between the image and the form. (12px measured
           34 against 32 -- the 2px is the line box's descender slack.) */
        margin: 22px var(--join-gutter) 24px;
    }

    /* Matches the navbar signup control: same translucent capsule, same
       inset hairline, same gold button at 14px. Vertical padding is the
       one deliberate difference -- the navbar's 1px would give a ~30px
       tap target, well under the ~44px minimum for a primary action. */
    .join-overlay .join-form {
        display: flex;
        align-items: center;
        /* Equal left/right margins within the content column. */
        width: 100%;
        margin-inline: auto;
        gap: 10px;
        padding: 6px 6px 6px 20px;
        border: none;
        border-radius: 1000px;
        background: rgba(255, 255, 255, 0.08);
        box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07);
        transition: background .18s ease, box-shadow .18s ease;
    }
    .join-overlay .join-form:focus-within {
        background: rgba(255, 255, 255, 0.12);
        box-shadow: inset 0 0 0 1px rgba(214, 178, 94, 0.55);
    }
    .join-overlay .join-form input[type="email"] {
        /* 16px minimum, or iOS Safari zooms the viewport on focus. */
        flex: 1 1 auto;
        min-width: 40px;
        height: 34px;
        padding: 0;
        border-radius: 0;
        background: transparent;
        box-shadow: none;
        font-size: 16px;
    }
    .join-overlay .join-form input[type="email"]:focus {
        background: transparent;
        box-shadow: none;
    }
    .join-overlay .join-form input[type="email"]::placeholder {
        color: rgba(255, 255, 255, 0.55);
        font-size: 15px;
    }
    .join-overlay .join-form button {
        flex: 0 0 auto;
        width: auto;
        height: 34px;
        margin-top: 0;
        padding: 0 16px;
        border-radius: 9999px;
        background: #D6B25E;
        color: var(--bg);
        font-size: 14px;
        font-weight: 400;
        white-space: nowrap;
    }

    /* Centred directly beneath the form, on the same column. */
    .join-overlay .join-note {
        width: 100%;
        margin-inline: auto;
        text-align: center;
        margin-top: 12px;
        font-size: 12px;
        color: rgba(245, 245, 243, 0.32);
    }

    /* Empty on load, but a <p> still generates a line box -- that was
       ~30px of dead space sitting under "Launching 2026" and pushing the
       whole block off the card's bottom edge. Collapse it until it has a
       message; setting textContent brings it (and its reserved space)
       straight back. */
    .join-overlay .join-error:empty { display: none; }

    .join-overlay .join-confetti { left: 50%; }
}

/* Very short screens: pull the vertical rhythm in so the form and its
   footnote stay above the fold without internal scrolling. */
@media (max-width: 900px) and (max-height: 700px) and (hover: none) and (pointer: coarse) {
    .join-overlay .join-pill  { margin-top: 18px; margin-bottom: 12px; }
    .join-overlay .join-title { font-size: 25px; }
    .join-overlay .join-sub   { margin-bottom: 16px; }
    .join-overlay .join-media { height: clamp(200px, 32vh, 260px); margin: 16px 0 16px; }
    .join-overlay .join-swap  { margin-bottom: 20px; }
}

@media (prefers-reduced-motion: reduce) {
    .join-overlay { transition: none; }
    .join-modal { transition: none; transform: none; }
}
