/* ═══════════════════════════════════════════════════════════════════════
 * dp-download.css — /download (views/download.ejs). Oct 31 2026: the homepage's
 * violet system (public/css/dp-landing.css, /hq): one solid violet, white ink,
 * white pill buttons, translucent white panels. One button, then the other ways
 * to install (public/install/hub.js).
 *
 * The first part is the page's older stylesheet, moved here as it was: the install
 * console and the walkthrough modals still draw from it. The "THE PAGE" section at
 * the bottom repaints it, so new rules go there.
 * ═══════════════════════════════════════════════════════════════════════ */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html {
    scroll-behavior: smooth;
    /* Solid dark base on the ROOT element. Safari paints the html
       background into its chrome + overscroll/rubber-band areas,
       without this they flashed WHITE against the dark purple page
       (iOS also ignores background-attachment: fixed, which could
       drop the body gradient entirely). Matches <meta theme-color>. */
    background: var(--mdp-ui-ink-void, #0c0220);
}
/* AND A LIGHT PAGE HAS TO SAY SO ON <html>, NOT JUST <body>.
   A root element with a non-transparent background stops the
   browser propagating BODY's background to the canvas — so
   `body.dl-light { background: #fff }` painted body's own box and
   nothing else, and everything outside that box kept the dark root
   colour above. Nothing gives body a min-height, so the moment the
   footer was removed body ended above the fold and the root colour
   showed through as a purple band across the bottom of the page.
   The class is on <html> in the markup, so this is a plain
   override rather than a :has() trick. */
/* Anchor jumps clear the fixed header. The 88px literal this
   replaced was 72px of header plus 16px of air, written as one
   number that nothing kept true. */
:target { scroll-margin-top: calc(var(--site-header-h) + 16px); }
body {
    /* ── THE PAGE'S INK, AS TOKENS ─────────────────────────
       Dark values here, paper values in the body.dl-light block.
       Everything below that colours TEXT, a RULE or a QUIET
       ACCENT reads one of these rather than stating a literal.

       This is not tidiness. The Learn More control, the whole
       FAQ and the release-notes prose were authored in white-page
       ink with no dl-light counterpart, back when this page was
       always white and the dark base underneath it was theory.
       The moment the theme became real they were black text on a
       near-black page — invisible, and invisible in a way nobody
       would catch by reading the diff, because every one of those
       rules is individually correct. Tokens are how the next
       section written for one theme is legible in both.

       --dl-halo is the odd one out: the whole gradient rather
       than a colour, because THE TWO THEMES NEED DIFFERENT STOPS
       and not just different ink. On the white page the halo is
       paper over pale artwork and it can be nearly opaque at the
       centre without looking like anything — it reads as the
       wall thinning out. The same opacities in ink-void over a
       dark page read as a black oval painted on top of the
       artwork, which is exactly what it looked like. Dark needs a
       shorter, softer falloff: enough to seat the wordmark, not
       enough to be an object. Hence two gradients, not one
       gradient and two colours. */
    --dl-halo:
        radial-gradient(closest-side,
            rgba(12, 2, 32, .90) 0%,
            rgba(12, 2, 32, .80) 34%,
            rgba(12, 2, 32, .55) 58%,
            rgba(12, 2, 32, .22) 80%,
            rgba(12, 2, 32, 0) 100%);
    --dl-ink: #ffffff;                        /* headings, emphasis */
    --dl-ink-body: rgba(235, 228, 245, .72);  /* prose */
    --dl-ink-quiet: rgba(235, 228, 245, .55); /* labels, Learn More */
    --dl-ink-faint: rgba(235, 228, 245, .38); /* disclosure chevrons */
    --dl-line: rgba(255, 255, 255, .12);      /* row rules */
    /* The accent, which CANNOT be the same violet in both themes.
       #7700ff is the brand violet and it is a fine ink on white
       and nearly unreadable on #0c0220 — it is barely lighter
       than the page. Dark gets violet-bright, the same swap every
       other dark surface on the site makes. */
    --dl-accent: #c77dff;
    --dl-accent-line: rgba(199, 125, 255, .34);
    --dl-accent-wash: rgba(199, 125, 255, .10);
    font-family: var(--mdp-ui-body);
    font-weight: 400;
    font-size: 16px;
    color: rgba(235, 228, 245, 0.78);
    background:
        radial-gradient(ellipse at 50% 0%, rgba(106, 27, 154, 0.45), transparent 55%),
        radial-gradient(ellipse at 80% 50%, rgba(255, 62, 181, 0.16), transparent 60%),
        radial-gradient(ellipse at 20% 80%, rgba(0, 229, 255, 0.10), transparent 55%),
        linear-gradient(180deg, var(--mdp-ui-ink-void, #0c0220) 0%, var(--mdp-ui-ink-deep, #1a0633) 100%);
    /* No background-attachment: fixed, iOS Safari doesn't support
       it and could render the gradient wrong or not at all. */
    background-color: var(--mdp-ui-ink-void, #0c0220);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    overflow-x: hidden;
}
a { color: inherit; text-decoration: none; }
::selection { background: var(--mdp-ui-gold); color: #2a1a00; }

/* The site header lives in views/partials/site-header.ejs now.
   Its CSS is /css/site-header.css; the violet CTA, the brand
   mark and the light/dark ink all come from the partial's
   theme prop rather than from a private .site-header system. */


/* ── Layout ─────────────────────────────────────────────────── */
.page { max-width: 1040px; margin: 0 auto; padding: calc(var(--site-header-h) + 32px) 24px 80px; }
section.block { margin: 72px 0 0; text-align: center; }

/* Section headers, same recipe as the homepage MyDreamPark
   section (.ps-title): Open Sans 800, white, gradient <em>. */
section.block h2, .hero h1 {
    font-family: var(--mdp-ui-heading);
    font-weight: 800;
    letter-spacing: -0.01em;
    line-height: 1.05;
    color: #fff;
    margin: 0 0 12px;
}
section.block h2 { font-size: clamp(1.7rem, 3.6vw, 2.4rem); }
.hero h1 { font-size: clamp(2.6rem, 6vw, 4.2rem); }
h1 em, h2 em {
    font-style: normal;
    font-weight: 800;
    background: linear-gradient(135deg, var(--mdp-ui-violet-bright) 0%, var(--mdp-ui-pink) 50%, var(--mdp-ui-gold) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
}
.lead {
    font-family: var(--mdp-ui-body);
    font-size: 1.08rem; line-height: 1.6;
    color: rgba(235, 228, 245, 0.78);
    max-width: 640px; margin: 0 auto;
}

/* Glassmorphic section shells = .mdp-ui-panel (+ --glow). */
.block.mdp-ui-panel { padding: 36px 38px 38px; }
@media (max-width: 600px) { .block.mdp-ui-panel { padding: 26px 22px; } }

.glass-card {
    background:
        linear-gradient(180deg, rgba(255,255,255,0.05), rgba(255,255,255,0.015) 40%, transparent),
        rgba(18, 10, 38, 0.55);
    border: 1px solid rgba(255, 255, 255, 0.09);
    border-radius: 18px;
    backdrop-filter: blur(14px) saturate(140%);
    -webkit-backdrop-filter: blur(14px) saturate(140%);
}

/* ── Hero ───────────────────────────────────────────────────── */
.hero { text-align: center; padding-top: 10px; }

/* App icon, simple and clean: the white mark above the wordmark. */
.hero-icon {
    display: block;
    width: 64px; height: 64px;
    margin: 0 auto 8px;
    filter: drop-shadow(0 8px 28px rgba(119, 0, 255, 0.5));
}

/* DREAMPARK wordmark (text logo, not the icon) */
.brand-logo { margin: 0; line-height: 0; }
.brand-logo img {
    width: clamp(240px, 36vw, 400px);
    height: auto;
    filter: drop-shadow(0 6px 28px rgba(119, 0, 255, 0.35));
}

.chrome-link {
    color: #fff; font-weight: 600; cursor: pointer;
    text-decoration: underline; text-underline-offset: 3px;
    text-decoration-color: rgba(199, 125, 255, 0.7);
}
.chrome-link:hover { text-decoration-color: #fff; }

/* The one line of pitch, sized to be read at a glance rather
   than a paragraph you have to work through. Outfit at 600, not
   the heading face at 800: Open Sans that heavy reads blunt at
   this size. White plus the bright brand violet on the last
   two words; the wordmark stays brightest by size, not by dimming the pitch. */
.hero .sub {
    /* 800, not the 600 this started at. The old note argued
       against weight here on the grounds that the HEADING face
       reads blunt at this size — which is still true, and this is
       still Outfit, which has a real 800 and holds its
       counters at scale. In caps a mid weight looked
       under-committed next to the wordmark directly above it;
       the line is a claim, so it should sound like one. */
    font-family: var(--mdp-ui-body); font-weight: 800;
    max-width: 22ch; margin: 16px auto 0;
    /* ONE LINE, AT WHATEVER SIZE THAT TAKES.
       The line is a single claim and it reads as one; breaking it
       across two made the reader assemble it. So the size is
       driven by the width available rather than picked and then
       wrapped — see white-space: nowrap below, the two go
       together.

       The divisor is measured, not guessed. Set in Outfit 800
       caps at this tracking, the string is 23.65em wide (checked
       at three sizes: 567px at 24px, 491px at 20.77px, 332px at
       14.04px). 24.2 leaves ~2% of slack. The 64px covers .page's
       48px of horizontal padding plus the scrollbar, which 100vw
       counts and clientWidth does not — that gap is what a plain
       vw coefficient got wrong, clipping the line below 360px.
       1.5rem caps it on a wide screen: smaller than the 2rem this
       replaced, which is the point. */
    font-size: min(1.5rem, calc((100vw - 64px) / 24.2));
    line-height: 1.24;
    color: #fff;
    text-transform: uppercase;
    /* Caps want the opposite tracking to lowercase. The -0.01em
       this replaced was tuned for a mixed-case line; on capitals
       the same value closes the letterforms up until the words
       read as one block. */
    letter-spacing: 0.015em;
    /* balance is gone with the second line it was balancing. */
    white-space: nowrap;
}
/* No measure. A max-width only decides WHERE a line breaks, and
   this one does not break — the font-size above is what keeps it
   on the screen. Leaving the old 27ch in place would have clipped
   a nowrap line rather than wrapped it. */
.hero .sub { max-width: none; }
.hero .sub em {
    font-style: normal;
    /* The period is deliberately OUTSIDE the emphasis: the brand
       colour carries the two words, and a dark full stop lands
       the line rather than trailing off in violet. */
    color: var(--mdp-ui-violet-bright);
}
.hero-empty {
    font-family: var(--mdp-ui-body); font-size: 1.05rem;
    color: rgba(235, 228, 245, 0.7); margin: 22px auto 0; max-width: 44ch;
}


.cta-stack { display: flex; flex-direction: column; align-items: center; gap: 16px; margin-top: 30px; }

/* ── Main CTA, brand-purple hero button ────────────────────
   DreamPark purple (#7700ff, same as the headset brand gradient)
   flowing into the mdp violet family. Three layers of motion:
   a breathing glow, a periodic glisten sweep, and hover lift.
   All motion disabled under prefers-reduced-motion. */
.dl-mega {
    position: relative;
    display: inline-flex; flex-direction: column; align-items: center; gap: 3px;
    overflow: hidden; isolation: isolate;
    cursor: pointer; text-decoration: none; border: 1px solid rgba(199, 125, 255, 0.65);
    font-family: var(--mdp-ui-heading); font-weight: 800; font-size: 17px;
    letter-spacing: 0.05em; text-transform: uppercase; color: #fff;
    border-radius: 999px; padding: 19px 58px;
    background:
        linear-gradient(180deg, rgba(255, 255, 255, 0.22) 0%, transparent 45%),
        linear-gradient(135deg, #7700ff 0%, #9d4edd 55%, #c77dff 115%);
    box-shadow:
        0 12px 48px rgba(119, 0, 255, 0.5),
        0 0 0 0 rgba(157, 78, 221, 0.4),
        inset 0 1px 0 rgba(255, 255, 255, 0.35);
    animation: dlBreathe 2.8s ease-in-out infinite;
    transition: transform .18s cubic-bezier(0.16, 1, 0.3, 1), box-shadow .18s ease, filter .18s ease;
}
/* Fixed line boxes so text swaps can't change the button's
   height, different labels, same metrics. min-height rather
   than height: a label that DOES need two lines (narrow
   phones) must push the button taller instead of overlapping
   the version line underneath it. */
/* Direct child only: this sets the main label's line box, and
   an unscoped `span` also caught the inline compatibility tag
   in the meta line and made it a block, breaking the line. */
.dl-mega > span { line-height: 22px; min-height: 22px; display: block; }
/* Compatibility lives on the button now, so it is read at the
   moment it matters instead of as a line of small print. */
.dl-plat { display: inline; }
.dl-mega small {
    font-family: var(--mdp-ui-body);
    font-size: 12px; font-weight: 600; letter-spacing: 0.02em;
    line-height: 15px; min-height: 15px; display: block;
    white-space: nowrap;
    text-transform: none; opacity: 0.85; text-shadow: none;
}
@media (max-width: 480px) {
    .dl-mega { padding: 17px 30px; font-size: 15px; }
}
.dl-mega:hover {
    transform: translateY(-3px) scale(1.025);
    filter: brightness(1.08);
    box-shadow:
        0 20px 64px rgba(119, 0, 255, 0.65),
        0 0 34px rgba(199, 125, 255, 0.45),
        inset 0 1px 0 rgba(255, 255, 255, 0.4);
}
.dl-mega:active { transform: translateY(-1px) scale(1.01); filter: brightness(0.98); }

/* Glisten, a soft light band sweeping across every few seconds */
.dl-mega::after {
    content: '';
    position: absolute; top: -20%; bottom: -20%; left: -80%;
    width: 45%; z-index: 1; pointer-events: none;
    background: linear-gradient(105deg,
        transparent 0%,
        rgba(255, 255, 255, 0.05) 35%,
        rgba(255, 255, 255, 0.38) 50%,
        rgba(255, 255, 255, 0.05) 65%,
        transparent 100%);
    transform: skewX(-18deg);
    animation: dlGlisten 3.4s ease-in-out infinite;
}
.dl-mega > * { position: relative; z-index: 2; }

@keyframes dlBreathe {
    0%, 100% { box-shadow: 0 12px 48px rgba(119, 0, 255, 0.5), 0 0 0 0 rgba(157, 78, 221, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.35); }
    50% { box-shadow: 0 14px 58px rgba(119, 0, 255, 0.68), 0 0 30px 4px rgba(157, 78, 221, 0.32), inset 0 1px 0 rgba(255, 255, 255, 0.35); }
}
@keyframes dlGlisten {
    0% { left: -80%; }
    55%, 100% { left: 130%; }
}
@media (prefers-reduced-motion: reduce) {
    .dl-mega { animation: none !important; }
    .dl-mega::after { animation: none; display: none; }
    .dl-mega:hover { transform: none; }
}

.hero-note { margin-top: 18px; font-size: 0.95rem; color: rgba(235, 228, 245, 0.5); }
/* Inside .cta-stack the 16px flex gap already separates it, so the
   margin above would double-count. -6px pulls it up tight under the
   button, which is the point of it being there. */
.cta-stack .hero-note { margin-top: -6px; }
.hero-note b { color: rgba(235, 228, 245, 0.85); font-weight: 600; }

/* Official App Store badge, Apple's exact layout rules: black
   pill, thin gray border, Apple mark left, two left-aligned text
   lines ("Download on the" small over "App Store" large) with
   tight line-height and NO wrapping. Scaled up ~1.4× but every
   internal proportion follows the official badge. */
.app-store-badge {
    display: inline-flex; align-items: center; gap: 12px;
    white-space: nowrap;
    background: #000; border: 1px solid #a6a6a6;
    border-radius: 13px; padding: 11px 24px 11px 18px;
    transition: transform .15s ease, border-color .15s ease;
}
.app-store-badge:hover { transform: translateY(-1px); border-color: #d9d9d9; }
.app-store-badge svg { width: 31px; height: 31px; color: #fff; flex-shrink: 0; }
.app-store-badge .btn-text {
    display: flex; flex-direction: column; align-items: flex-start;
    gap: 2px;
    font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, sans-serif;
}
.app-store-badge .btn-text small {
    font-size: 0.7rem; font-weight: 400; line-height: 1;
    color: #fff; letter-spacing: 0.01em;
}
.app-store-badge .btn-text span {
    font-size: 1.4rem; font-weight: 600; line-height: 1;
    color: #fff; letter-spacing: -0.015em;
}

/* ── Platform stats strip ────────────────────────────────────
   The SAME component the share pages use (.shr-stats /
   .shr-stat, defined in views/share/share-head.ejs), retuned
   for this page's dark chrome. Kept structurally identical so
   the two surfaces stay one design. */
/* EQUAL COLUMNS, matching the shared component (share-styles.ejs).
   This file keeps its own copy of these rules and its own comment
   says to keep them structurally identical — so when the strip
   stopped being sized by each label's own width over there, it had
   to stop over here too, or the two surfaces are one design in
   name only. */
.shr-stats {
    display: inline-grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    align-items: stretch;
    margin-top: 26px;
    max-width: 100%;
    /* SHRINK TO FIT, EVEN AS A FLEX ITEM. `inline-grid` is what
       keeps the strip content-width and centred by the parent's
       text-align on every share page — but this page's light
       hero is `display: flex; flex-direction: column`, and a
       flex container's default `align-items: stretch` blows an
       inline-level child out to the full width of the column.
       Three stats each took a third of the hero and the strip
       read as a spaced-out row of unrelated numbers instead of
       the share pages' tight set. align-self wins that back
       without touching the hero, where stretch is what every
       other child wants. */
    align-self: center;
}
.shr-stat { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; padding: 6px clamp(14px, 4vw, 28px); }
.shr-stat .v {
    font-family: var(--mdp-ui-heading); font-size: 19px; font-weight: 800;
    color: #fff; font-variant-numeric: tabular-nums;
}
.shr-stat .l {
    font-size: 9.5px; font-weight: 800; letter-spacing: 1.6px;
    text-transform: uppercase; color: rgba(235, 228, 245, 0.45);
}
.shr-stat + .shr-stat { border-left: 1px solid rgba(255, 255, 255, 0.12); }
@media (max-width: 430px) { .shr-stat { padding: 6px 17px; } }

/* ══ INSTALL CONSOLE ══════════════════════════════════════════
   An embedded terminal window, not a popout panel. The
   Download button collapses and this grows in its place: title
   bar, one big instruction, a live log welded to the bottom
   edge. It says one thing at a time and nothing else. */
.dpc {
    position: relative;
    width: min(520px, 100%); margin: 26px auto 0;
    border-radius: 14px; overflow: hidden; text-align: center;
    background: rgba(9, 3, 22, 0.9);
    border: 1px solid rgba(199, 125, 255, 0.22);
    box-shadow: 0 20px 60px -24px rgba(0, 0, 0, 0.9), inset 0 0 70px rgba(119, 0, 255, 0.07);
    transform-origin: top center;
    animation: dpcGrow 0.45s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes dpcGrow {
    from { opacity: 0; transform: scale(0.94) translateY(-12px); }
    to   { opacity: 1; transform: none; }
}
/* The CTA shrinking away is what sells "the button became this". */
.cta-stack.is-collapsing { animation: ctaCollapse 0.18s ease both; pointer-events: none; }
@keyframes ctaCollapse { to { opacity: 0; transform: scale(0.92); } }
@media (prefers-reduced-motion: reduce) {
    .dpc { animation: none; }
    .cta-stack.is-collapsing { animation: none; opacity: 0; }
}

/* Title bar */
.dpc-bar {
    display: flex; align-items: center; gap: 8px;
    padding: 9px 12px 9px 14px;
    background: rgba(255, 255, 255, 0.035);
    border-bottom: 1px solid rgba(255, 255, 255, 0.07);
    font-family: ui-monospace, Menlo, monospace;
    font-size: 11px; letter-spacing: 0.08em;
    color: rgba(235, 228, 245, 0.4);
}
.dpc-bar .caret { color: var(--mdp-ui-violet-bright); }
.dpc-x {
    margin-left: auto; width: 24px; height: 24px; border: 0; border-radius: 6px;
    background: none; cursor: pointer; color: rgba(235, 228, 245, 0.4);
    display: inline-flex; align-items: center; justify-content: center;
}
.dpc-x:hover { color: #fff; background: rgba(255, 255, 255, 0.07); }
.dpc-x svg { width: 11px; height: 11px; }

.dpc-body {
    display: flex; flex-direction: column; align-items: center; gap: 16px;
    padding: 26px 22px 22px;
}

/* Headset + scanning ring. Violet throughout; green means done,
   red means blocked. No third accent colour. */
.dpc-visual { position: relative; width: 78px; height: 78px; display: flex; align-items: center; justify-content: center; }
.usb-headset { width: 34px; height: 34px; color: var(--mdp-ui-violet-bright); transition: color .3s ease; }
.usb-ring {
    position: absolute; inset: 0; border-radius: 50%;
    border: 3px solid rgba(255, 255, 255, 0.08);
    border-top-color: var(--mdp-ui-violet-bright);
    animation: usbSpin 1.1s linear infinite;
}
@keyframes usbSpin { to { transform: rotate(360deg); } }
.dpc.is-connected .usb-ring {
    animation: none; border-color: var(--mdp-ui-xp);
    box-shadow: 0 0 22px rgba(0, 245, 160, 0.32);
}
.dpc.is-connected .usb-headset { color: var(--mdp-ui-xp); }
.dpc.is-error .usb-ring { animation: none; border-color: var(--mdp-ui-danger); }
.dpc.is-error .usb-headset { color: var(--mdp-ui-danger); }
.dpc.is-still .usb-ring { animation: none; border-color: rgba(199, 125, 255, 0.35); }
@media (prefers-reduced-motion: reduce) { .usb-ring { animation: none !important; } }

/* The instruction. This is the loudest thing on the screen. */
.dpc-head {
    font-family: var(--mdp-ui-heading); font-weight: 800;
    font-size: clamp(1.35rem, 3.6vw, 1.75rem); line-height: 1.2;
    letter-spacing: -0.01em; color: #fff; margin: 0;
}
.dpc-sub {
    font-size: 0.95rem; line-height: 1.5; margin: -6px 0 0;
    color: rgba(235, 228, 245, 0.6); max-width: 34ch;
}
.dpc-sub:empty { display: none; }
/* An action worded into the sentence. Keeps the screen's shape
   identical whether or not a retry is available, so nothing
   jumps when a button would otherwise appear or vanish. */
.dpc-inline {
    background: none; border: 0; padding: 0; cursor: pointer;
    font: inherit; color: #fff; font-weight: 600;
    text-decoration: underline; text-underline-offset: 3px;
    text-decoration-color: rgba(199, 125, 255, 0.75);
}
.dpc-inline:hover { text-decoration-color: #fff; }

/* Readiness lights. Quiet status, not controls. */
.dpc-flags {
    display: none; align-items: center; justify-content: center; gap: 20px;
    font-family: ui-monospace, Menlo, monospace;
    font-size: 10.5px; letter-spacing: 0.1em; text-transform: uppercase;
    color: rgba(235, 228, 245, 0.4);
}
.dpc.has-flags .dpc-flags { display: flex; }
.dpc-flag { display: inline-flex; align-items: center; gap: 7px; }
.led {
    width: 8px; height: 8px; border-radius: 50%; flex: none;
    background: rgba(255, 255, 255, 0.16);
    transition: background .25s ease, box-shadow .25s ease;
}
.led.on { background: var(--mdp-ui-xp); box-shadow: 0 0 9px rgba(0, 245, 160, 0.85); }
.led.off { background: var(--mdp-ui-danger); box-shadow: 0 0 9px rgba(255, 90, 110, 0.75); }
.led.wait { background: var(--mdp-ui-violet-bright); box-shadow: 0 0 9px rgba(199, 125, 255, 0.85); animation: pulse 1s infinite; }
@keyframes pulse { 50% { opacity: 0.3; } }

/* Help sits ABOVE the button: read the way out before you
   commit to the answer. */
.dpc-help { display: flex; justify-content: center; }
.dpc-help:empty { display: none; }
.dpc-actions { display: flex; align-items: center; justify-content: center; gap: 10px; flex-wrap: wrap; }
.dpc-actions:empty { display: none; }
.dpc-btn {
    cursor: pointer; font-family: var(--mdp-ui-heading); font-weight: 800; font-size: 12px;
    letter-spacing: 0.05em; text-transform: uppercase; color: #fff;
    border: 1px solid rgba(199, 125, 255, 0.65); border-radius: 999px; padding: 12px 28px;
    background:
        linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, transparent 45%),
        linear-gradient(135deg, #7700ff 0%, #9d4edd 60%, #c77dff 120%);
    box-shadow: 0 8px 26px rgba(119, 0, 255, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.28);
    transition: transform .15s ease, filter .15s ease;
}
.dpc-btn:hover { transform: translateY(-2px); filter: brightness(1.08); }
.dpc-btn:disabled { opacity: 0.45; cursor: default; transform: none; filter: none; }
.dpc-btn.ghost {
    background: rgba(255, 255, 255, 0.06); border-color: rgba(255, 255, 255, 0.18);
    box-shadow: none; padding: 12px 22px;
}
.dpc-btn.ghost:hover { background: rgba(255, 255, 255, 0.12); }

.dpc-foot { display: flex; align-items: center; justify-content: center; gap: 18px; flex-wrap: wrap; }
.dpc-foot:empty { display: none; }
.dpc-link {
    cursor: pointer; background: none; border: 0; padding: 0;
    font-family: var(--mdp-ui-body); font-size: 0.88rem; font-weight: 600; color: #fff;
    text-decoration: underline; text-underline-offset: 3px;
    text-decoration-color: rgba(199, 125, 255, 0.7);
}
.dpc-link:hover { text-decoration-color: #fff; }
.dpc-link.quiet { color: rgba(235, 228, 245, 0.4); font-weight: 500; text-decoration-color: rgba(235, 228, 245, 0.2); }
.dpc-link.quiet:hover { color: rgba(235, 228, 245, 0.8); }

/* Sideload options, manual path only. Plain rows, no cards. */
.dpc-tools { display: none; flex-direction: column; gap: 7px; width: 100%; max-width: 320px; }
.dpc.has-tools .dpc-tools { display: flex; }
.dpc-tool {
    display: block; padding: 10px 13px; border-radius: 10px; text-decoration: none;
    border: 1px solid rgba(255, 255, 255, 0.09); background: rgba(0, 0, 0, 0.28);
    font-family: var(--mdp-ui-heading); font-weight: 800; font-size: 0.85rem; color: #fff;
    transition: border-color .2s ease;
}
.dpc-tool:hover { border-color: rgba(199, 125, 255, 0.5); }

/* ── Progress + log, welded to the window ───────────────────── */
.usb-progress { display: none; width: 100%; }
.usb-track { height: 6px; border-radius: 999px; background: rgba(0,0,0,0.5); overflow: hidden; border: 1px solid rgba(255,255,255,0.06); }
.usb-fill {
    height: 100%; width: 0%; border-radius: 999px;
    background: linear-gradient(90deg, #7700ff, var(--mdp-ui-violet-bright));
    box-shadow: 0 0 12px rgba(157, 78, 221, 0.6);
    transition: width .2s ease;
}
.usb-progress-label {
    display: flex; justify-content: space-between; gap: 10px; margin-top: 6px;
    font-family: ui-monospace, Menlo, monospace; font-size: 0.72rem;
    color: rgba(235, 228, 245, 0.4);
}
.usb-progress-label span:last-child { font-variant-numeric: tabular-nums; }

/* The terminal is part of the window: flush to the edges, own
   border, no nested rounded box floating inside a panel. */
.usb-log {
    display: none; max-height: 116px; overflow-y: auto;
    padding: 10px 14px 12px;
    background: rgba(0, 0, 0, 0.42);
    border-top: 1px solid rgba(255, 255, 255, 0.07);
    font-family: ui-monospace, Menlo, monospace; font-size: 0.73rem; line-height: 1.8;
    color: rgba(235, 228, 245, 0.45); text-align: left;
    scrollbar-width: thin;
}
.usb-log::-webkit-scrollbar { width: 4px; }
.usb-log::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.13); border-radius: 999px; }
.usb-log div::before { content: '> '; color: rgba(199, 125, 255, 0.55); }
.usb-log .ok { color: var(--mdp-ui-xp); }
.usb-log .ok::before { color: var(--mdp-ui-xp); }
.usb-log .err { color: var(--mdp-ui-danger); }
.usb-log .err::before { color: var(--mdp-ui-danger); }
.usb-log::after {
    content: '_'; display: inline-block;
    color: rgba(199, 125, 255, 0.85); animation: caret 1.05s steps(1) infinite;
}
@keyframes caret { 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .usb-log::after { animation: none; } }

/* ── Get-the-app modal ──────────────────────────────────────
   Two type faces only: the heading face for the title and the
   button labels, the body face for everything else. The old
   version mixed in a monospace status line, which read as a
   third voice in a 400px box. */
/* .psf-modal is defined further down the sheet, so its gap and
   layout win at equal specificity. Double the class up to take
   the spacing back rather than reordering the whole file. */
.app-overlay { padding-top: calc(var(--site-header-h) + 2px); }
.psf-modal.app-modal {
    position: relative;
    gap: 0; align-items: center; text-align: center;
    /* The icon hangs half out of the top edge, so the panel's own
       top padding only has to clear the half that is inside.
       Same gap to the title, ~80px less panel. NO margin-top
       here: .psf-modal centres itself with `margin: auto`, and
       a fixed top margin replaces that auto and pins the card
       to the top of the screen. */
    max-width: 500px;
    padding: 66px 34px 30px;
    border-radius: 28px;
    background:
        radial-gradient(ellipse at 50% -10%, rgba(119, 0, 255, 0.28), transparent 62%),
        linear-gradient(180deg, rgba(28, 13, 58, 0.96) 0%, rgba(13, 5, 30, 0.98) 100%);
    border: 1px solid rgba(255, 255, 255, 0.11);
    box-shadow: 0 44px 110px -34px rgba(0, 0, 0, 0.95), inset 0 1px 0 rgba(255, 255, 255, 0.07);
}
.app-modal .psf-x { top: 14px; right: 14px; border-color: transparent; background: none; }
.app-modal .psf-x:hover { background: rgba(255, 255, 255, 0.07); }

.app-icon {
    position: absolute; top: -44px; left: 50%; transform: translateX(-50%);
    width: 88px; height: 88px; border-radius: 20px; display: block;
    box-shadow: 0 18px 40px -14px rgba(119, 0, 255, 0.8);
}
.app-modal h3 {
    font-family: var(--mdp-ui-heading); font-weight: 800;
    font-size: 1.6rem; line-height: 1.2; letter-spacing: -0.015em;
    color: #fff; margin: 0;
}
.app-modal p {
    font-family: var(--mdp-ui-body);
    font-size: 0.98rem; line-height: 1.6;
    color: rgba(235, 228, 245, 0.66);
    max-width: 36ch; margin: 12px 0 0;
}

.app-buttons { display: flex; gap: 12px; width: 100%; margin-top: 26px; }
/* Store-badge proportions: a tall pill, mark and label centred as
   one unit, label in the heading face at badge weight. */
.app-dl {
    /* flex-basis auto, not 0: an equal-share basis sized both
       pills to half the row regardless of their labels, which
       squeezed Android's side padding down to a few pixels. */
    flex: 1 1 auto; cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center; gap: 7px;
    height: 54px; padding: 0 28px; border-radius: 999px; text-decoration: none;
    font-family: var(--mdp-ui-heading); font-size: 13px; font-weight: 800;
    letter-spacing: 0.01em; color: #fff; white-space: nowrap;
    transition: transform .15s ease, filter .15s ease;
    /* ── THE LIT EDGE, and it belongs to BOTH pills ────────────
       These are one object with two fills, the same way .dp-cta
       and .dl-mega are one object: a white specular over the top
       of the fill, a 1px inset highlight along the lip, and a
       hairline that is BRIGHTEST AT THE TOP and falls off round
       to the bottom.

       The grading is what was missing. Light on this card comes
       from above, so an even ring all the way round reads as an
       outline drawn ON the button rather than as its edge
       catching the light. Green got away with it because its
       fill gradient already said which way was up; black had
       nothing else doing that work, so the iOS pill read as a
       sticker cut out of the modal next to a lit object.

       The drop is a variable rather than a per-variant
       box-shadow: a variant then sets only what is genuinely its
       own — the colour of its glow — and cannot silently drop
       the inset lip by redeclaring the shorthand. */
    border: 1px solid rgba(255, 255, 255, 0.13);
    border-top-color: rgba(255, 255, 255, 0.40);
    box-shadow: var(--app-dl-glow, 0 10px 26px rgba(0, 0, 0, 0.5)),
                inset 0 1px 0 rgba(255, 255, 255, 0.22);
}
.app-dl svg { width: 18px; height: 18px; flex: none; color: #fff; }
/* Opening the dialog moves focus to the primary action. Without
   this that arrives as a browser-blue ring around a black pill. */
/* Keyboard users still get a ring when they Tab here. Nobody
   gets one just for opening the dialog. */
.app-dl:focus { outline: none; }
.app-dl:focus-visible { outline: 2px solid var(--mdp-ui-violet-bright); outline-offset: 3px; }
.psf-modal:focus, .psf-modal:focus-visible { outline: none; }
.app-dl:hover { transform: translateY(-2px); filter: brightness(1.12); }
.app-dl:active { transform: translateY(0); }
/* Still Apple's black badge, just not a FLAT one: near-black to
   black under the specular, so the pill has a top and a bottom
   before the edge is asked to say so. */
.app-dl-ios {
    --app-dl-glow: 0 10px 26px rgba(0, 0, 0, 0.6);
    background:
        linear-gradient(180deg, rgba(255, 255, 255, 0.15) 0%, transparent 46%),
        linear-gradient(180deg, #1d1d1f 0%, #000000 100%);
}
/* Android green deep enough to carry white text: #3DDC84 is the
   robot's colour, not a background you can put white on. */
.app-dl-android {
    --app-dl-glow: 0 10px 26px rgba(21, 127, 69, 0.45);
    background:
        linear-gradient(180deg, rgba(255, 255, 255, 0.18) 0%, transparent 46%),
        linear-gradient(180deg, #22A45D 0%, #157F45 100%);
}
@media (max-width: 540px) {
    .app-modal { padding: 60px 20px 24px; }
    .app-dl { padding: 0 20px; }
    .app-buttons { flex-direction: column; }
}

/* Status, after the ask. It is a footnote about what the page is
   doing, not the first thing you should read. */
.app-wait {
    display: flex; align-items: center; justify-content: center; gap: 8px;
    margin-top: 14px;
    font-family: var(--mdp-ui-body); font-size: 0.82rem;
    color: rgba(235, 228, 245, 0.38);
}
.app-wait::before {
    content: ''; width: 6px; height: 6px; border-radius: 50%; flex: none;
    background: var(--mdp-ui-violet-bright);
    box-shadow: 0 0 8px rgba(199, 125, 255, 0.9);
    animation: pulse 1.4s infinite;
}

/* Desktop: a scannable TestFlight QR instead of store buttons.
   The card itself stays the dark glass it always was (no
   .dl-light restyle on .app-modal), so the caption keeps the
   dark-card ink. The pad is white in both themes so a phone
   camera can lock on. */
.app-qr {
    display: flex; flex-direction: column; align-items: center; gap: 12px;
    width: 100%; margin-top: 26px;
}
.app-qr-frame {
    display: flex; align-items: center; justify-content: center;
    padding: 14px; border-radius: 18px;
    background: #fff;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
}
.app-qr-frame img { display: block; image-rendering: pixelated; }
.app-qr-caption {
    margin: 0 !important; max-width: none;
    font-family: var(--mdp-ui-body);
    font-size: 0.92rem; line-height: 1.4;
    color: rgba(235, 228, 245, 0.72);
}

.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}


/* ══════════════════════════════════════════════════════════════
   LIGHT SHELL (Aug 2026)
   ──────────────────────────────────────────────────────────────
   The page went white to match the rebuilt homepage. This is an
   ADDITIVE scope, not a rewrite: every rule above is untouched
   and still governs the dark surfaces this page is full of.

   That works because everything load-bearing on this page — the
   install console (#dpc), the companion-app overlay, the
   walkthrough modals, the operators panel — sits on its own
   opaque background. They are unaffected by the shell going
   white, and they are the last code on the site anyone should be
   restyling casually.

   So what follows only recolours the page CHROME.
   ══════════════════════════════════════════════════════════════ */   /* the dark page's ambient glows */

/* The header's light ink is site-header's theme: 'light' —
   black brand asset, dark type, and the badge's --mdp-* token
   flip. The rules that used to live here inverted a white SVG
   with filter and re-coloured the nav by hand. */

/* ── The Discord CTA, left of the MyDreamPark badge ────────────
   Deliberately the quietest thing in the bar. This page has ONE
   call to action — the violet mega button in the middle of the
   hero — and a second filled button in the header would be
   arguing with it. So it is the ghost variant: the same glass
   fill, hairline and ink as the badge sitting next to it, which
   makes the two read as one cluster rather than as a second
   pitch. The Discord mark carries the meaning; no colour needed
   to say what it is.

   The svg needs a size from us. site-header only sets
   `flex: none` on a CTA icon — the homepage's CTA sizes its own
   mark through .dp-cta-icon, and this is the equivalent hook. */
.dl-discord-cta svg { width: 20px; height: auto; }
/* The blurple hover is .dph-cta-discord-ghost in
   public/css/site-header.css now, not a rule of this page's. It
   was here, and that is precisely why the homepage's Discord
   button — same classes otherwise — hovered differently. */
/* The hero is the wall's positioning context (.atw is absolute,
   not fixed — see public/css/dp-attraction-wall.css), so the rows
   scroll away with the hero instead of following the page down
   and passing behind the FAQ. */
/* ── THE HERO IS THE SCREEN ────────────────────────────────────
   A one-pager whose content stops two thirds of the way down is
   not a one-pager; it is a short page with a lot of nothing under
   it. The hero takes the whole viewport minus .page's padding and
   centres its contents, so the lockup, the button and Learn More
   sit in the middle of the screen rather than hugging the top.

   min-height, NOT flex-grow. Both fill the screen at rest, but a
   grown flex item gives that space back the instant Learn More
   reveals the FAQ and the page is no longer one screen — the hero
   shrank by ~530px and every pixel of the copy jumped upward as
   you pressed the button. A min-height does not move: the hero is
   the same height whether the FAQ is open or shut.

   svh, not vh: on mobile Safari vh is measured against the
   viewport WITHOUT the retracted URL bar, so 100vh is taller than
   what is actually visible and the CTA lands below the fold. The
   vh line above it is the fallback for anything predating svh. */
/* ── NOT A PALETTE RULE ────────────────────────────────────────
   Everything from here to the end of the halo block below used to
   be scoped to body.dl-light, back when this page was white and
   that class was always on. It is LAYOUT, not colour: the
   full-height hero, the wall's break-out to viewport width, the
   z-index order of the hero's children, and the legibility halo
   behind the copy. Gating it on the theme meant HOME_THEME=dark
   silently reverted the hero to its pre-wall arrangement — no
   halo, copy competing with the tiles, wall boxed inside the
   1040px column. The rules are unscoped now and the halo takes
   its colour from a token (--dl-halo-*, set per theme below), so
   the two themes differ in ink and in nothing else.

   When adding to this page: colour goes in the body.dl-light
   block near the bottom, structure goes here.

   THE `body ` PREFIX IS LOAD-BEARING, not a habit. These rules
   carried it for free while they were `body.dl-light X`, and
   dropping to a bare `.atw` handed the wall back to
   public/css/dp-attraction-wall.css — which declares `.atw {
   inset: 0 }` at the same specificity and is LINKED AFTER this
   inline <style>, so it won on order. The wall stopped breaking
   out of the 1040px column and sat boxed inside it with a dead
   strip down the left. One descendant element is the whole fix;
   keep it on anything here that an external sheet also names. */
body .page {
    /* THE HERO RUNS TO THE BOTTOM OF THE SCREEN, NOT TO 80px
       ABOVE IT.

       .page used to keep 80px of bottom padding, so the hero
       ended early and the wall only reached the fold by bleeding
       past it (.atw had bottom: -76px). That was fine while
       nothing followed the hero, and wrong the moment the FAQ
       became a real section underneath: the overhang landed on
       the FAQ's screen, and the FAQ's background sliced the last
       row of tiles in half.

       Dropping the padding lets the hero end exactly at 100svh,
       so the wall fills the screen without ever leaving the hero
       and the next section starts on clean white. Nothing else
       wants the padding — the FAQ carries its own, and
       #operators only ever appears with the footer beneath it. */
    --dl-page-pad: 104px;
    padding-bottom: 0;
}
body .hero {
    position: relative;
    z-index: 4;
    min-height: calc(100vh - var(--dl-page-pad));
    min-height: calc(100svh - var(--dl-page-pad));
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-bottom: 40px;
}
/* ── THE FIELD IS WIDER THAN THE PAGE ──────────────────────────
   The wall is inset:0 on the hero, and the hero is a child of
   .page — max-width 1040px. Rows that stopped 200px short of each
   edge would read as a widget embedded in the page rather than as
   the page's background, and the whole effect is that the
   catalogue is streaming past behind everything.

   So the field breaks out of the column: full viewport width, and
   up/down into .page's own padding. -28px stops short of the 72px
   fixed header rather than running rows under it, -76px stops
   just short of the fold. Nothing here changes layout — the field
   is absolute, so it contributes no height — and the horizontal
   bleed is covered by body { overflow-x: hidden }.

   No max-width, unlike the floater field this replaced. A ring of
   tiles orbiting the copy had to stay near it to make sense; rows
   running off both edges of the screen are better the wider they
   get. */
body .atw {
    /* calc(50% - 50vw) rather than left:50% + translateX(-50%).
       Both put a 100vw box in the middle of a 1040px column and
       they render identically (checked, frozen-frame A/B). This
       one is plain layout, so the field is not needlessly a
       transformed container wrapping eight animated layers. */
    left: calc(50% - 50vw);
    right: auto;
    width: 100vw;
    /* -28px reaches up into the gap under the fixed header. The
       bottom stays at the hero's edge — which is now the fold —
       so the field can never paint over the section below it. */
    top: -28px;
    bottom: 0;
    --atw-gap: clamp(10px, 1vw, 18px);
    --atw-rowgap: clamp(10px, 1vw, 18px);
}

/* ── THE LEGIBILITY HALO ───────────────────────────────────────
   A white wash behind the copy, so the wordmark and the button
   sit on paper while the wall runs uninterrupted around them.

   SIZED IN PIXELS, AND THAT IS THE WHOLE POINT. This started as
   a radial gradient on the wall itself, in percentages of the
   field — which meant it grew with the viewport. On a tall
   narrow window the field is ~950px high and 44% of that is an
   ellipse taller than the copy by a factor of two: it whited out
   four rows, and tiles drifting through its falloff faded in and
   out, which looked like the rows were breaking.

   The copy is 514px tall at every desktop size and only a little
   more on a phone, because it is content-sized. So the halo is
   too: a fixed box, centred on the copy, that does not care how
   much room the wall has. The svh cap only ever comes into play
   on a very short window, where it stops the halo overflowing
   the screen.

   z-index 4: above the wall (3), below the copy (5). The
   `> *` rule below does not match a pseudo-element, so this sets
   its own. */
body .hero::before {
    content: "";
    position: absolute;
    z-index: 4;
    pointer-events: none;
    left: 50%;
    /* The copy's centre sits a little above the hero's, because
       of the hero's own bottom padding. */
    top: calc(50% - 14px);
    transform: translate(-50%, -50%);
    width: min(860px, 94vw);
    height: min(680px, 88svh);
    /* Both themes' versions live in --dl-halo, up in the body
       block — see the note there for why this is a whole
       gradient rather than a colour. */
    background: var(--dl-halo);
}

body .hero > *:not(.atw) { position: relative; z-index: 5; }
/* The pointer-events dance that used to live here — every hero
   child inert, every control inside them opted back in — existed
   so the drifting floater tiles could be clicked through the
   transparent margins of the centred text boxes above them. The
   wall that replaced them is scenery with pointer-events: none,
   so there is nothing underneath to reach and the hack went with
   it. Selecting the tagline works again, which it did not while
   that rule was in force. */
/* Full black. At 58% it was a caption under the wordmark; the
   line is the pitch, and now that it is one short row of caps it
   can carry ink without competing — the wordmark is still twice
   its size. */
/* Beats the rule above on specificity, which the bare
   `.hero .sub em` would not. */

/* Panels that were glass-over-dark become paper-over-white.

   SCOPED TO .block DELIBERATELY. The only .mdp-ui-panel in this
   page's own markup is the #operators section, but the class is a
   SHARED component: views/partials/mydreampark-auth-panel.ejs puts
   it on .mdp-auth-shell, and site-header mounts that panel into
   this page as the singleton sign-in modal. Unscoped, this rule
   repainted that modal to paper while every string inside it
   stayed white — pressing Download opened a white-on-white dialog.
   Exactly the failure documented for .mdp-ui-action below: a rule
   written for this page's surfaces reaching into a component the
   page does not own. Anything the header mounts is out of reach
   now, and #operators still gets its paper. */

/* THE BUTTONS INSIDE THOSE CARDS WERE INVISIBLE.
   .mdp-ui-action (mydreampark-ui.css) is built for dark
   surfaces: white text on a 4%-white fill with a white hairline
   border. The panel above recolours the CARD to paper and the
   heading and body copy with it, but the shared button component
   was never in that list — so "Add via ArborXR", "Add via
   ManageXR" and "Copy direct APK link" were white-on-white and
   only appeared on hover.

   Scoped to .glass-card on the light page, so the component is
   untouched everywhere else — including .op-managed directly
   below, which keeps its dark violet panel and its own .om-cta.
   The palette is the one .dl-faq-links already uses for a pill on
   white, so the two link treatments on this page match. */

/* Small print that was light-on-dark. Left at this contrast it is
   legible but quiet, which is what it should be next to one very
   loud button. */

/* The divider lives on `.shr-stat + .shr-stat`, not on the strip —
   re-colouring `.shr-stats` here set a border-color on an element
   with no border, so the light page lost the hairlines between the
   stats entirely and the three numbers floated free. */
/* Inside a FAQ answer the table is nested one level deeper and
   has no section heading above it, so it gets its own top margin
   and loses the centring meant for a full-width block. */
.dl-faq-a .rel-table { margin: 4px 0 0; max-width: 100%; }

/* The mega CTA keeps its gradients and all three of its motions —
   only the glow is tightened. A 48px violet bloom reads as premium
   on #0a0617 and as a smudge on #fff.

   THE CLASS IS NOW CONDITIONAL IN THE MARKUP. It used to be
   written on the element unconditionally, from when the page was
   always white — so the dark page got the tightened glow AND the
   dim #7700ff hairline this sets, which all but disappears
   against a dark violet background. The dark page wants the base
   .dl-mega treatment, which is what it was designed for. */
.dl-mega--light {
    border-color: rgba(119, 0, 255, .35);
    box-shadow:
        0 10px 34px rgba(119, 0, 255, .34),
        0 0 0 0 rgba(157, 78, 221, .3),
        inset 0 1px 0 rgba(255, 255, 255, .38);
}
.dl-mega--light:hover {
    box-shadow:
        0 16px 46px rgba(119, 0, 255, .45),
        0 0 26px rgba(199, 125, 255, .32),
        inset 0 1px 0 rgba(255, 255, 255, .45);
}

/* ── Learn More ──────────────────────────────────────────────
   Quiet on purpose: it must not compete with the one button this
   page exists for. The arrow points at what it reveals and flips
   when it is open. */
.dl-learn {
    display: inline-flex; flex-direction: column; align-items: center; gap: 3px;
    appearance: none; border: 0; background: none; cursor: pointer;
    padding: 6px 14px 2px;
    /* The arrow is stroke="currentColor", so this one declaration
       colours the label AND the chevron. */
    color: var(--dl-ink-quiet);
    font-family: var(--mdp-ui-body); font-size: 14px; font-weight: 600;
    letter-spacing: .01em;
    transition: color .18s ease;
}
.dl-learn:hover { color: var(--dl-ink); }
.dl-learn:focus-visible { outline: 3px solid var(--dl-accent); outline-offset: 4px; border-radius: 10px; }
/* SIZED TO THE INK, NOT TO THE VIEWBOX.
   The chevron is drawn between y=9 and y=15 of a 24-unit box, so
   a 17px square svg carried ~6px of empty space above the glyph
   and ~6px below it. Stacked on the flex gap that read as a big
   hole between "Learn More" and the arrow. The viewBox below is
   cropped to the stroke's own bounds (y 8→16 covers the 2-unit
   stroke at both ends), so the element's box IS the chevron and
   the gap above is the only gap. */
.dl-learn-arrow {
    width: 18px; height: 9px;
    transition: transform .28s cubic-bezier(.16, 1, .3, 1);
    animation: dlLearnNudge 2.6s ease-in-out infinite;
}
.dl-learn[aria-expanded="true"] .dl-learn-arrow { transform: rotate(180deg); animation: none; }
@keyframes dlLearnNudge {
    0%, 72%, 100% { transform: translateY(0); }
    84% { transform: translateY(3px); }
}

/* ── FAQ ─────────────────────────────────────────────────────
   <details>/<summary>, the same primitive the Changelog block
   above already uses: opens with no JS, accessible for free, and
   find-in-page can open a closed section onto a match. */
/* ── THE FAQ IS ITS OWN SCREEN ────────────────────────────────
   It used to open as a block hanging off the bottom of the hero,
   which read as an appendix: the wall stopped, the questions
   started, and the two shared a screen awkwardly with whatever
   happened to be in view. A page that is one screen for the
   pitch should be one screen for the answers.

   The arithmetic is exact, and it has to be. .page's padding is
   104 top + 80 bottom, and the hero already subtracts both from
   100svh (--dl-page-pad). So hero + FAQ + that padding comes to
   exactly 100svh + this section's height — which means a flat
   100svh here makes the open page precisely two screens, with no
   sliver of a third to scroll into. Closed, the section is
   display:none and the page is one screen as before.

   min-height, not height: opening several answers grows past the
   screen, and that should push the page down rather than
   overflow the box. */
section.dl-faq {
    position: relative;
    z-index: 4;
    /* section.block sets `margin: 72px 0 0`, and it outranks a
       bare .dl-faq — hence the element in the selector. That 72px
       would put the page 72px past two screens. */
    margin: 0;
    min-height: 100vh;
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    /* TOP-ALIGNED, AND THE OFFSET IS COMPUTED FROM THE VIEWPORT
       RATHER THAN FROM THE CONTENT.

       justify-content: center looks right until you open an
       answer: the content grows, centring re-splits the leftover
       space, and the heading and every question above the one you
       clicked slide UPWARD as it expands. An accordion has to
       grow downward from a fixed top or it feels like the page is
       arguing with you.

       So the block starts at the top and the space above it is
       padding — max() of the header clearance and half the slack
       left over. Because both terms depend only on the viewport,
       the number does not move when an answer opens. */
    justify-content: flex-start;
    /* The measured height of the collapsed heading + list. A
       constant, because the list is a fixed twelve questions; the
       short-viewport rule below overrides it when the rows
       tighten. Feeding the REAL number in is what makes the two
       margins come out equal — 788 puts 326 above and 326 below
       on a 1440-tall screen. */
    --dl-faq-content: 788px;
    padding-top: max(
        calc(var(--site-header-h) + 28px),
        calc((100svh - var(--dl-faq-content)) / 2)
    );
    padding-bottom: 44px;
}
/* No background on this section, and none needed. The wall used
   to overhang the hero, so the FAQ required an opaque full-bleed
   backing to hide the half-row that landed on its screen; the
   field now stops at the hero's edge (`body .atw`, above) and
   there is nothing behind the FAQ to cover. */
/* AN AUTHOR `display` BEATS THE UA'S `[hidden] { display: none }`.
   The section is hidden until Learn More is pressed, and giving it
   display:flex above silently un-hid it on every load. Anything
   that sets display on an element which also uses the hidden
   attribute has to say this. */
section.dl-faq[hidden] { display: none; }
.dl-faq-list {
    /* width: 100% IS LOAD-BEARING. The section is a flex column,
       and `margin-inline: auto` on a flex item opts it out of
       stretching — which makes it shrink-to-fit, so the list was
       as wide as its widest LINE. Opening an answer introduced a
       longer line and the whole list jumped wider and re-centred.
       An explicit width takes the measurement away from the
       content; max-width still caps it. */
    width: 100%;
    margin: 26px auto 0;
    border-top: 1px solid var(--dl-line);
    max-width: 760px;
    /* .block centres its copy — right for a marketing section,
       wrong for prose someone is meant to actually read. */
    text-align: left;
}
.dl-faq-item { border-bottom: 1px solid var(--dl-line); }
.dl-faq-item > summary {
    display: flex; align-items: center; justify-content: space-between; gap: 16px;
    padding: 18px 2px; cursor: pointer; list-style: none;
    color: var(--dl-ink);
}
.dl-faq-item > summary::-webkit-details-marker { display: none; }
.dl-faq-item > summary:hover .dl-faq-q { color: var(--dl-accent); }
.dl-faq-item > summary:focus-visible { outline: 3px solid var(--dl-accent); outline-offset: 2px; border-radius: 8px; }
.dl-faq-q {
    font-family: var(--mdp-ui-heading); font-weight: 700; font-size: 1.02rem;
    letter-spacing: -.005em; line-height: 1.35;
    transition: color .16s ease;
}
/* A SHORT LAPTOP STILL GETS ONE SCREEN.
   Twelve questions at the default row height come to 932px, so a
   1280x800 window opened the FAQ as 2.17 screens — the same
   "sliver of a third screen" the hero's own arithmetic exists to
   avoid. Six pixels off each row's padding buys 145px and it
   fits. Height-only, and above the phone breakpoint: on a phone
   the viewport is short too, but a 47px row is under the tap
   target everyone should be hitting. */
@media (max-height: 920px) and (min-width: 701px) {
    .dl-faq-item > summary { padding: 12px 2px; }
    section.dl-faq {
        --dl-faq-content: 644px;   /* the same stack, tighter rows */
        padding-bottom: 32px;
    }
}

.dl-faq-item .chev { flex: 0 0 auto; color: var(--dl-ink-faint); }
.dl-faq-item .chev svg { width: 18px; height: 18px; display: block; transition: transform .22s cubic-bezier(.16,1,.3,1); }
.dl-faq-item[open] .chev svg { transform: rotate(180deg); }
.dl-faq-a { padding: 0 2px 22px; max-width: 62ch; }
.dl-faq-a p { margin: 0; color: var(--dl-ink-body); font-size: 0.96rem; line-height: 1.68; }
.dl-faq-a em { color: var(--dl-ink); font-style: normal; font-weight: 600; }
.dl-faq-links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px !important; }
.dl-faq-links a {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 8px 15px; border-radius: 999px;
    border: 1px solid var(--dl-accent-line);
    background: var(--dl-accent-wash);
    color: var(--dl-accent); text-decoration: none;
    font-size: 0.86rem; font-weight: 700;
    transition: background .16s ease, border-color .16s ease;
}
.dl-faq-links a:hover {
    background: color-mix(in srgb, var(--dl-accent) 16%, transparent);
    border-color: var(--dl-accent);
}
.dl-faq-links a svg { width: 13px; height: 13px; }
/* The one FAQ link that is not a quiet violet pill. It is the
   only answer whose payload IS the button — every other section
   answers the question in its prose and offers the link as a
   footnote — so it gets Discord's blurple and reads as something
   to press. Same three gradient stops as the homepage break and
   /dreamjam's hero CTA, so all three are recognisably one button.

   The mark leads rather than trails: `order: -1` instead of
   reordering the markup, because download-faq.ejs renders the
   icon after the label for every link and the other two want it
   that way. */
.dl-faq-links a.dl-faq-discord {
    border-color: rgba(88, 101, 242, .55);
    background: linear-gradient(180deg, #6f7bfb 0%, #5865f2 52%, #4048d8 100%);
    color: #fff;
    box-shadow: 0 10px 24px -10px rgba(48, 56, 180, .7);
}
.dl-faq-links a.dl-faq-discord:hover {
    border-color: rgba(88, 101, 242, .8);
    background: linear-gradient(180deg, #7d88fc 0%, #6570f5 52%, #4a53e2 100%);
}
.dl-faq-links a.dl-faq-discord:focus-visible { outline: 3px solid #5865f2; outline-offset: 2px; }
.dl-faq-links a.dl-faq-discord svg { order: -1; width: 16px; height: 16px; }

@media (prefers-reduced-motion: reduce) {
    .dl-learn-arrow { animation: none; transition: none; }
    .dl-faq-item .chev svg { transition: none; }
}

/* ── Operators section ───────────────────────────────────────── */
.op-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 14px; margin-top: 28px; }
.op-card { padding: 24px; display: flex; flex-direction: column; align-items: center; text-align: center; gap: 10px; }
.op-card h3 {
    font-family: var(--mdp-ui-heading); font-size: 1.05rem; font-weight: 800;
    letter-spacing: -0.005em; margin: 0; color: #fff;
}
.op-card p { font-size: 0.95rem; color: rgba(235, 228, 245, 0.68); line-height: 1.6; margin: 0; flex: 1; }

/* Fully managed experience, DreamPark-branded: brand purple
   gradient family (#7700ff → violet), logo mark, purple CTA. */
.op-managed {
    margin-top: 22px; padding: 34px 22px; border-radius: 20px;
    border: 1px solid rgba(199, 125, 255, 0.5);
    background:
        radial-gradient(circle at 20% 0%, rgba(119, 0, 255, 0.35), transparent 55%),
        radial-gradient(circle at 85% 100%, rgba(199, 125, 255, 0.18), transparent 50%),
        linear-gradient(135deg, rgba(119, 0, 255, 0.22) 0%, rgba(42, 15, 102, 0.5) 100%),
        rgba(18, 10, 38, 0.6);
    box-shadow: 0 18px 60px -20px rgba(119, 0, 255, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.12);
    display: flex; flex-direction: column; align-items: center; text-align: center; gap: 18px;
}
.op-managed .om-logo {
    width: 44px; height: 44px;
    filter: drop-shadow(0 4px 14px rgba(119, 0, 255, 0.6));
}
.op-managed h3 {
    font-family: var(--mdp-ui-heading); font-weight: 800; font-size: 1.4rem;
    letter-spacing: -0.005em; margin: 0; color: #fff;
}
.op-managed p { font-size: 0.98rem; color: rgba(235, 228, 245, 0.78); margin: 0; max-width: 740px; line-height: 1.6; }
.om-cta {
    display: inline-block; cursor: pointer; text-decoration: none;
    font-family: var(--mdp-ui-heading); font-weight: 800; font-size: 13px;
    letter-spacing: 0.05em; text-transform: uppercase; color: #fff;
    border: 1px solid rgba(199, 125, 255, 0.65); border-radius: 999px; padding: 14px 34px;
    background:
        linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, transparent 45%),
        linear-gradient(135deg, #7700ff 0%, #9d4edd 60%, #c77dff 120%);
    box-shadow: 0 10px 36px rgba(119, 0, 255, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.3);
    transition: transform .15s ease, filter .15s ease;
}
.om-cta:hover { transform: translateY(-2px); filter: brightness(1.08); }

/* Kit offer, no box at all: floating case shot tight against the
   offer text, Buy now centered underneath the pair. */
.kit-offer {
    display: flex; flex-direction: column; align-items: center; gap: 18px;
    width: 100%; max-width: 760px; margin-top: 6px;
}
.kit-row {
    display: flex; align-items: center; justify-content: center;
    gap: 14px; flex-wrap: wrap;
}
.kit-photo-img {
    width: min(340px, 82vw); height: auto;
    filter: drop-shadow(0 24px 38px rgba(0, 0, 0, 0.6));
}
.kit-info { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.kit-hl {
    display: inline-block;
    font-family: var(--mdp-ui-heading); font-weight: 800; font-size: 1.18rem;
    letter-spacing: -0.005em; color: #fff; margin-bottom: 2px;
}
.dp-icon { height: 1.05em; width: auto; vertical-align: -0.18em; }
.kit-name { font-family: var(--mdp-ui-heading); font-weight: 800; font-size: 1rem; color: #fff; }
/* Price lives inside the buy button, one decisive element.
   Label in the heading face, price in Russo One (the brand's
   display face for numbers: step numerals, eyebrows, stats). */
.kit-buy {
    display: inline-flex; align-items: center; gap: 16px;
    padding: 16px 38px;
    margin-top: 14px;
}
.kit-buy-label {
    font-family: var(--mdp-ui-heading); font-weight: 800;
    font-size: 13px; letter-spacing: 0.07em; text-transform: uppercase;
}
.kit-buy-divider {
    width: 1px; height: 26px;
    background: rgba(255, 255, 255, 0.35);
}
.kit-buy-price {
    font-family: var(--mdp-ui-display);
    font-weight: 400; font-size: 1.55rem; line-height: 1;
    letter-spacing: 0.01em;
    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
}
.kit-includes { font-size: 0.92rem; color: rgba(235, 228, 245, 0.68); line-height: 1.75; }
.kit-deal { display: inline-block; margin-top: 4px; font-size: 0.78rem; color: var(--mdp-ui-xp); }
.kit-buy { margin-top: 8px; padding: 12px 30px; }
.kit-note { font-size: 0.88rem; color: rgba(235, 228, 245, 0.55); }
.kit-note a { color: rgba(235, 228, 245, 0.85); text-decoration: underline; text-underline-offset: 3px; }

/* ── Changelog (collapsed by default) ───────────────────────── */
details.changelog { margin: 0; }
details.changelog summary {
    list-style: none; cursor: pointer; user-select: none;
    display: flex; align-items: center; justify-content: center; gap: 12px;
}
details.changelog summary::-webkit-details-marker { display: none; }
details.changelog summary h2 { margin: 0; }
details.changelog summary .chev {
    width: 30px; height: 30px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid rgba(255, 255, 255, 0.18);
    color: rgba(235, 228, 245, 0.7);
    transition: transform .2s ease;
    flex-shrink: 0;
}
details.changelog summary .chev svg { width: 14px; height: 14px; }
details.changelog[open] summary .chev { transform: rotate(180deg); }
details.changelog summary:hover .chev { border-color: rgba(255, 255, 255, 0.4); color: #fff; }

.rel-table { width: 100%; max-width: 720px; margin-left: auto; margin-right: auto; border-collapse: collapse; margin-top: 22px; font-size: 0.95rem; }
.rel-table th {
    text-align: center;
    font-family: var(--mdp-ui-heading); font-size: 0.72rem; font-weight: 800;
    letter-spacing: 0.12em; text-transform: uppercase;
    color: rgba(235, 228, 245, 0.5); padding: 8px 10px;
    border-bottom: 1px solid rgba(255,255,255,0.12);
}
.rel-table td { padding: 12px 10px; text-align: center; border-bottom: 1px solid rgba(255,255,255,0.05); color: rgba(235, 228, 245, 0.72); }
.rel-table td b { color: #fff; font-weight: 700; }
.rel-pill {
    font-family: var(--mdp-ui-heading); font-size: 0.68rem; font-weight: 800;
    letter-spacing: 0.1em; border-radius: 999px; padding: 4px 11px;
}
.rel-pill.stable { background: rgba(0, 245, 160, 0.14); color: var(--mdp-ui-xp); }
.rel-pill.beta { background: rgba(255, 209, 102, 0.14); color: var(--mdp-ui-gold); }

/* ── Per-version changelog ───────────────────────────────────
   The notes row that follows each version row. The row above it
   is centered and scannable; this one is prose, so it goes left
   aligned and full width. It also carries the version row's
   bottom border, so the pair reads as one entry instead of two
   unrelated rows. */
.rel-table tr.rel-has-notes td { border-bottom: 0; padding-bottom: 4px; }
.rel-notes-row td { text-align: left; padding: 0 10px 14px; }
.rel-notes { margin: 0; padding: 0 0 0 18px; list-style: none; }
.rel-notes li {
    position: relative;
    padding: 2px 0;
    font-size: 0.88rem;
    line-height: 1.55;
    color: rgba(235, 228, 245, 0.62);
}
/* Drawn, not a list-style bullet, so it can sit in the brand
   violet without tinting the text with it. */
.rel-notes li::before {
    content: "";
    position: absolute;
    left: -13px; top: 0.72em;
    width: 5px; height: 5px; border-radius: 50%;
    background: var(--mdp-ui-violet-bright, #8b5cf6);
}

/* ── Help modals, shared shell ───────────────────────────────
   Two simulated walkthroughs share this chrome: the Meta Horizon
   phone mock (Developer Mode) and the in-headset ADB prompt
   mock (USB debugging). Every one is reachable ON DEMAND from a
   step's help link, so nothing is ever a one-shot the visitor
   can miss. Scrollable + close affordances on all of them. */
.psf-overlay {
    position: fixed; inset: 0; z-index: 3000;
    display: flex; align-items: center; justify-content: center;
    background: rgba(6, 2, 16, 0.84);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    padding: 20px;
    overflow-y: auto;
    overscroll-behavior: contain;
    animation: psfIn 0.28s ease both;
}
@keyframes psfIn { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .psf-overlay { animation: none; } }
.psf-modal {
    position: relative;
    display: flex; flex-direction: column; align-items: center; gap: 20px;
    max-width: 640px; width: 100%; margin: auto;
}
/* Corner dismiss, every modal can be closed, always. */
.psf-x {
    position: absolute; top: -6px; right: -4px; z-index: 6;
    width: 38px; height: 38px; border-radius: 50%; cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid rgba(255, 255, 255, 0.2);
    background: rgba(12, 2, 32, 0.7); color: rgba(235, 228, 245, 0.75);
    transition: color .2s ease, border-color .2s ease;
}
.psf-x:hover { color: #fff; border-color: rgba(255, 255, 255, 0.45); }
.psf-x svg { width: 15px; height: 15px; }
@media (max-width: 700px) { .psf-x { top: -8px; right: 0; } }
.psf-heading { text-align: center; }
.psf-heading h3 {
    font-family: var(--mdp-ui-heading); font-weight: 800; font-size: 1.5rem;
    color: #fff; margin: 0 0 8px;
}
.psf-heading p { font-size: 0.98rem; color: rgba(235, 228, 245, 0.78); margin: 0; line-height: 1.6; }
.psf-path { color: var(--mdp-ui-violet-bright); font-weight: 600; }

/* The mock panel, Quest's light settings chrome */

.psf-toggle {
    position: relative; flex-shrink: 0;
    width: 38px; height: 22px; border-radius: 999px;
    background: #ccd0d5; transition: background .35s ease;
}
.psf-toggle::after {
    content: ''; position: absolute; top: 2px; left: 2px;
    width: 18px; height: 18px; border-radius: 50%; background: #fff;
    box-shadow: 0 1px 3px rgba(0,0,0,0.25);
    transition: transform .35s ease;
}
.psf-toggle.on { background: #31a24c; }
.psf-toggle.on::after { transform: translateX(16px); }

/* Pointer dot, the "hand cursor" doing the walkthrough */
@keyframes psfClick {
    0% { transform: scale(1); box-shadow: 0 4px 14px rgba(0,0,0,0.35); }
    40% { transform: scale(0.72); box-shadow: 0 0 0 10px rgba(28, 30, 33, 0.15); }
    100% { transform: scale(1); }
}

.psf-ack { padding: 15px 46px; font-size: 14px; }

/* ── Developer Mode walkthrough, mock Meta Horizon phone app ──
   A phone frame whose screens advance the way the real app
   does: your device → Headset Settings → Developer Mode →
   toggle on. Same cursor-dot language as the Quest mock above
   so the two walkthroughs feel like one family. */
.mh-stage { display: flex; align-items: center; justify-content: center; width: 100%; }
.mh-phone {
    position: relative; flex: none;
    width: 244px; height: 430px; padding: 9px; border-radius: 36px;
    background: linear-gradient(160deg, #33363f 0%, #0d0e12 60%);
    box-shadow: 0 30px 80px rgba(0, 0, 0, 0.65), inset 0 0 0 1px rgba(255, 255, 255, 0.09);
}
.mh-screen {
    position: relative; width: 100%; height: 100%; overflow: hidden;
    border-radius: 28px; background: #fff; color: #1c1e21;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.mh-notch {
    position: absolute; top: 9px; left: 50%; transform: translateX(-50%); z-index: 5;
    width: 62px; height: 5px; border-radius: 999px; background: rgba(0, 0, 0, 0.25);
}
.mh-bar {
    position: absolute; top: 0; left: 0; right: 0; z-index: 4;
    display: flex; align-items: center; gap: 8px;
    height: 54px; padding: 20px 14px 0;
    background: #fff; border-bottom: 1px solid #e4e6eb;
}
.mh-bar .mh-chev-back { width: 13px; height: 13px; color: #1c1e21; opacity: 0; transition: opacity .3s ease; }
.mh-bar.has-back .mh-chev-back { opacity: 1; }
.mh-bar-title { font-size: 13px; font-weight: 800; letter-spacing: -0.01em; }
.mh-pane {
    position: absolute; inset: 54px 0 0; padding: 6px 0;
    opacity: 0; transform: translateX(16px); pointer-events: none;
    transition: opacity .32s ease, transform .32s ease;
}
.mh-pane.is-on { opacity: 1; transform: none; }
.mh-row {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    margin: 0 8px; padding: 11px 8px; border-bottom: 1px solid #eff1f4; border-radius: 10px;
    transition: background .3s ease, box-shadow .3s ease;
}
.mh-row .t { font-size: 12.5px; font-weight: 650; }
.mh-row .s { font-size: 10.5px; color: #65676b; margin-top: 2px; }
.mh-row .chev { color: #b0b3b8; font-size: 15px; line-height: 1; }
.mh-row.is-highlight { background: #fff; box-shadow: 0 0 0 2px #7700ff, 0 6px 18px rgba(119, 0, 255, 0.2); border-bottom-color: transparent; }
.mh-dev { display: flex; align-items: center; gap: 11px; }
.mh-dev img { width: 46px; height: auto; filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.25)); }
.mh-dot-on { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: #31a24c; margin-right: 5px; vertical-align: 0.5px; }
.mh-cursor {
    position: absolute; z-index: 6; width: 18px; height: 18px; border-radius: 50%;
    background: rgba(255, 255, 255, 0.9); border: 2px solid rgba(28, 30, 33, 0.55);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
    top: 90px; left: 100px; pointer-events: none; opacity: 0;
    transition: top 0.85s cubic-bezier(0.4, 0, 0.2, 1), left 0.85s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.4s ease;
}
.mh-cursor.click { animation: psfClick .45s ease; }
.mh-caption {
    width: 100%; max-width: 470px; margin: 0; text-align: center;
    font-size: 0.92rem; line-height: 1.6; color: rgba(235, 228, 245, 0.72);
}
.mh-caption b { color: #fff; font-weight: 700; }
.mh-caption a { color: var(--mdp-ui-violet-bright); text-decoration: underline; text-underline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .mh-pane.mh-cursor { transition: none; } }

/* ── USB debugging walkthrough, mock in-headset prompt ────────
   A passthrough-ish room with the system dialog floating in it,
   the cursor ticking "Always allow from this computer" and
   pressing Allow. */
.adb-stage {
    position: relative; width: 100%; height: 292px; border-radius: 18px; overflow: hidden;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
}
.adb-room {
    position: absolute; inset: 0;
    background:
        radial-gradient(ellipse at 28% 14%, rgba(255, 255, 255, 0.09), transparent 58%),
        repeating-linear-gradient(112deg, rgba(255, 255, 255, 0.028) 0 2px, transparent 2px 30px),
        linear-gradient(180deg, #2c3038 0%, #14161b 100%);
}
.adb-room::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 38%;
    background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.45));
}
.adb-dialog {
    position: relative; z-index: 2; width: min(330px, 84%);
    padding: 18px 18px 15px; border-radius: 16px; text-align: center;
    background: rgba(26, 28, 34, 0.97); color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.14);
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.65);
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    opacity: 0; transform: scale(0.9);
    transition: opacity .45s ease, transform .45s cubic-bezier(0.16, 1, 0.3, 1);
}
.adb-dialog.is-on { opacity: 1; transform: none; }
.adb-dialog h4 { margin: 0 0 7px; font-size: 14.5px; font-weight: 700; }
.adb-fp {
    margin: 0 0 13px; font-family: ui-monospace, Menlo, monospace; font-size: 10px;
    color: rgba(255, 255, 255, 0.42); word-break: break-all;
}
.adb-check {
    display: flex; align-items: center; gap: 9px; text-align: left;
    margin-bottom: 14px; font-size: 11.5px; color: rgba(255, 255, 255, 0.86);
}
.adb-box {
    flex: none; width: 16px; height: 16px; border-radius: 4px;
    border: 1.5px solid rgba(255, 255, 255, 0.5);
    display: flex; align-items: center; justify-content: center;
    transition: background .25s ease, border-color .25s ease;
}
.adb-box svg { width: 10px; height: 10px; opacity: 0; transition: opacity .2s ease; }
.adb-box.on { background: #31a24c; border-color: #31a24c; }
.adb-box.on svg { opacity: 1; }
.adb-btns { display: flex; gap: 9px; }
.adb-btn {
    flex: 1; padding: 9px 0; border-radius: 999px;
    font-size: 12.5px; font-weight: 700; text-align: center;
    transition: transform .2s ease, filter .2s ease;
}
.adb-btn.sec { background: rgba(255, 255, 255, 0.1); color: rgba(255, 255, 255, 0.75); }
.adb-btn.pri { background: #0866ff; color: #fff; }
.adb-btn.pri.press { transform: scale(0.94); filter: brightness(1.25); }
.adb-cursor {
    position: absolute; z-index: 4; width: 18px; height: 18px; border-radius: 50%;
    background: rgba(255, 255, 255, 0.9); border: 2px solid rgba(28, 30, 33, 0.55);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
    top: 50%; left: 12%; pointer-events: none; opacity: 0;
    transition: top 0.85s cubic-bezier(0.4, 0, 0.2, 1), left 0.85s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.4s ease;
}
.adb-cursor.click { animation: psfClick .45s ease; }
@media (prefers-reduced-motion: reduce) { .adb-dialog.adb-cursor { transition: none; } }
@media (max-width: 560px) { .adb-stage { height: 268px; } }

.toast {
    position: fixed; bottom: 20px; left: 16px; right: 16px;
    max-width: 480px; margin: 0 auto;
    transform: translateY(calc(100% + 60px));
    background: linear-gradient(135deg, rgba(255, 209, 102, 0.97) 0%, rgba(246, 168, 33, 0.97) 100%);
    color: #2a1a00; font-family: var(--mdp-ui-heading); font-size: 0.88rem; font-weight: 800;
    line-height: 1.35; text-align: center;
    padding: 14px 20px; border-radius: 14px; box-shadow: 0 10px 36px rgba(0,0,0,0.55);
    transition: transform .3s cubic-bezier(0.16, 1, 0.3, 1); pointer-events: none; z-index: 2000;
}
.toast.show { transform: translateY(0); }

.hidden { display: none !important; }


/* ═══ THE PAGE ═══════════════════════════════════════════════════════════ */
html.dl-root { background: #6a40f0; }
body.dl {
  --dl-ink: #fff; --dl-ink-body: rgba(255, 255, 255, 0.93); --dl-ink-soft: rgba(255, 255, 255, 0.88);
  --dl-line: rgba(255, 255, 255, 0.18); --dl-accent: #fff;
  background: #6a40f0; color: #fff; min-height: 100vh;
  font-family: 'DM Sans', 'Inter', system-ui, -apple-system, sans-serif;
  overflow-x: clip;
}
body.dl::before, body.dl::after { display: none; }
body.dl .page { max-width: 640px; min-height: 0; margin: 0 auto; padding: calc(var(--site-header-h, 72px) + 44px) 16px 56px; background: none; }
body.dl .hero { display: block; min-height: 0; margin: 0; padding: 0; text-align: center; overflow: visible; }
body.dl .hero::before { display: none; }
body.dl .hero > * { position: static; }

.dl-title, body.dl h1.dl-title { margin: 0; font: 800 clamp(36px, 7.4vw, 60px)/1.02 'Zalando Sans Expanded', 'DM Sans', system-ui, sans-serif; letter-spacing: -0.02em; text-wrap: balance; color: #fff; }
.dl-lede { margin: 14px 0 0; font-size: clamp(17px, 2vw, 20px); line-height: 1.45; color: rgba(255, 255, 255, 0.93); }

body.dl h1.dl-title { font-family: 'Zalando Sans Expanded', 'DM Sans', system-ui, sans-serif; }
body.dl .dl-faq h2, body.dl .dpc-head { font-family: 'Zalando Sans Expanded', 'DM Sans', system-ui, sans-serif; font-weight: 800; }
body.dl .dl-faq-q { font-family: 'DM Sans', 'Inter', system-ui, sans-serif; }

body.dl :focus-visible { outline: 3px solid #fff; outline-offset: 2px; }

/* the one button */
body.dl .hero .dl-cable { margin: 30px 0 0; scroll-margin-top: calc(var(--site-header-h, 72px) + 16px); }
body.dl .cta-stack { display: flex; flex-direction: column; align-items: center; gap: 14px; margin: 0; }
body.dl .dl-mega {
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  width: 100%; max-width: 360px; min-height: 72px; padding: 12px 32px; border: 0; border-radius: 999px;
  background: #fff; color: #6a40f0; box-shadow: none; animation: none; filter: none;
  font: 800 19px/1.2 'DM Sans', 'Inter', system-ui, sans-serif; letter-spacing: 0; text-transform: none;
  transition: transform 0.15s ease;
}
body.dl .dl-mega::after { display: none; }
body.dl .dl-mega:hover { transform: translateY(-2px); box-shadow: none; filter: none; }
body.dl .dl-mega:active { transform: none; }
body.dl .dl-mega small { font: 600 13px/1.2 'DM Sans', 'Inter', system-ui, sans-serif; opacity: 1; color: #6a40f0; }
body.dl .cta-stack .hero-note { margin: 0; font-size: 15px; line-height: 1.4; color: rgba(255, 255, 255, 0.93); }
body.dl .hero-note b { color: #fff; font-weight: 700; }
body.dl .hero-note a, body.dl .chrome-link { color: #fff; font-weight: 700; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
body.dl .hero-empty { color: #fff; }

/* the divider + the other ways (the hub's own styles are in /install/hub.css) */
.dl-rule { height: 1px; margin: 44px 0 36px; border: 0; background: rgba(255, 255, 255, 0.18); }
body.dl .hero .dl-hub { width: 100%; margin: 0; text-align: left; }

/* the FAQ, behind Learn More */
body.dl .hero .dl-learn { margin: 28px auto 0; color: #fff; display: inline-flex; }
body.dl section.dl-faq { min-height: 0; background: none; color: #fff; }
body.dl .dl-faq a { color: #fff; }

/* ── the installer: violet and white, nothing else. Done = a filled white disc, blocked = a dashed ring; the words say the rest. ── */
body.dl .dpc { width: 100%; margin: 0 auto; border: 1px solid rgba(255, 255, 255, 0.18); border-radius: 28px; background: rgba(255, 255, 255, 0.1); box-shadow: none; }
body.dl .dpc-bar { padding: 14px 14px 0 24px; background: none; border: 0; font: 700 15px/1.2 'DM Sans', 'Inter', system-ui, sans-serif; letter-spacing: 0; color: #fff; }
body.dl .dpc-x { width: 36px; height: 36px; border-radius: 50%; background: rgba(255, 255, 255, 0.12); color: #fff; }
body.dl .dpc-x:hover { background: rgba(255, 255, 255, 0.2); }
body.dl .dpc-x svg { width: 13px; height: 13px; }
body.dl .dpc-body { gap: 16px; padding: 10px 24px 28px; }
body.dl .dpc-visual { width: 92px; height: 92px; border-radius: 50%; background: rgba(255, 255, 255, 0.12); }
body.dl .usb-headset { width: 42px; height: 42px; color: #fff; }
body.dl .usb-ring { border: 3px solid rgba(255, 255, 255, 0.2); border-top-color: #fff; }
body.dl .dpc.is-still .usb-ring { border-color: rgba(255, 255, 255, 0.34); }
body.dl .dpc.is-connected .dpc-visual { background: #fff; }
body.dl .dpc.is-connected .usb-ring { border-color: #fff; box-shadow: none; }
body.dl .dpc.is-connected .usb-headset { color: #6a40f0; }
body.dl .dpc.is-error .usb-ring { border: 3px dashed #fff; }
body.dl .dpc.is-error .usb-headset { color: #fff; }
body.dl .dpc-head { font-size: clamp(24px, 3.8vw, 30px); line-height: 1.12; letter-spacing: -0.01em; }
body.dl .dpc-sub { margin: -6px 0 0; max-width: 38ch; font-size: 16px; color: #fff; }
body.dl .dpc-inline, body.dl .dpc-link { color: #fff; font-family: 'DM Sans', 'Inter', system-ui, sans-serif; font-size: 15px; font-weight: 700; text-decoration-color: rgba(255, 255, 255, 0.55); }
body.dl .dpc-inline { font-size: inherit; }
body.dl .dpc-link.quiet { color: #fff; font-weight: 500; text-decoration-color: rgba(255, 255, 255, 0.4); }
body.dl .dpc-link.quiet:hover { color: #fff; text-decoration-color: #fff; }
body.dl .dpc-flags { gap: 22px; font: 600 14px/1.2 'DM Sans', 'Inter', system-ui, sans-serif; letter-spacing: 0; text-transform: none; color: #fff; }
body.dl .led { width: 9px; height: 9px; background: rgba(255, 255, 255, 0.28); }
body.dl .led.on { background: #fff; box-shadow: none; }
body.dl .led.off { background: none; box-shadow: inset 0 0 0 2px #fff; }
body.dl .led.wait { background: #fff; box-shadow: none; }
/* .dpc scope: the cookie bar's buttons are .dpc-btn too */
.dpc .dpc-btn {
  height: 50px; padding: 0 28px; border: 0; border-radius: 999px; box-shadow: none; filter: none;
  background: #fff; color: #6a40f0; font: 800 16px/1 'DM Sans', 'Inter', system-ui, sans-serif; letter-spacing: 0; text-transform: none;
}
.dpc .dpc-btn:hover { filter: none; }
.dpc .dpc-btn.ghost { padding: 0 24px; background: rgba(255, 255, 255, 0.14); color: #fff; }
.dpc .dpc-btn.ghost:hover { background: rgba(255, 255, 255, 0.22); }
body.dl .dpc-tool { padding: 13px 16px; border: 0; border-radius: 14px; background: rgba(255, 255, 255, 0.12); font: 700 15px/1.2 'DM Sans', 'Inter', system-ui, sans-serif; }
body.dl .dpc-tool:hover { background: rgba(255, 255, 255, 0.2); }
body.dl .usb-track { height: 8px; border: 0; background: rgba(255, 255, 255, 0.2); }
body.dl .usb-fill { background: #fff; box-shadow: none; }
body.dl .usb-progress-label { margin-top: 8px; font: 600 14px/1.2 'DM Sans', 'Inter', system-ui, sans-serif; color: #fff; }
body.dl .usb-log { margin: 0; padding: 12px 24px 14px; border: 0; background: rgba(255, 255, 255, 0.08); font-size: 12px; line-height: 1.7; color: rgba(255, 255, 255, 0.86); }
body.dl .usb-log div::before { color: rgba(255, 255, 255, 0.5); }
body.dl .usb-log .ok, body.dl .usb-log .ok::before { color: #fff; }
body.dl .usb-log .err, body.dl .usb-log .err::before { color: #fff; font-weight: 700; }
/* the walkthroughs that open over it: violet and white, no shadows */
body.dl .psf-overlay { background: rgba(106, 64, 240, 0.86); }
body.dl .psf-modal { padding: 32px 24px 28px; border: 1px solid rgba(255, 255, 255, 0.18); border-radius: 28px; background: #6a40f0; box-shadow: none; }
body.dl .psf-modal .psf-x { top: 12px; right: 12px; border: 0; background: rgba(255, 255, 255, 0.14); color: #fff; }
body.dl .psf-modal .psf-x:hover { background: rgba(255, 255, 255, 0.24); }
body.dl .psf-heading { padding: 0 34px; }
body.dl .psf-heading h3 { font-family: 'Zalando Sans Expanded', 'DM Sans', system-ui, sans-serif; font-weight: 800; color: #fff; }
body.dl .psf-heading p, body.dl .mh-caption, body.dl .mh-caption b { color: #fff; }
body.dl .psf-path { color: #fff; }
body.dl .mh-caption a { color: #fff; }
body.dl .psf-modal .om-cta { border: 0; border-radius: 999px; background: #fff; color: #6a40f0; box-shadow: none; font-family: 'DM Sans', 'Inter', system-ui, sans-serif; font-weight: 800; letter-spacing: 0; text-transform: none; }
/* the phone mock */
body.dl .mh-phone { background: #fff; box-shadow: none; }
body.dl .mh-screen { color: #6a40f0; font-family: 'DM Sans', 'Inter', system-ui, sans-serif; }
body.dl .mh-notch { background: rgba(106, 64, 240, 0.3); }
body.dl .mh-bar .mh-chev-back, body.dl .mh-row .s, body.dl .mh-row .chev { color: #6a40f0; }
body.dl .mh-row { box-shadow: none; }
body.dl .mh-row .t, body.dl .mh-row .s { display: block; }
body.dl .mh-row.is-highlight { background: #fff; box-shadow: 0 0 0 2px #6a40f0; }
body.dl .mh-dev img { filter: none; }
body.dl .mh-dot-on { background: #6a40f0; }
body.dl .psf-toggle { background: rgba(106, 64, 240, 0.25); }
body.dl .psf-toggle.on { background: #6a40f0; }
body.dl .psf-toggle::after { box-shadow: none; }
/* the in-headset prompt mock */
body.dl .adb-stage { height: 220px; background: none; box-shadow: none; }
body.dl .adb-room { display: none; }
body.dl .adb-dialog { border: 0; background: #fff; color: #6a40f0; box-shadow: none; font-family: 'DM Sans', 'Inter', system-ui, sans-serif; }
body.dl .adb-fp, body.dl .adb-check { color: #6a40f0; }
body.dl .adb-box { border-color: #6a40f0; }
body.dl .adb-box.on { background: #6a40f0; border-color: #6a40f0; }
body.dl .adb-btn.sec { background: rgba(106, 64, 240, 0.14); color: #6a40f0; }
body.dl .adb-btn.pri { background: #6a40f0; color: #fff; }
body.dl .adb-btn.pri.press { filter: none; }
body.dl .adb-cursor.click, body.dl .mh-cursor.click { animation: dlTap .45s ease; }
@keyframes dlTap { 40% { transform: scale(0.72); } }
body.dl .adb-cursor, body.dl .mh-cursor { background: #6a40f0; border: 3px solid #fff; box-shadow: 0 0 0 2px #6a40f0; }
/* the release list + FAQ links in the FAQ */
body.dl .rel-table th, body.dl .rel-table td, body.dl .rel-notes li { color: #fff; }
body.dl .rel-pill, body.dl .rel-pill.stable, body.dl .rel-pill.beta { padding: 0; background: none; color: #fff; font-family: 'DM Sans', 'Inter', system-ui, sans-serif; font-size: 14px; font-weight: 600; letter-spacing: 0; }
body.dl .rel-notes li::before { background: #fff; }
body.dl .dl-faq-item .chev { color: #fff; }
body.dl .dl-faq-links a, body.dl .dl-faq-links a.dl-faq-discord { border: 0; background: rgba(255, 255, 255, 0.14); color: #fff; box-shadow: none; }
body.dl .dl-faq-links a:hover, body.dl .dl-faq-links a.dl-faq-discord:hover { background: rgba(255, 255, 255, 0.22); color: #fff; box-shadow: none; }
body.dl .toast { background: #fff; box-shadow: none; color: #6a40f0; font-family: 'DM Sans', 'Inter', system-ui, sans-serif; }
@media (max-width: 719px) {
  body.dl .page { padding-top: calc(var(--site-header-h, 72px) + 28px); }
  .dl-rule { margin: 36px 0 28px; }
}
