/**
 * SQUARE JUMP - Stylesheet
 * Handles the layout of the 3-panel system and arcade aesthetics.
 */

/* --- 1. RESET & BASE LAYOUT --- */
/*
 * A column: the site header sits at the top of the page, the game is centred
 * in whatever is left. Note the axes flip with the direction - `align-items`
 * is the horizontal one here and `justify-content` the vertical one, which the
 * two media queries at the bottom of this file have to respect.
 */
body {
    margin: 0;
    background-color: #121212;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    height: 100vh;
    color: white;
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    overflow: hidden;
}

.center-wrapper {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    /* Centres the game in the space the header leaves, without the header
       having to know how tall the game is. */
    margin-block: auto;
}

/* --- 2. GAME UI & ANIMATIONS --- */
.ui {
    margin-bottom: 20px;
    font-size: 1.2em;
    letter-spacing: 2px;
}

#scoreValue,
#comboValue {
    display: inline-block;
    transition: color 0.2s;
}

/*
 * Site header: the way back to the site the game belongs to.
 *
 * A band of its own across the top rather than a line above the title, so it
 * reads as page chrome and not as part of the game's own heading. It is in
 * normal flow, not fixed - the narrow and short layouts both scroll, and a
 * pinned corner would end up floating over the arena.
 *
 * The link is deliberately an <a> and not a <button>: Space is bound to jump,
 * and a focused button would swallow it. A link only activates on Enter, which
 * the game does not bind, so it can hold focus without costing the player a
 * jump. `e2e/smoke.spec.js` holds that.
 */
.site-header {
    width: 100%;
    box-sizing: border-box;
    padding: 10px 28px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}

/*
 * Gold at rest, so it is read rather than found. That is the same gold the
 * nickname field and the score numbers already use - it is this page's accent
 * for chrome, and the "exit is gold" rule in CLAUDE.md is about the arena.
 *
 * Which leaves the hover with nowhere brighter to go on colour alone, so it
 * says it twice instead: the text lifts to white and the arrow slides back
 * the way the link goes. The slide is the half that survives for anyone who
 * cannot pick gold out from white.
 */
.site-link {
    display: inline-block;
    font-family: 'Courier New', monospace;
    font-size: 0.78em;
    letter-spacing: 2px;
    text-decoration: none;
    color: #ffd700;
    transition: color 0.2s;
}

.site-link-arrow {
    display: inline-block;
    transition: transform 0.2s;
}

    .site-link:hover,
    .site-link:focus-visible {
        color: #ffffff;
    }

        .site-link:hover .site-link-arrow,
        .site-link:focus-visible .site-link-arrow {
            transform: translateX(-4px);
        }

@media (prefers-reduced-motion: reduce) {
    .site-link-arrow {
        transition: none;
    }

    .site-link:hover .site-link-arrow,
    .site-link:focus-visible .site-link-arrow {
        transform: none;
    }
}

.controls {
    font-size: 0.65em;
    opacity: 0.45;
    letter-spacing: 1px;
}

.readout .sep {
    opacity: 0.2;
    margin: 0 10px;
}

#zoneName {
    font-size: 0.7em;
    letter-spacing: 3px;
    opacity: 0.5;
}

/* Bullet-time fuel. The pawn's colour carries the same value, but the bar is
   what you can read without taking your eyes off the hazard you are dodging. */
#slowMeter {
    width: 100%;
    height: 4px;
    margin-top: 12px;
    background: rgba(255, 255, 255, 0.08);
    overflow: hidden;
}

#meterFill {
    height: 100%;
    width: 100%;
    background: #7df9ff;
    box-shadow: 0 0 10px rgba(125, 249, 255, 0.6);
    transition: width 0.08s linear;
}

.combo-hot {
    color: #7df9ff !important;
    font-weight: bold;
}

/* The cap, matching the pawn-side number: the two hot ends of COMBO_HOT_COLORS,
   flickering, with the red end of the ramp carried by the glow instead of the
   text. Same division of labour as the canvas glyph - the letters stay on the
   bright, legible colours and the fire around them is what is red. Not the
   hazard red on the text itself: red lettering on the readout would read as
   damage rather than as a streak at its ceiling. */
.combo-fire {
    color: #ffe6bf !important;
    animation: comboFire 0.45s steps(3, end) infinite alternate;
}

/* A streak whose room has already missed its deadline. It is still the number
   the exit will resolve against, so it is dimmed rather than changed. */
.combo-lost {
    color: #6b6b6b !important;
    text-shadow: none !important;
    animation: none;
    opacity: 0.55;
    text-decoration: line-through;
}

.pulse-cyan {
    animation: comboPop 0.25s ease-out;
}

@keyframes comboFire {
    0% {
        color: #ffe6bf;
        text-shadow: 0 0 8px rgba(255, 122, 26, 0.75);
    }

    50% {
        color: #ffd394;
        text-shadow: 0 0 16px rgba(228, 42, 8, 0.9);
    }

    100% {
        color: #ffc06a;
        text-shadow: 0 0 22px rgba(228, 42, 8, 1);
    }
}

@keyframes comboPop {
    0% {
        transform: scale(1);
    }

    50% {
        transform: scale(1.6);
        text-shadow: 0 0 20px rgba(125, 249, 255, 0.9);
    }

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

#gameOver .stat {
    font-size: 0.8em;
    opacity: 0.6;
    margin: 6px 0;
}

    #gameOver .stat span {
        color: #7df9ff;
        font-weight: bold;
    }

.pulse-yellow {
    animation: scorePop 0.3s ease-out;
    color: #ffd700 !important;
}

@keyframes scorePop {
    0% {
        transform: scale(1);
    }

    50% {
        transform: scale(1.5);
        text-shadow: 0 0 20px rgba(255, 215, 0, 0.8);
    }

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

/* --- 3. SIDE PANELS (Leaderboard & Identity) --- */
#leaderboard {
    position: absolute;
    right: 115%;
    top: 50px;
    width: 200px;
    text-align: right;
    border-right: 1px solid rgba(255, 255, 255, 0.1);
    padding-right: 20px;
}

#identity-panel {
    position: absolute;
    left: 115%;
    top: 50px;
    width: 200px;
    text-align: left;
    border-left: 1px solid rgba(255, 255, 255, 0.1);
    padding-left: 20px;
}

h3 {
    font-size: 0.7em;
    letter-spacing: 4px;
    opacity: 0.4;
    margin-bottom: 20px;
    text-transform: uppercase;
}

/* Leaderboard List */
#scoreList {
    list-style: none;
    padding: 0;
    font-family: 'Courier New', monospace;
}

    #scoreList li {
        margin-bottom: 10px;
        font-size: 1.1em;
    }

.score-name {
    opacity: 0.5;
    margin-right: 10px;
}

.score-num {
    color: #ffd700;
    font-weight: bold;
}

/* Mode tabs. These pick the game mode as well as the board being shown - one
   control, because a board for a mode you are not playing is a curiosity. The
   run restarts on a switch, which is why they sit out here and not near the
   canvas where a stray click could reach them mid-jump. */
#boardTabs {
    display: flex;
    justify-content: flex-end;
    gap: 4px;
    margin-bottom: 16px;
}

.board-tab {
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.12);
    color: rgba(255, 255, 255, 0.4);
    font-family: 'Courier New', monospace;
    font-size: 0.6em;
    letter-spacing: 2px;
    padding: 4px 9px;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}

    /* Hovering the tab you are not on: brighter, with a faint wash so the
       target reads as a surface rather than as floating text. Explicitly not
       gold - gold is what says "this is the mode you are playing". */
    .board-tab:not(.active):hover:not(:disabled) {
        color: rgba(255, 255, 255, 0.9);
        border-color: rgba(255, 255, 255, 0.35);
        background: rgba(255, 255, 255, 0.07);
    }

    .board-tab.active {
        color: #ffd700;
        border-color: rgba(255, 215, 0, 0.5);
        background: rgba(255, 215, 0, 0.06);
    }

    /* Hovering the tab you are already on has to stay gold: taking the colour
       away would say the mode had changed when clicking it does nothing. */
    .board-tab.active:hover:not(:disabled) {
        border-color: rgba(255, 215, 0, 0.75);
        background: rgba(255, 215, 0, 0.13);
    }

    .board-tab:disabled {
        opacity: 0.25;
        cursor: default;
    }

/* Loading / offline / empty. The board is the one panel that can now fail, and
   an empty list with no explanation reads as "nobody has ever scored". */
#boardNote {
    font-size: 0.6em;
    letter-spacing: 1px;
    opacity: 0.35;
    margin: 0;
    min-height: 1em;
    font-family: 'Courier New', monospace;
}

    #boardNote.offline {
        color: #ff6b6b;
        opacity: 0.55;
    }

#yesterdayLine {
    font-size: 0.6em;
    letter-spacing: 1px;
    opacity: 0.4;
    margin-top: 18px;
    padding-top: 12px;
    border-top: 1px solid rgba(255, 255, 255, 0.07);
    font-family: 'Courier New', monospace;
    min-height: 1em;
}

    #yesterdayLine .yd-name {
        color: #ffd700;
        opacity: 0.75;
    }

    #yesterdayLine:empty {
        border-top: none;
        padding-top: 0;
        margin-top: 0;
    }

/* Personal best, under the nickname. Local, per mode, and the one number on
   screen that is nobody else's business. */
#personalBest {
    margin-top: 22px;
    font-family: 'Courier New', monospace;
    font-size: 0.85em;
}

.pb-label {
    font-size: 0.7em;
    letter-spacing: 3px;
    opacity: 0.35;
    margin-right: 8px;
}

#pbValue {
    color: #ffd700;
    font-weight: bold;
}

/* Identity Inputs */
#nickname {
    background: transparent;
    border: none;
    border-bottom: 1px solid rgba(255, 255, 255, 0.3);
    color: #ffd700;
    padding: 8px 0;
    font-family: 'Courier New', monospace;
    font-weight: bold;
    outline: none;
    width: 100%;
    text-transform: uppercase;
}

    #nickname:focus {
        border-bottom-color: #ffd700;
        background: rgba(255, 215, 0, 0.05);
        box-shadow: 0 5px 15px rgba(255, 215, 0, 0.05);
    }

.hint {
    font-size: 0.6em;
    opacity: 0.3;
    margin-top: 10px;
    line-height: 1.4;
}

/* --- 4. MAIN GAME ELEMENTS --- */
.game-container {
    position: relative;
    text-align: center;
}

.canvas-wrap {
    position: relative;
}

canvas {
    border: 4px solid white;
    background-color: black;
    box-shadow: 0 0 30px rgba(255, 255, 255, 0.05);
}

/* --- 5. OVERLAYS & BUTTONS --- */
#gameOver {
    display: none;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: rgba(0, 0, 0, 0.95);
    padding: 40px;
    border: 2px solid white;
    z-index: 10;
    width: 280px;
    text-align: center;
}

/*
 * Scoped to the restart button on purpose. This was a bare `button` rule back
 * when that was the only button on the page, and it then leaked into the mode
 * tabs: `button:hover` painted them #ddd behind their own white text, which is
 * a hover state you cannot read. A rule this opinionated is not a base style.
 */
#restartBtn {
    background: white;
    color: black;
    border: none;
    padding: 15px;
    font-weight: bold;
    cursor: pointer;
    text-transform: uppercase;
    width: 100%;
    margin-top: 20px;
    transition: background 0.2s;
}

    #restartBtn:hover {
        background: #ddd;
    }

.input-group {
    display: flex;
    flex-direction: column;
    margin-top: 20px;
}

/* --- 6. SHORT VIEWPORT --- */
/*
 * `body` is a centred flexbox with `overflow: hidden`, which means a viewport
 * shorter than the content does not scroll - it crops it, symmetrically, so
 * the top of the header and the bottom of the arena both disappear. The header
 * plus the game column measures 773px, so the breakpoint sits just above it:
 * anything shorter scrolls, rather than being squeezed into a few pixels of
 * slack and then cropped by the next thing that grows.
 *
 * So below that, scroll instead. This is only a fallback for a short window;
 * the arcade layout above it is untouched, and `overflow: hidden` is not what
 * stops the arrow keys scrolling the page anyway - input.js calls
 * preventDefault on every bound key.
 */
@media (max-height: 790px) {
    body {
        height: auto;
        min-height: 100vh;
        overflow-y: auto;
        padding-bottom: 12px;
    }

    /* Stop centring in the leftover space - there is none. */
    .center-wrapper {
        margin-block: 20px;
    }
}

/* --- 7. NARROW LAYOUT --- */
/*
 * The three-panel layout hangs the side panels off the arena at ±115%, which
 * needs roughly 1100px. Below that they used to be simply off-screen - and one
 * of them holds the nickname field, so a player embedding the game in a column
 * narrower than that could see the board but never put their name on it.
 *
 * So below the breakpoint the panels come back into normal flow, stacked under
 * the arena. The arena itself is left alone at its fixed 500x500 - the code
 * assumes BOX_SIZE and the canvas are the same number - so a viewport under
 * 500px scrolls rather than shrinking it. The game is keyboard-only and
 * unplayable at that size regardless.
 */
@media (max-width: 1100px) {
    body {
        height: auto;
        min-height: 100vh;
        overflow-y: auto;
        padding-bottom: 40px;
    }

    .site-header {
        padding-inline: 12px;
    }

    .center-wrapper {
        width: 100%;
        max-width: 500px;
        margin-block: 24px;
    }

    #leaderboard,
    #identity-panel {
        position: static;
        width: 100%;
        margin-top: 32px;
        padding: 24px 0 0;
        border: none;
        border-top: 1px solid rgba(255, 255, 255, 0.1);
        text-align: left;
    }

    /* The tabs are right-aligned against the panel edge on desktop; here the
       panel is the full width, so they read better beside their own heading. */
    #boardTabs {
        justify-content: flex-start;
    }

    #scoreList li {
        display: flex;
        justify-content: space-between;
    }

    .score-name {
        margin-right: 0;
    }

    /* Identity first: naming yourself comes before reading other people's
       scores, and on a phone the board is a long way down otherwise. */
    #identity-panel {
        order: 1;
    }

    #leaderboard {
        order: 2;
    }

    .game-container {
        order: 0;
        width: 100%;
    }
}
