@font-face {
    font-family: 'Orbitron';
    src: url('../fonts/Orbitron-Regular.ttf') format('truetype');
    font-weight: normal;
    font-style: normal;
}

@font-face {
    font-family: 'Orbitron';
    src: url('../fonts/Orbitron-Bold.ttf') format('truetype');
    font-weight: bold;
    font-style: normal;
}

:root {
    --bg-color: #050505;
    --text-color: #e0e0e0;
    --neon-cyan: #0ff;
    --neon-red: #f00;
    --accent-glow: rgba(0, 255, 255, 0.4);
    --font-main: 'Orbitron', sans-serif;
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    background-color: var(--bg-color);
    color: var(--text-color);
    font-family: var(--font-main);
    overflow-x: hidden;
    overflow-y: auto;
    min-height: 100vh;
    width: 100vw;
}

/* Transitions */
.view-active {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    padding-top: 5rem;
    min-height: 100vh;
    transition: opacity 0.5s ease-in-out;
}

.view-hidden {
    display: none;
    opacity: 0;
}

/* Landing Screen */
#landing-screen {
    position: relative;
    z-index: 10;
}

.background-beams {
    position: fixed;   /* fixed, not absolute — otherwise height:100% only
                          covers the first viewport and the radial glow cuts
                          off sharply partway down tall/scrolling pages. */
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: radial-gradient(circle at 50% 50%, #1a1a2e 0%, #050505 100%);
    z-index: -1;
}

/* Animated hex field — the persistent landing background. Above the static
   gradient fallback, below the intro and the content. */
#hex-bg {
    position: fixed;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 0;
    pointer-events: none;
}

/* Opening laser/fog canvas — sits above the hex field but behind the
   content; intro.js fades it out after a few seconds to reveal the hex
   background beneath. */
#intro-fx {
    position: fixed;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 1;
    pointer-events: none;
    will-change: opacity;
}

.content {
    text-align: center;
    position: relative;
    z-index: 2;
}

h1.glitch-text {
    font-size: 5rem;
    font-weight: bold;
    letter-spacing: 0.5rem;
    color: var(--neon-cyan);
    text-shadow: 0 0 10px var(--neon-cyan), 0 0 20px var(--neon-cyan);
    margin-bottom: 1rem;
    will-change: filter, text-shadow;
}

/* Occasional neon-tube flare — flicker.js adds .lc-flare at random
   intervals for a single burst, then removes it. The title spikes to a
   white-hot bloom and stutters like a struggling tube. On HDR / wide-gamut
   displays the flare uses display-p3 white and a wider bloom so it reads as
   a genuine brightness spike (see the (dynamic-range: high) block below);
   on SDR it's a bright cyan-white flash. */
@keyframes lc-flare {
    0%, 100% { }
    6%  { color: #fff; filter: brightness(1.9);
          text-shadow: 0 0 14px #fff, 0 0 44px var(--neon-cyan), 0 0 90px var(--neon-cyan); }
    11% { filter: brightness(0.65); }
    19% { color: #fff; filter: brightness(2.5);
          text-shadow: 0 0 22px #fff, 0 0 64px var(--neon-cyan), 0 0 130px var(--neon-cyan); }
    26% { filter: brightness(0.85); }
    38% { color: #fff; filter: brightness(1.6);
          text-shadow: 0 0 16px #fff, 0 0 50px var(--neon-cyan); }
}

.glitch-text.lc-flare {
    animation: lc-flare 0.5s ease-out;
}

@media (dynamic-range: high) {
    @keyframes lc-flare {
        0%, 100% { }
        6%  { color: color(display-p3 1 1 1); filter: brightness(2.6);
              text-shadow: 0 0 18px color(display-p3 1 1 1), 0 0 64px var(--neon-cyan), 0 0 130px var(--neon-cyan); }
        11% { filter: brightness(0.55); }
        19% { color: color(display-p3 1 1 1); filter: brightness(3.3);
              text-shadow: 0 0 30px color(display-p3 1 1 1), 0 0 96px var(--neon-cyan), 0 0 190px var(--neon-cyan); }
        26% { filter: brightness(0.9); }
        38% { color: color(display-p3 1 1 1); filter: brightness(2.1);
              text-shadow: 0 0 22px color(display-p3 1 1 1), 0 0 72px var(--neon-cyan); }
    }
}

@media (prefers-reduced-motion: reduce) {
    .glitch-text.lc-flare { animation: none; }
}

/* The landing footer must sit ABOVE the fixed #hex-bg canvas (z-index 0),
   which otherwise paints over this in-flow block and hides it (the reason
   'laserchess.org' wasn't visible). .content already does this at z-index 2. */
#landing-screen .footer {
    position: relative;
    z-index: 2;
}

/* Footer wordmark */
.site-tag {
    font-size: 1.15rem;
    font-weight: bold;
    letter-spacing: 0.3rem;
    margin-bottom: 1rem;
}

.site-tag a {
    color: var(--neon-cyan);
    text-decoration: none;
    text-shadow: 0 0 8px var(--neon-cyan);
    transition: text-shadow 0.3s;
}

.site-tag a:hover {
    text-shadow: 0 0 18px var(--neon-cyan);
}

.subtitle {
    font-size: 1.2rem;
    margin-bottom: 2rem;
    color: #888;
    letter-spacing: 0.1rem;
}

.variety-selector {
    display: flex;
    justify-content: center;
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.variety-selector.secondary {
    margin-bottom: 3rem;
}

.variety-btn {
    background: transparent;
    border: 1px solid #444;
    color: #666;
    padding: 0.5rem 1rem;
    font-family: var(--font-main);
    font-size: 0.8rem;
    cursor: pointer;
    transition: all 0.3s;
    letter-spacing: 0.1rem;
}

.variety-btn.active {
    border-color: var(--neon-cyan);
    color: var(--neon-cyan);
    box-shadow: 0 0 10px var(--neon-cyan);
}

.variety-btn:hover:not(.active) {
    border-color: #888;
    color: #888;
}

/* Pre-paint variety restore: the head script stamps html.pref-alc /
   html.pref-lc from localStorage before the body renders, and these
   rules make the FIRST paint match the saved choice (the HTML defaults
   to LC-active). main.js removes the pref-* class at DOMContentLoaded
   and takes over with inline styles/classes, so these only govern the
   initial frames. !important is needed to beat the ALC elements'
   inline style="display:none" defaults. */
html.pref-alc .variety-selector .variety-btn:first-child {
    border-color: #444;
    color: #666;
    box-shadow: none;
}
html.pref-alc .variety-selector .variety-btn:last-child {
    border-color: var(--neon-cyan);
    color: var(--neon-cyan);
    box-shadow: 0 0 10px var(--neon-cyan);
}
html.pref-alc .variety-content[data-variety="LC"] { display: none !important; }
html.pref-alc .variety-content[data-variety="ALC"] { display: block !important; }
html.pref-alc .piece-card[data-variety="LC"] { display: none !important; }
html.pref-alc .piece-card[data-variety="ALC"] { display: flex !important; }

.neon-button {
    background: transparent;
    border: 2px solid var(--neon-cyan);
    color: var(--neon-cyan);
    padding: 1rem 2.5rem;
    font-family: var(--font-main);
    font-size: 1.2rem;
    cursor: pointer;
    transition: all 0.3s;
    box-shadow: 0 0 5px var(--neon-cyan);
    letter-spacing: 0.2rem;
}

.neon-button:hover {
    background: var(--neon-cyan);
    color: var(--bg-color);
    box-shadow: 0 0 30px var(--neon-cyan);
}

/* Platform-detected download CTA — THE primary action on the landing
   screen, so it's the filled button (INITIALIZE LINK stays outline).
   main.js rewrites its label/target per visitor platform. */
.dl-hero {
    display: inline-block;
    background: var(--neon-cyan);
    color: var(--bg-color);
    font-weight: bold;
    text-decoration: none;
    margin-bottom: 1.75rem;
    box-shadow: 0 0 18px var(--neon-cyan);
}

.dl-hero:hover {
    background: #7ff9ff;
    color: var(--bg-color);
    box-shadow: 0 0 36px var(--neon-cyan);
}

/* Secondary "try before you install" action — quiet ghost button so it
   reads clearly below the download hero, waking up on hover. */
.ghost-button {
    background: transparent;
    border: 1px solid #444;
    color: #888;
    padding: 0.6rem 1.4rem;
    font-family: var(--font-main);
    font-size: 0.8rem;
    letter-spacing: 0.15rem;
    cursor: pointer;
    transition: all 0.3s;
}

.ghost-button:hover {
    border-color: var(--neon-cyan);
    color: var(--neon-cyan);
    box-shadow: 0 0 10px rgba(0, 255, 255, 0.35);
}

.downloads-section {
    margin-top: 4rem;
}

.download-header {
    font-size: 0.8rem;
    color: #888;
    font-weight: bold;
    letter-spacing: 0.3rem;
    margin-bottom: 2rem;
    text-shadow: 0 0 2px rgba(255, 255, 255, 0.1);
}

.downloads-container {
    display: flex;
    justify-content: center;
    gap: 2rem;
    flex-wrap: wrap;
}

.downloads-container.legacy {
    /* 44 icons no longer fit on one line below ~1250px, so the strip wraps
     * into as many centered rows as it needs instead of overflowing into a
     * horizontal scroller (where the left half was unreachable). The row
     * gap leaves room for the hover label that pops up above each icon;
     * the side padding keeps a label on a row-edge icon inside the box. */
    gap: 1.25rem 0.5rem;
    flex-wrap: wrap;
    justify-content: center;
    max-width: 100%;
    margin: 1.5rem auto 0;
    padding: 1.75rem 2rem 0.75rem;
}

.device-link {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.4rem;
    text-decoration: none;
    flex-shrink: 0;
    position: relative;
    transition: transform 0.3s ease;
}

.device-link img {
    width: 32px;
    height: 32px;
    filter: invert(1) brightness(2) drop-shadow(0 0 2px var(--neon-cyan));
    opacity: 0.6;
    transition: all 0.3s ease;
}

.downloads-container.legacy .device-link img {
    width: 20px;
    height: 20px;
    opacity: 0.5;
}

.device-label {
    font-size: 0.5rem;
    color: #555;
    letter-spacing: 0.1rem;
    transition: all 0.3s ease;
}

.downloads-container.legacy .device-label {
    position: absolute;
    bottom: 100%;
    left: 50%;
    transform: translateX(-50%);
    margin-bottom: 0.25rem;
    opacity: 0;
    pointer-events: none;
    white-space: nowrap;
    transition: opacity 0.2s ease;
}

/* Row-edge icons (classes set by main.js, since the strip wraps and CSS
   can't see flex row breaks): anchor the label to the edge instead of
   centering it, so it can't spill past the strip. */
.downloads-container.legacy .device-link.row-start .device-label {
    left: 0;
    transform: none;
}

.downloads-container.legacy .device-link.row-end .device-label {
    left: auto;
    right: 0;
    transform: none;
}

.device-link:hover {
    transform: translateY(-3px);
}

.device-link:hover img {
    filter: invert(1) brightness(2) drop-shadow(0 0 8px var(--neon-cyan));
    opacity: 1;
}

.device-link:hover .device-label {
    opacity: 1;
    color: var(--neon-cyan);
    text-shadow: 0 0 5px var(--neon-cyan);
}

/* Content Pages */
.content-page {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    padding: 5rem 2rem;
    overflow-y: auto;
}

.page-container {
    max-width: 800px;
    width: 100%;
    text-align: center;
}

.text-content {
    margin-top: 4rem;
    text-align: left;
    line-height: 1.8;
    color: #bbb;
    font-family: monospace;
}

.text-content p {
    margin-bottom: 1.5rem;
}

.text-content .storage-list {
    margin: 0 0 1.5rem 1.25rem;
    padding: 0;
}

.text-content .storage-list li {
    margin-bottom: 0.6rem;
}

.text-content .storage-list strong {
    color: var(--neon-cyan);
}

/* Support Page Specifics */
.faq-item {
    margin-bottom: 2rem;
    border-bottom: 1px solid #1a1a1a;
    padding-bottom: 1.5rem;
}

.faq-question {
    display: block;
    color: var(--neon-cyan);
    font-size: 0.9rem;
    margin-bottom: 0.5rem;
    letter-spacing: 0.1rem;
    font-weight: bold;
}

.faq-answer {
    font-size: 0.85rem;
    color: #888;
}

.support-contact-section {
    margin-top: 5rem;
    padding-top: 3rem;
    border-top: 1px solid #333;
}

.support-info-list {
    margin-top: 1.5rem;
    list-style: none;
    padding-left: 0;
}

.support-info-list li {
    margin-bottom: 1rem;
    padding-left: 1.5rem;
    position: relative;
    font-size: 0.85rem;
}

.support-info-list li::before {
    content: '>';
    position: absolute;
    left: 0;
    color: var(--neon-cyan);
}

.blank-page {
    background-color: var(--bg-color);
}

.footer {
    padding: 4rem 0 2rem;
    width: 100%;
    text-align: center;
    font-size: 0.8rem;
    color: #444;
}

.footer-nav {
    margin-bottom: 1rem;
    display: flex;
    justify-content: center;
    gap: 2rem;
}

.footer-nav a {
    color: #666;
    text-decoration: none;
    transition: color 0.3s;
    font-size: 0.7rem;
    letter-spacing: 0.1rem;
}

.footer-nav a:hover {
    color: var(--neon-cyan);
}

.footer-credit {
    margin-top: 0.5rem;
    font-size: 0.6rem;   /* 75% of the footer's 0.8rem */
}

.footer-credit a {
    color: #666;
    text-decoration: none;
    transition: color 0.3s;
}

.footer-credit a:hover {
    color: var(--neon-cyan);
}

/* Instructions Section */
.instructions-section {
    /* Centered with auto side margins: this is a block box, and the
       parent's text-align:center only centers inline content. .content is
       full viewport width (the retro download strip is its widest child),
       so without these the 900px section hugs the left edge. */
    margin: 6rem auto 4rem;
    max-width: 900px;
    width: 90%;
    text-align: center;
    border-top: 1px solid #222;
    padding-top: 4rem;
}

.section-title {
    font-size: 1.5rem;
    color: var(--neon-cyan);
    letter-spacing: 0.5rem;
    margin-bottom: 2rem;
    text-shadow: 0 0 10px rgba(0, 255, 255, 0.3);
}

.mission-brief {
    font-family: monospace;
    color: #888;
    line-height: 1.6;
    margin-bottom: 4rem;
}

.mission-brief p {
    margin-bottom: 1rem;
}

.subsection-title {
    font-size: 1rem;
    color: #555;
    letter-spacing: 0.3rem;
    margin-bottom: 2rem;
}

.piece-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 2rem;
    text-align: left;
}

.piece-card {
    background: #080808;
    border: 1px solid #1a1a1a;
    padding: 1.5rem;
    transition: border-color 0.3s;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}

.piece-card:hover {
    border-color: #333;
}

/* Crop individual tiles out of the in-game 1024×1024 sprite sheet
   (8 cols × 8 rows of 128 px tiles). We render at 64 px so the sheet
   is scaled to 512×512 and each tile occupies a 64×64 cell. The
   per-piece classes below set background-position to pick the
   cardinal-orientation tile listed in game.js's TILE_POS_MAP. */
.piece-img {
    display: inline-block;
    width: 64px;
    height: 64px;
    margin-bottom: 0.75rem;
    align-self: center;
    background-image: url(../assets/alc_tiles.png?v=1);   /* bump the ?v when the sheet art changes so browsers refetch it */
    background-repeat: no-repeat;
    background-size: 512px 512px;
    filter: drop-shadow(0 0 6px rgba(0, 255, 255, 0.35));
}

/* col, row → background-position: -col*64 px -row*64 px. Source of
   truth: game.js TILE_POS_MAP (n* fields = cardinal orientation). */
.piece-img.tile-K { background-position: -384px  -64px; } /* King            6,1 */
.piece-img.tile-L { background-position:    0px -128px; } /* Laser           0,2 */
.piece-img.tile-T { background-position: -384px    0px; } /* TriangularMirror 6,0 */
.piece-img.tile-B { background-position: -256px -192px; } /* Block           4,3 */
.piece-img.tile-H { background-position: -192px    0px; } /* Hypercube       3,0 */
.piece-img.tile-Y { background-position: -448px  -64px; } /* Hypergon        7,1 */
.piece-img.tile-S { background-position: -128px -128px; } /* Stunner         2,2 */
.piece-img.tile-V { background-position: -128px  -64px; } /* BeamSplitter    2,1 */
.piece-img.tile-W { background-position: -256px  -64px; } /* OneWayMirror    4,1 */
.piece-img.tile-O { background-position: -128px    0px; } /* FullyMirrorOct  2,0 */
.piece-img.tile-P { background-position: -256px    0px; } /* PartlyMirrorOct 4,0 */
.piece-img.tile-X { background-position: -256px -128px; } /* Bomb            4,2 */
.piece-img.tile-N { background-position:    0px -256px; } /* StraightMirror  0,4 (LC rows) */
.piece-img.tile-D { background-position:  -64px -256px; } /* Deflector       1,4 (LC rows) */
/* Upcoming ALC pieces — alc_tiles.png 6th row (row 5), 2nd shape onward. */
.piece-img.tile-portal     { background-position:  -64px -320px; } /* 1,5 */
.piece-img.tile-prism      { background-position: -128px -320px; } /* 2,5 */
.piece-img.tile-tractor    { background-position: -192px -320px; } /* 3,5 */
.piece-img.tile-teleporter { background-position: -256px -320px; } /* 4,5 */
.piece-img.tile-capturer   { background-position: -320px -320px; } /* 5,5 */

/* Same 64×64 footprint as the sprite-cropped .piece-img cells, but
   sourced from a separate GIF rather than the tile sheet — used for
   pieces whose tile-sheet rendering doesn't match the original art
   (e.g. the LC Block, which is a square with a single mirrored side). */
.piece-img-gif {
    display: inline-block;
    width: 64px;
    height: 64px;
    object-fit: contain;
    margin-bottom: 0.75rem;
    align-self: center;
    image-rendering: pixelated;
    filter: drop-shadow(0 0 6px rgba(0, 255, 255, 0.35));
}

.piece-name {
    display: block;
    color: var(--neon-cyan);
    font-size: 0.8rem;
    margin-bottom: 0.5rem;
    letter-spacing: 0.1rem;
    font-weight: bold;
}

.piece-desc {
    font-size: 0.75rem;
    color: #777;
    line-height: 1.5;
}

.piece-more {
    display: inline-block;
    margin-top: 0.75rem;
    font-size: 0.7rem;
    letter-spacing: 0.1rem;
    color: var(--neon-cyan);
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: border-color 0.2s;
}
.piece-more:hover { border-bottom-color: var(--neon-cyan); }

/* Per-piece sections on instructions_*.html — image + paragraph
   block, anchor target for "More info" links from the roster. */
.piece-doc {
    display: flex;
    align-items: flex-start;
    gap: 1.25rem;
    margin-bottom: 2rem;
    padding-bottom: 1.5rem;
    border-bottom: 1px solid #1a1a1a;
    scroll-margin-top: 5rem;
}

.piece-doc .piece-img {
    flex: 0 0 auto;
    margin-bottom: 0;
}

.piece-doc-body {
    flex: 1 1 auto;
}

.piece-doc-name {
    color: var(--neon-cyan);
    font-size: 0.95rem;
    margin: 0 0 0.5rem 0;
    letter-spacing: 0.1rem;
    text-shadow: 0 0 4px var(--neon-cyan);
}

.rule-list {
    list-style: square;
    padding-left: 1.5rem;
    margin-bottom: 1.5rem;
    color: #999;
    font-size: 0.85rem;
}
.rule-list li { margin-bottom: 0.5rem; }

/* Piece art on instructions_*.html — uses the original site's GIFs
   (small, ~30–50 px source). Pinned to a 64 px box so all pieces
   line up no matter the source dimensions. */
.piece-art {
    flex: 0 0 auto;
    width: 64px;
    height: 64px;
    object-fit: contain;
    image-rendering: pixelated;
    background: #050505;
    padding: 4px;
    border: 1px solid #1a1a1a;
}
.piece-art-thin { object-fit: contain; }

.board-figure {
    margin: 1.5rem 0 2.5rem 0;
    text-align: center;
}

.board-img {
    max-width: 100%;
    height: auto;
    border: 1px solid #222;
    image-rendering: pixelated;
    box-shadow: 0 0 20px rgba(0, 255, 255, 0.08);
}

.board-figure figcaption {
    margin-top: 0.5rem;
    font-size: 0.75rem;
    color: #666;
    font-family: monospace;
    letter-spacing: 0.05rem;
}

/* Game Screen */
#game-screen {
    flex-direction: column;
    padding: 2rem;
}

.game-header {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 2rem;
    border-bottom: 1px solid #333;
    padding-bottom: 1rem;
}

.mini-logo {
    color: var(--neon-cyan);
    font-size: 1.5rem;
    text-shadow: 0 0 5px var(--neon-cyan);
}

.text-button {
    background: transparent;
    border: none;
    color: #666;
    font-family: var(--font-main);
    cursor: pointer;
    transition: color 0.3s;
}

.text-button:hover {
    color: var(--neon-red);
}

.game-container {
    display: flex;
    gap: 2rem;
    width: 100%;
    max-width: 1200px;
    height: calc(100vh - 150px);
}

.board-wrapper {
    flex: 1;
    background: #111;
    border: 2px solid #333;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: inset 0 0 20px rgba(0,0,0,0.5);
    position: relative;
}

#board-canvas {
    image-rendering: auto;
    max-width: 100%;
    max-height: 100%;
    /* Suppress the "Save Image" / selection callout iOS shows on a
       long press over a canvas, and the grey tap highlight Android
       paints on quick taps. */
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
}

.game-controls {
    width: 200px;
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

/* Side panel that hosts the player indicator + control buttons —
   ported from the in-server WebContent build. Uses neutral greys so
   it sits visually under the neon page chrome. */
#panel {
    width: 160px;
    background: #1a1a2a;
    display: flex;
    flex-direction: column;
    padding: 20px;
    gap: 20px;
    box-shadow: -2px 0 10px rgba(0,0,0,0.5);
    border: 1px solid #333;
}

.indicator-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 4px;
    aspect-ratio: 1;
    width: 60px;
    margin: 0 auto;
}

.player-sq {
    width: 100%;
    height: 100%;
    border-radius: 2px;
    transition: transform 0.2s;
}

.player-sq.red { background: #ff0000; }
.player-sq.green { background: #00ff00; }
.player-sq.blue { background: #5080ff; }
.player-sq.purple { background: #9050e0; }
.player-sq.active { transform: scale(1.1); box-shadow: 0 0 10px rgba(255,255,255,0.3); }
.player-sq.dim { transform: scale(0.6); opacity: 0.5; }

.pips {
    display: flex;
    justify-content: center;
    gap: 8px;
}

.pip {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #444;
    transition: background 0.2s;
}
.pip.active { background: white; }

.btn-panel {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: auto;
    margin-bottom: auto;
}

#panel button {
    height: 60px;
    background: #2a2a38;
    border: none;
    border-radius: 6px;
    color: #e0e0e0;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    transition: background 0.1s, transform 0.05s;
    /* Suppress iOS / Android long-press behaviours: copy/paste
       callout, text selection, and the highlight overlay. The
       FIRE button is press-and-hold; without these the OS menu
       hijacks the gesture mid-fire. */
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
}
#panel button:hover:not(:disabled) { background: #3a3a48; }
#panel button:active:not(:disabled) { transform: translateY(2px); }
#panel button:disabled { opacity: 0.2; cursor: default; }
#panel button svg {
    width: 26px;
    height: 26px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2.5;
}

.btn-label {
    font-family: var(--font-main);
    font-size: 0.55rem;
    letter-spacing: 0.08em;
    color: #ccd;
    text-transform: uppercase;
    line-height: 1;
    user-select: none;
}

#overlay {
    position: absolute;
    inset: 0;
    background: rgba(0,0,0,0.8);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 100;
    visibility: hidden;
}
#overlay.visible { visibility: visible; }

.win-card {
    background: #000;
    border: 3px solid white;
    padding: 30px 60px;
    border-radius: 12px;
    text-align: center;
    box-shadow: 0 0 30px rgba(0,0,0,1);
}
.win-title { font-size: 32px; font-weight: bold; margin-bottom: 10px; color: #fff; }

#bad-action-toast {
    position: absolute;
    left: 50%;
    bottom: 24px;
    transform: translateX(-50%);
    background: rgba(0, 0, 0, 0.6);
    color: white;
    font-weight: bold;
    font-size: 14px;
    padding: 6px 14px;
    border-radius: 6px;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.18s ease-out;
    z-index: 50;
    white-space: nowrap;
}
#bad-action-toast.show { opacity: 1; }

/* Responsive Design */
@media (max-width: 768px) {
    h1.glitch-text {
        font-size: 2.5rem;
        letter-spacing: 0.2rem;
    }

    .subtitle {
        font-size: 0.9rem;
        padding: 0 1rem;
    }

    .variety-selector {
        flex-direction: column;
        align-items: center;
        width: 90%;
        margin: 0 auto 1.5rem;
    }

    .variety-btn {
        width: 100%;
        max-width: 250px;
    }

    .neon-button {
        width: 90%;
        padding: 1rem;
        font-size: 1rem;
    }

    .downloads-container {
        gap: 1rem;
    }

    /* Phones/tablets: wrap the retro strip into a centered grid so every
       icon is visible at once (no horizontal scrolling), and show the
       platform name under each icon — there's no hover on touch, so the
       hover-tooltip labels would never appear otherwise. */
    .downloads-container.legacy {
        flex-wrap: wrap;
        overflow-x: visible;
        justify-content: center;
        gap: 1rem 0.5rem;
    }

    .device-link img {
        width: 28px;
        height: 28px;
    }

    .downloads-container.legacy .device-link {
        width: 3.6rem;          /* uniform tiles so the grid aligns */
    }

    .downloads-container.legacy .device-link img {
        width: 26px;            /* bigger tap targets than the strip's 20px */
        height: 26px;
        opacity: 0.8;
    }

    .downloads-container.legacy .device-label {
        position: static;       /* was an absolute hover-tooltip; show it inline */
        opacity: 1;
        transform: none;
        left: auto;
        bottom: auto;
        margin: 0.25rem 0 0;
        white-space: normal;    /* let long names (TI-99/4A) wrap in-tile */
        line-height: 1.2;
        font-size: 0.55rem;
        color: #999;
    }

    /* Phone portrait: let the board span edge-to-edge so the user
       gets the largest possible play surface. Override the parent
       #game-screen's 2rem horizontal padding with a negative margin
       on the container — the header and footer keep their padding
       (only the board widens). */
    #game-screen {
        padding-left: 0;
        padding-right: 0;
    }
    .game-header {
        padding-left: 1rem;
        padding-right: 1rem;
        box-sizing: border-box;
    }
    .game-container {
        flex-direction: column;
        height: auto;
        align-items: center;
        width: 100%;
        padding: 0;
    }

    .board-wrapper {
        width: 100%;
        aspect-ratio: 1 / 1;
        max-width: 100vw;
        height: auto;
        border-left: none;
        border-right: none;
    }

    #board-canvas {
        width: 100%;
        height: auto;
    }

    .game-controls {
        width: 100%;
        max-width: 100vw;
        box-sizing: border-box;
    }

    /* Phone-portrait panel: indicator + four buttons need to fit
       inside the viewport without overflowing.  Distribute the
       available space with space-between, shrink the indicator's
       margin, and let the buttons flex (still capped by the
       per-button max-width below) so a 320 px-wide phone doesn't
       push the rightmost button off screen. */
    #panel {
        width: 100%;
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        gap: 8px;
        padding: 10px;
        box-sizing: border-box;
    }

    .btn-panel {
        flex: 1 1 auto;
        flex-direction: row;
        justify-content: space-around;
        gap: 8px;
        margin: 0;
        min-width: 0;
    }

    #panel button {
        flex: 1 1 0;
        width: auto;
        min-width: 0;
        max-width: 64px;
        height: 52px;
    }
    #panel button svg {
        width: 28px;
        height: 28px;
    }

    .piece-grid {
        grid-template-columns: 1fr;
    }

    .view-active {
        padding-top: 3rem;
    }

    .footer-nav {
        flex-wrap: wrap;
        gap: 1rem;
        padding: 0 1rem;
    }
}

@media (max-width: 480px) {
    h1.glitch-text {
        font-size: 2rem;
    }

    .section-title {
        font-size: 1.2rem;
        letter-spacing: 0.2rem;
    }

    /* Stack the LASERCHESS title above the SOUND / DISCONNECT
       buttons on narrow phones — at this width the row layout
       runs the title into the buttons (or pushes a button off
       screen). flex-wrap with the title forced to full width
       drops the buttons onto their own row, right-aligned. */
    .game-header {
        flex-wrap: wrap;
        row-gap: 0.5rem;
        margin-bottom: 1rem;
        padding-bottom: 0.5rem;
    }
    .mini-logo {
        flex: 1 0 100%;
        text-align: center;
    }
    #btn-sound,
    #btn-exit {
        flex: 0 0 auto;
    }
}

/* ---- Download page (WebSite/download/*.html) ---- */

.download-card {
    margin: 3rem auto 0;
    max-width: 520px;
    padding: 2.5rem 2rem;
    border: 1px solid #1a1a1a;
    background: #080808;
    text-align: center;
}

.download-icon {
    width: 128px;
    height: 128px;
    margin: 0 auto 1.5rem;
    filter: invert(1) brightness(2) drop-shadow(0 0 6px var(--neon-cyan));
    opacity: 0.85;
}

/* Vision Pro visor: animated hazy blue cloud overlay mimicking the
   external EyeSight display on the real device. Three radial-gradient
   "blobs" drift with different periods so the motion never repeats
   exactly. Screen blend mode fills the transparent visor interior with
   the cloud while leaving the icon's white outline untouched (screen
   of any colour with white is white). */
.visor-frame {
    position: relative;
    display: inline-block;
    margin: 0 auto 1.5rem;
    line-height: 0;
}

.visor-frame .download-icon {
    margin: 0;
    display: block;
}

.visor-haze {
    position: absolute;
    inset: 0;
    pointer-events: none;
    mix-blend-mode: screen;
}

.haze-blob {
    position: absolute;
    display: block;
    border-radius: 50%;
    filter: blur(12px);
    will-change: transform, opacity;
}

.haze-blob.haze-a {
    width: 60%; height: 42%;
    left: 6%; top: 28%;
    background: radial-gradient(circle, rgba(80, 180, 255, 0.95), rgba(80, 180, 255, 0) 70%);
    animation: visor-drift-a 9s ease-in-out infinite alternate;
}

.haze-blob.haze-b {
    width: 55%; height: 40%;
    left: 38%; top: 30%;
    background: radial-gradient(circle, rgba(60, 130, 255, 0.95), rgba(60, 130, 255, 0) 70%);
    animation: visor-drift-b 11s ease-in-out infinite alternate;
}

.haze-blob.haze-c {
    width: 48%; height: 32%;
    left: 26%; top: 38%;
    background: radial-gradient(circle, rgba(170, 225, 255, 0.85), rgba(170, 225, 255, 0) 70%);
    animation: visor-drift-c 13s ease-in-out infinite alternate;
}

@keyframes visor-drift-a {
    0%   { transform: translate(-3%, -2%) scale(1);    opacity: 0.75; }
    100% { transform: translate(6%, 3%)   scale(1.1);  opacity: 1; }
}

@keyframes visor-drift-b {
    0%   { transform: translate(4%, 2%)   scale(1.05); opacity: 0.85; }
    100% { transform: translate(-7%, -1%) scale(0.95); opacity: 1; }
}

@keyframes visor-drift-c {
    0%   { transform: translate(0%, 3%)   scale(0.95); opacity: 0.7; }
    100% { transform: translate(-2%, -4%) scale(1.15); opacity: 0.95; }
}

.download-blurb {
    color: #888;
    font-family: monospace;
    font-size: 0.85rem;
    line-height: 1.6;
    margin-bottom: 2rem;
}

/* Generic store badge — bordered, centered, fixed-height pill. The
   per-store classes paint the brand colour without me having to ship
   the official badge artwork yet. Swap each for the real PNG when
   you've grabbed them from Apple / Google / Microsoft brand pages. */
.store-badge {
    display: inline-block;
    /* Space adjacent download buttons apart (horizontal gap, plus a
     * vertical gap for when they wrap to multiple rows). */
    margin: 0 0.6rem 0.6rem 0;
    padding: 0.85rem 1.5rem;
    border: 2px solid var(--neon-cyan);
    color: var(--neon-cyan);
    background: transparent;
    text-decoration: none;
    font-family: var(--font-main);
    font-size: 0.85rem;
    letter-spacing: 0.15rem;
    text-transform: uppercase;
    transition: all 0.2s ease;
    box-shadow: 0 0 8px rgba(0, 255, 255, 0.25);
}
.store-badge:hover {
    background: var(--neon-cyan);
    color: var(--bg-color);
    box-shadow: 0 0 20px var(--neon-cyan);
}

.store-apple  { border-color: #fff;   color: #fff;   box-shadow: 0 0 8px rgba(255,255,255,0.25); }
.store-apple:hover  { background: #fff; color: #000; }
.store-google { border-color: #34a853; color: #34a853; box-shadow: 0 0 8px rgba(52,168,83,0.3); }
.store-google:hover { background: #34a853; color: #000; }
.store-ms     { border-color: #00a4ef; color: #00a4ef; box-shadow: 0 0 8px rgba(0,164,239,0.3); }
.store-ms:hover     { background: #00a4ef; color: #000; }
.store-amazon { border-color: #ff9900; color: #ff9900; box-shadow: 0 0 8px rgba(255,153,0,0.3); }
.store-amazon:hover { background: #ff9900; color: #000; }
/* Not-yet-released badge: reads as a badge but is inert (no store link). */
.store-soon   { border-color: #ff9900; color: #ff9900; opacity: 0.55; cursor: default; box-shadow: none; }
.store-soon:hover   { background: transparent; color: #ff9900; box-shadow: none; }

.store-note {
    margin-top: 1rem;
    color: #555;
    font-size: 0.7rem;
    font-style: italic;
}

/* Full SHA-256 digest — monospace and free to wrap anywhere so the
   64-char hash never overflows the card on narrow screens. */
.download-meta .sha {
    font-family: monospace;
    word-break: break-all;
    color: #9aa;
}

.download-meta {
    margin-top: 0.6rem;
    color: #888;
    font-size: 0.7rem;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    letter-spacing: 0.02em;
}

.download-back {
    margin-top: 2.5rem;
    font-size: 0.8rem;
}
.download-back a {
    color: var(--neon-cyan);
    text-decoration: none;
    opacity: 0.75;
}
.download-back a:hover { opacity: 1; }
