/* ==========================================================================
   Holotype — a theme for sale. Cards lean back like consoles and project
   their own interface: hover one and a wireframe icon blinks to life above
   it (cold-start flicker, then a settling brightness strobe), the card face
   tips back 75deg, its body copy/tags fade off, and its h2 counter-rotates
   upright to ride down the tilted plane as a projected caption. Recreated
   verbatim from the owner's pasted pen (tokens, timings, keyframes unchanged)
   with a theme-kit built around it in the site's usual idiom (badge + h1 +
   theme_card + tags + two buttons). Ambient, same "themes breathe"
   convention as every other theme: a slow-drifting perspective-grid floor
   + a faint page-wide scanline shimmer. No light/dark toggle — a cold
   hologram has no light mode, so this theme is dark-native by design.
   ========================================================================== */


/* ---------- 1. TOKENS ---------- */
:root {
    /* Palette — cold projector in a dark room */
    --bg-0: #05080f;
    /* page depth */
    --bg-1: #0a1220;
    /* upper glow of page gradient */
    --ink: #d9e6f2;
    /* primary text */
    --ink-muted: #93a7bd;
    /* secondary text */
    --holo: #4de3ff;
    /* the one accent — hologram cyan */
    --holo-dim: rgba(77, 227, 255, 0.28);
    --holo-faint: rgba(77, 227, 255, 0.10);
    --surface: rgba(13, 22, 36, 0.72);
    --surface-edge: rgba(77, 227, 255, 0.18);

    /* Type */
    --font: "Inter", system-ui, sans-serif;

    /* Motion — ambient + hologram share one named clock */
    --drift-duration: 14s;
    --shimmer-duration: 9s;
    --boot-duration: 0.9s;
    --spin-duration: 7s;
    --holo-z: 90px;
    /* projection depth toward the viewer */
    --header-drop: 160px;
    /* how far the h2 slides down the tilted plane;
                              pure CSS can't measure "distance to bottom
                              edge" — tune this by eye if content changes */

    /* Layout */
    --card-radius: 10px;
    --deck-gap: clamp(1.25rem, 3vw, 2.5rem);
}

/* ---------- 2. BASE ---------- */
* {
    box-sizing: border-box;
}

body {
    margin: 0;
    min-height: 100vh;
    font-family: var(--font);
    font-size: 1rem;
    line-height: 1.6;
    color: var(--ink);
    background:
        radial-gradient(120% 80% at 50% 0%, var(--bg-1) 0%, var(--bg-0) 60%);
    background-attachment: fixed;
    overflow-x: hidden;
}

h1,
h2 {
    line-height: 1.15;
    margin: 0;
    font-weight: 700;
}

p {
    margin: 0;
}

/* ---------- 3. AMBIENT ---------- */
/* Perspective grid floor: two repeating hairline gradients tilted back.
   The perspective() inside the transform makes distant lines converge;
   animating background-position Y crawls the grid toward the viewer. */
.floor {
    position: fixed;
    left: -25vw;
    bottom: -12vh;
    width: 150vw;
    /* oversized so the tilt never shows edges */
    height: 55vh;
    background-image:
        repeating-linear-gradient(to top,
            var(--holo-dim) 0 1px,
            transparent 1px 60px);
    transform: perspective(600px) rotateX(62deg);
    transform-origin: 50% 100%;
    animation: floor-drift var(--drift-duration) linear infinite;
    /* Fade the horizon so the grid dissolves instead of hard-stopping */
    -webkit-mask-image: linear-gradient(to top, black 30%, transparent 95%);
    mask-image: linear-gradient(to top, black 30%, transparent 95%);
    pointer-events: none;
    z-index: -1;
}

@keyframes floor-drift {
    from {
        background-position: 0 0;
    }

    to {
        background-position: 0 60px;
    }

    /* exactly one cell = seamless loop */
}

/* Page-wide CRT scanlines + slow breathing shimmer */


/* ---------- 4. INTRO ---------- */
main {
    max-width: 72rem;
    margin: 0 auto;
    padding: clamp(3rem, 8vh, 6rem) clamp(1.25rem, 4vw, 3rem);
}

.theme_intro {
    max-width: 44rem;
    margin: 0 auto clamp(3rem, 8vh, 5.5rem);
    text-align: center;
}

.badge {
    display: inline-block;
    margin: 0 0 1rem;
    padding: 0.35rem 0.9rem;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--holo);
    border: 1px solid var(--surface-edge);
    border-radius: 999px;
    background: var(--holo-faint);
}

h1 {
    font-size: clamp(2.5rem, 7vw, 4.5rem);
    letter-spacing: 0.02em;
    text-shadow: 0 0 24px var(--holo-dim);
    /* faint projector bloom */
    margin-bottom: 1.5rem;
}

.theme_card {
    display: grid;
    gap: 1.1rem;
    padding: clamp(1.5rem, 4vw, 2.25rem);
    text-align: left;
    color: var(--ink-muted);
    background: var(--surface);
    border: 1px solid var(--surface-edge);
    border-radius: var(--card-radius);
}

/* Shared tag chips (intro + cards) */
.theme_tags,
.card_tags {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.theme_tags li,
.card_tags li {
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--holo);
    padding: 0.25rem 0.7rem;
    border: 1px solid var(--surface-edge);
    border-radius: 999px;
}

.theme_actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 0.4rem;
}

.btn {
    display: inline-block;
    padding: 0.75rem 1.5rem;
    font-weight: 600;
    font-size: 0.95rem;
    text-decoration: none;
    color: var(--bg-0);
    background: var(--holo);
    border: 1px solid var(--holo);
    border-radius: 6px;
    box-shadow: 0 0 18px var(--holo-dim);
    transition: box-shadow 0.25s, background 0.25s, color 0.25s;
}

.btn:hover {
    box-shadow: 0 0 32px var(--holo-dim);
}

.btn--ghost {
    color: var(--holo);
    background: transparent;
    box-shadow: none;
}

.btn--ghost:hover {
    background: var(--holo-faint);
}

.btn:focus-visible {
    outline: 2px solid var(--holo);
    outline-offset: 3px;
}

/* ---------- 5. DECK ---------- */
.cards {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--deck-gap);
}

@media (max-width: 56rem) {
    .cards {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 36rem) {
    .cards {
        grid-template-columns: 1fr;
    }
}

.card {
    position: relative;
    perspective: 1200px;
    /* gives .holo's translateZ real depth */
}

.card:hover {
    z-index: 5;
    /* the hovered card stands up out of its lean and has to clear its
       neighbours; at rest every card projects, and each holo already paints
       over the row above on DOM order alone */
}

/* Hologram: hidden here, and lit by the hover-capable block below.
   Base opacity stays 0 and the boot ANIMATION is what raises it - the
   animation beats the opacity TRANSITION while it runs (cascade order) and
   fill-mode:both holds the end state, so dropping the animation name hands
   opacity back to the transition = clean fade out. Re-adding the name
   restarts the cold start, which is why the reveal flickers once every time
   it comes back. THE STATES ARE INVERTED (owner, 2026-08-24): see below. */
.holo {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: calc(100% - 3rem);
    z-index: 99;
    /* projection base overlaps the card top */
    height: 150px;
    display: flex;
    justify-content: center;
    align-items: flex-end;
    opacity: 0;
    transform: translateZ(var(--holo-z));
    transition: opacity 0.35s ease;
    perspective: 700px;
    /* lets the icon's rotateY read as 3D */
    pointer-events: none;
}

/* Cold start: collapsed bright line → snap open → decaying strobe →
   brightness settle. Adjacent keyframes 1% apart = hard cuts. */
@keyframes holo-boot {
    0% {
        opacity: 0;
        transform:
            translateZ(var(--holo-z)) translateY(48px) scale(1.35, 0.04);
        filter: brightness(3.5);
    }

    9% {
        opacity: 1;
        transform:
            translateZ(var(--holo-z)) translateY(48px) scale(1.35, 0.04);
        filter: brightness(3.5);
    }

    13% {
        opacity: 0;
        transform:
            translateZ(var(--holo-z)) translateY(20px) scale(1, 1.08);
        filter: brightness(3);
    }

    14% {
        opacity: .85;
    }

    21% {
        opacity: .15;
    }

    25% {
        opacity: .9;
        transform:
            translateZ(var(--holo-z)) translateY(0) scale(1, 1);
        filter: brightness(2.2);
    }

    33% {
        opacity: .35;
    }

    40% {
        opacity: 1;
        filter: brightness(1.7);
    }

    100% {
        opacity: 1;
        transform:
            translateZ(var(--holo-z)) translateY(0) scale(1, 1);
        filter: brightness(1);
    }
}

.holo_icon {
    width: 76px;
    height: 76px;
    color: var(--holo);
    filter: drop-shadow(0 0 6px var(--holo-dim)) drop-shadow(0 0 18px var(--holo-faint));
    animation: holo-spin var(--spin-duration) linear infinite;
}

@keyframes holo-spin {
    from {
        transform: rotateY(0deg);
    }

    to {
        transform: rotateY(360deg);
    }
}

.holo_glow {
    position: absolute;
    bottom: -6px;
    left: 50%;
    transform: translateX(-50%);
    width: 120px;
    height: 26px;
    background: radial-gradient(ellipse at center, var(--holo-dim), transparent 70%);
}

.card_face {
    height: 100%;
    transform-origin: 50% 100%;
    /* tips back from its base, like a console */
    transform-style: preserve-3d;
    /* children keep 3D → h2 can lift OFF the tilted plane */
    transition: transform 0.7s, box-shadow 0.7s;
    text-align: center;
    display: grid;
    gap: 0.8rem;
    align-content: start;
    padding: 1.5rem;
    background: var(--surface);
    border: 1px solid var(--surface-edge);
    border-radius: var(--card-radius);
}

/* THE FLAT, FULLY READABLE POSE IS THE BASE. Every word on the face, no lean,
   no projection - which is exactly what a device with no hover keeps at both
   ends. The lit console pose lives in the hover-capable block below, so real
   copy is never gated behind a gesture a touch screen cannot make. Transitions
   are declared here because both poses share them. */
.card_face p,
.card_face .card_tags {
    transition: opacity 0.25s ease;
}

.card_face h2 {
    font-size: 1.15rem;
    color: var(--ink);
    transition: transform 0.35s ease, text-shadow 0.35s ease;
}

/* ---- THE CARD RESTS LIT, AND HOVER STANDS IT BACK UP (owner, 2026-08-24) ----
   The projection used to be the hover reward; it is now the poster. A pointer
   that can hover arrives on a deck of leaned-back consoles already projecting,
   and hovering one is what tips it upright and puts the body copy and tags
   back on its face. Every value below is the old choreography with its ends
   swapped: what used to sit in the :hover rules is now the base pose here, and
   the :hover rules hold what the base used to say.

   Gated on (hover: hover) and (pointer: fine) DELIBERATELY. Inverted, the lean
   is what hides the reading copy, and a coarse pointer has no reliable way to
   ask for it back - so a touch screen keeps the flat card and simply never
   sees a hologram, which is what it got before this change too.

   The 0.7s lean is still the master clock, now measured from mouse-IN:
   0.00s   face stands up out of its 75deg lean; the hologram starts fading
   0.35s   body copy + tags fade back onto the face (exact midpoint)
   0.35s   h2 drops its billboard counter-rotation and travels back up the
           plane to sit as a heading
   0.70s   the face lands flat and fully readable
   Mouse-out reverses all of it at once, and the hologram re-boots on the far
   side of the lean. Delays live ONLY in the :hover rules, which is what keeps
   the return one motion instead of an un-staggered one. */
@media (hover: hover) and (pointer: fine) {

    /* The 0.7s delay is the lean: on mouse-out the projection waits for the
       console to finish tipping back before it cold-starts. At load the card
       is already leaning, so the deck powers up 0.7s in. */
    .holo {
        animation: holo-boot var(--boot-duration) ease-out 0.7s both;
    }

    /* Dropping the name hands opacity back to the base 0 and its transition. */
    .card:hover .holo {
        animation: none;
    }

    .card_face {
        transform: perspective(900px) rotateX(75deg);
        box-shadow: 0 30px 32px -12px rgba(0, 0, 0, 0.7);
    }

    .card:hover .card_face {
        transform: none;
        box-shadow: none;
    }

    .card_face p,
    .card_face .card_tags {
        opacity: 0;
    }

    .card:hover .card_face p,
    .card:hover .card_face .card_tags {
        opacity: 1;
        transition-delay: 0.35s;
    }

    /* Transform order reads RIGHT-TO-LEFT: upright first (rotateX(-75deg)
       cancels the plane's tilt = a billboard facing the viewer, "part of the
       hologram"), lift 24px off the plane, then travel down it. */
    .card_face h2 {
        transform: translateY(var(--header-drop)) translateZ(24px) rotateX(-75deg);
        text-shadow: 0 0 18px var(--holo-dim);
        /* projected label, ties to the holo */
    }

    .card:hover .card_face h2 {
        transform: none;
        text-shadow: none;
        transition-delay: 0.35s;
    }
}

.card_face p {
    font-size: 0.92rem;
    color: var(--ink-muted);
}

.card_tags {
    margin-top: auto;
    /* pins chips to the card bottom across the row */
    justify-content: center;
}

/* ---------- 6. REDUCED MOTION ----------
   No flicker, no spin, no 3D tip. Inverted along with everything else: the
   hologram is simply present at rest and fades out on hover, and the face
   never leans, so the copy is readable at both ends.
   BOTH FORMS, and they are not interchangeable. The media query catches the OS
   setting; html.reduce-motion is the site's own toggle, which reaches this page
   because deck.js stamps it on every page it loads, this one included. Before
   the inversion the gap was invisible - nothing moved until you hovered. Now
   the boot flicker fires on load, so a stamp-only visitor would have seen the
   one thing the setting exists to prevent. Edit the two together. */
@media (prefers-reduced-motion: reduce) {

    .floor,
    .scanlines,
    .holo_icon {
        animation: none;
    }

    .holo {
        animation: none;
        opacity: 1;
    }

    .card:hover .holo {
        opacity: 0;
    }

    .card_face {
        transform: none;
        box-shadow: none;
    }

    .card_face p,
    .card_face .card_tags {
        opacity: 1;
    }

    .card_face h2 {
        transform: none;
        text-shadow: none;
    }
}

html.reduce-motion .floor,
html.reduce-motion .scanlines,
html.reduce-motion .holo_icon {
    animation: none;
}

html.reduce-motion .holo {
    animation: none;
    opacity: 1;
}

html.reduce-motion .card:hover .holo {
    opacity: 0;
}

html.reduce-motion .card_face {
    transform: none;
    box-shadow: none;
}

html.reduce-motion .card_face p,
html.reduce-motion .card_face .card_tags {
    opacity: 1;
}

html.reduce-motion .card_face h2 {
    transform: none;
    text-shadow: none;
}

/* ==========================================================================
   7. THE NAV WHEEL — this theme's own navigation
   ==========================================================================
   Moved here 2026-07-21 (the owner) from the main site's mobile nav drawer, where
   it was stage 1 of the bottom sheet. It was always skinned as Holotype
   ("lean into the holotype theme heavily"), so this is the page it belongs
   on: a vertical ring of page cards dragged like a physical wheel, each
   landed card projecting a spinning wireframe emblem above itself. The site
   nav went back to its plain bar in the same change.

   Four deliberate differences from the site build it came from:

   1. It runs at EVERY viewport here, not just under 768px. There it was a
      mobile-only stand-in for a bottom bar; here it IS the navigation, so a
      desktop visitor has to be able to drive it.
   2. It is opened by its own launcher button, and keys off .is-open on the
      element itself — NEVER html.nav-drawer. deck.js stamps that class from
      sessionStorage, so it can land on this page as a leftover from the main
      site; binding to it would make the wheel's visibility depend on where
      the visitor happened to have been.
   3. The colorway is no longer re-declared locally. It reads this theme's
      own :root tokens (--holo, --holo-dim, --holo-faint, --spin-duration),
      which is what the site copy was imitating in the first place. Retune
      the theme, and the wheel follows.
   4. The cards are real markup, not built from nav links by JS. This page
      has no #main_nav to read from, and static anchors keep every
      destination crawlable. theme.js owns motion and state only.

   Ambient: the page's own .floor and .scanlines sit behind the scrim, so the
   overlay grows its own pair on ::before and ::after. They reuse this
   theme's floor-drift and scan-breathe keyframes rather than duplicating
   them — which is why the grid cell here is 60px, matching the one cell
   floor-drift travels. Change one, change the other.
   ========================================================================== */

/* Screen-reader-only, the site's own recipe. The theme page does not load
   style.css, so it needs its own copy for the wheel's live status line. */
.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;
}

/* ---- the launcher ----
   Bottom-right: the wheel is a thumb instrument, so its opener sits where a
   thumb rests, and it stays clear of the badge and h1 in the top-left. Above
   .scanlines (z-index 2) and the cards' own projections (99). */
.nav_launch {
    position: fixed;
    right: clamp(1rem, 3vw, 2rem);
    bottom: clamp(1rem, 3vw, 2rem);
    z-index: 190;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    padding: 0;
    border: 1px solid var(--holo-dim);
    border-radius: 50%;
    background: rgba(13, 22, 36, 0.72);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    color: var(--holo);
    cursor: pointer;
    box-shadow: 0 0 18px rgba(77, 227, 255, 0.10);
    transition: box-shadow 0.25s ease, background 0.25s ease, border-color 0.25s ease;
}

.nav_launch svg {
    width: 24px;
    height: 24px;
    filter: drop-shadow(0 0 6px var(--holo-dim));
}

.nav_launch:hover,
.nav_launch:focus-visible {
    border-color: var(--holo);
    background: rgba(13, 22, 36, 0.9);
    box-shadow: 0 0 26px var(--holo-dim);
}

.nav_launch:focus-visible {
    outline: 2px solid var(--holo);
    outline-offset: 3px;
}

/* A slow emitter pulse so the launcher reads as powered, not as a static
   glyph — the same "themes breathe" convention as .floor and .scanlines. */
.nav_launch::after {
    content: "";
    position: absolute;
    inset: -1px;
    border-radius: 50%;
    border: 1px solid var(--holo);
    opacity: 0;
    animation: nav-launch-pulse 3.2s ease-out infinite;
    pointer-events: none;
}

@keyframes nav-launch-pulse {
    0% {
        opacity: 0.5;
        transform: scale(1);
    }

    70%,
    100% {
        opacity: 0;
        transform: scale(1.45);
    }
}

/* ---- the overlay ---- */
.nav_wheel {
    /* Geometry the physics reads back with getComputedStyle to place each
       card as a continuous function of its ring-distance. These resolve to
       px there, so clamp()/svh stay responsive. Motion is JS-owned; no
       transition on the cards, or it would fight the rAF loop. Roomier than
       the site's mobile-only values now that this also runs on desktop. */
    /* The spacing has to clear TWO things that stick out of a card, or they
       land on its neighbours: the projection above it (122px tall, anchored
       at the card's top edge) and the section-chip tray below it (a 28px row
       plus its gap). The binding constraint is the chip tray against the top
       edge of the next card down — at 0.95 spread and 0.88 scale, anything
       under about 136px overlaps. The site build could run tighter because
       it was mobile-only and its cards were shorter. Do not lower the floor
       without re-checking the chips against the card below. */
    --wheel-spread: clamp(152px, 17svh, 190px);
    --wheel-rot: 32deg;
    --wheel-depth: -120px;
    --wheel-arc: -12px;
    --wheel-surface: rgba(13, 22, 36, 0.72);
    --wheel-edge: rgba(77, 227, 255, 0.30);
    --wheel-ease: cubic-bezier(0.54, 0.35, 0.29, 0.99);
    position: fixed;
    inset: 0;
    z-index: 200;
    background: radial-gradient(130% 90% at 50% 42%,
            rgba(10, 18, 32, 0.9) 0%,
            rgba(5, 8, 15, 0.96) 70%);
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
    overflow: hidden;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    /* the overlay owns every gesture on it: without this a drag on the wheel
       would also scroll the page underneath */
    touch-action: none;
    transition: opacity 0.45s var(--wheel-ease), visibility 0s linear 0.45s;
}

.nav_wheel.is-open {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity 0.45s var(--wheel-ease);
}

/* THE CLOSED WHEEL HAS TO BE MADE INERT HERE, and the !important is the point
   rather than a shortcut. theme.js places the ring by writing inline
   `visibility: visible` and `pointer-events: auto` onto the landed card every
   time it positions, and an inline declaration outranks the overlay above it:
   a descendant that re-declares visibility:visible is visible again inside a
   hidden ancestor, and pointer-events:none on an ancestor never survives a
   descendant's auto. Only an author !important beats an inline declaration,
   and the rAF loop rewrites those constantly, so nothing quieter holds.
   Left alone, a closed overlay at opacity 0 parked the centred page card (420px
   by 88px of invisible link, plus its section chips) dead centre of the
   viewport: it ate hover and clicks on the content underneath, so the middle
   deck card could not be hovered at all, and it kept every wheel control in the
   tab order. The navigation really was still in place with the wheel shut.
   The 0.45s delay on visibility is the overlay's own close transition, so the
   cards still fade out with the scrim instead of popping. pointer-events goes
   dead immediately, which is the half that matters. */
.nav_wheel:not(.is-open) * {
    visibility: hidden !important;
    pointer-events: none !important;
    transition: visibility 0s linear 0.45s;
}

/* WHITE-FLASH belt, carried over from the site build. The overlay is a
   backdrop-filter surface, and glass captured into a view-transition
   snapshot has no backdrop to filter — it renders as its bare films.
   theme.js closes the wheel before any of its links navigate, so this
   should never match anything visible; it exists so an edge (a bfcache
   back/forward with the wheel open, or the 0.45s fade-out still running
   when the snapshot is taken) can never paint bare films mid-transition. */
:root:active-view-transition .nav_wheel {
    display: none !important;
}


.nav_wheel_stage {
    position: absolute;
    inset: 0;
    z-index: 1;
    perspective: 1000px;
}

/* Every card is placed by JS (inline transform/opacity/z-index, recomputed
   each rAF frame from the continuous ring position), so no CSS transform or
   transition belongs here — that is what makes the ring move continuously
   instead of stepping between fixed poses. */
.nav_wheel_card {
    position: absolute;
    top: 50%;
    left: 50%;
    width: min(78vw, 420px);
    transform-style: preserve-3d;
    will-change: transform, opacity;
}

/* Card face: a translucent navy plate with a cyan projector edge. The card
   for the page you are on wears a bright ring — the wheel's quiet
   aria-current cue. */
.nav_wheel_link {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.9rem;
    width: 100%;
    min-height: 88px;
    padding: 1.2rem 1.4rem;
    border-radius: 18px;
    border: 1px solid var(--wheel-edge);
    background: var(--wheel-surface);
    box-shadow:
        0 18px 44px -18px rgba(0, 0, 0, 0.75),
        inset 0 0 30px rgba(77, 227, 255, 0.05);
    color: var(--ink);
    text-decoration: none;
}

.nav_wheel_link[aria-current="page"] {
    border-color: var(--holo);
    box-shadow:
        0 0 0 1px var(--holo),
        0 0 26px var(--holo-dim),
        inset 0 0 34px rgba(77, 227, 255, 0.1);
}

.nav_wheel_icon {
    display: flex;
    flex-shrink: 0;
    color: var(--holo);
    filter: drop-shadow(0 0 6px var(--holo-dim));
}

.nav_wheel_icon svg {
    width: 26px;
    height: 26px;
}

/* Label over an optional one-line gloss. .nav_wheel_label holds the page
   NAME ALONE — theme.js reads its textContent for the chevron labels and the
   live status line, so nothing else may be nested inside it. */
.nav_wheel_text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.nav_wheel_label {
    font-size: 1.1rem;
    letter-spacing: 0.04em;
}

.nav_wheel_sub {
    margin-top: 0.15rem;
    color: var(--ink-muted);
    font-size: 0.8rem;
    letter-spacing: 0.02em;
}

/* ---- the projected emblem ----
   A wireframe glyph lifted above the card on an emitter glow and spun on
   rotateY, exactly like the cards further down this page. It is retracted
   while the ring turns and cold-boots once a card lands (gated on the wheel
   NOT moving), so the projection reads as the landed page unpacking itself.
   Pure decoration, so aria-hidden and never focusable. */
.nav_wheel_holo {
    position: absolute;
    width: 128px;
    height: 122px;
    left: 125%;
    bottom: 0;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    perspective: 700px;
    opacity: 0;
    pointer-events: none;
}

.nav_wheel_holo_glow {
    position: absolute;
    bottom: -4px;
    left: 50%;
    transform: translateX(-50%);
    width: 120px;
    height: 26px;
    background: radial-gradient(ellipse at center, var(--holo-dim), transparent 70%);
}

.nav_wheel_holo_icon {
    width: 86px;
    height: 86px;
    color: var(--holo);
    filter: drop-shadow(0 0 6px var(--holo-dim)) drop-shadow(0 0 18px var(--holo-faint));
    animation: holo-spin var(--spin-duration) linear infinite;
}

.nav_wheel:not(.is-moving) .nav_wheel_card[data-pos="0"] .nav_wheel_holo {
    animation: nav-holo-boot 0.9s ease-out 0.15s both;
}

/* This theme's own holo-boot bakes translateZ(--holo-z) into every frame,
   which is right for a card in the deck and wrong for a wheel card centred
   with translateX(-50%) — a transform keyframe replaces the base transform
   outright, so reusing it would yank the projection off-centre. Same cold
   start, this rig's positioning. */
@keyframes nav-holo-boot {
    0% {
        opacity: 0;
        transform: translateX(-50%) scale(1.3, 0.04);
        filter: brightness(3);
    }

    9% {
        opacity: 1;
        transform: translateX(-50%) scale(1.3, 0.04);
        filter: brightness(3);
    }

    13% {
        opacity: 0;
        transform: translateX(-50%) scale(1, 1.06);
        filter: brightness(2.6);
    }

    14% {
        opacity: 0.85;
    }

    21% {
        opacity: 0.15;
    }

    25% {
        opacity: 0.9;
        transform: translateX(-50%) scale(1, 1);
        filter: brightness(2);
    }

    33% {
        opacity: 0.35;
    }

    40% {
        opacity: 1;
        filter: brightness(1.6);
    }

    100% {
        opacity: 1;
        transform: translateX(-50%) scale(1, 1);
        filter: brightness(1);
    }
}

/* ---- child slide chips ----
   A visually distinct, child-level layer under the big page card, so a page
   and its sections never read as the same kind of thing. Only this page's
   own card carries them (they jump to the six deck cards below); the other
   cards lead off this document, and inventing section counts for them would
   rot. The reveal is delayed past the settle so it reads as the landed card
   unpacking. Each tile is a 28px-plus target (WCAG 2.5.8). */
.nav_wheel_dots {
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.4rem;
    width: min(78vw, 420px);
    padding-top: 0.5rem;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.25s ease;
}

.nav_wheel:not(.is-moving) .nav_wheel_card[data-pos="0"] .nav_wheel_dots {
    opacity: 1;
    pointer-events: auto;
    transition: opacity 0.35s ease 0.3s;
}

.nav_wheel_dot {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 30px;
    height: 28px;
    padding: 0 0.4rem;
    border: 1px solid var(--wheel-edge);
    border-radius: 7px;
    background: rgba(13, 22, 36, 0.6);
    color: var(--holo);
    font-family: var(--font);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    cursor: pointer;
    transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}

.nav_wheel_dot:hover,
.nav_wheel_dot:focus-visible,
.nav_wheel_dot:active {
    border-color: var(--holo);
    background: var(--holo);
    color: #05080f;
    box-shadow: 0 0 14px var(--holo-dim);
}

/* ---- controls ----
   Chevrons stacked on the wheel's vertical axis (the drag lane itself),
   close pinned clear of them in the top corner. 48px targets. */
.nav_wheel_btn {
    position: absolute;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    padding: 0;
    border: 1px solid var(--wheel-edge);
    border-radius: 50%;
    background: rgba(13, 22, 36, 0.55);
    color: var(--holo);
    cursor: pointer;
    box-shadow: 0 0 14px rgba(77, 227, 255, 0.08);
    transition: box-shadow 0.2s ease, background 0.2s ease;
}

.nav_wheel_btn:hover {
    background: rgba(13, 22, 36, 0.75);
    box-shadow: 0 0 22px var(--holo-dim);
}

.nav_wheel_btn svg {
    width: 20px;
    height: 20px;
}

.nav_wheel_btn:focus-visible {
    outline: 2px solid var(--holo);
    outline-offset: 2px;
}

.nav_wheel_prev {
    left: 50%;
    top: max(3.5rem, env(safe-area-inset-top, 0px));
    transform: translateX(-50%);
}

.nav_wheel_next {
    left: 50%;
    bottom: calc(4.5rem + env(safe-area-inset-bottom, 0px));
    transform: translateX(-50%);
}

.nav_wheel_close {
    top: max(1rem, env(safe-area-inset-top, 0px));
    right: 1rem;
}

/* ---- reduced motion, both forms ----
   The wheel stays fully usable — theme.js snaps instead of throwing with
   momentum — and every ambient/projection animation is stilled. The emblem
   simply holds on, no spin and no flicker. The site toggle reaches this page
   too: deck.js stamps html.reduce-motion on every page it loads, which is
   every page including this one. */
@media (prefers-reduced-motion: reduce) {

    .nav_launch::after,
    .nav_wheel::before,
    .nav_wheel::after,
    .nav_wheel_holo_icon {
        animation: none;
    }

    .nav_wheel:not(.is-moving) .nav_wheel_card[data-pos="0"] .nav_wheel_holo {
        animation: none;
        opacity: 1;
    }

    .nav_wheel_dots {
        transition: none;
    }
}

html.reduce-motion .nav_launch::after,
html.reduce-motion .nav_wheel::before,
html.reduce-motion .nav_wheel::after,
html.reduce-motion .nav_wheel_holo_icon {
    animation: none;
}

html.reduce-motion .nav_wheel:not(.is-moving) .nav_wheel_card[data-pos="0"] .nav_wheel_holo {
    animation: none;
    opacity: 1;
}

html.reduce-motion .nav_wheel_dots {
    transition: none;
}

/* Short viewports (landscape phones): the ring's flanks and the chevrons run
   out of room, so tighten the spacing rather than let cards collide. */
@media (max-height: 560px) {
    .nav_wheel {
        /* Shorter cards here, so the chip tray clears at a smaller spread —
           but the same constraint applies; 140px is its floor. At this
           height only the active card and one flank fit, which is the
           honest result on a landscape phone. */
        --wheel-spread: clamp(140px, 33svh, 156px);
    }

    .nav_wheel_link {
        min-height: 68px;
        padding: 0.85rem 1.1rem;
    }

    .nav_wheel_holo {
        width: 96px;
        height: 84px;
    }

    .nav_wheel_holo_icon {
        width: 60px;
        height: 60px;
    }
}