/* BingeVibe — Thura. Mobile-first, dark. No framework, no build step. */

/* ==========================================================================
   TUNING — change these, everything else follows.
   ========================================================================== */
:root {
    /* ---- Brand ------------------------------------------------------------ */
    --white: #ffffff;
    --ink: #010039;
    --light: #dfedee;
    --coral: #FF6B6B;
    --teal: #00fff0;
    --ink-2: #0a0a52; /* lifted panel over --ink */
    --line: rgba(223,237,238,.28); /* default edge colour */
    --line-soft: rgba(223,237,238,.14); /* dashed / ghost edges */
    /* ---- Shape ------------------------------------------------------------ */
    --radius: 0px; /* buttons, inputs, cards. 0 = hard square */
    --radius-sm: 0px; /* posters, small chips */
    --radius-pill: 999px; /* progress dots only */
    --tap: 52px; /* minimum touch target */
    /* ---- Bevel ------------------------------------------------------------ */
    --bevel: 2px; /* depth. 3px chunky, 1px subtle */
    --bevel-hi: rgba(255,255,255,.22); /* light edge, top-left */
    --bevel-lo: rgba(1,0,57,.55); /* shade edge, bottom-right */
    --bevel-hi-soft: rgba(255,255,255,.14);
    --bevel-hi-bright: rgba(255,255,255,.38);
    --bevel-lo-soft: rgba(1,0,57,.42);
    /* Edge colour. Override per-element to recolour the 1px outline:
     e.g.  .btn-out.is-on{ --edge-color:var(--teal); }                        */
    --edge-color: var(--line);
    --edge: inset 0 0 0 1px var(--edge-color);
    /* Composed shadows — use these, don't hand-write inset stacks.            */
    --raise: var(--edge), inset var(--bevel) var(--bevel) 0 var(--bevel-hi), inset calc(var(--bevel) * -1) calc(var(--bevel) * -1) 0 var(--bevel-lo);
    --press: var(--edge), inset var(--bevel) var(--bevel) 0 var(--bevel-lo), inset calc(var(--bevel) * -1) calc(var(--bevel) * -1) 0 var(--bevel-hi-soft);
    /* Recessed — for inputs. Light and shade inverted so it reads pushed in.  */
    --sink: var(--edge), inset var(--bevel) var(--bevel) 0 var(--bevel-lo), inset calc(var(--bevel) * -1) calc(var(--bevel) * -1) 0 var(--bevel-hi-soft);
    /* Brighter bevel for the gradient button, which has no outline.           */
    --raise-bright: inset var(--bevel) var(--bevel) 0 var(--bevel-hi-bright), inset calc(var(--bevel) * -1) calc(var(--bevel) * -1) 0 var(--bevel-lo-soft);
    /* ---- Motion ----------------------------------------------------------- */
    --t-fast: .16s ease;
    --t-slide: .32s cubic-bezier(.22,.8,.28,1);
    /* ---- Layout ----------------------------------------------------------- */
    --col: 520px; /* max content width */
    --pad: 20px; /* horizontal gutter */
    --gap: 24px; /* space between blocks */
}

/* ==========================================================================
   Base
   ========================================================================== */
[hidden] {
    display: none !important
}

* {
    box-sizing: border-box
}

html, body {
    margin: 0;
    height: 100%;
    max-width: 100%;
    overflow-x: hidden
}

img, svg {
    max-width: 100%
}

body {
    background: var(--ink);
    color: var(--white);
    font: 400 17px/1.5 ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
    -webkit-text-size-adjust: 100%;
    -webkit-font-smoothing: antialiased;
}

/* ---- Glow field: 1 large coral, 3 small teal ---------------------------- */
.glows {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    overflow: hidden;
    width: 100vw;
    max-width: 100%
}

.glow {
    position: absolute;
    border-radius: 50%;
    filter: blur(70px)
}

    .glow.g1 {
        width: 420px;
        height: 420px;
        background: var(--coral);
        opacity: .30;
        top: -130px;
        right: -150px
    }

    .glow.g2 {
        width: 190px;
        height: 190px;
        background: var(--teal);
        opacity: .20;
        top: 38%;
        left: -90px
    }

    .glow.g3 {
        width: 150px;
        height: 150px;
        background: var(--teal);
        opacity: .16;
        bottom: 16%;
        right: -70px
    }

    .glow.g4 {
        width: 210px;
        height: 210px;
        background: var(--teal);
        opacity: .13;
        bottom: -110px;
        left: 22%
    }

@media (prefers-reduced-motion:no-preference) {
    .glow.g2 {
        animation: drift 22s ease-in-out infinite alternate
    }

    .glow.g3 {
        animation: drift 28s ease-in-out infinite alternate-reverse
    }
}

@keyframes drift {
    from {
        transform: translate3d(0,0,0)
    }

    to {
        transform: translate3d(26px,-32px,0)
    }
}

/* ---- Shell -------------------------------------------------------------- */
.wrap {
    position: relative;
    z-index: 1;
    max-width: var(--col);
    margin: 0 auto;
    padding: 14px var(--pad) calc(28px + env(safe-area-inset-bottom));
    min-height: 100%;
    display: flex;
    flex-direction: column
}

.topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    min-height: 40px;
    margin-bottom: 22px
}

.logo {
    height: 26px;
    width: auto;
    display: block
}

/* The ScreenSona mark above the intro's name fields. Sized to the
   viewport on a phone, capped on anything wider. */
.intro-mark {
    display: block;
    width: min(46vw, 170px);
    height: auto;
    margin: 0 auto 6px
}

.logo-fallback {
    font: 600 13px/1 ui-sans-serif,system-ui;
    letter-spacing: .22em;
    text-transform: uppercase;
    color: var(--light);
    opacity: .85;
    text-decoration: none
}

.progress {
    display: flex;
    align-items: center;
    gap: 7px
}

    .progress i {
        display: block;
        width: 8px;
        height: 8px;
        border-radius: var(--radius-pill);
        background: var(--light);
        opacity: .30
    }

        .progress i.on {
            width: 30px;
            border-radius: var(--radius-pill);
            opacity: 1;
            background: var(--teal)
        }

/* ---- Blocks & type ------------------------------------------------------ */
.block {
    margin-bottom: var(--gap)
}

.block-grow {
    flex: 1 1 auto
}

.foot-block {
    margin-top: auto;
    padding-top: 14px
}

h1 {
    font-size: 30px;
    line-height: 1.14;
    font-weight: 700;
    margin: 0 0 8px;
    letter-spacing: -.01em
}

h2 {
    font-size: 21px;
    font-weight: 600;
    margin: 0 0 10px
}

.med {
    font-size: 17px;
    color: var(--light);
    margin: 0 0 6px
}

.small {
    font-size: 13px;
    color: var(--light);
    opacity: .75;
    margin: 0
}

.teal {
    color: var(--teal)
}

.coral {
    color: var(--coral)
}

.wordmark {
    font-size: clamp(38px,13vw,64px);
    font-weight: 800;
    letter-spacing: -.02em;
    line-height: 1;
    margin: 0 0 10px
}

    .wordmark .b {
        color: var(--white)
    }

    .wordmark .v {
        color: var(--teal)
    }

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    width: 100%;
    min-height: var(--tap);
    padding: 15px 18px;
    font: 600 17px/1.2 inherit;
    text-align: center;
    text-decoration: none;
    border: 0;
    border-radius: var(--radius);
    box-shadow: var(--raise);
    background: transparent;
    color: var(--light);
    cursor: pointer;
    position: relative;
    transition: background var(--t-fast),color var(--t-fast), box-shadow var(--t-fast),transform .1s ease;
}

    .btn:active {
        transform: translate(1px,1px);
        box-shadow: var(--press)
    }

/* Gradient primary. Outer glow uses filter, which survives any future clip. */
.btn-grad {
    color: var(--ink);
    font-weight: 800;
    letter-spacing: .02em;
    background: linear-gradient(135deg,var(--teal) 0%,var(--teal) 55%,var(--coral) 100%);
    box-shadow: var(--raise-bright);
    filter: drop-shadow(0 8px 20px rgba(0,255,240,.22));
}

    .btn-grad:active {
        box-shadow: inset var(--bevel) var(--bevel) 0 var(--bevel-lo-soft), inset calc(var(--bevel) * -1) calc(var(--bevel) * -1) 0 var(--bevel-hi-bright)
    }

/* Outline. Selected states recolour the edge via --edge-color.              */
.btn-out {
    background: var(--ink);
    color: var(--light)
}

    .btn-out.is-on {
        --edge-color: var(--teal);
        background: var(--teal);
        color: var(--ink);
        font-weight: 700;
        box-shadow: var(--press)
    }

    .btn-out input {
        position: absolute;
        opacity: 0;
        pointer-events: none
    }

.btn-ghost {
    --edge-color: transparent;
    color: var(--light);
    opacity: .8;
    font-weight: 500;
    box-shadow: none
}

.btn-row {
    display: flex;
    gap: 10px
}

    .btn-row .btn {
        flex: 1
    }

.stack {
    display: flex;
    flex-direction: column;
    gap: 11px
}

.btn-chip {
    text-align: left;
    justify-content: flex-start;
    font-weight: 500;
    font-size: 16px;
    line-height: 1.35;
    padding: 16px;
    /* A chip is a whole sentence about the title (0023, #70), so it wraps:
       the flex child may shrink below its text and a long word breaks
       rather than pushing the button past 360 px. */
    white-space: normal;
    overflow-wrap: anywhere
}

    .btn-chip > span {
        min-width: 0
    }

/* ==========================================================================
   Form fields — recessed, so they read as inputs not buttons
   ========================================================================== */
.field {
    display: flex;
    flex-direction: column;
    gap: 5px;
    margin-bottom: 10px
}

    .field span {
        font-size: 13px;
        color: var(--light);
        opacity: .8
    }

    .field input {
        font: inherit;
        color: var(--white);
        background: rgba(255,255,255,.06);
        border: 0;
        border-radius: var(--radius);
        padding: 14px;
        min-height: var(--tap);
        width: 100%;
        min-width: 0;
        box-shadow: var(--sink);
    }

        .field input::placeholder {
            color: var(--light);
            opacity: .5
        }

        .field input:focus {
            outline: none;
            --edge-color: var(--teal)
        }

.field-row {
    display: flex;
    gap: 10px
}

/* Consent row under the e-mail field (intro). A native checkbox, teal when
   ticked; the label is the whole row so the tap target is not a 20px box. */
.consent {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin: 2px 0 0;
    font-size: 14px;
    line-height: 1.4;
    color: var(--light);
    cursor: pointer
}

    .consent input {
        flex: none;
        width: 20px;
        height: 20px;
        margin: 1px 0 0;
        accent-color: var(--teal);
        cursor: pointer
    }

.consent-note {
    margin: -4px 0 4px 30px
}

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

/* ==========================================================================
   Search + poster rail (page 2)
   ========================================================================== */
.search {
    position: relative;
    margin-bottom: 6px
}

    .search svg {
        position: absolute;
        left: 15px;
        top: 50%;
        transform: translateY(-50%);
        width: 19px;
        height: 19px;
        stroke: var(--light);
        opacity: .7;
        fill: none;
        stroke-width: 2;
        z-index: 1;
        pointer-events: none
    }

    .search input {
        --edge-color: var(--light);
        width: 100%;
        min-width: 0;
        min-height: var(--tap);
        padding: 14px 14px 14px 44px;
        font: inherit;
        color: var(--white);
        border: 0;
        border-radius: var(--radius);
        background: rgba(1,0,57,.10);
        backdrop-filter: blur(6px);
        box-shadow: var(--sink);
    }

        .search input::placeholder {
            color: var(--light);
            opacity: .6
        }

        .search input:focus {
            outline: none;
            --edge-color: var(--teal)
        }

.railwrap {
    position: relative;
    min-height: 186px
}

.rail {
    align-items:flex-start;
    display: flex;
    gap: 12px;
    overflow-x: auto;
    padding: 2px 0 10px;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none
}

    .rail::-webkit-scrollbar {
        display: none
    }

    .rail .pick {
        flex: 0 0 116px;
        scroll-snap-align: start;
        background: none;
        border: 0;
        padding: 0;
        cursor: pointer;
        text-align: left;
        color: var(--white)
    }

        .rail .pick .poster {
            width: 116px;
            height: 160px
        }

        .rail .pick .nm {
            display: -webkit-box;
            -webkit-line-clamp: 2;
            -webkit-box-orient: vertical;
            overflow: hidden;
            font-size: 13px;
            line-height: 1.25;
            margin-top: 6px;
            color: var(--light);
            min-height: calc(2 * 1.25em);
        }

.empty {
    position: relative;
    height: 186px;
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: inset 0 0 0 1px var(--line-soft);
    display: grid;
    place-items: center
}

    .empty img {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        object-fit: cover;
        filter: blur(9px);
        opacity: .5
    }

    .empty .ph {
        position: absolute;
        inset: 0;
        filter: blur(22px);
        opacity: .5;
        background: linear-gradient(120deg,var(--coral),var(--ink) 45%,var(--teal))
    }

    .empty span {
        position: relative;
        font-size: 28px;
        font-weight: 700;
        letter-spacing: -.01em;
        color: var(--white);
        text-shadow: 0 2px 12px rgba(1,0,57,.9)
    }

/* ---- The request tile (P12) ------------------------------------------- */
/* The last tile in the rail whenever the box holds a real query. It is a
   .pick like the posters, so width, snap and the two-line name come from
   the rules above; only the face differs: no artwork, a quiet ghost edge,
   the search glyph and the word "Search", with the query in the .nm line
   under it. Disabled with its own short line once the session's cap is
   reached. */
    .rail .pick-ask .poster-ask {
        grid-auto-flow: row;
        align-content: center;
        gap: 10px;
        background: rgba(1,0,57,.32);
        box-shadow: inset 0 0 0 1px var(--line);
        font-size: 15px;
        letter-spacing: .02em;
        color: var(--white)
    }

        .rail .pick-ask .poster-ask svg {
            width: 32px;
            height: 32px;
            stroke: var(--teal);
            stroke-width: 2;
            fill: none
        }

        .rail .pick-ask .nm {
            color: var(--white)
        }

        /* The press itself. A plain button inside the face, so the two
           type pills can sit beside it (a button cannot hold buttons). */
        .rail .pick-ask .ask-go {
            display: grid;
            justify-items: center;
            gap: 10px;
            width: 100%;
            padding: 0;
            border: 0;
            background: none;
            font: inherit;
            letter-spacing: inherit;
            color: inherit;
            cursor: pointer
        }

            .rail .pick-ask .ask-go:disabled {
                opacity: .45;
                cursor: default
            }

        /* Film / Series: a two-way toggle, neither pressed = either. */
        .rail .pick-ask .ask-type {
            display: flex;
            gap: 6px;
            justify-content: center
        }

            .rail .pick-ask .ask-type button {
                font: inherit;
                font-size: 11px;
                line-height: 1;
                letter-spacing: .02em;
                padding: 5px 9px;
                border-radius: 999px;
                border: 1px solid var(--line);
                background: transparent;
                color: var(--light);
                cursor: pointer
            }

                .rail .pick-ask .ask-type button.is-on {
                    background: var(--teal);
                    border-color: var(--teal);
                    color: var(--ink)
                }

                .rail .pick-ask .ask-type button:disabled {
                    opacity: .45;
                    cursor: default
                }

/* One line for everything the request path says: recorded, not found, a
   problem, or "Added" when the title lands. Sits under the rail. */
.rail-note {
    margin: 4px 0 0;
    font-size: 14px;
    line-height: 1.4;
    font-weight: 600;
    color: var(--light)
}

/* ---- Deck of cards ------------------------------------------------------ */
.deck {
    position: relative;
    min-height: 190px;
    display: flex;
    flex-direction: column;
    gap: 8px
}

    .deck .slot {
        display: flex;
        align-items: center;
        gap: 12px;
        padding: 9px 12px;
        border-radius: var(--radius);
        box-shadow: var(--raise);
        background: rgba(255,255,255,.05);
        animation: dealin .3s ease
    }

        .deck .slot .poster {
            width: 38px;
            height: 52px;
            font-size: 13px;
            flex: none
        }

        .deck .slot .nm {
            flex: 1;
            font-size: 15px;
            line-height: 1.25
        }

        .deck .slot .x {
            background: none;
            border: 0;
            color: var(--light);
            font-size: 20px;
            cursor: pointer;
            padding: 6px 8px;
            line-height: 1
        }

    .deck .ghost {
        border: 1px dashed var(--line-soft);
        border-radius: var(--radius);
        min-height: 70px;
        display: grid;
        place-items: center;
        font-size: 13px;
        color: var(--light);
        opacity: .45
    }

@keyframes dealin {
    from {
        opacity: 0;
        transform: translateY(-16px) scale(.97)
    }

    to {
        opacity: 1;
        transform: none
    }
}

/* ---- Poster tiles ------------------------------------------------------- */
.poster {
    display: grid;
    place-items: center;
    border-radius: var(--radius-sm);
    font-weight: 700;
    color: #fff;
    letter-spacing: .04em;
    font-size: 20px;
    overflow: hidden;
    text-align: center;
    background: linear-gradient(150deg,hsl(var(--h) 62% 40%),hsl(var(--h2) 66% 22%));
    box-shadow: 0 6px 18px rgba(0,0,0,.35)
}

.poster-wide {
    width: 100%;
    aspect-ratio: 16/9;
    font-size: 26px
}

/* ---- Title card (pages 3 & 4) ------------------------------------------ */
.titlecard {
    animation: slidein var(--t-slide)
}

@keyframes slidein {
    from {
        opacity: 0;
        transform: translateX(38px)
    }

    to {
        opacity: 1;
        transform: none
    }
}

.titlecard .poster {
    width: 120px;
    height: 166px;
    margin: 0 auto 12px
}

.titlecard h2 {
    text-align: center;
    margin-bottom: 2px
}

.titlecard .small {
    text-align: center
}

.chipset {
    animation: slidein var(--t-slide)
}

/* ---- Reaction squares (page 4) ------------------------------------------ */
.reacts {
    display: grid;
    grid-template-columns: repeat(4,1fr);
    gap: 9px
}

    .reacts .rx {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 7px;
        padding: 14px 4px;
        min-height: 84px;
        border: 0;
        border-radius: var(--radius);
        box-shadow: var(--raise);
        background: var(--ink);
        color: var(--light);
        font-size: 12px;
        font-weight: 600;
        cursor: pointer;
        position: relative;
        text-align: center;
        transition: background var(--t-fast),color var(--t-fast),box-shadow var(--t-fast)
    }

        .reacts .rx svg {
            width: 24px;
            height: 24px;
            fill: none;
            stroke: currentColor;
            stroke-width: 2
        }

        .reacts .rx input {
            position: absolute;
            opacity: 0;
            pointer-events: none
        }

        .reacts .rx.is-on {
            --edge-color: var(--teal);
            background: var(--teal);
            color: var(--ink);
            box-shadow: var(--press)
        }


/* ---- Vibe heading (page 5) ---------------------------------------------- */
.vibe-head {
    --vibe-align: center; /* left | center | right */
    text-align: var(--vibe-align);
}

.vibe-label {
    font-size: clamp(34px,11vw,52px);
    font-weight: 800;
    letter-spacing: .1em;
    line-height: 1.05;
    margin: 0 0 10px;
}

.vibe-name {
    font-size: 16px;
    font-weight: 600;
    letter-spacing: .10em;
    text-transform: uppercase;
    color: var(--light);
    opacity: .85;
    margin: 0;
}
/* ---- Spiderchart (page 5) ----------------------------------------------- */
.chartwrap {
    margin: 6px 0 14px;
    text-align: center
}

.hexchart {
    width: 100%;
    max-width: 330px;
    height: auto
}

    .hexchart .ring {
        fill: none;
        stroke: var(--light);
        stroke-width: 2;
        opacity: .55
    }

    .hexchart .spoke {
        stroke: var(--light);
        stroke-width: 1;
        opacity: .28
    }

    .hexchart .shape {
        stroke: var(--teal);
        stroke-width: 1
    }

    .hexchart .inner {
        fill: none;
        stroke: var(--light);
        stroke-width: 1;
        opacity: .5;
        stroke-dasharray: 3 3
    }

    .hexchart .lbl {
        font-size: 10.5px;
        fill: var(--light);
        opacity: .9;
        font-family: inherit;
        letter-spacing: .04em;
        text-transform: uppercase
    }

/* ---- Share -------------------------------------------------------------- */
/* Five square buttons, one size, centred: Facebook, Instagram, X, Text,
   Link (DECISIONS #75). The side is the tap target, so the row is 5 x 52px
   plus gaps and fits a 320px viewport with room to spare. */
.share {
    display: flex;
    gap: 12px;
    justify-content: center
}

    .share button {
        flex: 0 0 var(--tap);
        width: var(--tap);
        height: var(--tap);
        display: grid;
        place-items: center;
        border: 0;
        border-radius: var(--radius);
        box-shadow: var(--raise);
        background: transparent;
        color: var(--light);
        cursor: pointer;
        transition: box-shadow var(--t-fast),transform .1s ease
    }

        .share button:active {
            transform: translate(1px,1px);
            box-shadow: var(--press)
        }

    .share svg {
        width: 21px;
        height: 21px;
        fill: currentColor
    }

/* ---- Services (page 6) -------------------------------------------------- */
.svcgrid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px
}

    .svcgrid .btn {
        flex-direction: column;
        gap: 6px;
        font-size: 15px;
        padding: 12px 10px
    }

.btn-out.svc.is-on {
    --edge-color: var(--light);
    background: rgba(223,237,238,.5);
    color: var(--ink)
}

/* Logo plate. The selected state above is a PALE wash and six of the nine
   marks are white with transparency, so the plate keeps its own --ink
   background in both states. Unselected it is invisible against the button;
   selected it reads as a deliberate dark tile. Either way the mark never
   sits on the pale fill. The caption below it is the readable label
   regardless, and is the whole button when there is no mark. */
.svcgrid .svc-plate {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 40px;
    padding: 0 6px;
    background: var(--ink);
    border-radius: var(--radius-sm)
}

/* Per-service height cap. Every mark is a wide wordmark - ratios 1.84
   (Disney+) to 5.52 (Crunchyroll) - and ink coverage of the frame runs 18%
   (Disney+) to 59% (Netflix), so one flat height for all nine reads badly
   uneven: Netflix looks shouty next to a Disney+ half its apparent size.
   Each cap is a 22px base scaled by (coverage x ratio) ^ -0.3 and rounded -
   part way between equal height and equal ink area, which is where the nine
   look like one set. max-width takes over on narrow phones; Crunchyroll is
   the first to hit it, at roughly 360px viewport and below.

   The cap is a TOKEN, --svc-cap, so both surfaces that draw these marks work
   from one set of numbers: the picker uses it as authored, the
   recommendations rail scales it down. Retune here and both follow. */
.svc-mark {
    display: block;
    width: auto;
    max-width: 100%;
    object-fit: contain;
    --svc-cap: 22px
}

.svc-mark[data-svc="disney"]      { --svc-cap: 30px }
.svc-mark[data-svc="prime"]       { --svc-cap: 24px }
.svc-mark[data-svc="peacock"]     { --svc-cap: 24px }
.svc-mark[data-svc="paramount"]   { --svc-cap: 23px }
.svc-mark[data-svc="apple"]       { --svc-cap: 22px }
.svc-mark[data-svc="hulu"]        { --svc-cap: 20px }
.svc-mark[data-svc="crunchyroll"] { --svc-cap: 20px }
.svc-mark[data-svc="netflix"]     { --svc-cap: 18px }
.svc-mark[data-svc="hbomax"]      { --svc-cap: 17px }

.svcgrid .svc-mark { max-height: var(--svc-cap) }

/* No logo file - a catalog service config/services.php does not list. The
   name is then the whole button, which is why it is not shown otherwise:
   beside a mark it only repeats what the mark already says. */
.svcgrid .svc-name {
    font-size: 13px;
    font-weight: 600;
    line-height: 1.2;
    text-align: center
}

.svcgrid .svc-textonly .svc-name {
    font-size: 15px
}

/* ---- Final recommendations --------------------------------------------- */
.recrail{display:flex;gap:14px;overflow-x:auto;padding-bottom:12px;
         scroll-snap-type:x mandatory;margin:0 -20px;padding-left:20px;padding-right:20px;
         scrollbar-width:none}
.recrail::-webkit-scrollbar{display:none}
.reccard{flex:0 0 168px;scroll-snap-align:start}
.reccard .poster{width:168px;height:232px}
.reccard .nm{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
              overflow:hidden;font-size:15px;font-weight:600;margin:8px 0 2px;
              line-height:1.25;min-height:calc(2 * 1.25em)}
.reccard .svc{display:block;font-size:12px;color:var(--light);opacity:.7;margin-bottom:8px}
/* 1:2 - "more" is a word, the action button carries a logo and needs the
   room to render it legibly. */
.reccard .two{display:grid;grid-template-columns:1fr 2fr;gap:7px}
.reccard .two a,.reccard .two .more{display:flex;align-items:center;justify-content:center;
                font-size:12px;text-align:center;padding:9px 4px;border-radius:9px;
                border:1px solid var(--line);color:var(--light);text-decoration:none}
/* "learn more" wears the teal fill the action button used to. The action
   button goes dark so the service marks - transparent PNGs, six of the nine
   white - sit straight on it with nothing behind them. */
.reccard .two .more{background:var(--teal);color:var(--ink);border-color:var(--teal);
                      font-weight:700;text-transform:uppercase;letter-spacing:.04em}
.reccard .two a.go{background:var(--ink);color:var(--white);border-color:var(--line);font-weight:700}

.pricebox{display:grid;grid-template-columns:1fr 1fr;gap:12px;align-items:center;
          padding:16px;border:1px solid var(--line);border-radius:var(--radius);
          background:rgba(255,255,255,.05);margin-bottom:16px}
.pitch{font-size:17px;font-weight:700;margin:0 0 10px}
.bullets{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:7px}
.bullets li{font-size:15px;color:var(--light);display:flex;gap:9px;align-items:center}
.bullets li::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--teal);flex:none}


/* ---- Recs page (7) additions -------------------------------------------- */
.disclosure{line-height:1.4;opacity:.65;font-style:italic}
.svc-head{font-size:19px;font-weight:700;margin:0 0 12px}
/* The primary offer (features.primary_offer): one service mark on the ink
   ground the rails' action buttons use, drawn larger, then the pitch. */
.offer-mark{display:flex;align-items:center;justify-content:center;min-height:72px;
            padding:14px 18px;margin-bottom:12px;border-radius:var(--radius);
            background:var(--ink);border:1px solid var(--line)}
.offer-mark .svc-mark{max-height:calc(var(--svc-cap) * 1.6)}
.offer-mark .svclogo-text{font-size:17px}
.offer-pitch{margin:0}
.sub-block{padding-top:8px;border-top:1px solid var(--line-soft)}
.sub-head{font-size:22px;font-weight:700;line-height:1.2;margin:18px 0 14px}
.recrail{align-items:flex-start}
.reccard .two .more{--edge-color:var(--teal);background:var(--teal);color:var(--ink);font-weight:700}
.reccard .two a.go{--edge-color:var(--line);background:var(--ink);color:var(--white)}


/* The action button on every rail: a short label over the service's own
   mark, so the logo says where it plays and the name is only a fallback.
   The button is ThuraInk with a light edge, which is what lets the marks
   sit on it unplated - they are transparent PNGs and six of the nine are
   white. Heights come from --svc-cap, the picker's per-service numbers,
   scaled to this card. */
.reccard .two a.go-watch{display:flex;flex-direction:column;align-items:center;
                         justify-content:center;gap:5px;padding:7px 4px;
                         min-height:46px;overflow:hidden}
.reccard .go-label{font-size:10px;font-weight:700;line-height:1;
                   letter-spacing:.02em;text-transform:uppercase;opacity:.8}
.reccard .svc-mark{max-height:calc(var(--svc-cap) * .78)}

/* No logo file: the service name carries the button instead. */
.svclogo-text{font-size:11px;font-weight:700;line-height:1.1;text-align:center;
              letter-spacing:.01em;overflow:hidden;display:-webkit-box;
              -webkit-line-clamp:2;-webkit-box-orient:vertical}

/* ---- Misc --------------------------------------------------------------- */
/* The wait line under a submit button (react.php, last reaction). */
.busy-line{text-align:center;margin-top:10px;opacity:.85}
.error{background:rgba(255,107,107,.16);border:1px solid var(--coral);
       color:var(--white);padding:11px 14px;border-radius:var(--radius);
       font-size:14px;margin-bottom:14px}
.sitefoot{margin-top:26px;padding-top:14px;border-top:1px solid var(--line-soft);
          font-size:12px;color:var(--light);opacity:.55}
.sitefoot a{color:var(--light)}
.modal{position:fixed;inset:0;z-index:9;background:rgba(1,0,57,.86);
       display:none;place-items:center;padding:24px}
.modal.on{display:grid}
.modal .panel{background:var(--ink-2);border:1px solid var(--line);
              border-radius:var(--radius);padding:22px;max-width:420px;width:100%}

            
/* ---- Combined rail divider ---------------------------------------------
   Shown only when features.affiliate_live is false, separating titles on
   services the user has from titles on services they do not.

   The page background is already --ink, so a "dark bar" needs to be darker
   than the page AND carry light hairlines, or it disappears. Height is tied
   to the poster (232px) so it reads as a full-height bar beside the cards
   rather than a floating line.                                            */
.raildiv{flex:0 0 10px;align-self:stretch;min-height:232px;margin:0 6px;
         background:#000016;
         border-left:1px solid var(--line-soft);
         border-right:1px solid var(--line-soft);
         scroll-snap-align:start}

/* Titles on services the user did NOT select. Held back visually so the
   watchable ones read first, without hiding them.                         */
.reccard.is-other .poster{opacity:.62}
.reccard.is-other .svc{opacity:.55}
.reccard .two a.go-alt{background:transparent;color:var(--light);
                       border-color:var(--line);font-weight:600;
                       font-size:11px;overflow:hidden;white-space:nowrap;
                       text-overflow:ellipsis}

/* ---- Share block -------------------------------------------------------- */
.sharelede{margin:0 0 10px;opacity:.75;letter-spacing:.02em;text-transform:uppercase;
           font-size:11px;font-weight:700}
.sharenote{min-height:1.2em;margin:10px 0 0;opacity:.85}
.sharewarn{margin:8px 0 0;opacity:.7;line-height:1.45}
.sharewarn code{background:rgba(255,255,255,.08);padding:1px 5px;font-size:11px}
.share button.is-on{background:var(--teal);border-color:var(--teal)}
.share button.is-on svg{stroke:var(--ink);fill:var(--ink)}

/* Outcome states on the copy button (copyLink in app.js). Text replaces the
   icon and the button takes a full-width line of its own: the row's seven
   icon cells are about 40px each on a phone and cannot hold a word, and
   flex-wrap is what lets the last cell drop below the others. */
.share{flex-wrap:wrap}
/* The Link button while it reports: it takes a full row of its own so the
   message fits, then returns to its square. */
.share button.is-text{flex:1 0 100%;width:auto;height:auto;min-height:var(--tap);
                      padding:8px 12px;font-family:inherit;font-size:14px;
                      font-weight:700;line-height:1.3}
.share button.is-on{color:var(--ink)}
.share button.is-off{background:rgba(255,107,107,.14);color:var(--light);
                     box-shadow:inset 0 0 0 1px var(--coral)}
/* The Instagram button while the card is still rendering ("Preparing your
   card…"), or when there is no card to hand over: present, visibly
   waiting, not tappable. */
.share button:disabled{opacity:.4;cursor:default;transform:none}
/* The share url as text, revealed when copying fails so it can be selected
   or long-pressed. break-all: a signed token has no spaces to wrap at. */
.sharelink{display:block;margin:8px 0 0;color:var(--teal);line-height:1.45;
           word-break:break-all;-webkit-user-select:all;user-select:all}

/* Arrived from someone else's link. */
.sharedfrom{margin-bottom:6px}
.sharedfrom strong{font-weight:700}

/* ---- Legal pages: privacy, disclosure, contact --------------------------- */
/* Placeholders are loud on purpose: an unfinished policy must not be able to
   ship looking finished. */
.ph{background:var(--coral);color:var(--ink);font-weight:700;padding:1px 6px;
    font-size:.85em}
.draftwarn{border:1px solid var(--coral);padding:10px 12px;line-height:1.5;
           background:rgba(255,107,107,.08)}
.disclosure a{color:var(--light)}
/* There is no global link colour, so an inline link in body copy would fall
   back to the browser's default blue on a navy page - unreadable. Scoped to
   .legal rather than set on .med so it cannot reach .disclosure above, which
   has its own colour and sits at lower specificity. */
.legal a{color:var(--teal)}
/* Only rendered once a program is 'active'. Kept here so it is not a browser
   default the day it first appears. */
.legal-list{margin:0 0 6px;padding-left:22px}
.legal-list li{margin-bottom:4px}

/* Real poster artwork, where a file exists under assets/img/posters/.
   Posters are 2:3; the tiles are not quite, so cover-crop rather than
   letterbox or squash. Falls back to the gradient tile when absent. */
.poster-img{object-fit:cover;display:block}
.reccard .poster-img{width:168px;height:232px}

/* ---- The title popup (P18) ------------------------------------------------
   One component on the react card and every recs card (render.php
   tt_title_popup_*). Title - year on top; two columns, the left third the
   services (the rails' ink "Watch on:" button, one per service, up to
   three), the right two thirds the trailer or the backdrop and synopsis;
   Close at the bottom as it always looked. Phone width stacks the columns,
   services first. It reuses .modal and .panel, only wider.              */
.more[hidden]{display:none}
.reccard .two .more{font:inherit;font-size:11px;font-weight:700;line-height:1.15;
                    cursor:pointer;padding:6px 4px}
.more-card{display:block;margin:10px auto 0;padding:9px 18px;border-radius:9px;
           border:1px solid var(--teal);background:var(--teal);color:var(--ink);
           font:inherit;font-size:12px;font-weight:700;text-transform:uppercase;
           letter-spacing:.04em;cursor:pointer}
.modal .panel.tpop{max-width:760px;max-height:calc(100vh - 48px);max-height:calc(100dvh - 48px);
                   display:flex;flex-direction:column;overflow:hidden}
/* The body scrolls and Close stays put (P19): a long synopsis must never
   push the only way out off a 360 px screen. */
.tpop-body{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;
           -webkit-overflow-scrolling:touch}
.tpop #tpopClose{flex:none;margin-top:12px}
.tpop-head{margin:0 0 14px;font-size:20px;line-height:1.25}
.tpop-grid{display:grid;grid-template-columns:1fr 2fr;gap:14px;margin-bottom:4px;
           align-items:start}
.tpop-grid.no-svc{grid-template-columns:1fr}
.tpop-svcs{display:flex;flex-direction:column;gap:8px}
.tpop-svc{display:flex;flex-direction:column;align-items:center;justify-content:center;
          gap:5px;min-height:52px;padding:8px 6px;border-radius:9px;
          border:1px solid var(--line);background:var(--ink);color:var(--white);
          text-decoration:none;font-weight:700}
.tpop-svc .go-label{font-size:10px;font-weight:700;line-height:1;letter-spacing:.02em;
                    text-transform:uppercase;opacity:.8}
.tpop-svc .svc-mark{max-height:calc(var(--svc-cap) * .78)}
.tpop-svc.is-nolink{opacity:.6}
.tpop-video{position:relative;aspect-ratio:16/9;border-radius:9px;overflow:hidden;
            background:#000016}
.tpop-video iframe{position:absolute;inset:0;width:100%;height:100%;border:0}
.tpop-backdrop{display:block;width:100%;aspect-ratio:16/9;object-fit:cover;border-radius:9px}
.tpop-poster{display:block;width:140px;aspect-ratio:2/3;object-fit:cover;border-radius:9px}
.tpop-synopsis{margin:10px 0 0;font-size:14px;line-height:1.45}
@media (max-width:560px){
  .modal{padding:12px}
  .modal .panel.tpop{padding:16px;max-height:calc(100vh - 24px);max-height:calc(100dvh - 24px)}
  .tpop-grid{grid-template-columns:1fr}
  .tpop-svcs{flex-direction:row}
  .tpop-svc{flex:1 1 0;min-width:0}
}

/* ---- Result storage (P22) ----------------------------------------------
   The results box on the recommendations page and the private result page
   at /r/<id>. Both render only with features.result_storage on. */
.resultbox{padding-top:8px;border-top:1px solid var(--line-soft)}
.resultbox form{margin-top:10px}
.resultbox-msg{margin:10px 0 0;padding:10px 12px;border-radius:9px;line-height:1.45;
               overflow-wrap:anywhere}
.resultbox-msg.is-done{background:rgba(0,255,240,.10);border:1px solid rgba(0,255,240,.45)}
.resultbox-msg.is-error{background:rgba(255,107,107,.16);border:1px solid var(--coral)}
.resultbox-msg a{color:var(--teal)}
.resultlist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:14px}
.resultrow{display:grid;grid-template-columns:92px 1fr;gap:12px;align-items:start}
.resultrow .poster{width:92px;height:128px}
.resultrow-body{min-width:0}
.resultrow .nm{display:block;font-weight:700;line-height:1.3}
.resultrow .svc{display:block;font-size:12px;color:var(--light);opacity:.7;margin:2px 0 8px}
.resultrow-svcs{display:flex;flex-wrap:wrap;gap:7px}
.resultrow-svcs a.go-watch{display:flex;flex-direction:column;align-items:center;justify-content:center;
                           gap:4px;min-width:88px;min-height:46px;padding:6px 8px;border-radius:9px;
                           border:1px solid var(--line);background:var(--ink);color:var(--white);
                           text-decoration:none;font-weight:700}
.resultrow-svcs .go-label{font-size:10px;font-weight:700;line-height:1;letter-spacing:.02em;
                          text-transform:uppercase;opacity:.8}
.resultrow-svcs .svc-mark{max-height:calc(var(--svc-cap) * .7)}

/* ---- The splash (P24, DECISIONS #85) -----------------------------------
   No gate: logo, the ScreenSona mark and wordmark, the persona ticker,
   Begin. The ticker is CSS only, vertical since P26 (#93), and holds
   still under reduced motion. */
.splash-brand{text-align:center}
.splash-brand .intro-mark{width:min(40vw,150px);margin-bottom:10px}
/* 12.5vw: the ten letters fit a 360px screen with the 20px gutters. */
.splash-wordmark{font-size:clamp(40px,12.5vw,72px);margin-bottom:8px}
.splash-tag{margin:0}
.splash-vibes{text-align:center}
.splash-lead{margin:0 0 12px;font-size:19px;font-weight:600;line-height:1.3;color:var(--white)}
.splash-you{margin:12px 0 0;font-size:clamp(34px,11vw,48px);font-weight:800;line-height:1.05;
            letter-spacing:-.02em;color:var(--white)}
.splash-note{text-align:center;margin:0}

/* The names drift UPWARD, as on the app's splash (DECISIONS #93: the owner
   preferred the app's vertical scroll). A fixed window five names tall,
   one name per centred line, faded top and bottom like the app's mask
   (0 / 20% / 80% / 100%). The track holds the list twice and slides up by
   one copy (-50%), so the loop is seamless.

   Speed is the app's, 18 px a second (PersonaNameScroll.speed in the
   app's splash_screen.dart). --ticker-line is the one line height, in px
   without the unit so calc() can divide it; intro.php sets --ticker-n to
   the number of names, so one copy is n x line px tall and takes
   n x line / 18 seconds.

   Nothing moves the layout: the window's height is fixed, every line is
   one line (a long name ends in an ellipsis rather than wrapping), and
   overflow is clipped inside .ticker, so the page never scrolls sideways
   or changes height while the names move. */
.ticker{--ticker-line:34;--ticker-n:10;
        overflow:hidden;width:100%;height:calc(var(--ticker-line) * 5px);
        -webkit-mask-image:linear-gradient(180deg,transparent,#000 20%,#000 80%,transparent);
                mask-image:linear-gradient(180deg,transparent,#000 20%,#000 80%,transparent)}
.ticker-track{display:block}
.ticker-list{display:block;margin:0;padding:0;list-style:none}
.ticker-list li{display:block;height:calc(var(--ticker-line) * 1px);line-height:calc(var(--ticker-line) * 1px);
                padding:0 12px;font-size:17px;font-weight:700;color:var(--teal);text-align:center;
                white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
@media (prefers-reduced-motion:no-preference){
  .ticker-track{animation:ticker calc(var(--ticker-n) * var(--ticker-line) / 18 * 1s) linear infinite}
}
@keyframes ticker{from{transform:translate3d(0,0,0)}to{transform:translate3d(0,-50%,0)}}
/* Reduced motion: held still. The window stays the same height and shows
   the first copy's top names; the second copy, there only for the loop,
   is hidden. */
@media (prefers-reduced-motion:reduce){
  .ticker-copy{display:none}
}

/* ---- The recs-page save box (P24) --------------------------------------
   Submit is dim and ignores taps until the checkbox is ticked (#85). The
   :has() rule works without JS; app.js also blocks the submit itself and
   sets aria-disabled, which the second rule reads for browsers without
   :has(). */
.resultbox-save{margin:4px 0 0;font-weight:600;color:var(--white)}
.resultbox-note{margin:0;text-align:center}

/* ---- The app line (P26) -------------------------------------------------
   Behind features.app_link; plain links until store badges exist. */
.applink{text-align:center}
.applink-head{margin:0 0 6px;font-weight:600;color:var(--white)}
.applink-stores{margin:0;display:flex;flex-wrap:wrap;justify-content:center;gap:6px 18px}
.applink-stores a{color:var(--teal);font-weight:700}
.resultbox-note + .resultbox-note{margin-top:-6px}
.resultbox-form:has(input[name="consent_marketing"]:not(:checked)) .resultbox-submit,
.resultbox-submit[aria-disabled="true"]{opacity:.35;box-shadow:none;pointer-events:none;cursor:not-allowed}

/* The site's address in the top bar (vibe, recs, /r/<id>), right of the logo,
   so a screenshot says where it came from. */
.topbar-url{font-size:14px;font-weight:600;letter-spacing:.01em;color:var(--light);
            text-decoration:none;white-space:nowrap}
