/*
 * Scroll story for the welcome page: deck transitions, transmission wire,
 * scrollytelling scenes and section emblems.
 * Everything animated is scoped under html.story-on, which scroll-story.js
 * adds only when the page has deck panels and the visitor allows motion.
 * Without that class (no JS, reduced motion, other pages) the page renders
 * statically: emblems and scene one fully drawn, no pinning.
 */

/* ---------- Wire segments (one SVG per panel, drawn by scroll-story.js) ---------- */

/*
 * Desktop: the wire rides above the section content (it only ever travels
 * through gutters and top padding, so it never sits on text) and stays below
 * each panel's dimming ::after (z-index 999), so covered sections fade as
 * one. Mobile: cards nearly touch the screen edge where the rail runs, so
 * the wire drops behind the content there.
 */
.panel-wire {
    position: absolute;
    left: 0;
    z-index: 5;
    pointer-events: none;
    display: block;
}

@media (max-width: 767px) {
    .panel-wire {
        z-index: -1;
    }
}

html:not(.story-on) .panel-wire {
    display: none;
}

/* One color from the form to the CTA burst, so the wire reads as a single
   unbroken line instead of restyling per section */
.wire-trace {
    fill: none;
    stroke: #22d3ee;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.wire-glow-path {
    fill: none;
    stroke: rgba(34, 211, 238, 0.14);
    stroke-width: 7;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.wire-node {
    fill: #0b1020;
    stroke: rgba(148, 163, 184, 0.35);
    stroke-width: 1.5;
    transition: fill 0.4s ease, stroke 0.4s ease, filter 0.4s ease;
}

.wire-node.lit {
    fill: #22d3ee;
    stroke: rgba(34, 211, 238, 0.9);
    filter: drop-shadow(0 0 6px rgba(34, 211, 238, 0.9));
}

/* Terminal node: the packet "self-destructs" here */
.wire-node-end {
    transform-box: fill-box;
    transform-origin: center;
}

.wire-node-end.lit {
    fill: #34d399;
    stroke: rgba(52, 211, 153, 0.9);
    filter: drop-shadow(0 0 12px rgba(52, 211, 153, 0.9));
    animation: wire-burst 0.9s cubic-bezier(0.22, 1, 0.36, 1) 1;
}

@keyframes wire-burst {
    0% {
        transform: scale(1);
    }

    45% {
        transform: scale(2.6);
        opacity: 0.85;
    }

    100% {
        transform: scale(1.4);
        opacity: 1;
    }
}

.wire-packet {
    fill: #22d3ee;
    filter: drop-shadow(0 0 7px rgba(34, 211, 238, 1));
    opacity: 0;
    transition: opacity 0.3s ease;
}

/* ---------- Rail medallions: themed ports that pop when the line arrives ---------- */

.wire-emblem .we-ring {
    fill: #0b1020;
    stroke: rgba(148, 163, 184, 0.35);
    stroke-width: 1.5;
    transition: stroke 0.4s ease, filter 0.4s ease;
}

.wire-emblem .we-icon {
    fill: none;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: 0.35;
    transition: opacity 0.4s ease;
}

.wire-emblem .we-spark {
    stroke: #22d3ee;
    stroke-width: 2;
    stroke-linecap: round;
    opacity: 0;
    transform-box: fill-box;
    transform-origin: center;
}

.wire-emblem .we-pop {
    transform-box: fill-box;
    transform-origin: center;
}

.wire-emblem.lit .we-ring {
    stroke: rgba(34, 211, 238, 0.9);
    filter: drop-shadow(0 0 8px rgba(34, 211, 238, 0.7));
}

.wire-emblem.lit .we-icon {
    opacity: 1;
}

html.story-on .wire-emblem.lit .we-pop {
    animation: wire-emblem-pop 0.7s cubic-bezier(0.34, 1.56, 0.64, 1) 1;
}

html.story-on .wire-emblem.lit .we-spark {
    animation: wire-spark-pop 0.6s ease-out 1 both;
}

@keyframes wire-emblem-pop {
    0% {
        transform: scale(0.4);
    }

    100% {
        transform: scale(1);
    }
}

@keyframes wire-spark-pop {
    0% {
        opacity: 0;
        transform: scale(0.4);
    }

    35% {
        opacity: 1;
    }

    100% {
        opacity: 0;
        transform: scale(1.25);
    }
}

/* On mobile the rail runs behind the cards at the screen edge; a half-buried
   medallion reads as a glitch, so the plain rail stays alone there */
@media (max-width: 767px) {
    .wire-emblem {
        display: none;
    }
}

/* Origin emitter: a glowing dot marking where the wire is born at the form */
.wire-source {
    fill: #22d3ee;
    stroke: rgba(34, 211, 238, 0.9);
    stroke-width: 1.5;
    transform-box: fill-box;
    transform-origin: center;
    filter: drop-shadow(0 0 8px rgba(34, 211, 238, 0.95));
}

html.story-on .wire-source {
    animation: wire-source-pulse 2.2s ease-in-out infinite;
}

@keyframes wire-source-pulse {

    0%,
    100% {
        transform: scale(1);
        opacity: 0.9;
    }

    50% {
        transform: scale(1.35);
        opacity: 1;
    }
}

/* Section headings pulse once when the wire reaches them */
.wire-lit {
    animation: wire-heading-glow 1.2s ease-out 1;
}

@keyframes wire-heading-glow {
    0% {
        text-shadow: none;
    }

    30% {
        text-shadow: 0 0 26px rgba(34, 211, 238, 0.75);
    }

    100% {
        text-shadow: none;
    }
}

/* ---------- Deck: sections pin, recede and get covered by the next ---------- */

html.story-on [data-deck-hero] {
    position: -webkit-sticky;
    position: sticky;
    transform-origin: 50% 20%;
}

html.story-on .deck-panel {
    position: -webkit-sticky;
    position: sticky;
    margin-top: -3rem;
    /* extra tail room so a section's last elements get read before the next
       panel slides over them */
    padding-bottom: 9rem;
    border-radius: 2.5rem 2.5rem 0 0;
    background: #06060d;
    border-top: 1px solid rgba(34, 211, 238, 0.14);
    box-shadow: 0 -30px 80px rgba(0, 0, 0, 0.65);
    transform-origin: 50% 0;
    /* transform is applied by scroll-story.js only while a panel is being
       covered, so inner position:sticky (scrollytelling media) keeps working
       while the panel is read */
}

/* Neon hairline along the top edge of each incoming panel */
html.story-on .deck-panel::before {
    content: "";
    position: absolute;
    top: -1px;
    left: 8%;
    right: 8%;
    height: 1px;
    background: linear-gradient(90deg,
            transparent,
            rgba(34, 211, 238, 0.55),
            rgba(59, 130, 246, 0.55),
            transparent);
    pointer-events: none;
    z-index: 2;
}

/* Darkening overlay while a section is being covered (cheap, compositor only) */
html.story-on [data-deck-hero]::after,
html.story-on .deck-panel::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: #030308;
    opacity: calc(var(--cover, 0) * 0.85);
    pointer-events: none;
    z-index: 999;
}

/*
 * Only the panel currently in transition gets composited layers: its scale
 * and its dim overlay then animate on the compositor instead of repainting
 * the panel's full content texture every scroll frame (the main source of
 * scroll jank). The class comes and goes with the transition, so the extra
 * layer memory is held for at most one or two panels at a time.
 */
html.story-on [data-deck-hero].is-covering,
html.story-on .deck-panel.is-covering {
    will-change: transform;
}

html.story-on [data-deck-hero].is-covering::after,
html.story-on .deck-panel.is-covering::after {
    will-change: opacity;
}

/* The receding panel's wire gets the inverse scale every frame; keep that
   on the compositor too */
html.story-on [data-deck-hero].is-covering .panel-wire,
html.story-on .deck-panel.is-covering .panel-wire {
    will-change: transform;
}

/* Fully covered panels stop painting entirely */
html.story-on [data-deck-hero].is-covered,
html.story-on .deck-panel.is-covered {
    visibility: hidden;
}

/*
 * While scroll-story.js measures flow positions it must neutralize sticky
 * AND the inline top offsets, otherwise tall panels are measured shifted up
 * by their negative sticky top. Added and removed within one task, so this
 * never paints.
 */
html.story-on.story-measure [data-deck-hero],
html.story-on.story-measure .deck-panel {
    position: relative !important;
    top: auto !important;
    transform: none !important;
}

/*
 * AOS elements wait for their entrance in a shifted/scaled state
 * (fade-up sits 100px low, zoom-in at scale .6). Wire anchors measured in
 * that state land below their real targets, so measurement reads every
 * AOS element at its final resting geometry. transition:none matters:
 * without it the transform change animates over AOS's 1s transition and a
 * synchronous getBoundingClientRect still reads the shifted matrix.
 */
html.story-on.story-measure [data-aos] {
    transition: none !important;
    transform: none !important;
}

/* The footer ends the page: no tail padding, the ghost tagline is the crop */
html.story-on footer.deck-panel {
    padding-bottom: 0;
}

@media (max-width: 767px) {
    html.story-on .deck-panel {
        margin-top: -1.5rem;
        border-radius: 1.25rem 1.25rem 0 0;
    }
}

/* ---------- Section emblems: security elements that pop and draw in ---------- */

.section-emblem {
    pointer-events: none;
}

.section-emblem path,
.section-emblem circle,
.section-emblem rect,
.section-emblem line {
    fill: none;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.section-emblem text {
    stroke: none;
}

/* Hidden strokes + pop only when the story engine runs */
html.story-on .section-emblem {
    opacity: 0;
    transform: scale(0.7) translateY(14px);
}

html.story-on .section-emblem [pathLength] {
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
}

html.story-on .section-emblem text {
    opacity: 0;
}

html.story-on .section-emblem.emblem-draw {
    opacity: 1;
    transform: none;
    transition: opacity 0.6s ease, transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}

html.story-on .section-emblem.emblem-draw [pathLength] {
    animation: emblem-stroke 1.1s cubic-bezier(0.45, 0, 0.25, 1) forwards;
}

html.story-on .section-emblem.emblem-draw text {
    opacity: 1;
    transition: opacity 0.5s ease 0.9s;
}

/* Stagger the strokes so the drawing builds up */
html.story-on .section-emblem.emblem-draw [pathLength]:nth-of-type(2) { animation-delay: 0.12s; }
html.story-on .section-emblem.emblem-draw [pathLength]:nth-of-type(3) { animation-delay: 0.24s; }
html.story-on .section-emblem.emblem-draw [pathLength]:nth-of-type(4) { animation-delay: 0.36s; }
html.story-on .section-emblem.emblem-draw [pathLength]:nth-of-type(5) { animation-delay: 0.48s; }
html.story-on .section-emblem.emblem-draw [pathLength]:nth-of-type(6) { animation-delay: 0.6s; }
html.story-on .section-emblem.emblem-draw [pathLength]:nth-of-type(7) { animation-delay: 0.72s; }
html.story-on .section-emblem.emblem-draw [pathLength]:nth-of-type(8) { animation-delay: 0.84s; }
html.story-on .section-emblem.emblem-draw [pathLength]:nth-of-type(9) { animation-delay: 0.96s; }
html.story-on .section-emblem.emblem-draw [pathLength]:nth-of-type(10) { animation-delay: 1.08s; }
html.story-on .section-emblem.emblem-draw [pathLength]:nth-of-type(11) { animation-delay: 1.2s; }
html.story-on .section-emblem.emblem-draw [pathLength]:nth-of-type(12) { animation-delay: 1.32s; }

@keyframes emblem-stroke {
    to {
        stroke-dashoffset: 0;
    }
}

/* The pricing heart beats after it draws in */
.emblem-heart.emblem-draw .emblem-beat {
    transform-box: fill-box;
    transform-origin: center;
    animation: emblem-heartbeat 2.4s ease-in-out 1.6s infinite;
}

@keyframes emblem-heartbeat {

    0%,
    100% {
        transform: scale(1);
    }

    12% {
        transform: scale(1.08);
    }

    24% {
        transform: scale(1);
    }

    36% {
        transform: scale(1.05);
    }
}

/* ---------- Scrollytelling: sticky scene + scroll-stepped text ---------- */

.scrolly-scene svg {
    width: 100%;
    height: auto;
    display: block;
}

.scrolly-scene [data-scene] {
    opacity: 0;
    transition: opacity 0.45s ease;
}

.scrolly-scene [data-scene].is-active {
    opacity: 1;
}

.scrolly-scene [data-scene] path,
.scrolly-scene [data-scene] circle,
.scrolly-scene [data-scene] rect,
.scrolly-scene [data-scene] line {
    fill: none;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.scrolly-scene [data-scene] text {
    stroke: none;
}

html.story-on .scrolly-scene [data-scene] [pathLength] {
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
}

html.story-on .scrolly-scene [data-scene].is-active [pathLength] {
    animation: emblem-stroke 0.9s cubic-bezier(0.45, 0, 0.25, 1) forwards;
}

html.story-on .scrolly-scene [data-scene].is-active [pathLength]:nth-of-type(2) { animation-delay: 0.08s; }
html.story-on .scrolly-scene [data-scene].is-active [pathLength]:nth-of-type(3) { animation-delay: 0.16s; }
html.story-on .scrolly-scene [data-scene].is-active [pathLength]:nth-of-type(4) { animation-delay: 0.24s; }
html.story-on .scrolly-scene [data-scene].is-active [pathLength]:nth-of-type(5) { animation-delay: 0.32s; }
html.story-on .scrolly-scene [data-scene].is-active [pathLength]:nth-of-type(6) { animation-delay: 0.4s; }
html.story-on .scrolly-scene [data-scene].is-active [pathLength]:nth-of-type(7) { animation-delay: 0.48s; }
html.story-on .scrolly-scene [data-scene].is-active [pathLength]:nth-of-type(8) { animation-delay: 0.56s; }
html.story-on .scrolly-scene [data-scene].is-active [pathLength]:nth-of-type(9) { animation-delay: 0.64s; }
html.story-on .scrolly-scene [data-scene].is-active [pathLength]:nth-of-type(10) { animation-delay: 0.72s; }
html.story-on .scrolly-scene [data-scene].is-active [pathLength]:nth-of-type(11) { animation-delay: 0.8s; }
html.story-on .scrolly-scene [data-scene].is-active [pathLength]:nth-of-type(12) { animation-delay: 0.88s; }
html.story-on .scrolly-scene [data-scene].is-active [pathLength]:nth-of-type(13) { animation-delay: 0.96s; }
html.story-on .scrolly-scene [data-scene].is-active [pathLength]:nth-of-type(14) { animation-delay: 1.04s; }

html.story-on .scrolly-scene [data-scene] text {
    opacity: 0;
}

html.story-on .scrolly-scene [data-scene].is-active text {
    opacity: 1;
    transition: opacity 0.4s ease 0.8s;
}

/* Fallback when the engine is off: show scene one fully drawn */
html:not(.story-on) .scrolly-scene [data-scene]:first-of-type {
    opacity: 1;
}

/* Steps dim until the reader reaches them */
html.story-on .scrolly-step {
    opacity: 0.35;
    transition: opacity 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease;
}

/* Stacked layout: the pinned console already focuses the eye, so waiting
   cards stay readable instead of washing out */
@media (max-width: 1023px) {
    html.story-on .scrolly-step {
        opacity: 0.6;
    }
}

html.story-on .scrolly-step.is-active {
    opacity: 1;
    border-color: rgba(34, 211, 238, 0.4);
    box-shadow: 0 0 40px rgba(34, 211, 238, 0.08), inset 0 0 24px rgba(34, 211, 238, 0.03);
}

.scrolly-step .step-index {
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.2em;
}

/* Progress dots in the scene console footer: the active layer lights up */
.scene-dot {
    background: rgba(255, 255, 255, 0.14);
    transition: background 0.35s ease, box-shadow 0.35s ease;
}

.scene-dot.is-on {
    background: linear-gradient(90deg, #22d3ee, #3b82f6);
    box-shadow: 0 0 12px rgba(34, 211, 238, 0.55);
}

/* ---------- Idle layer: scenes and emblems keep living after the draw ---------- */

/* Hidden until the stroke drawing finishes, then fades in and loops */
html.story-on .scrolly-scene [data-scene] .idle,
html.story-on .section-emblem .idle {
    opacity: 0;
}

html.story-on .scrolly-scene [data-scene].is-active .idle,
html.story-on .section-emblem.emblem-draw .idle {
    opacity: 1;
    transition: opacity 0.6s ease 1.15s;
}

/* Continuous rotation (timer hand, radar sweep, vault dial). Inline styles
   set transform-origin and can override duration/delay per element. */
.rot-cw {
    transform-box: view-box;
}

html.story-on [data-scene].is-active .rot-cw,
html.story-on .section-emblem.emblem-draw .rot-cw {
    animation: story-spin 8s linear infinite;
}

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

/* Blinking text cursor in the browser scene */
html.story-on [data-scene].is-active .caret {
    animation: story-blink 1.1s steps(1) infinite;
}

@keyframes story-blink {
    50% {
        opacity: 0;
    }
}

/* Cipher glyphs shimmer as if recomputing */
html.story-on [data-scene].is-active .glyphs {
    animation: story-shimmer 2.4s ease-in-out infinite;
}

@keyframes story-shimmer {

    0%,
    100% {
        opacity: 0.35;
    }

    50% {
        opacity: 1;
    }
}

/* Expanding ring (download badge, team nodes) */
.ping {
    transform-box: fill-box;
    transform-origin: center;
}

html.story-on [data-scene].is-active .ping,
html.story-on .section-emblem.emblem-draw .ping {
    animation: story-ping 2.6s cubic-bezier(0, 0, 0.2, 1) infinite;
}

@keyframes story-ping {
    0% {
        transform: scale(1);
        opacity: 0.7;
    }

    75%,
    100% {
        transform: scale(1.6);
        opacity: 0;
    }
}

/* Sparks and sparkles flicker */
html.story-on [data-scene].is-active .flick,
html.story-on .section-emblem.emblem-draw .flick {
    animation: story-flicker 1.3s steps(2) infinite alternate;
}

@keyframes story-flicker {
    0% {
        opacity: 0.25;
    }

    100% {
        opacity: 1;
    }
}

/* Burned fragments drift away and fade */
.drift {
    transform-box: fill-box;
}

html.story-on [data-scene].is-active .drift {
    animation: story-drift 2.8s ease-in-out infinite;
}

@keyframes story-drift {
    0% {
        transform: translate(0, 0);
        opacity: 0.9;
    }

    80% {
        opacity: 0;
    }

    100% {
        transform: translate(var(--dx, 22px), var(--dy, -14px));
        opacity: 0;
    }
}

/* 2FA broadcast waves radiate from the phone */
.wave {
    transform-box: view-box;
}

html.story-on [data-scene].is-active .wave {
    animation: story-wave 2.2s ease-out infinite;
}

@keyframes story-wave {
    0% {
        transform: scale(0.88);
        opacity: 0.15;
    }

    45% {
        opacity: 0.9;
    }

    100% {
        transform: scale(1.18);
        opacity: 0;
    }
}

/* Entropy bars equalize */
.eq {
    transform-box: view-box;
}

html.story-on [data-scene].is-active .eq {
    animation: story-eq 1.1s ease-in-out 1.2s infinite alternate;
}

@keyframes story-eq {
    from {
        transform: scaleY(1);
    }

    to {
        transform: scaleY(0.55);
    }
}

/* TOTP code rotates between two values */
html.story-on [data-scene].is-active .code-a {
    animation: story-code 5s steps(1) infinite;
}

html.story-on [data-scene].is-active .code-b {
    animation: story-code 5s steps(1) 2.5s infinite;
}

@keyframes story-code {

    0%,
    49% {
        opacity: 1;
    }

    50%,
    100% {
        opacity: 0;
    }
}

/* The lock shackle clicks shut right after it draws */
html.story-on [data-scene].is-active .shackle-click {
    animation: story-shackle 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 1.25s 1 both;
}

@keyframes story-shackle {
    0% {
        transform: translateY(-8px);
    }

    100% {
        transform: translateY(0);
    }
}

/* Packets travel from the vault to each team node */
html.story-on .section-emblem.emblem-draw .hub-dot {
    animation: story-hub 3.2s ease-in-out infinite;
}

@keyframes story-hub {
    0% {
        transform: translate(0, 0);
        opacity: 0;
    }

    12% {
        opacity: 1;
    }

    88% {
        opacity: 1;
    }

    100% {
        transform: translate(var(--tx, 80px), var(--ty, 60px));
        opacity: 0;
    }
}

/* Current running through connectors (heart pulse line, hub traces,
   scene links): a bright dash train that never stops moving */
html.story-on .section-emblem.emblem-draw .ecg-flow,
html.story-on [data-scene].is-active .ecg-flow {
    stroke-dasharray: 6 16;
    animation: story-dashrun 1.4s linear infinite;
}

@keyframes story-dashrun {
    to {
        stroke-dashoffset: -44;
    }
}

/* The FAQ question mark bobs */
.qm {
    transform-box: view-box;
}

html.story-on .section-emblem.emblem-draw .qm {
    animation: story-bob 2.8s ease-in-out 1.4s infinite;
}

@keyframes story-bob {

    0%,
    100% {
        transform: translateY(0);
    }

    50% {
        transform: translateY(-5px);
    }
}

/* ---------- CTA: the card wakes up when the wire's packet reaches it ---------- */

html.story-on #cta > div {
    filter: brightness(0.45) saturate(0.7);
    transform: scale(0.985);
    transition: filter 0.9s ease, transform 0.9s cubic-bezier(0.22, 1, 0.36, 1);
}

html.story-on #cta .neon-border-animate {
    animation-play-state: paused;
}

html.story-on #cta.cta-lit > div {
    filter: none;
    transform: none;
}

html.story-on #cta.cta-lit .neon-border-animate {
    animation-play-state: running;
}

/* No point spinning the conic border while the card is off screen */
html.story-on #cta:not(.cta-onscreen) .neon-border-animate {
    animation-play-state: paused !important;
}

/* ---------- Footer ghost tagline: homepage story overrides ---------- */

html.story-on .footer-ghost {
    margin-bottom: -0.22em;
    padding-bottom: 0;
}

/* Rises into place the first time it appears */
html.story-on .footer-ghost span {
    opacity: 0;
    transform: translateY(35%);
}

html.story-on .footer-ghost.emblem-draw span {
    opacity: 1;
    transform: none;
    transition: opacity 1s ease, transform 1.1s cubic-bezier(0.22, 1, 0.36, 1);
}
