@charset "utf-8";
/* ==========================================================================
   GhostCfg - shared UI layer
   --------------------------------------------------------------------------
   The @charset above must stay on line 1: this file contains the glyphs
   U+29C9 and U+2713 in generated content, and without it they depend on the
   charset nginx happens to send.
   --------------------------------------------------------------------------
   Loaded AFTER each page's inline <style> block, so equal-specificity rules
   here win. Every page keeps its own colours and layout; this file only adds
   typography refinement, the mobile behaviour those pages never had, and a
   few interaction touches.

   No external requests: no web fonts, no CDN. A site with a strict privacy
   page should not be calling out to Google for a typeface.
   ========================================================================== */

:root {
    /* Segoe UI Variable is the modern Windows face and reads sharper than
       plain Segoe UI; the rest of the stack covers macOS, Android and Linux. */
    --font-body: "Segoe UI Variable Text", "Segoe UI", -apple-system,
                 BlinkMacSystemFont, "Inter", "Roboto", "Helvetica Neue",
                 Arial, sans-serif;
    --font-display: "Segoe UI Variable Display", "Segoe UI", -apple-system,
                    BlinkMacSystemFont, "Inter", "Roboto", Arial, sans-serif;
    --font-mono: "Cascadia Code", "JetBrains Mono", "SF Mono", Consolas,
                 "Courier New", monospace;
    --header-h: 78px;
}

*, *::before, *::after {
    font-family: var(--font-body);
    -webkit-tap-highlight-color: rgba(160, 86, 243, 0.25);
}

html {
    /* Anchor jumps and in-page scrolls stop below the fixed header instead
       of underneath it. */
    scroll-padding-top: calc(var(--header-h) + 16px);
    -webkit-text-size-adjust: 100%;
}

body {
    /* Optical polish: kerning and ligatures on, greyscale smoothing so the
       light-on-dark text does not bloom. */
    font-feature-settings: "kern" 1, "liga" 1, "calt" 1;
    text-rendering: optimizeLegibility;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    line-height: 1.6;
}

/* --------------------------------------------------------------------------
   Fluid type. clamp() replaces the fixed rem sizes, so a 5.2rem headline no
   longer overflows a 360px phone and no longer needs a breakpoint per size.
   -------------------------------------------------------------------------- */
h1, h2, h3, h4, .logo, .stat-number, .step-number, .price {
    font-family: var(--font-display);
    letter-spacing: -0.02em;   /* tight tracking is what reads as premium */
    line-height: 1.1;
    text-wrap: balance;        /* no orphan word on its own line */
}

.hero h1        { font-size: clamp(2.1rem, 8.5vw, 5.2rem); }
.section-title  { font-size: clamp(1.7rem, 5.5vw, 2.8rem); }
.pricing-section h1 { font-size: clamp(1.9rem, 7vw, 3rem); }
.slogan         { font-size: clamp(0.95rem, 3.2vw, 1.6rem); letter-spacing: 0.18em; }
.section-subtitle { font-size: clamp(0.95rem, 2.4vw, 1.1rem); text-wrap: pretty; }
.price          { font-size: clamp(2.2rem, 9vw, 3rem); }
.stat-number    { font-size: clamp(1.9rem, 8vw, 3rem); font-variant-numeric: tabular-nums; }
.step-number    { font-size: clamp(2.4rem, 9vw, 3.5rem); }

/* Tabular figures stop counting numbers jittering as they animate. */
.stat-number, .stat-unit, .price { font-variant-numeric: tabular-nums; }

.status-terminal, .game-tags, code, pre, kbd {
    font-family: var(--font-mono);
}

/* --------------------------------------------------------------------------
   Header. The original nav is position:absolute centred inside a fixed
   header, so on a narrow screen it lands on top of the logo - the overlap
   reported on mobile. Below 900px the nav becomes a drawer instead.
   -------------------------------------------------------------------------- */
header { min-height: var(--header-h); gap: 16px; }

/* Injected by ghostcfg-ui.js; hidden on desktop. */
.nav-toggle {
    display: none;
    width: 46px; height: 46px;
    flex: 0 0 auto;
    background: rgba(160, 86, 243, 0.10);
    border: 1px solid rgba(160, 86, 243, 0.35);
    border-radius: 10px;
    cursor: pointer;
    padding: 0;
    position: relative;
    z-index: 120;
}
.nav-toggle span,
.nav-toggle span::before,
.nav-toggle span::after {
    position: absolute;
    left: 50%;
    width: 20px; height: 2px;
    margin-left: -10px;
    background: #fff;
    border-radius: 2px;
    transition: transform 0.28s ease, opacity 0.2s ease;
    content: '';
}
.nav-toggle span { top: 50%; margin-top: -1px; }
.nav-toggle span::before { top: -6px; margin-left: 0; left: 0; }
.nav-toggle span::after  { top:  6px; margin-left: 0; left: 0; }

.nav-toggle[aria-expanded="true"] span { background: transparent; }
.nav-toggle[aria-expanded="true"] span::before { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span::after  { transform: translateY(-6px) rotate(-45deg); }

@media (max-width: 900px) {
    header { padding: 14px 5%; }
    .nav-toggle { display: block; }

    /* Static flow, not absolute centring: the drawer drops below the bar. */
    header nav {
        position: fixed;
        top: var(--header-h);
        left: 0;
        transform: none;
        width: 100%;
        flex-direction: column;
        gap: 0;
        background: rgba(9, 7, 15, 0.97);
        backdrop-filter: blur(14px);
        border-bottom: 1px solid rgba(160, 86, 243, 0.25);
        padding: 8px 5% 18px;
        /* Collapsed by default. max-height keeps the open/close animatable. */
        max-height: 0;
        overflow: hidden;
        opacity: 0;
        visibility: hidden;
        transition: max-height 0.32s ease, opacity 0.22s ease, visibility 0.22s;
    }
    header nav.is-open {
        max-height: 70vh;
        overflow-y: auto;
        opacity: 1;
        visibility: visible;
    }
    header nav a {
        display: block;
        width: 100%;
        padding: 15px 12px;      /* 48px tall: comfortable thumb target */
        font-size: 1.05rem;
        border-radius: 8px;
        border-bottom: 1px solid rgba(160, 86, 243, 0.10);
    }
    header nav a:last-child { border-bottom: none; }
    .logo { font-size: 22px; }
}

/* The nav is centred with position:absolute, so it does not push the logo aside -
   it overlaps it. Five items, two of them two words long, are wide enough to
   collide well past the old 1150px ceiling, so the band runs to 1290px now.

   Two steps rather than one: the squeeze needed at 1000px would look cramped at
   1250px, where there is only a little to claw back. Labels stay whole - both
   "Bottleneck Calculator" and "Game Guides" say what they are, and a nav that
   reads "Bottleneck" to save 60px trades away the word people search for. */
@media (min-width: 1151px) and (max-width: 1290px) {
    header { padding: 20px 5%; }
    header nav { gap: 24px; }
    header nav a { font-size: 15px; padding: 5px 8px; }
}
@media (min-width: 901px) and (max-width: 1150px) {
    header { padding: 20px 3%; }
    header nav { gap: 14px; }
    header nav a { font-size: 13.5px; padding: 5px 5px; letter-spacing: 0.1px; }
    .logo { font-size: 21px; }
}

/* --------------------------------------------------------------------------
   Mobile layout. The pages had no media queries at all, so these are the
   fixes for what actually breaks on a phone.
   -------------------------------------------------------------------------- */
@media (max-width: 780px) {
    .hero {
        /* 100vh on iOS includes the browser chrome, so content gets clipped.
           dvh tracks the visible area; min-height lets it grow if it must. */
        height: auto;
        min-height: 100dvh;
        padding: calc(var(--header-h) + 28px) 20px 56px;
        justify-content: center;
    }
    .section-wrapper { padding: 62px 5%; }
    .section-subtitle { margin-bottom: 38px; }

    .btn-primary, .btn-secondary {
        /* Full-width primary action, and never a 5px-tall tap target. */
        display: block;
        width: 100%;
        max-width: 340px;
        text-align: center;
        padding: 17px 24px;
        font-size: 16px;
    }

    .pricing-section { padding: calc(var(--header-h) + 34px) 18px 44px; }
    .cards-container { gap: 20px; }
    .card {
        /* Was width:350px fixed, which overflows a 360px screen. */
        width: 100%;
        max-width: 420px;
        padding: 34px 24px;
    }
    /* The featured card is scaled up on desktop; stacked, that just clips it. */
    .card.featured, .card.featured:hover { transform: none; }
    .card:hover { transform: none; }

    .games-bar { gap: 14px 22px; padding: 18px 16px; font-size: 0.78rem; }
    .steps-container { gap: 18px; }
    .step-box { min-width: 0; padding: 26px 22px; }
    .interactive-box { padding: 26px 18px; }
    .stats-display { gap: 22px; margin-bottom: 30px; }
    .slider-container { padding: 0; }
    .privacy-box { padding: 26px 20px; }
    .privacy-box::before { left: 16px; font-size: 0.75rem; }
    footer a { display: inline-block; margin: 6px 12px; padding: 4px 0; }
}

@media (max-width: 420px) {
    .hero-features-pills { gap: 8px; }
    .pill { font-size: 0.72rem; padding: 5px 10px; }
    .section-wrapper { padding: 52px 18px; }
    .vt-banner { padding: 14px 16px; }
    .btn-virustotal { white-space: normal; text-align: center; width: 100%; }
}

/* --------------------------------------------------------------------------
   Engine cards: the detail panel was hover-only, so on a touch screen it was
   unreachable - a third of the homepage content invisible on mobile. Tapping
   toggles .is-open (set in JS) and below 780px the height is not fixed.
   -------------------------------------------------------------------------- */
.engine-card.is-open .engine-content { opacity: 0; transform: translate(-50%, -80%); }
.engine-card.is-open .engine-hover   { bottom: 0; }
.engine-card.is-open { border-color: var(--purple-neon); box-shadow: 0 10px 30px var(--purple-glow); }

/* On a phone the desktop layout collapses in two ways at once, and both were
   visible as text sitting on top of other text:

   1. .engine-content is centred with position:absolute + translate(-50%,-50%)
      inside a card of fixed height. "Survival & Open World" wraps to two lines
      on a narrow screen, so it grows out of the centre in both directions and
      runs straight into the 'TAP FOR DETAILS' label pinned to the bottom.
   2. .engine-hover holds an h4 plus three list items in 190px minus 48px of
      padding. It does not fit, and overflow:hidden cuts it off mid-line.

   Absolute positioning is what causes both, so below 780px it goes away: the
   card becomes an accordion in normal flow that grows downwards. Nothing is
   stacked on anything, so nothing can overlap regardless of how the text wraps
   or how large the phone's font setting is. */
@media (max-width: 780px) {
    .engine-grid { gap: 16px; }
    .engine-card {
        min-width: 0;
        width: 100%;
        height: auto;              /* was a fixed 190px */
        overflow: visible;         /* let the panel lengthen the card */
        padding: 22px 20px 16px;
    }

    /* Back into normal flow. The transform override matters: the base
       :hover rule shifts it by -50%/-80%, which off a centred position
       would drag the text off the left edge. */
    .engine-card .engine-content,
    .engine-card:hover .engine-content,
    .engine-card.is-open .engine-content {
        position: static;
        top: auto; left: auto;
        transform: none;
        opacity: 1;
        width: auto;
        text-align: left;
    }
    .engine-card .engine-content h3 { font-size: 1.25rem; line-height: 1.3; }

    /* The panel becomes a block underneath, revealed by height rather than by
       sliding over the title. max-height animates; auto would not. */
    .engine-card .engine-hover,
    .engine-card:hover .engine-hover,
    .engine-card.is-open .engine-hover {
        position: static;
        bottom: auto; left: auto;
        width: auto;
        height: auto;
        max-height: 0;
        overflow: hidden;
        padding: 0;
        background: transparent;
        align-items: stretch;
        transition: max-height 0.35s ease, opacity 0.25s ease, margin-top 0.35s ease;
        opacity: 0;
        margin-top: 0;
    }
    .engine-card.is-open .engine-hover {
        max-height: 340px;
        opacity: 1;
        margin-top: 16px;
        padding-top: 16px;
        border-top: 1px solid rgba(160, 86, 243, 0.28);
    }
    /* On transparent background the deep purple heading loses contrast. */
    .engine-card .engine-hover h4 { margin-bottom: 10px; font-size: 1rem; }
    .engine-card .engine-hover li { font-size: 0.9rem; margin-bottom: 8px; }

    /* The hint joins the flow too, so it can never land on the title. */
    .engine-card::after {
        content: 'TAP FOR DETAILS';
        display: block;
        position: static;
        margin-top: 14px;
        text-align: left;
        font-size: 0.68rem;
        letter-spacing: 0.14em;
        color: var(--purple-neon);
        opacity: 0.75;
        transition: opacity 0.25s ease;
        pointer-events: none;
    }
    .engine-card.is-open::after { content: 'TAP TO CLOSE'; }
}

/* On a touch device the CSS :hover state sticks after a tap, leaving cards
   stuck open. Only let hover drive it on devices that really hover.

   Held above 780px on purpose. Below that the accordion rules take over, and
   this block sits later in the file at equal specificity - unscoped it would
   put translate(-50%,-50%) and bottom:-100% back and undo the fix on exactly
   the screens that need it. This still covers a touch tablet wide enough to
   get the desktop layout. */
@media (hover: none) and (min-width: 781px) {
    .engine-card:hover .engine-content { opacity: 1; transform: translate(-50%, -50%); }
    .engine-card:hover .engine-hover   { bottom: -100%; }
    .engine-card.is-open .engine-content { opacity: 0; transform: translate(-50%, -80%); }
    .engine-card.is-open .engine-hover   { bottom: 0; }
}

/* --------------------------------------------------------------------------
   Reveal on scroll. The class is only added by JS, so with JS disabled every
   element stays visible - the content is never hidden by CSS alone.
   -------------------------------------------------------------------------- */
.reveal-init {
    opacity: 0;
    transform: translateY(22px);
    transition: opacity 0.6s cubic-bezier(0.22, 1, 0.36, 1),
                transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
    will-change: opacity, transform;
}
.reveal-init.is-visible { opacity: 1; transform: none; }

/* The featured plan card carries transform: scale(1.05) at the same
   specificity as the rule above, and this file loads later, so the reveal
   would otherwise flatten it permanently. Restore the scale on arrival.
   Desktop only: below 780px the cards stack and the scale is dropped on
   purpose, and these selectors are specific enough to beat that rule. */
@media (min-width: 781px) {
    .card.featured.reveal-init            { transform: scale(1.05) translateY(22px); }
    .card.featured.reveal-init.is-visible { transform: scale(1.05); }
}

/* Staggered children so a row of cards arrives in sequence, not as a block. */
.reveal-init:nth-child(2) { transition-delay: 0.08s; }
.reveal-init:nth-child(3) { transition-delay: 0.16s; }
.reveal-init:nth-child(4) { transition-delay: 0.24s; }

/* --------------------------------------------------------------------------
   Accessibility and input polish
   -------------------------------------------------------------------------- */
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible {
    outline: 2px solid var(--purple-neon, #a056f3);
    outline-offset: 3px;
    border-radius: 6px;
}

/* Pressed feedback on touch, where there is no hover to give it. */
.btn-primary:active, .btn-secondary:active, .btn-buy:active,
.btn-unlock:active, .btn-download:active {
    transform: scale(0.975);
}

/* The :not() is what lets the footer toggle override the operating system. A
   visitor whose OS asks for reduced motion but who explicitly turns animation
   back on for this site gets .gc-motion-on on <html>, and this block stops
   applying to them. Without it, the !important declarations below would win over
   anything the toggle could do. */
@media (prefers-reduced-motion: reduce) {
    html:not(.gc-motion-on) *,
    html:not(.gc-motion-on) *::before,
    html:not(.gc-motion-on) *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }
    html:not(.gc-motion-on) .reveal-init { opacity: 1; transform: none; }
}

/* The same suppression, asked for by the footer toggle instead of the OS. */
.gc-motion-off *,
.gc-motion-off *::before,
.gc-motion-off *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
}
.gc-motion-off .reveal-init { opacity: 1; transform: none; }

/* --------------------------------------------------------------------------
   Copy-to-clipboard for the promo code, plus its toast
   -------------------------------------------------------------------------- */
.copy-code {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    border: 1px dashed rgba(0, 255, 204, 0.45);
    background: rgba(0, 255, 204, 0.07);
    border-radius: 8px;
    padding: 4px 12px;
    font-family: var(--font-mono);
    font-weight: 800;
    letter-spacing: 0.08em;
    transition: background 0.25s ease, border-color 0.25s ease;
}
.copy-code::after {
    content: '⧉ copy';
    font-size: 0.72rem;
    font-weight: 600;
    opacity: 0.7;
    letter-spacing: 0.04em;
}
.copy-code:hover { background: rgba(0, 255, 204, 0.14); border-color: rgba(0, 255, 204, 0.8); }
.copy-code.is-copied::after { content: '✓ copied'; }

.gc-toast {
    position: fixed;
    left: 50%;
    bottom: 26px;
    transform: translate(-50%, 20px);
    background: rgba(19, 14, 29, 0.97);
    border: 1px solid var(--purple-neon, #a056f3);
    color: #fff;
    padding: 13px 24px;
    border-radius: 10px;
    font-weight: 600;
    font-size: 0.92rem;
    box-shadow: 0 12px 34px rgba(0, 0, 0, 0.55);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.25s ease, transform 0.25s ease;
    z-index: 9999;
    max-width: calc(100% - 32px);
    text-align: center;
}
.gc-toast.is-shown { opacity: 1; transform: translate(-50%, 0); }

/* --------------------------------------------------------------------------
   Optimizer page on mobile. This is the page that has to work on a phone:
   it is where the money is taken.
   -------------------------------------------------------------------------- */
@media (max-width: 780px) {
    .tool-container {
        margin: calc(var(--header-h) + 22px) auto 62px;
        padding: 26px 18px;
        border-radius: 14px;
    }
    .tool-header { margin-bottom: 28px; }
    .tool-header h1 { font-size: clamp(1.5rem, 6.5vw, 2.5rem); }

    /* Two columns at 360px gives 150px-wide fields nobody can read. */
    .form-row { grid-template-columns: 1fr; gap: 14px; }

    /* 16px minimum: iOS Safari zooms the whole page on any smaller field. */
    input, select, textarea { font-size: 16px; }

    .paywall-box, .gate-notice { padding: 20px 16px; }
    .btn-unlock, .btn-download { width: 100%; text-align: center; padding: 17px 18px; }

    /* Autodetect and slot cards on a phone. */
    .autodetect { padding: 14px 14px; gap: 13px; }
    .btn-autodetect { width: 100%; padding: 14px 18px; }
    .slot-card { padding: 16px 14px; }
    .slot-engine { margin-left: 0; flex-basis: 100%; text-align: center; }
    .form-section-title { margin: 28px 0 14px; }

    /* Thinner and quieter: the meter must not eat a phone screen while scrolling. */
    .form-progress { padding: 10px 13px; margin-bottom: 18px; }
    .form-progress-text { font-size: 0.74rem; margin-top: 6px; }
    /* The report is paragraphs, not preformatted text, so it reflows on its
       own; only the padding needs to come down. */
    .result-box { padding: 20px 16px; }
    .report-summary { padding: 18px 15px; font-size: 0.92rem; }
}

/* --------------------------------------------------------------------------
   Legal and about pages. Their .content block reserves 160px at the top for
   the desktop header, which leaves an empty screen-height gap on a phone.
   -------------------------------------------------------------------------- */
@media (max-width: 780px) {
    .content {
        margin: calc(var(--header-h) + 26px) auto 58px !important;
        padding: 0 18px;
    }
    .content h1 { font-size: clamp(1.6rem, 6.5vw, 2.5rem); }
    .content h2 { font-size: clamp(1.15rem, 4.6vw, 1.6rem); }
    .content li { margin-bottom: 12px; }
}

/* --------------------------------------------------------------------------
   Hero aurora. Two slow-drifting blurred blobs behind the headline. Pure CSS
   transforms, so it runs on the compositor and costs nothing on the main
   thread - a canvas particle field would cost far more on a phone.
   -------------------------------------------------------------------------- */
.hero { overflow: hidden; isolation: isolate; }

.hero::before,
.hero::after {
    content: '';
    position: absolute;
    border-radius: 50%;
    filter: blur(90px);
    opacity: 0.5;
    z-index: -1;
    pointer-events: none;
    will-change: transform;
}
.hero::before {
    width: 46vw; height: 46vw;
    max-width: 620px; max-height: 620px;
    top: -12%; left: -10%;
    background: radial-gradient(circle, rgba(160, 86, 243, 0.55), transparent 68%);
    animation: auroraA 19s ease-in-out infinite alternate;
}
.hero::after {
    width: 40vw; height: 40vw;
    max-width: 540px; max-height: 540px;
    bottom: -14%; right: -8%;
    background: radial-gradient(circle, rgba(0, 240, 255, 0.30), transparent 68%);
    animation: auroraB 24s ease-in-out infinite alternate;
}

@keyframes auroraA {
    0%   { transform: translate3d(0, 0, 0) scale(1); }
    50%  { transform: translate3d(8vw, 5vh, 0) scale(1.14); }
    100% { transform: translate3d(3vw, -4vh, 0) scale(0.96); }
}
@keyframes auroraB {
    0%   { transform: translate3d(0, 0, 0) scale(1.05); }
    50%  { transform: translate3d(-7vw, -6vh, 0) scale(0.92); }
    100% { transform: translate3d(-2vw, 4vh, 0) scale(1.12); }
}

@media (prefers-reduced-motion: reduce) {
    .hero::before, .hero::after { animation: none; }
}

/* --------------------------------------------------------------------------
   Wordmark. The ghost is a ::before background-image rather than an <img>,
   so it needs no markup change in six files and stays out of the
   accessibility tree - it is decoration next to text that already says
   "GhostCfg", so a screen reader announcing it twice would be noise.
   -------------------------------------------------------------------------- */
/* Deliberately NOT a flex container. inline-flex would turn the text node
   "Ghost" and <span>Cfg</span> into two separate flex items, so any `gap`
   meant for the ghost would also split the word in half, and the blockified
   span would be clipped to line-height - cutting the descender off the "g",
   because background-clip:text only paints inside the element box. */
.logo {
    display: inline-block;
    white-space: nowrap;
}

.logo::before {
    content: '';
    /* The glyph is 36x43 in its viewBox. A square box with `contain` would
       letterbox it and make it look undersized next to the text, so the box
       matches the artwork's ratio. */
    display: inline-block;
    vertical-align: -0.22em;   /* optical baseline match, not a metric one */
    margin-right: 10px;
    height: 1.22em;
    width: 1.02em;
    /* Resolved against this stylesheet, not the page, so it works the same at
       /about and /about.html. logo-ghost.svg is the untiled crop of the
       favicon - the tiled version would read as a badge here. */
    background: url("logo-ghost.svg") center / contain no-repeat;
    /* The SVG is flat purple; the drop-shadow is what makes it read as neon. */
    filter: drop-shadow(0 0 6px rgba(160, 86, 243, 0.55));
    transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), filter 0.35s ease;
}

/* Slow bob, so the header has one thing quietly alive in it. */
@media (prefers-reduced-motion: no-preference) {
    .logo::before { animation: ghostFloat 3.6s ease-in-out infinite; }
}

@keyframes ghostFloat {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-3px); }
}

/* animation must be dropped, not paused: a running or paused animation wins
   over normal declarations in the cascade, so the keyframe transform would
   beat the hover transform and the ghost would never lift. */
.logo:hover::before {
    animation: none;
    transform: translateY(-3px) scale(1.1) rotate(-6deg);
    filter: drop-shadow(0 0 12px rgba(160, 86, 243, 0.95));
}

/* "Cfg" is the purple half of the wordmark. A gradient sweep across it reads
   as premium; the same treatment on the whole logo would kill the white/purple
   split that makes the name legible. */
.logo span {
    /* Padding on an inline element does not change the line box height, so
       this costs no layout but guarantees the painting area reaches below the
       baseline - insurance for the descender on the "g". */
    padding-bottom: 0.14em;
    background: linear-gradient(
        100deg,
        var(--purple-neon) 0%,
        #d9b8ff 42%,
        #00f0ff 52%,
        var(--purple-neon) 100%
    );
    background-size: 260% 100%;
    background-position: 100% 0;
    -webkit-background-clip: text;
            background-clip: text;
    -webkit-text-fill-color: transparent;
    transition: background-position 0.9s ease;
}
.logo:hover span { background-position: 0 0; }

/* Fallback: if background-clip:text is unsupported the text would be
   invisible, so only opt in where we know the paint works. */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
    .logo span {
        background: none;
        -webkit-text-fill-color: currentColor;
        color: var(--purple-neon);
    }
}

/* --------------------------------------------------------------------------
   Optimizer form: sections, fields, slot cards, progress and autodetect.
   -------------------------------------------------------------------------- */

/* Each numbered section reads as its own panel, so a long form scans as steps
   rather than one wall of inputs. */
.form-section-title {
    position: relative;
    margin: 34px 0 16px;
    padding-bottom: 10px;
    border-bottom: 1px solid rgba(160, 86, 243, 0.18);
}

.form-section-title::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: -1px;
    width: 54px;
    height: 2px;
    background: linear-gradient(90deg, var(--purple-neon, #a056f3), transparent);
}

/* Focus was invisible before: no ring, no colour change. That is both a usability
   problem and an accessibility one, since keyboard users had nothing to follow. */
.form-control:focus {
    outline: none;
    border-color: var(--purple-neon, #a056f3);
    box-shadow: 0 0 0 3px rgba(160, 86, 243, 0.18);
}

.form-control:hover:not(:focus) { border-color: rgba(160, 86, 243, 0.45); }

/* :not(:placeholder-shown) so an untouched empty field is not scolded before the
   customer has had a chance to type in it. */
.form-control:required:invalid:not(:placeholder-shown) {
    border-color: rgba(255, 92, 92, 0.55);
}

.field-hint {
    display: block;
    margin-top: 7px;
    font-size: 0.8rem;
    line-height: 1.4;
    min-height: 1.1em;
    color: var(--text-muted, #7d7490);
}

.field-hint.is-ok { color: var(--success-green, #00ffcc); }
.field-hint.is-warn { color: var(--warning-orange, #ffb347); }

/* Marks a field the engine can work without. Lighter and smaller than the label
   it sits in, so the eye can skip the whole row when filling the form in a
   hurry - which is the point of saying "optional" at all. */
.field-optional {
    font-weight: 400;
    font-size: 0.78rem;
    text-transform: none;
    letter-spacing: 0;
    color: var(--text-muted, #7d7490);
}

/* Autodetect offer, above section 1. */
.autodetect {
    display: flex;
    align-items: center;
    gap: 18px;
    flex-wrap: wrap;
    margin-bottom: 8px;
    padding: 16px 18px;
    border-radius: 12px;
    background: linear-gradient(135deg, rgba(160, 86, 243, 0.12), rgba(0, 240, 255, 0.05));
    border: 1px solid rgba(160, 86, 243, 0.28);
}

.autodetect-text { flex: 1 1 260px; font-size: 0.87rem; line-height: 1.5; }
.autodetect-text strong { display: block; color: #fff; margin-bottom: 3px; }
.autodetect-text span { color: var(--text-secondary, #b8b0c8); }

.btn-autodetect {
    flex: 0 0 auto;
    padding: 12px 22px;
    border-radius: 8px;
    border: 1px solid var(--purple-neon, #a056f3);
    background: rgba(160, 86, 243, 0.14);
    color: #fff;
    font-family: inherit;
    font-size: 0.9rem;
    font-weight: 700;
    cursor: pointer;
    transition: background 0.25s ease, transform 0.25s ease;
}

.btn-autodetect:hover:not(:disabled) {
    background: var(--purple-neon, #a056f3);
    transform: translateY(-2px);
}

.btn-autodetect:disabled { opacity: 0.6; cursor: wait; }

.autodetect-result {
    margin: 10px 0 0;
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--text-secondary, #b8b0c8);
}

.autodetect-result:empty { display: none; }
.autodetect-result.is-done { color: var(--success-green, #00ffcc); }

/* Completion meter. Sticky under the header so it stays useful on a form that is
   several screens tall. */
.form-progress {
    position: sticky;
    top: calc(var(--header-h, 70px) + 8px);
    z-index: 20;
    margin-bottom: 22px;
    padding: 12px 16px;
    border-radius: 10px;
    background: rgba(9, 7, 15, 0.92);
    border: 1px solid rgba(160, 86, 243, 0.25);
    backdrop-filter: blur(8px);
}

.form-progress-bar {
    height: 6px;
    border-radius: 99px;
    background: rgba(160, 86, 243, 0.16);
    overflow: hidden;
}

.form-progress-bar span {
    display: block;
    width: 0;
    height: 100%;
    border-radius: 99px;
    background: linear-gradient(90deg, var(--purple-neon, #a056f3), #00f0ff);
    transition: width 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}

.form-progress-text {
    margin-top: 8px;
    font-size: 0.78rem;
    letter-spacing: 0.3px;
    color: var(--text-secondary, #b8b0c8);
    font-variant-numeric: tabular-nums;
}

.form-progress.is-complete { border-color: rgba(0, 255, 204, 0.4); }
.form-progress.is-complete .form-progress-text { color: var(--success-green, #00ffcc); }

/* Per-game slot cards. Styles used to be inline in script.js. */
.slot-card {
    margin-bottom: 20px;
    padding: 20px;
    border-radius: 12px;
    background: rgba(9, 7, 15, 0.6);
    border: 1px solid rgba(160, 86, 243, 0.2);
    transition: border-color 0.25s ease, transform 0.25s ease;
}

.slot-card:focus-within {
    border-color: rgba(160, 86, 243, 0.5);
    transform: translateY(-2px);
}

.slot-head {
    display: flex;
    align-items: center;
    gap: 11px;
    flex-wrap: wrap;
    margin-bottom: 16px;
}

.slot-num {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 27px;
    height: 27px;
    border-radius: 50%;
    background: rgba(160, 86, 243, 0.18);
    border: 1px solid rgba(160, 86, 243, 0.45);
    color: var(--purple-neon, #a056f3);
    font-weight: 800;
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
}

.slot-title {
    font-weight: 800;
    font-size: 0.9rem;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--purple-neon, #a056f3);
}

/* Engine badge, filled in once the title resolves. */
.slot-engine {
    margin-left: auto;
    padding: 4px 11px;
    border-radius: 99px;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.4px;
    white-space: nowrap;
}

.slot-engine:empty { display: none; }

.slot-engine.is-known {
    background: rgba(0, 255, 204, 0.12);
    border: 1px solid rgba(0, 255, 204, 0.4);
    color: var(--success-green, #00ffcc);
}

.slot-engine.is-generic {
    background: rgba(255, 179, 71, 0.12);
    border: 1px solid rgba(255, 179, 71, 0.4);
    color: var(--warning-orange, #ffb347);
}

/* --------------------------------------------------------------------------
   Withdrawal-waiver consent, directly above the checkout button.
   -------------------------------------------------------------------------- */
.consent-row {
    display: flex;
    align-items: flex-start;
    gap: 11px;
    max-width: 560px;
    margin: 0 auto 18px;
    padding: 14px 16px;
    border-radius: 10px;
    background: rgba(9, 7, 15, 0.5);
    border: 1px solid rgba(160, 86, 243, 0.22);
    text-align: left;
    font-size: 0.86rem;
    line-height: 1.5;
    color: var(--text-secondary, #b8b0c8);
    cursor: pointer;
}

/* 18px, not the 13px default: this is a legal gate and a fat-finger miss on a
   phone is a lost sale. Also stops iOS zooming the page on focus. */
.consent-row input[type="checkbox"] {
    flex: 0 0 auto;
    width: 18px;
    height: 18px;
    margin: 1px 0 0;
    accent-color: var(--purple-neon, #a056f3);
    cursor: pointer;
}

.consent-row a { color: var(--purple-neon, #a056f3); }

.consent-row:has(input:checked) {
    border-color: rgba(0, 255, 204, 0.35);
    background: rgba(0, 255, 204, 0.05);
}

.consent-hint {
    margin: 10px 0 0;
    font-size: 0.8rem;
    color: var(--text-secondary, #b8b0c8);
    opacity: 0.85;
}

@media (max-width: 780px) {
    .consent-row { padding: 13px 13px; font-size: 0.84rem; }
}

/* --------------------------------------------------------------------------
   Re-order block, under the download button on the optimizer page.
   -------------------------------------------------------------------------- */
.reorder-box {
    margin-top: 26px;
    padding: 20px 22px;
    border-radius: 12px;
    background: rgba(9, 7, 15, 0.55);
    border: 1px solid rgba(160, 86, 243, 0.22);
    text-align: left;
}

.reorder-warn {
    margin: 0 0 12px;
    font-size: 0.92rem;
    line-height: 1.55;
    color: var(--warning-orange, #ffb347);
    padding-left: 12px;
    border-left: 3px solid var(--warning-orange, #ffb347);
}
.reorder-warn strong { color: #fff; }

/* Turns green once the file has actually been saved, so the warning stops
   nagging a customer who already did the thing it asks for. */
.reorder-box.is-saved .reorder-warn {
    color: var(--success-green, #00ffcc);
    border-left-color: var(--success-green, #00ffcc);
}

.reorder-note {
    margin: 0 0 16px;
    font-size: 0.88rem;
    line-height: 1.55;
    color: var(--text-muted, #a09cb0);
}

.btn-reorder {
    display: inline-block;
    padding: 13px 26px;
    border-radius: 8px;
    font-weight: 700;
    font-size: 0.95rem;
    text-decoration: none;
    color: var(--purple-neon, #a056f3);
    border: 1px solid var(--purple-neon, #a056f3);
    background: rgba(160, 86, 243, 0.08);
    transition: background 0.25s ease, color 0.25s ease, transform 0.25s ease;
}
.btn-reorder:hover {
    background: var(--purple-neon, #a056f3);
    color: #fff;
    transform: translateY(-2px);
}

.reorder-actions { display: flex; flex-wrap: wrap; gap: 12px; }

/* The free action is the filled one: regenerating is what most returning
   customers actually want, and it costs them nothing. */
.btn-reorder-primary {
    background: var(--purple-neon, #a056f3);
    color: #fff;
    cursor: pointer;
    font-family: inherit;
}
.btn-reorder-primary:hover { filter: brightness(1.12); color: #fff; }

/* The review invitation. Smaller than the buttons above it on purpose: it is a
   favour being asked, not an action the customer came here for. */
.reorder-review {
    margin: 16px 0 0;
    padding-top: 14px;
    border-top: 1px solid rgba(160, 86, 243, 0.15);
    font-size: 0.84rem;
    line-height: 1.6;
    color: var(--text-muted, #a09cb0);
}
.reorder-review a {
    color: var(--accent-cyan, #00f0ff);
    font-weight: 600;
    text-decoration: none;
}
.reorder-review a:hover { text-decoration: underline; }

@media (max-width: 780px) {
    .reorder-box { padding: 17px 15px; margin-top: 20px; }
    .reorder-actions { flex-direction: column; }
    .btn-reorder { display: block; width: 100%; text-align: center; padding: 15px 18px; }
}

/* --------------------------------------------------------------------------
   Trust strip under the hero. Four numbers that count up on first view.
   -------------------------------------------------------------------------- */
.trust-strip {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
    padding: 30px 6%;
    background: linear-gradient(180deg, rgba(160, 86, 243, 0.07), transparent);
    border-bottom: 1px solid rgba(160, 86, 243, 0.12);
    text-align: center;
}

.trust-item { display: flex; flex-direction: column; gap: 6px; }

.trust-num {
    font-family: var(--font-display);
    font-size: clamp(1.6rem, 4.4vw, 2.6rem);
    font-weight: 900;
    line-height: 1;
    color: #fff;
    /* tabular figures stop the number jittering while it counts */
    font-variant-numeric: tabular-nums;
    background: linear-gradient(135deg, #fff 20%, var(--purple-neon) 100%);
    -webkit-background-clip: text;
            background-clip: text;
    -webkit-text-fill-color: transparent;
}

.trust-label {
    font-size: clamp(0.64rem, 1.5vw, 0.78rem);
    text-transform: uppercase;
    letter-spacing: 1.4px;
    color: var(--text-muted);
    font-weight: 600;
}

@media (max-width: 620px) {
    .trust-strip { grid-template-columns: repeat(2, 1fr); gap: 22px 10px; padding: 26px 20px; }
}

/* --------------------------------------------------------------------------
   Cursor spotlight. JS writes --mx / --my (percentages) on the hovered card;
   this draws a soft highlight that follows the pointer. Pointer devices only -
   on a phone there is no cursor to follow, so JS never attaches the listener.
   No overflow:hidden on purpose: plans.html hangs .popular-badge at top:-15px
   outside the card and clipping would cut it in half. The ::after is inset:0
   with an inherited radius, so it stays inside the card regardless.
   -------------------------------------------------------------------------- */
.spotlight { position: relative; }

.spotlight::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: radial-gradient(
        340px circle at var(--mx, 50%) var(--my, 0%),
        rgba(160, 86, 243, 0.16),
        transparent 62%
    );
    opacity: 0;
    transition: opacity 0.3s ease;
    pointer-events: none;
    z-index: 0;
}
.spotlight:hover::after { opacity: 1; }
/* Deliberately no `.spotlight > * { position: relative }` rule here: it would
   overwrite position:absolute on children like .popular-badge and knock them
   out of place. The highlight is only 0.16 alpha, so sitting above the text
   costs nothing in readability. */

/* --------------------------------------------------------------------------
   Featured plan card: a slow rotating conic-gradient ring. Drawn on a wrapper
   pseudo-element so it never fights the card's own scale(1.05) transform.
   -------------------------------------------------------------------------- */
@property --ring-angle {
    syntax: '<angle>';
    inherits: false;
    initial-value: 0deg;
}

/* Two stacked backgrounds: the card face clipped to padding-box, the gradient
   to border-box. With a transparent border the gradient shows only in that 2px
   frame. No pseudo-element, which matters here - .card.featured carries
   transform: scale(1.05), so it is its own stacking context and a z-index:-1
   child would paint on top of the card face instead of behind it.
   Browsers without @property support just render a static gradient frame. */
.card.featured {
    border: 2px solid transparent;
    background:
        linear-gradient(180deg, #181028 0%, var(--bg-panel) 100%) padding-box,
        conic-gradient(
            from var(--ring-angle),
            rgba(160, 86, 243, 0.25) 0deg,
            var(--purple-neon) 60deg,
            #00f0ff 105deg,
            rgba(160, 86, 243, 0.25) 190deg,
            rgba(160, 86, 243, 0.25) 360deg
        ) border-box;
}

/* Repainting a conic gradient every frame is not free, so the spin is limited
   to real pointer devices; touch screens keep the static frame. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
    .card.featured { animation: ringSpin 7s linear infinite; }
}

@keyframes ringSpin {
    to { --ring-angle: 360deg; }
}

/* --------------------------------------------------------------------------
   Games bar marquee. JS wraps the plain <span> list in .marquee > .marquee-track
   and duplicates the run once, so translateX(-50%) loops with no visible seam.
   Without JS the spans stay exactly as they were - a wrapped static row.
   -------------------------------------------------------------------------- */
.games-bar.has-marquee {
    gap: 24px;
    flex-wrap: nowrap;
    justify-content: space-between;
    padding: 18px 5%;
}

.marquee {
    flex: 1 1 auto;
    min-width: 0;              /* lets the flex item actually shrink */
    overflow: hidden;
    /* Fades the titles out at both ends instead of cutting them mid-letter. */
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
            mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

.marquee-track {
    display: flex;
    align-items: center;
    width: max-content;
    animation: marqueeScroll 42s linear infinite;
    will-change: transform;
}
.marquee-track > span { margin-right: 46px; white-space: nowrap; }

@keyframes marqueeScroll {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}

.marquee:hover .marquee-track { animation-play-state: paused; }
.games-bar.has-marquee .promo-badge { flex: 0 0 auto; }

@media (prefers-reduced-motion: reduce) {
    .marquee-track { animation: none; }
}

@media (max-width: 700px) {
    .games-bar.has-marquee {
        flex-direction: column;
        gap: 16px;
        padding: 16px 0 18px;
    }
    .marquee { width: 100%; }
    .games-bar.has-marquee .promo-badge { font-size: 0.8rem; }
}

/* --------------------------------------------------------------------------
   Why GhostCfg + reaction test.

   Laid out with auto-fit rather than fixed columns so it reflows from four
   across to one without a single media query, which is what stopped the engine
   cards from working on a phone in the first place.
   -------------------------------------------------------------------------- */
.why-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 20px;
    width: 100%;
    max-width: 1100px;
    margin: 0 auto 46px;
    text-align: left;
}
.why-card {
    background: var(--bg-main, #09070f);
    border: 1px solid rgba(160, 86, 243, 0.2);
    border-radius: 12px;
    padding: 26px 22px;
    transition: border-color 0.3s ease, transform 0.3s ease, box-shadow 0.3s ease;
}
.why-card:hover {
    border-color: var(--purple-neon, #a056f3);
    transform: translateY(-4px);
    box-shadow: 0 10px 30px var(--purple-glow, rgba(160, 86, 243, 0.4));
}
.why-icon { font-size: 1.8rem; display: block; margin-bottom: 12px; }
.why-card h3 {
    color: #fff;
    font-size: 1.08rem;
    line-height: 1.35;
    margin-bottom: 10px;
}
.why-card p {
    color: var(--text-muted, #8b839c);
    font-size: 0.94rem;
    line-height: 1.6;
    margin: 0;
}

/* --- reaction test ------------------------------------------------------- */
.reaction-game {
    width: 100%;
    max-width: 640px;
    margin: 0 auto;
    background: var(--bg-main, #09070f);
    border: 1px solid rgba(160, 86, 243, 0.25);
    border-radius: 16px;
    padding: 32px 26px;
    text-align: center;
}
.reaction-title { color: #fff; font-size: 1.3rem; margin-bottom: 8px; }
.reaction-intro {
    color: var(--text-muted, #8b839c);
    font-size: 0.94rem;
    line-height: 1.6;
    margin: 0 auto 24px;
    max-width: 440px;
}

/* A real <button>, so Enter and Space work and screen readers announce it
   without any extra wiring. */
.reaction-pad {
    display: block;
    width: 100%;
    min-height: 132px;
    border-radius: 12px;
    border: 2px solid rgba(160, 86, 243, 0.4);
    background: rgba(160, 86, 243, 0.08);
    color: #fff;
    font-family: var(--font-mono, monospace);
    font-size: 1.15rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, transform 0.08s ease;
    /* Stops a double-tap on iOS zooming the page instead of registering. */
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}
.reaction-pad:hover { background: rgba(160, 86, 243, 0.16); }
.reaction-pad:active { transform: scale(0.99); }
.reaction-pad:focus-visible {
    outline: 2px solid var(--accent-cyan, #00f0ff);
    outline-offset: 3px;
}
.reaction-pad.is-waiting {
    background: rgba(255, 51, 102, 0.10);
    border-color: rgba(255, 51, 102, 0.45);
    cursor: default;
}
.reaction-pad.is-go {
    background: var(--accent-cyan, #00f0ff);
    border-color: var(--accent-cyan, #00f0ff);
    color: #04212a;
}
.reaction-msg {
    color: var(--text-muted, #8b839c);
    font-size: 0.9rem;
    margin: 16px 0 0;
    min-height: 1.4em;   /* reserve the line so nothing jumps as text changes */
}

.reaction-scores {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 14px;
    margin-top: 22px;
}
.reaction-score {
    background: rgba(160, 86, 243, 0.07);
    border: 1px solid rgba(160, 86, 243, 0.2);
    border-radius: 10px;
    padding: 14px 12px;
}
.reaction-score-label {
    display: block;
    color: var(--text-muted, #8b839c);
    font-size: 0.74rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    margin-bottom: 6px;
}
.reaction-score-value {
    display: block;
    font-family: var(--font-mono, monospace);
    font-size: 1.5rem;
    font-weight: 800;
    color: #fff;
}
.reaction-score-bad { border-color: rgba(255, 51, 102, 0.3); }
.reaction-score-bad .reaction-score-value { color: var(--error-red, #ff3366); }
.reaction-score-good { border-color: rgba(0, 240, 255, 0.3); }
.reaction-score-good .reaction-score-value { color: var(--accent-cyan, #00f0ff); }

.reaction-verdict {
    color: var(--text-main, #e2dfec);
    font-size: 0.97rem;
    line-height: 1.65;
    margin: 22px auto 0;
    max-width: 480px;
}
.reaction-cta { margin-top: 20px; }

@media (max-width: 780px) {
    .why-grid { gap: 14px; margin-bottom: 34px; }
    .why-card { padding: 22px 18px; }
    .reaction-game { padding: 26px 18px; border-radius: 14px; }
    .reaction-pad { min-height: 116px; font-size: 1.05rem; }
    .reaction-scores { gap: 10px; }
    .reaction-score-value { font-size: 1.3rem; }
}

/* Honour a reduced-motion preference: the pad still changes colour, it just
   does not scale or ease. */
@media (prefers-reduced-motion: reduce) {
    .reaction-pad, .why-card { transition: none; }
    .reaction-pad:active { transform: none; }
}

/* --------------------------------------------------------------------------
   Results area.

   The old panel was one column of <p> lines: every number, every game and
   every caveat at the same visual weight, so the three figures that decide
   whether the purchase was worth it sat in the middle of a wall of text. This
   splits it into a headline strip, one card per game, and notes that no longer
   compete with the numbers.

   Loaded after optimize.html's inline <style>, so these win at equal
   specificity - the older .report-summary rules stay where they are and are
   overridden here rather than deleted.
   -------------------------------------------------------------------------- */
.report-summary {
    background: transparent;
    border: 0;
    border-radius: 0;
    padding: 0;
    margin-bottom: 26px;
    font-size: 1rem;
}

/* --- headline figures --- */

.rp-headline {
    display: grid;
    /* auto-fit, so three cards become two then one without a media query. */
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 14px;
    margin-bottom: 24px;
}

.rp-stat {
    background: linear-gradient(160deg, rgba(0, 255, 204, 0.09), rgba(9, 7, 15, 0.9));
    border: 1px solid rgba(0, 255, 204, 0.28);
    border-radius: 12px;
    padding: 20px 18px;
    text-align: center;
}

.rp-stat-label {
    display: block;
    font-size: 0.72rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-muted, #8b839c);
    margin-bottom: 10px;
}

.rp-stat-value {
    display: block;
    font-size: 2rem;
    font-weight: 700;
    line-height: 1.1;
    color: var(--success-green, #00ffcc);
}

.rp-stat-sub {
    display: block;
    margin-top: 8px;
    font-size: 0.82rem;
    line-height: 1.45;
    color: var(--text-muted, #8b839c);
}

/* The bottleneck card is not always good news, so it carries its own colour
   rather than borrowing the green used for wins. */
.rp-stat-neutral {
    background: linear-gradient(160deg, rgba(160, 86, 243, 0.12), rgba(9, 7, 15, 0.9));
    border-color: rgba(160, 86, 243, 0.32);
}
.rp-stat-neutral .rp-stat-value { font-size: 1.35rem; }

/* --- per-game cards --- */

.rp-section-title {
    display: block;
    font-size: 0.74rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--text-muted, #8b839c);
    margin: 0 0 12px;
}

.rp-games {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 12px;
    margin-bottom: 24px;
}

.rp-game {
    background: #09070f;
    border: 1px solid rgba(160, 86, 243, 0.22);
    border-radius: 10px;
    padding: 16px 18px;
    text-align: left;
}

.rp-game-head {
    display: flex;
    flex-wrap: wrap;         /* a long title must push the engine tag down,
                                not squash it into an unreadable column */
    align-items: baseline;
    gap: 8px;
    margin-bottom: 4px;
}

.rp-game-name {
    color: #fff;
    font-size: 1.02rem;
    font-weight: 600;
    /* Titles come from a free-text field, so one long word must wrap rather
       than widen the card past the grid track. */
    overflow-wrap: anywhere;
}

.rp-game-engine {
    font-size: 0.72rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--purple-neon, #a056f3);
    background: rgba(160, 86, 243, 0.12);
    border: 1px solid rgba(160, 86, 243, 0.3);
    border-radius: 999px;
    padding: 2px 9px;
    white-space: nowrap;
}

.rp-game-file {
    display: block;
    font-family: 'Consolas', 'Courier New', monospace;
    font-size: 0.8rem;
    color: var(--accent-cyan, #00f0ff);
    margin-bottom: 12px;
    overflow-wrap: anywhere;
}

/* The FPS line: before, arrow, after, delta. */
.rp-fps {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
}

.rp-fps-from { color: var(--text-muted, #8b839c); font-size: 1.05rem; }
.rp-fps-arrow { color: var(--text-muted, #8b839c); }
.rp-fps-to {
    color: var(--success-green, #00ffcc);
    font-size: 1.4rem;
    font-weight: 700;
}
.rp-fps-delta {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--success-green, #00ffcc);
    background: rgba(0, 255, 204, 0.1);
    border-radius: 999px;
    padding: 2px 9px;
}

/* Proportional bar. Width is set inline by JS because it is data, not style. */
.rp-bar {
    position: relative;
    height: 6px;
    background: rgba(255, 255, 255, 0.07);
    border-radius: 999px;
    margin-top: 12px;
    overflow: hidden;
}
.rp-bar-base {
    position: absolute;
    top: 0; left: 0; bottom: 0;
    background: rgba(139, 131, 156, 0.55);
    border-radius: 999px;
}
.rp-bar-gain {
    position: absolute;
    top: 0; bottom: 0;
    background: linear-gradient(90deg, var(--purple-neon, #a056f3), var(--success-green, #00ffcc));
    border-radius: 999px;
}

.rp-game-note {
    display: block;
    margin-top: 10px;
    font-size: 0.78rem;
    line-height: 1.5;
    color: var(--text-muted, #8b839c);
}

.rp-game-generic .rp-game-name { color: var(--text-main, #e2dfec); }
.rp-game-generic { border-color: rgba(139, 131, 156, 0.28); }

/* --- advisor and tips --- */

.rp-callout {
    text-align: left;
    border-left: 3px solid;
    border-radius: 0 8px 8px 0;
    padding: 12px 16px;
    margin-bottom: 12px;
    font-size: 0.92rem;
    line-height: 1.6;
}

.rp-callout-advisor {
    border-color: var(--warning-orange, #ffaa00);
    background: rgba(255, 170, 0, 0.06);
    color: #ffd280;
}

.rp-callout-tip {
    border-color: var(--accent-cyan, #00f0ff);
    background: rgba(0, 240, 255, 0.05);
    color: #9fe9f7;
}

/* --- the fine print, folded away --- */

/* A <details> element: the caveats are honest and stay in the DOM, but they no
   longer sit between the customer and the download button. Closed by default,
   and readable when opened - not a tooltip that vanishes. */
.rp-fineprint {
    text-align: left;
    margin-top: 18px;
    border-top: 1px solid rgba(160, 86, 243, 0.18);
    padding-top: 14px;
}

.rp-fineprint > summary {
    cursor: pointer;
    font-size: 0.82rem;
    letter-spacing: 0.04em;
    color: var(--text-muted, #8b839c);
    list-style: none;
    display: flex;
    align-items: center;
    gap: 8px;
}
.rp-fineprint > summary::-webkit-details-marker { display: none; }
.rp-fineprint > summary::before {
    content: '+';
    display: inline-block;
    width: 16px;
    text-align: center;
    color: var(--purple-neon, #a056f3);
    font-weight: 700;
}
.rp-fineprint[open] > summary::before { content: '\2212'; }   /* minus */
.rp-fineprint > summary:hover { color: var(--text-main, #e2dfec); }
.rp-fineprint > summary:focus-visible {
    outline: 2px solid var(--purple-neon, #a056f3);
    outline-offset: 3px;
    border-radius: 4px;
}

.rp-fineprint p {
    margin: 12px 0 0;
    font-size: 0.83rem;
    line-height: 1.6;
    color: var(--text-muted, #8b839c);
}
.rp-fineprint p.rp-warn { color: var(--warning-orange, #ffaa00); }

@media (max-width: 600px) {
    .rp-stat { padding: 16px 14px; }
    .rp-stat-value { font-size: 1.7rem; }
    .rp-stat-neutral .rp-stat-value { font-size: 1.15rem; }
    .rp-game { padding: 14px 15px; }
    .rp-fps-to { font-size: 1.25rem; }
}

@media (prefers-reduced-motion: reduce) {
    .rp-bar-base, .rp-bar-gain { transition: none; }
}

/* --------------------------------------------------------------------------
   Scroll progress bar. Purely decorative, driven by JS.
   -------------------------------------------------------------------------- */
.gc-progress {
    position: fixed;
    top: 0; left: 0;
    height: 2px;
    width: 0;
    background: linear-gradient(90deg, var(--purple-deep, #5e17eb),
                var(--purple-neon, #a056f3), var(--accent-cyan, #00f0ff));
    z-index: 200;
    transition: width 0.1s linear;
    pointer-events: none;
}

/* ==========================================================================
   How It Works - connectors, per-step footer, archive contents
   ========================================================================== */

/* The row needs to be a positioning context for the connector arrows. */
.steps-container { position: relative; }

.step-box {
    position: relative;
    /* Pushes the .step-io footer to the bottom edge, so the three footers line
       up with each other even when the paragraphs above them differ in length. */
    display: flex;
    flex-direction: column;
}

.step-box > p { flex: 1 0 auto; }

/* Arrow between cards. On the first two boxes only, and drawn in the flex gap
   rather than inside either card - so it never overlaps text at any width. */
.step-box:not(:last-child)::after {
    content: '';
    position: absolute;
    top: 50%;
    right: -28px;              /* into the 40px gap, clear of both card edges */
    width: 13px;
    height: 13px;
    margin-top: -7px;
    border-right: 2px solid rgba(160, 86, 243, 0.45);
    border-bottom: 2px solid rgba(160, 86, 243, 0.45);
    transform: rotate(-45deg);
    transition: border-color 0.3s ease;
}

.step-box:not(:last-child):hover::after {
    border-color: var(--purple-neon, #a056f3);
}

.step-io {
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid rgba(160, 86, 243, 0.15);
    display: flex;
    align-items: baseline;
    gap: 8px;
    text-align: left;
}

.step-io-label {
    font-size: 0.68rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-muted, #8b839c);
}

.step-io-value {
    font-size: 0.86rem;
    font-weight: 600;
    color: var(--accent-cyan, #00f0ff);
}

/* --- the archive contents block --- */

.deliverables {
    width: 100%;
    max-width: 1000px;
    margin-top: 46px;
    padding: 26px 30px;
    background: rgba(9, 7, 15, 0.55);
    border: 1px solid rgba(160, 86, 243, 0.18);
    border-radius: 14px;
    text-align: left;
}

.deliverables-title {
    font-size: 0.72rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--text-muted, #8b839c);
    margin-bottom: 16px;
}

.deliverables-list {
    list-style: none;
    display: grid;
    /* One column on a phone, two once there is room. No breakpoint needed. */
    grid-template-columns: repeat(auto-fit, minmax(330px, 1fr));
    gap: 10px 30px;
    margin: 0 0 16px;
}

.deliverables-list li {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 9px 0 9px 26px;
    position: relative;
    border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}

/* A file glyph, not a bullet: these are things on disk. */
.deliverables-list li::before {
    content: '';
    position: absolute;
    left: 2px;
    top: 13px;
    width: 9px;
    height: 12px;
    border: 1px solid rgba(160, 86, 243, 0.55);
    border-radius: 1px 3px 1px 1px;
    /* Clips the top-right corner, so it reads as a dog-eared page. */
    clip-path: polygon(0 0, 68% 0, 100% 22%, 100% 100%, 0 100%);
}

.deliverables-list code {
    font-family: 'Consolas', 'Courier New', monospace;
    font-size: 0.86rem;
    color: var(--accent-cyan, #00f0ff);
    overflow-wrap: anywhere;
}

.deliverables-list span {
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--text-muted, #8b839c);
}

.deliverables-note {
    font-size: 0.82rem;
    color: var(--text-muted, #8b839c);
    margin: 0;
    padding-top: 4px;
}

/* ==========================================================================
   Safety section - two-column guarantee
   ========================================================================== */

/* The lead paragraph no longer ends in a colon, so it needs its own spacing
   rather than the shared .privacy-box p margin. */
.privacy-box .privacy-lead { margin-bottom: 22px; }

.guarantee-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 14px;
    margin-bottom: 24px;
}

.guarantee-col {
    padding: 18px 20px;
    border-radius: 10px;
    border: 1px solid;
}

/* Green for what it does, red for what it refuses. The red column keeps the
   colour the section already used, so the box still reads as one thing. */
.guarantee-does {
    background: rgba(0, 255, 204, 0.04);
    border-color: rgba(0, 255, 204, 0.22);
}
.guarantee-not {
    background: rgba(255, 51, 102, 0.04);
    border-color: rgba(255, 51, 102, 0.22);
}

.guarantee-head {
    font-size: 0.7rem !important;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    font-weight: 700;
    margin-bottom: 12px !important;
}
.guarantee-does .guarantee-head { color: var(--success-green, #00ffcc) !important; }
.guarantee-not .guarantee-head { color: var(--error-red, #ff3366) !important; }

.guarantee-col ul {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 9px;
}

.guarantee-col li {
    position: relative;
    padding-left: 26px;
    font-size: 0.92rem;
    line-height: 1.55;
    color: var(--text-main, #e2dfec);
}

/* Icons drawn in CSS rather than typed as emoji: an emoji renders at a
   different size and baseline on every platform, which is what made the
   original three lines look unaligned. */
.guarantee-does li::before {
    content: '';
    position: absolute;
    left: 4px;
    top: 5px;
    width: 11px;
    height: 6px;
    border-left: 2px solid var(--success-green, #00ffcc);
    border-bottom: 2px solid var(--success-green, #00ffcc);
    transform: rotate(-45deg);
}

.guarantee-not li::before,
.guarantee-not li::after {
    content: '';
    position: absolute;
    left: 3px;
    top: 10px;
    width: 12px;
    height: 2px;
    background: var(--error-red, #ff3366);
}
.guarantee-not li::before { transform: rotate(45deg); }
.guarantee-not li::after { transform: rotate(-45deg); }

/* Below this width three 250px cards plus two 40px gaps no longer fit, and the
   flex row would wrap to an awkward 2 + 1. Forcing a single column also makes
   the connector arrow's direction knowable - it is always downward. */
@media (max-width: 940px) {
    .steps-container { flex-direction: column; align-items: center; }
    .step-box { width: 100%; max-width: 440px; flex: 0 0 auto; }

    .step-box:not(:last-child)::after {
        top: auto;
        bottom: -27px;
        /* right:50% puts the arrow's right edge on centre; half its 13px width
           back to the right lands its centre on centre. */
        right: calc(50% - 6.5px);
        margin-top: 0;
        transform: rotate(45deg);      /* points down, not right */
    }
}

@media (max-width: 780px) {
    .deliverables { padding: 20px 18px; margin-top: 34px; }
    .deliverables-list { gap: 4px; }
    .guarantee-col { padding: 15px 16px; }
    .guarantee-col li { font-size: 0.88rem; }
}

/* ==========================================================================
   Reviews

   The section carries [hidden] in the markup and JS removes it only once the
   server reports three approved reviews. Everything here therefore styles
   content that may never exist on a given page load, which is fine - none of it
   reserves space.
   ========================================================================== */

.reviews-section {
    display: flex;
    flex-direction: column;
    align-items: center;
}

/* [hidden] loses to the display:flex above - specificity is equal and this rule
   comes later, so without it the section would show while empty. */
.reviews-section[hidden] { display: none; }

.reviews-grid {
    width: 100%;
    max-width: 1000px;
    display: grid;
    /* auto-fit rather than a fixed column count: three reviews make three columns,
       four wrap to two rows, and one would still be centred rather than stranded
       at the left edge. */
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 16px;
    margin-top: 34px;
}

.review-card {
    display: flex;
    flex-direction: column;
    padding: 22px 24px;
    border: 1px solid rgba(160, 86, 243, 0.18);
    border-radius: 12px;
    background: rgba(9, 7, 15, 0.5);
    text-align: left;
    transition: border-color 0.25s ease;
}
.review-card:hover { border-color: rgba(160, 86, 243, 0.4); }

.review-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 14px;
}

.review-stars {
    color: #ffc857;
    font-size: 1.05rem;
    letter-spacing: 2px;
    white-space: nowrap;
}

.review-plan {
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--purple-neon, #a056f3);
    border: 1px solid rgba(160, 86, 243, 0.3);
    border-radius: 999px;
    padding: 4px 10px;
    white-space: nowrap;
}

.review-body {
    margin: 0 0 16px 0;
    font-size: 0.95rem;
    line-height: 1.7;
    color: var(--text-main, #e2dfec);
    /* Someone else's words: wrap them wherever they have to break rather than
       letting one long unbroken string widen the whole grid column. */
    overflow-wrap: break-word;
    /* Pushes the author line to the bottom, so cards of different lengths still
       align their attributions. */
    flex: 1;
}

.review-author {
    margin: 0;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-muted, #8b839c);
}

.reviews-note {
    max-width: 620px;
    margin-top: 30px;
    font-size: 0.85rem;
    line-height: 1.7;
    color: var(--text-muted, #8b839c);
}

@media (max-width: 600px) {
    .review-card { padding: 18px 18px; }
    .review-body { font-size: 0.9rem; }
}

/* ==========================================================================
   FAQ
   ========================================================================== */

/* The section centres the list, which is itself left-aligned. Without this the
   .faq-list max-width would take effect but sit flush left inside the wrapper. */
.faq-section {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.faq-list {
    width: 100%;
    max-width: 860px;
    text-align: left;
}

.faq-item {
    border: 1px solid rgba(160, 86, 243, 0.18);
    border-radius: 12px;
    background: rgba(9, 7, 15, 0.5);
    margin-bottom: 12px;
    transition: border-color 0.25s ease, background 0.25s ease;
}

.faq-item[open] {
    border-color: rgba(160, 86, 243, 0.45);
    background: rgba(9, 7, 15, 0.75);
}

.faq-item > summary {
    /* list-style:none plus the -webkit- rule below removes the native marker in
       every engine; we draw our own so it can sit on the right. */
    list-style: none;
    cursor: pointer;
    padding: 18px 54px 18px 22px;
    position: relative;
    font-size: 1.02rem;
    font-weight: 600;
    color: var(--text-main, #e2dfec);
    line-height: 1.5;
    transition: color 0.2s ease;
}
.faq-item > summary::-webkit-details-marker { display: none; }
.faq-item > summary:hover { color: #fff; }

.faq-item > summary:focus-visible {
    outline: 2px solid var(--purple-neon, #a056f3);
    outline-offset: -2px;
    border-radius: 12px;
}

/* Chevron, rotated when open. Two borders on a rotated square: one element, no
   font dependency, and it scales with the text. */
.faq-item > summary::after {
    content: '';
    position: absolute;
    right: 24px;
    top: 50%;
    width: 9px;
    height: 9px;
    margin-top: -6px;
    border-right: 2px solid var(--purple-neon, #a056f3);
    border-bottom: 2px solid var(--purple-neon, #a056f3);
    transform: rotate(45deg);
    transition: transform 0.25s ease;
}
.faq-item[open] > summary::after {
    transform: rotate(-135deg);
    margin-top: -2px;      /* the rotation moves its visual centre */
}

.faq-answer {
    padding: 0 22px 20px;
}

.faq-answer p {
    margin: 0;
    font-size: 0.95rem;
    line-height: 1.7;
    color: var(--text-muted, #8b839c);
}

.faq-answer a {
    color: var(--accent-cyan, #00f0ff);
    text-decoration: underline;
    text-underline-offset: 2px;
}

@media (max-width: 780px) {
    .faq-item > summary { padding: 15px 46px 15px 16px; font-size: 0.95rem; }
    .faq-item > summary::after { right: 18px; }
    .faq-answer { padding: 0 16px 17px; }
    .faq-answer p { font-size: 0.9rem; }
}

/* ==========================================================================
   Motion toggle in the footer
   ========================================================================== */

.gc-motion-row {
    margin-top: 22px;
    padding-top: 20px;
    border-top: 1px solid rgba(160, 86, 243, 0.1);
}

.gc-motion-btn {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    background: transparent;
    border: 1px solid rgba(160, 86, 243, 0.25);
    border-radius: 999px;
    padding: 7px 16px;
    font: inherit;
    font-size: 0.78rem;
    color: var(--text-muted, #8b839c);
    cursor: pointer;
    transition: color 0.25s ease, border-color 0.25s ease, background 0.25s ease;
}

.gc-motion-btn:hover {
    color: var(--text-main, #e2dfec);
    border-color: rgba(160, 86, 243, 0.5);
    background: rgba(160, 86, 243, 0.08);
}

.gc-motion-btn:focus-visible {
    outline: 2px solid var(--purple-neon, #a056f3);
    outline-offset: 3px;
}

.gc-motion-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--success-green, #00ffcc);
    box-shadow: 0 0 8px rgba(0, 255, 204, 0.6);
    /* No transition: this element is only ever repainted after a reload, and a
       fading dot would suggest the change is still in progress. */
}

.gc-motion-btn.is-off .gc-motion-dot {
    background: var(--text-muted, #8b839c);
    box-shadow: none;
}

/* ==========================================================================
   About page - structure for what was a wall of text
   ========================================================================== */

.page-kicker {
    font-size: 0.72rem !important;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--purple-neon, #a056f3) !important;
    margin-bottom: 14px !important;
}

/* Fluid, so the h1 stops overflowing a narrow phone at its 3rem fixed size. */
.content h1 { font-size: clamp(1.9rem, 7vw, 3rem); }

/* A left rule down the lead paragraph: it separates the promise from the body
   copy without adding another box to the page. */
.content .lead-text {
    padding-left: 18px;
    border-left: 2px solid rgba(160, 86, 243, 0.45);
}

.about-facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
    gap: 1px;                          /* the gap *is* the divider, see below */
    margin: 40px 0 10px;
    background: rgba(160, 86, 243, 0.15);
    border: 1px solid rgba(160, 86, 243, 0.15);
    border-radius: 12px;
    overflow: hidden;
}

/* Each cell paints its own opaque background, so the 1px grid gap shows through
   as a hairline divider. Borders per cell would double up between neighbours
   and break where the grid wraps. */
.about-fact {
    background: var(--bg-secondary, #130e1d);
    padding: 20px 16px;
    text-align: center;
}

.about-fact-value {
    display: block;
    font-size: 1.75rem;
    font-weight: 900;
    line-height: 1.1;
    color: #fff;
    font-variant-numeric: tabular-nums;
}

.about-fact-label {
    display: block;
    margin-top: 6px;
    font-size: 0.74rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted, #8b839c);
}

.about-quote {
    margin: 34px 0;
    padding: 22px 26px;
    background: linear-gradient(135deg, rgba(160, 86, 243, 0.1), rgba(9, 7, 15, 0.4));
    border-left: 3px solid var(--purple-neon, #a056f3);
    border-radius: 0 12px 12px 0;
    font-size: 1.1rem;
    line-height: 1.65;
    color: var(--text-main, #e2dfec);
    font-style: italic;
}

.about-plain {
    margin: 34px 0 10px;
    padding: 24px 26px;
    background: rgba(9, 7, 15, 0.5);
    border: 1px solid rgba(160, 86, 243, 0.18);
    border-radius: 12px;
}

.about-plain-head {
    font-size: 0.72rem !important;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--accent-cyan, #00f0ff) !important;
    margin-bottom: 16px !important;
}

.about-plain ul {
    list-style: none;
    display: grid;
    gap: 13px;
    margin: 0;
    padding: 0;
}

.about-plain li {
    position: relative;
    padding-left: 22px;
    font-size: 0.96rem;
    line-height: 1.65;
    color: var(--text-muted, #8b839c);
}

.about-plain li::before {
    content: '';
    position: absolute;
    left: 2px;
    top: 0.62em;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: rgba(0, 240, 255, 0.55);
}

.about-cta {
    margin-top: 46px;
    padding-top: 30px;
    border-top: 1px solid rgba(160, 86, 243, 0.15);
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 18px;
}

.about-cta-line {
    margin: 0 !important;
    font-size: 1.05rem !important;
    color: var(--text-main, #e2dfec) !important;
    font-weight: 600;
}

.about-cta-btn {
    background: linear-gradient(135deg, var(--purple-deep, #5e17eb), var(--purple-neon, #a056f3));
    color: #fff;
    text-decoration: none;
    padding: 14px 32px;
    border-radius: 8px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: 0.9rem;
    white-space: nowrap;
    transition: transform 0.25s ease, box-shadow 0.25s ease, filter 0.25s ease;
    box-shadow: 0 0 18px rgba(94, 23, 235, 0.3);
}

.about-cta-btn:hover {
    transform: translateY(-2px);
    filter: brightness(1.12);
    box-shadow: 0 6px 26px var(--purple-glow, rgba(160, 86, 243, 0.4));
}

/* The value cards were flat panels; give them the same top-rule reveal the
   feature cards on the home page use, so the two pages feel related. */
.value-card {
    position: relative;
    overflow: hidden;
    transition: transform 0.3s ease, border-color 0.3s ease, background 0.3s ease;
}

.value-card::before {
    content: '';
    position: absolute;
    top: 0; left: 0;
    width: 100%;
    height: 2px;
    background: linear-gradient(90deg, var(--purple-neon, #a056f3), var(--accent-cyan, #00f0ff));
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.4s ease;
}

.value-card:hover {
    transform: translateY(-4px);
    border-color: rgba(160, 86, 243, 0.5);
}
.value-card:hover::before { transform: scaleX(1); }

@media (max-width: 780px) {
    .content .lead-text { padding-left: 14px; }
    .about-facts { margin-top: 30px; }
    .about-fact { padding: 16px 12px; }
    .about-fact-value { font-size: 1.5rem; }
    .about-quote { padding: 18px 20px; font-size: 1rem; margin: 26px 0; }
    .about-plain { padding: 20px 18px; }
    .about-cta { flex-direction: column; align-items: stretch; text-align: center; }
    .about-cta-btn { text-align: center; }
}

/* ==========================================================================
   Plans - per-game value line and the Legendary card
   ========================================================================== */

/* The price had a 30px margin under it; the per-game line takes that space, so
   the margin moves to this element and the price closes up against it. */
.card .price { margin-bottom: 10px; }

.per-game {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 7px;
    margin-bottom: 26px;
    padding-bottom: 16px;
    border-bottom: 1px solid rgba(160, 86, 243, 0.12);
}

.per-game-value {
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--text-main, #e2dfec);
    font-variant-numeric: tabular-nums;
}

.per-game-label {
    font-size: 0.8rem;
    color: var(--text-muted, #8b839c);
}

.per-game-save {
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--success-green, #00ffcc);
    background: rgba(0, 255, 204, 0.1);
    border: 1px solid rgba(0, 255, 204, 0.28);
    border-radius: 999px;
    padding: 2px 9px;
}

/* Legendary carries cyan, not purple. The featured card already owns purple,
   and two cards competing in the same colour is why Legendary read as an
   afterthought next to it. */
.card-legendary {
    border-color: rgba(0, 240, 255, 0.35);
    background: linear-gradient(180deg, rgba(0, 240, 255, 0.045) 0%, var(--bg-panel, #130e1d) 55%);
    /* Deliberately NOT overflow:hidden. That would clip .value-badge, which
       sits at top:-14px to match the featured card's badge. The sheen below is
       kept inside the box by limiting its travel instead. */
}

.card-legendary:hover {
    border-color: var(--accent-cyan, #00f0ff);
    box-shadow: 0 15px 40px rgba(0, 240, 255, 0.18);
}

.value-badge {
    position: absolute;
    top: -14px;
    left: 50%;
    transform: translateX(-50%);
    background: linear-gradient(135deg, #0891a8, var(--accent-cyan, #00f0ff));
    color: #04121a;                 /* dark on cyan: the light-on-cyan version fails contrast */
    padding: 5px 20px;
    border-radius: 20px;
    font-size: 0.8rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 1px;
    white-space: nowrap;
    box-shadow: 0 0 15px rgba(0, 240, 255, 0.4);
}

.card-legendary .per-game-value { color: var(--accent-cyan, #00f0ff); }

.card-legendary .btn-buy {
    background: linear-gradient(135deg, #0b7c91, var(--accent-cyan, #00f0ff));
    color: #04121a;
    box-shadow: 0 0 15px rgba(0, 240, 255, 0.3);
}
.card-legendary .btn-buy:hover { box-shadow: 0 0 26px rgba(0, 240, 255, 0.55); }

/* A light sweep across the card every few seconds. The element does not move -
   its background does - because a translating child would need overflow:hidden
   on the card to stay inside it, and that would clip the badge. */
.card-legendary::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background-image: linear-gradient(100deg,
                      transparent 38%, rgba(0, 240, 255, 0.10) 50%, transparent 62%);
    background-size: 300% 100%;
    background-position: 130% 0;
    background-repeat: no-repeat;
    pointer-events: none;
}

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
    .card-legendary::after { animation: legendarySheen 5.5s ease-in-out infinite; }
}

@keyframes legendarySheen {
    /* Idle for the first 70% of the cycle, so it is a glint and not a strobe. */
    0%, 70%  { background-position: 130% 0; }
    100%     { background-position: -30% 0; }
}

/* ==========================================================================
   Hero starfield and ghost mascot
   --------------------------------------------------------------------------
   Both are injected by ghostcfg-ui.js, so no page markup changes and pages
   without a .hero simply never get them.
   ========================================================================== */

/* .hero already declares isolation:isolate, so z-index:-1 stays inside the
   section instead of dropping behind the page background. The aurora blobs sit
   at -1 too and ::before paints before any child, so the purple glow reads as
   a nebula behind the stars - which is the effect we want, not a bug. */
.gc-stars {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: -1;
    pointer-events: none;
    /* Fades the field out where the headline sits, so stars never compete with
       text for contrast. Cheaper than dimming every star in JS. */
    -webkit-mask-image: radial-gradient(ellipse 62% 46% at 50% 46%,
                        transparent 8%, rgba(0, 0, 0, 0.55) 42%, #000 72%);
    mask-image: radial-gradient(ellipse 62% 46% at 50% 46%,
                transparent 8%, rgba(0, 0, 0, 0.55) 42%, #000 72%);
}

/* --------------------------------------------------------------------------
   Mascot. Bottom-right corner: the hero content is centred, so the corner is
   the one place that is free at every width without measuring anything.
   -------------------------------------------------------------------------- */
.gc-mascot {
    position: absolute;
    right: clamp(12px, 4vw, 68px);
    bottom: clamp(16px, 4vh, 56px);
    width: clamp(58px, 9vw, 132px);
    z-index: 1;
    line-height: 0;
}

/* A real <button>: it is clickable, so it must be reachable by keyboard and
   announce itself. Styling it away is deliberate - the ghost is the control. */
.gc-mascot-btn {
    display: block;
    width: 100%;
    padding: 0;
    background: none;
    border: 0;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.gc-mascot-btn:focus { outline: none; }
.gc-mascot-btn:focus-visible {
    outline: 2px solid var(--purple-neon, #a056f3);
    outline-offset: 6px;
    border-radius: 12px;
}

.gc-mascot svg {
    display: block;
    width: 100%;
    height: auto;
    overflow: visible;      /* the glow must not be clipped to the viewBox */
    filter: drop-shadow(0 6px 22px rgba(160, 86, 243, 0.45));
    transition: filter 0.35s ease;
}

.gc-mascot-btn:hover svg,
.gc-mascot-btn:focus-visible svg {
    filter: drop-shadow(0 8px 30px rgba(160, 86, 243, 0.8));
}

/* Two nested groups on purpose: JS writes a translate on .gc-eyes to follow the
   pointer, and the blink animation scales .gc-lids. One group doing both would
   mean the animation's transform overwriting the tracking every frame. */
/* fill-box on both: without it transform-origin resolves against the viewBox
   origin, so `center` lands outside the artwork and the ghost swings like a
   pendulum instead of rocking on the spot. */
.gc-body, .gc-lids {
    transform-box: fill-box;
    transform-origin: center;
}

@media (prefers-reduced-motion: no-preference) {
    .gc-body { animation: gcFloat 5.2s ease-in-out infinite; }
    .gc-lids { animation: gcBlink 6.4s ease-in-out infinite; }
    /* Hop on click. The class is removed on animationend, so it can retrigger. */
    .gc-mascot.is-happy .gc-body { animation: gcHop 0.62s cubic-bezier(0.34, 1.56, 0.64, 1); }
}

@keyframes gcFloat {
    0%, 100% { transform: translateY(0) rotate(0deg); }
    50%      { transform: translateY(-8px) rotate(-1.6deg); }
}

/* 96% of the cycle is open, so the blink is a blink and not a squint. */
@keyframes gcBlink {
    0%, 94%, 100% { transform: scaleY(1); }
    96%           { transform: scaleY(0.08); }
    98%           { transform: scaleY(1); }
}

@keyframes gcHop {
    0%   { transform: translateY(0) scale(1); }
    28%  { transform: translateY(-16px) scale(1.07, 0.93); }
    58%  { transform: translateY(2px) scale(0.94, 1.06); }
    100% { transform: translateY(0) scale(1); }
}

/* --------------------------------------------------------------------------
   Speech bubble. Opens to the left, because the ghost is already at the right
   edge and a bubble on its right would leave the viewport.
   -------------------------------------------------------------------------- */
.gc-mascot-say {
    position: absolute;
    right: calc(100% + 12px);
    bottom: 62%;
    width: max-content;
    max-width: min(210px, 42vw);
    background: rgba(19, 14, 29, 0.96);
    border: 1px solid rgba(160, 86, 243, 0.4);
    border-radius: 12px;
    padding: 9px 13px;
    font-size: 0.8rem;
    line-height: 1.45;
    color: var(--text-main, #e2dfec);
    text-align: left;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55);
    opacity: 0;
    transform: translateX(8px) scale(0.94);
    transform-origin: right center;
    transition: opacity 0.22s ease, transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
    pointer-events: none;
}

.gc-mascot-say::after {
    /* Tail: a rotated square, half hidden behind the bubble edge. */
    content: '';
    position: absolute;
    right: -5px;
    top: 50%;
    width: 9px;
    height: 9px;
    margin-top: -5px;
    background: rgba(19, 14, 29, 0.96);
    border-right: 1px solid rgba(160, 86, 243, 0.4);
    border-bottom: 1px solid rgba(160, 86, 243, 0.4);
    transform: rotate(-45deg);
}

.gc-mascot.is-talking .gc-mascot-say {
    opacity: 1;
    transform: translateX(0) scale(1);
}

/* Hint that the ghost does something, shown once until it is first clicked. */
.gc-mascot-hint {
    position: absolute;
    right: 50%;
    bottom: -18px;
    transform: translateX(50%);
    font-size: 0.62rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-muted, #8b839c);
    white-space: nowrap;
    opacity: 0.75;
    transition: opacity 0.3s ease;
    pointer-events: none;
}
.gc-mascot.is-used .gc-mascot-hint { opacity: 0; }

/* Star burst on click, positioned by JS in hero-relative coordinates. */
.gc-spark {
    position: absolute;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--accent-cyan, #00f0ff);
    box-shadow: 0 0 8px currentColor;
    pointer-events: none;
    z-index: 1;
    animation: gcSpark 0.72s ease-out forwards;
}

/* --dx/--dy are written inline per spark: direction is data, not style. The
   fallback values keep the keyframe valid if the properties never arrive. */
@keyframes gcSpark {
    0%   { opacity: 1; transform: translate(0, 0) scale(1); }
    100% { opacity: 0; transform: translate(var(--dx, 0), var(--dy, 0)) scale(0.2); }
}

@media (max-width: 780px) {
    .hero {
        /* Reserves the corner the mascot sits in, so it can never land on top
           of the promo line on a short phone. Same specificity as the earlier
           .hero rule in this file and declared later, so it wins. */
        padding-bottom: 124px;
    }
    /* The headline is closer to full width on a phone, so the mask has to open
       up or the stars vanish from most of the section. */
    .gc-stars {
        -webkit-mask-image: radial-gradient(ellipse 78% 38% at 50% 42%,
                            transparent 12%, rgba(0, 0, 0, 0.6) 46%, #000 76%);
        mask-image: radial-gradient(ellipse 78% 38% at 50% 42%,
                    transparent 12%, rgba(0, 0, 0, 0.6) 46%, #000 76%);
    }
    .gc-mascot-say { font-size: 0.74rem; max-width: min(180px, 52vw); }
    .gc-mascot-hint { display: none; }
}

/* A landscape phone has almost no vertical room; shrink rather than overlap. */
@media (max-height: 620px) and (max-width: 900px) {
    .gc-mascot { width: clamp(46px, 7vw, 68px); bottom: 10px; right: 10px; }
    .hero { padding-bottom: 92px; }
}

@media (prefers-reduced-motion: reduce) {
    /* Eye tracking stays: it is not vestibular motion, and it is the part that
       makes the ghost feel alive. The float, hop and sparks go. */
    .gc-mascot-say { transition: opacity 0.2s ease; transform: none; }
    .gc-mascot.is-talking .gc-mascot-say { transform: none; }
    .gc-spark { display: none; }
}

/* --------------------------------------------------------------------------
   Scroll CTA. A fixed bar, not a section: it sits outside the document flow,
   so nothing is added to the page's length and no existing block moves. It
   only exists between the hero leaving the screen and the footer arriving -
   the stretch where a visitor is reading and has no link to the plans in
   reach. Built by ghostcfg-ui.js, so a visitor without JS simply never sees
   it rather than seeing a bar that cannot be dismissed.
   -------------------------------------------------------------------------- */
.gc-cta {
    position: fixed;
    left: 50%;
    bottom: 22px;
    z-index: 95;                 /* under the header (100), over the page */
    display: flex;
    align-items: center;
    gap: 16px;
    max-width: min(680px, calc(100vw - 32px));
    padding: 13px 16px 13px 20px;
    border-radius: 14px;
    background: rgba(16, 11, 28, 0.92);
    backdrop-filter: blur(14px);
    border: 1px solid rgba(160, 86, 243, 0.38);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.45);
    /* translateX centres it; translateY is the entrance. Both live in one
       transform because a second one would overwrite the first. */
    transform: translate(-50%, calc(100% + 32px));
    opacity: 0;
    visibility: hidden;
    transition: transform 0.42s cubic-bezier(0.22, 1, 0.36, 1),
                opacity 0.3s ease, visibility 0.3s;
}
.gc-cta.is-in {
    transform: translate(-50%, 0);
    opacity: 1;
    visibility: visible;
}

.gc-cta-text {
    font-size: 0.95rem;
    line-height: 1.35;
    color: #efecf7;
    margin: 0;
}
.gc-cta-text strong { color: var(--purple-neon); font-weight: 700; }
.gc-cta-sub {
    display: block;
    font-size: 0.8rem;
    opacity: 0.68;
    margin-top: 2px;
}

.gc-cta-btn {
    flex: 0 0 auto;
    padding: 11px 20px;
    border-radius: 10px;
    background: linear-gradient(135deg, #a056f3, #5e17eb);
    color: #fff;
    font-weight: 700;
    font-size: 0.92rem;
    text-decoration: none;
    white-space: nowrap;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.gc-cta-btn:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 22px rgba(160, 86, 243, 0.45);
}
.gc-cta-btn:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px;
}

/* A real button, not an x glyph in a span: it has to be reachable by keyboard
   and announced as a control. 34px square is small on screen but the padding
   round the glyph keeps the tap target at the 44px minimum. */
.gc-cta-close {
    flex: 0 0 auto;
    width: 34px; height: 34px;
    display: grid;
    place-items: center;
    padding: 0;
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, 0.14);
    background: rgba(255, 255, 255, 0.06);
    color: #cfc8e0;
    font-size: 17px;
    line-height: 1;
    cursor: pointer;
    transition: background 0.2s ease, color 0.2s ease;
}
.gc-cta-close:hover { background: rgba(255, 255, 255, 0.12); color: #fff; }
.gc-cta-close:focus-visible { outline: 2px solid var(--purple-neon); outline-offset: 2px; }

@media (max-width: 560px) {
    /* Stacked, full width, and tight to the bottom edge: at this size a row of
       three would squeeze the button below a usable width. */
    .gc-cta {
        left: 12px; right: 12px;
        bottom: 12px;
        max-width: none;
        transform: translateY(calc(100% + 24px));
        padding: 12px 12px 12px 15px;
        gap: 11px;
    }
    .gc-cta.is-in { transform: translateY(0); }
    .gc-cta-text { font-size: 0.88rem; }
    .gc-cta-sub { display: none; }   /* the headline carries it alone */
    .gc-cta-btn { padding: 10px 15px; font-size: 0.87rem; }
}

@media (prefers-reduced-motion: reduce) {
    .gc-cta { transition: opacity 0.2s ease, visibility 0.2s; }
    .gc-cta, .gc-cta.is-in { transform: translate(-50%, 0); }
    .gc-cta-btn:hover { transform: none; }
}
@media (prefers-reduced-motion: reduce) and (max-width: 560px) {
    .gc-cta, .gc-cta.is-in { transform: none; }
}

/* ==========================================================================
   Social links in the footer
   ==========================================================================
   A row of its own rather than two more entries in the link list. Those links
   are all internal or legal, and a visitor scanning for "where else are they"
   should not have to read past Privacy Policy to find it.

   Class selectors on purpose: every page carries its own inline `footer a`
   rule, and a bare descendant selector would tie the result to which
   stylesheet happened to load last. `.gc-social a` outranks `footer a` on
   specificity, so this wins regardless of order.
   -------------------------------------------------------------------------- */
.gc-social {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 10px;
    margin-top: 20px;
    flex-wrap: wrap;
}

.gc-social a {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    margin: 0;
    padding: 8px 14px;
    border: 1px solid rgba(160, 86, 243, 0.28);
    border-radius: 999px;
    color: var(--text-muted, #8b839c);
    text-decoration: none;
    font-size: 0.85rem;
    transition: color 0.2s, border-color 0.2s, background 0.2s;
}

.gc-social a:hover,
.gc-social a:focus-visible {
    color: #fff;
    border-color: var(--purple-neon, #a056f3);
    background: rgba(160, 86, 243, 0.12);
}

.gc-social a:focus-visible {
    outline: 2px solid var(--purple-neon, #a056f3);
    outline-offset: 2px;
}

/* currentColor, so the glyph follows the hover state of its own link without a
   second rule. aria-hidden on the svg keeps it out of the accessible name,
   which the visible text already provides. */
.gc-social svg {
    width: 16px;
    height: 16px;
    fill: currentColor;
    flex-shrink: 0;
}
