/* ══════════════════════════════════════════════════════════════════════════
   PIXEL WARS  (/pixel-wars, /pixel-wars/archive, /admin/pixel-wars)

   Everything is scoped under .pw, and every surface colour comes from the
   Bulma scheme variables so the page follows html.theme-light /
   html.theme-dark like the rest of the site. The two things that do NOT
   follow the theme are deliberate: the board's void (#0f1117) and the
   controls floating on top of it. The board is a lit object in a room, not a
   panel — inverting it in the light theme would turn a 64×64 painting into a
   different picture.

   This stylesheet is loaded BEFORE common.css on every one of these pages, so
   nothing in here may rely on winning a specificity tie with it.
   ═══════════════════════════════════════════════════════════════════════ */
.pw {
    --pw-mono: 'Geist Mono', ui-monospace, 'SFMono-Regular', monospace;

    /* Stated as bare HSL triples so the alpha variants below stay readable —
       same trick as evolutionCost.css. Dark theme values; the light overrides
       are at the foot of this file. */
    --pw-accent: 256 86% 70%;
    --pw-good: 152 56% 48%;
    --pw-warn: 40 92% 60%;
    --pw-bad: 356 74% 64%;
    /* --pw-bad is tuned to read as an alarm ON the dark page — as a solid
       block under white button text it only reaches 3:1, so the one filled
       red control has its own, darker value. */
    --pw-danger: 356 66% 48%;

    /* PixelPalette index 0 — an untouched cell and the "erase" colour are the
       same thing. Kept in sync by hand; the frame below is what stops it
       bleeding into a dark page. */
    --pw-void: #0f1117;
    --pw-frame: rgb(255 255 255 / 0.12);
    --pw-glass: rgb(11 13 18 / 0.76);
    --pw-glass-line: rgb(255 255 255 / 0.15);
    --pw-glass-ink: rgb(233 236 244 / 0.82);

    --pw-surface: var(--bulma-scheme-main-bis);
    --pw-sunken: var(--bulma-scheme-main);
    --pw-line: var(--bulma-border-weak);
    --pw-ink: var(--bulma-text-strong);
    --pw-ink-soft: var(--bulma-text);
    --pw-ink-weak: var(--bulma-text-weak);

    max-width: 1220px;
    margin: 0 auto;
    padding: clamp(18px, 3vw, 34px) clamp(12px, 2.5vw, 24px) 64px;
    color: var(--pw-ink-soft);
}

/* ── Head ──────────────────────────────────────────────────────────────── */
.pw-head {
    max-width: 74ch;
    margin-bottom: clamp(16px, 2.5vw, 26px);
}

.pw-eyebrow {
    display: flex;
    align-items: center;
    margin: 0 0 6px;
    font-family: var(--pw-mono);
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: hsl(var(--pw-accent));
}

/* Four pixels in a 2×2 checker, drawn out of one 4px box and three shadows.
   The two dim ones read as "half painted", which is the whole game. */
.pw-mark {
    display: inline-block;
    flex: 0 0 auto;
    width: 4px;
    height: 4px;
    margin-inline-end: 12px;
    background: currentColor;
    box-shadow: 5px 5px 0 currentColor,
                5px 0 0 color-mix(in srgb, currentColor 32%, transparent),
                0 5px 0 color-mix(in srgb, currentColor 32%, transparent);
}

.pw-title {
    margin: 0;
    font-size: clamp(1.55rem, 4vw, 2.2rem);
    font-weight: 800;
    letter-spacing: -0.025em;
    line-height: 1.08;
    color: var(--pw-ink);
}

/* The same chip the admin badges wear, in the accent rather than a warning
   hue: beta is a promise about polish, not an alarm. */
.pw-beta {
    display: inline-block;
    margin-inline-start: 6px;
    padding: 2px 8px;
    border: 1px solid color-mix(in srgb, hsl(var(--pw-accent)) 55%, transparent);
    border-radius: 99px;
    background: hsl(var(--pw-accent) / 0.1);
    font-family: var(--pw-mono);
    font-size: 0.58rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    vertical-align: 0.45em;
    color: hsl(var(--pw-accent));
}

.pw-sub {
    margin: 0;
    font-size: 0.93rem;
    line-height: 1.55;
    color: var(--pw-ink-weak);
}

.pw-sub strong {
    color: var(--pw-ink);
    font-weight: 700;
}

/* The head is a stack of bare <p>s in three different orders across the four
   templates, so the gap is stated once here rather than per paragraph. */
.pw-head > p:not(.pw-eyebrow) {
    margin-top: 12px;
}

.pw-link {
    color: hsl(var(--pw-accent));
    font-size: 0.86rem;
    font-weight: 600;
    text-decoration: none;
}

.pw-link:hover {
    text-decoration: underline;
}

.pw-dot {
    margin: 0 7px;
    color: var(--pw-ink-weak);
}

.pw-hint {
    margin: 10px 0 0;
    max-width: 62ch;
    font-size: 0.76rem;
    line-height: 1.5;
    color: var(--pw-ink-weak);
}

.pw-hint em {
    font-style: italic;
    color: var(--pw-ink-soft);
}

.pw-empty {
    margin: 24px 0;
    padding: 26px 20px;
    border: 1px dashed var(--pw-line);
    border-radius: 12px;
    text-align: center;
    font-size: 0.9rem;
    color: var(--pw-ink-weak);
}

.pw-empty p {
    margin: 0;
}

.pw-notice {
    margin: 0 0 16px;
    padding: 9px 13px;
    border: 1px solid color-mix(in srgb, hsl(var(--pw-good)) 40%, transparent);
    border-radius: 9px;
    background: color-mix(in srgb, hsl(var(--pw-good)) 10%, transparent);
    font-family: var(--pw-mono);
    font-size: 0.74rem;
    color: var(--pw-ink);
}

.pw-notice.is-error {
    border-color: color-mix(in srgb, hsl(var(--pw-bad)) 45%, transparent);
    background: color-mix(in srgb, hsl(var(--pw-bad)) 10%, transparent);
}

/* ── Freeze countdown ──────────────────────────────────────────────────────
   Reads as a clock rather than a sentence: the label is a caption and the
   value carries the size. Tabular figures because the seconds tick in place
   and a proportional font makes the whole pill twitch once a second. */
.pw-clock {
    display: inline-flex;
    align-items: baseline;
    gap: 10px;
    margin-top: 16px;
    padding: 7px 14px;
    border: 1px solid var(--pw-line);
    border-radius: 999px;
    background: var(--pw-surface);
}

.pw-clock__label {
    font-family: var(--pw-mono);
    font-size: 0.58rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--pw-ink-weak);
}

.pw-clock__value {
    font-family: var(--pw-mono);
    font-variant-numeric: tabular-nums;
    font-size: 1.05rem;
    font-weight: 600;
    line-height: 1.2;
    color: var(--pw-ink);
}

/* Nothing is counting down any more, so the whole thing steps back rather
   than sitting there looking like a live readout that stopped. */
.pw-clock.is-frozen {
    border-style: dashed;
    background: none;
}

.pw-clock.is-frozen .pw-clock__value {
    font-size: 0.82rem;
    font-weight: 500;
    color: var(--pw-ink-weak);
}

/* ── Stage: board + rail ───────────────────────────────────────────────────
   The board is the page. The rail is given a fixed, modest column so it
   cannot grow into a second column of equal weight, and it stacks under the
   board on a phone. */
.pw-stage {
    display: grid;
    grid-template-columns: minmax(0, 1fr) clamp(258px, 25vw, 330px);
    align-items: start;
    gap: clamp(12px, 1.6vw, 20px);
}

.pw-side {
    display: grid;
    gap: 12px;
    min-width: 0;
}

/* The board and its control bar are one column of the stage. Without this
   wrapper the bar would land in the rail's grid column. */
.pw-canvasarea {
    display: grid;
    gap: 8px;
    min-width: 0;
}

/* Controls left, connection state right — the two things that are about the
   board rather than on it. */
.pw-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px;
}

/* ── Board ─────────────────────────────────────────────────────────────────
   A definite box: the canvas is sized off this element, so without one there
   is nothing for the script to measure on first paint. aspect-ratio keeps it
   square while there is room, and the max-height stops a wide desktop from
   pushing the whole rail below the fold — the leftover strip is void colour,
   which is the same thing the board is made of.

   The cap must stay LOOSER than the one the scripts fit into (they stop at
   the viewport less 220px); a box shorter than the canvas would crop the
   bottom row of pixels rather than letterbox it. */
.pw-board {
    position: relative;
    width: 100%;
    aspect-ratio: 1;
    max-height: calc(100vh - 200px);
    overflow: hidden;
    border: 1px solid var(--pw-frame);
    border-radius: 12px;
    background: var(--pw-void);
    cursor: crosshair;
    /* The script owns dragging and pinching; letting the browser scroll the
       page from a drag on the board makes panning impossible on a phone. */
    touch-action: none;
    user-select: none;
}

.pw-board--replay {
    cursor: default;
}

/* Crisp squares, not the browser's idea of a nice smooth 64×64 photograph.
   The two fallbacks are for older Firefox and the pre-standard keyword. */
#pwCanvas,
#pwReplayCanvas,
.pw-board img {
    position: absolute;
    inset: 0;
    /* Centres whatever pixel size the script hands the element; before the
       script runs, the 64×64 canvas sits in the middle instead of the corner. */
    margin: auto;
    display: block;
    image-rendering: -moz-crisp-edges;
    image-rendering: crisp-edges;
    image-rendering: pixelated;
}

.pw-board img {
    max-width: 100%;
    max-height: 100%;
}

/* ── Board furniture ───────────────────────────────────────────────────────
   The HUD, the tooltip and the live pill all float on the void, so they keep
   one fixed dark palette in both themes: theming them would mean tinting
   controls that are never on a page-coloured ground. */
.pw-hud {
    display: flex;
    align-items: center;
    gap: 5px;
}

.pw-hud__btn {
    width: 30px;
    height: 30px;
    display: grid;
    place-items: center;
    padding: 0;
    border: 1px solid var(--pw-line);
    border-radius: 7px;
    background: var(--pw-surface);
    color: var(--pw-ink);
    font-family: inherit;
    font-size: 0.78rem;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}

.pw-hud__btn:hover {
    border-color: hsl(var(--pw-accent));
    color: hsl(var(--pw-accent));
}

.pw-hud__btn:focus-visible {
    outline: 2px solid hsl(var(--pw-accent));
    outline-offset: 1px;
}

/* A toggle, so it has to look pressed rather than merely hovered — the other
   three buttons in this row do a thing and return to rest. */
.pw-hud__btn.is-on {
    border-color: hsl(var(--pw-accent));
    background: color-mix(in srgb, hsl(var(--pw-accent)) 16%, var(--pw-surface));
    color: hsl(var(--pw-accent));
}

/* The icon is the thing it controls: a 2x2 grid drawn in the button's own
   colour, so it follows the pressed and hover states for free. */
.pw-hud__btn--grid span {
    width: 13px;
    height: 13px;
    background:
        linear-gradient(currentColor, currentColor) 0 50% / 100% 1px no-repeat,
        linear-gradient(currentColor, currentColor) 50% 0 / 1px 100% no-repeat;
    box-shadow: inset 0 0 0 1px currentColor;
    opacity: 0.85;
}

.pw-hud__coords {
    padding: 0 8px;
    height: 30px;
    display: inline-flex;
    align-items: center;
    border: 1px solid var(--pw-line);
    border-radius: 7px;
    background: var(--pw-sunken);
    color: var(--pw-ink-weak);
    font-family: var(--pw-mono);
    font-variant-numeric: tabular-nums;
    font-size: 0.68rem;
    /* The readout changes on every mouse move; a fixed width keeps the pill
       from resizing under the cursor. */
    min-width: 7ch;
    justify-content: center;
    white-space: nowrap;
}

/* Who painted this cell, and when. The script fills it with three spans in a
   row, so it is laid out as one — a hover readout that reflows into two lines
   under the cursor is unreadable. */
.pw-tip {
    position: absolute;
    z-index: 4;
    display: flex;
    align-items: baseline;
    gap: 7px;
    max-width: 240px;
    padding: 5px 8px;
    border: 1px solid var(--pw-glass-line);
    border-radius: 7px;
    background: rgb(8 10 14 / 0.92);
    color: #e9ecf4;
    font-family: var(--pw-mono);
    font-size: 0.66rem;
    line-height: 1.35;
    white-space: nowrap;
    pointer-events: none;
}

.pw-tip[hidden] {
    display: none;
}

.pw-tip__who {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    color: #fff;
}

.pw-tip__rank {
    color: color-mix(in srgb, hsl(var(--pw-accent)) 60%, #fff);
}

.pw-tip__when {
    margin-inline-start: auto;
    color: rgb(233 236 244 / 0.6);
}

.pw-live {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border: 1px solid var(--pw-line);
    border-radius: 999px;
    background: var(--pw-surface);
    color: var(--pw-ink-weak);
    font-family: var(--pw-mono);
    font-size: 0.58rem;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    pointer-events: none;
}

.pw-live__text {
    line-height: 1;
}

.pw-live__dot {
    width: 7px;
    height: 7px;
    flex: 0 0 auto;
    border-radius: 50%;
    background: hsl(var(--pw-warn));
}

.pw-live[data-state="live"] .pw-live__dot {
    background: hsl(var(--pw-good));
    animation: pw-breathe 2.4s ease-in-out infinite;
}

.pw-live[data-state="live"] {
    color: var(--pw-ink);
}

.pw-live[data-state="offline"] {
    color: hsl(var(--pw-bad));
    border-color: color-mix(in srgb, hsl(var(--pw-bad)) 55%, transparent);
}

.pw-live[data-state="offline"] .pw-live__dot {
    background: hsl(var(--pw-bad));
}

@keyframes pw-breathe {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.3; }
}

/* ── Cards ─────────────────────────────────────────────────────────────── */
.pw-card {
    padding: 13px 15px 15px;
    border: 1px solid var(--pw-line);
    border-radius: 12px;
    background: var(--pw-surface);
}

.pw-card__title {
    margin: 0 0 9px;
    font-size: 0.82rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    line-height: 1.3;
    color: var(--pw-ink);
}

.pw-card__body {
    margin: 0 0 12px;
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--pw-ink-weak);
}

/* The one card asking for something. Tinted rather than outlined loudly — it
   is an invitation to join in, not a paywall. */
.pw-card--cta {
    border-color: color-mix(in srgb, hsl(var(--pw-accent)) 40%, transparent);
    background: linear-gradient(180deg,
                hsl(var(--pw-accent) / 0.11), transparent 70%),
                var(--pw-surface);
}

/* ── Buttons ───────────────────────────────────────────────────────────── */
.pw-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    padding: 8px 14px;
    border: 1px solid transparent;
    border-radius: 9px;
    background: hsl(var(--pw-accent));
    color: #fff;
    font-family: inherit;
    font-size: 0.82rem;
    font-weight: 700;
    line-height: 1.2;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: filter 0.12s, background 0.12s, border-color 0.12s, color 0.12s;
}

.pw-btn:hover {
    filter: brightness(1.08);
}

.pw-btn:focus-visible {
    outline: 2px solid hsl(var(--pw-accent));
    outline-offset: 2px;
}

.pw-btn--ghost {
    background: transparent;
    border-color: var(--bulma-border);
    color: var(--pw-ink);
}

.pw-btn--ghost:hover {
    filter: none;
    border-color: hsl(var(--pw-accent));
    color: hsl(var(--pw-accent));
}

/* Filled red, and the only filled red on any of these pages: every button
   wearing it undoes work that other people did. */
.pw-btn--danger {
    background: hsl(var(--pw-danger));
    color: #fff;
}

.pw-btn--danger:hover {
    filter: none;
    background: hsl(var(--pw-danger) / 0.88);
    box-shadow: 0 0 0 3px hsl(var(--pw-danger) / 0.25);
}

.pw-btn--icon {
    width: 34px;
    height: 34px;
    flex: 0 0 auto;
    padding: 0;
    font-size: 0.78rem;
}

/* ── Charges ───────────────────────────────────────────────────────────────
   Three lines, one glance: balance beside the title, the meter as the body,
   and everything that ticks in a single quiet foot. The card sits between
   the head and the board on a phone, so its height is board real estate. */
.pw-charges__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

.pw-charges__head .pw-card__title {
    margin-bottom: 0;
}

.pw-charges__count {
    margin: 0;
    font-family: var(--pw-mono);
    font-variant-numeric: tabular-nums;
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1;
    color: var(--pw-ink);
}

.pw-charges__cap {
    margin-inline-start: 4px;
    font-size: 0.76rem;
    font-weight: 500;
    color: var(--pw-ink-weak);
}

.pw-meter {
    height: 9px;
    margin-top: 8px;
    border-radius: 999px;
    background: var(--pw-sunken);
    border: 1px solid var(--pw-line);
    overflow: hidden;
}

/* Width is set by the script on every tick; the transition is what turns a
   once-a-second recalculation into something that looks like it is filling.
   The notches keep it reading as whole charges rather than a fuel gauge. */
.pw-meter__fill {
    width: 0;
    height: 100%;
    background-color: hsl(var(--pw-accent));
    background-image: repeating-linear-gradient(90deg,
                      transparent 0 7px, rgb(0 0 0 / 0.16) 7px 8px);
    transition: width 0.55s cubic-bezier(0.22, 0.7, 0.25, 1);
}

/* Rate, refill and season total on one line. It wraps on the narrowest rail
   rather than overflowing, and the nbsp the script parks in pwNext keeps
   the line's height before there is anything to say. */
.pw-charges__foot {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 2px 10px;
    margin: 7px 0 0;
    font-size: 0.68rem;
    font-variant-numeric: tabular-nums;
    color: var(--pw-ink-weak);
}

.pw-charges__rate {
    font-family: var(--pw-mono);
    font-weight: 600;
    color: hsl(var(--pw-accent));
}

.pw-charges__next {
    font-family: var(--pw-mono);
}

.pw-charges__spent {
    margin-inline-start: auto;
}

/* ── Palette ───────────────────────────────────────────────────────────────
   Sixteen chunky squares. auto-fit rather than a fixed count so the tiles
   stay above a thumb-sized 38px whatever the rail is doing. */
.pw-palette {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(38px, 1fr));
    gap: 6px;
}

.pw-swatch {
    aspect-ratio: 1;
    min-height: 34px;
    padding: 0;
    border: 0;
    border-radius: 4px;
    background: var(--swatch, #888);
    cursor: pointer;
    /* Shadows rather than a border, which would resize the tile the moment the
       selected ring appears. Two of them because the palette's extremes are
       both edge cases: the inset keeps white from vanishing into a light card,
       the outer line keeps the void colour (index 0, a real colour here) from
       reading as a hole in a dark one. */
    box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.22),
                0 0 0 1px var(--pw-line);
    transition: transform 0.1s;
}

.pw-swatch:hover {
    transform: translateY(-1px);
}

/* The ring sits OUTSIDE the tile with a gap of card colour between, so it
   reads on a near-black swatch and on a white one alike — a border or a
   contrasting inset stroke can always collide with the colour it frames. */
.pw-swatch.is-selected {
    box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.22),
                0 0 0 2px var(--pw-surface),
                0 0 0 4px hsl(var(--pw-accent));
}

.pw-swatch:focus-visible {
    outline: 2px solid hsl(var(--pw-accent));
    outline-offset: 3px;
}

/* ── Where your pixels come from ───────────────────────────────────────────
   A timeline, not a table. The banked lines ARE history — the rank held
   when each era closed — so the card draws them in the order they happened,
   on a rail that ends at the one line still moving. One line per source:
   the name, the rank that explains it, what it pays.                      */
.pw-rate__track {
    position: relative;
    margin: 2px 0 0;
    padding: 0 0 0 19px;
    list-style: none;
}

/* The rail. Its ends sit inside the first and last node, so the season
   reads as a segment with two ends rather than a strip passing through.
   Drawn from ink rather than the border colour — a 1px line between 9px
   nodes has to punch above the card's hairlines or the timeline reads as
   a plain list with coloured bullets. */
.pw-rate__track::before {
    content: "";
    position: absolute;
    left: 4px;
    top: 14px;
    bottom: 14px;
    width: 1px;
    background: color-mix(in srgb, var(--pw-ink-weak) 40%, transparent);
}

.pw-rate__stop {
    position: relative;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: baseline;
    gap: 8px;
    padding: 5px 0;
    font-size: 0.78rem;
}

/* The node: square, hard-edged and the era's own colour — on a pixel canvas
   a marker standing for a pixel should look like one, not like a bullet.
   Opaque, so the rail passes behind it, not through it. */
.pw-rate__stop::before {
    content: "";
    position: absolute;
    left: -19px;
    top: 10px;
    width: 9px;
    height: 9px;
    border-radius: 1px;
    background: var(--era, var(--pw-ink-soft));
}

.pw-rate__era {
    color: var(--pw-ink);
    font-weight: 600;
}

/* The whole reason in three words: the rank held, and when it counted. */
.pw-rate__why {
    font-size: 0.7rem;
    color: var(--pw-ink-weak);
}

.pw-rate__why b {
    font-family: var(--pw-mono);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: var(--pw-ink-soft);
}

.pw-rate__val {
    text-align: right;
    font-family: var(--pw-mono);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    font-size: 0.76rem;
    color: var(--pw-ink);
}

/* Era hues, matched to the palette the board itself paints with, so a Rare
   node on this card is the same green as a Rare block anywhere else. */
.pw-rate__stop[data-era="RARE"]   { --era: #7bd66a; }
.pw-rate__stop[data-era="EPIC"]   { --era: #3d9bff; }
.pw-rate__stop[data-era="MYSTIC"] { --era: #ff4d4d; }
.pw-rate__stop[data-era="FINAL"]  { --era: #ffd23f; }
.pw-rate__stop[data-era="NOW"]    { --era: hsl(var(--pw-accent)); }
.pw-rate__stop[data-era="BASE"]   { --era: var(--pw-ink-weak); }

/* The one line that is still moving, and looks it. */
.pw-rate__stop.is-live .pw-rate__era,
.pw-rate__stop.is-live .pw-rate__val {
    color: hsl(var(--pw-accent));
}

.pw-rate__stop.is-live::before {
    animation: pw-stop-pulse 1.6s ease-in-out infinite;
}

@keyframes pw-stop-pulse {
    0%, 100% { box-shadow: 0 0 0 0 hsl(var(--pw-accent) / 0.45); }
    50% { box-shadow: 0 0 0 4px transparent; }
}

/* A line that pays nothing keeps its place — and, live, its pulse; the rank
   still moves — but a zero must not be the brightest number on the card. */
.pw-rate__stop.is-nil .pw-rate__val {
    color: var(--pw-ink-weak);
    font-weight: 400;
}

/* ── Total ─────────────────────────────────────────────────────────────── */

/* The one figure to leave with, and visibly the sum of the rows above it. */
.pw-rate__total {
    display: flex;
    align-items: baseline;
    gap: 6px;
    margin: 12px 0 0;
    padding-top: 10px;
    border-top: 1px solid var(--pw-line);
    font-size: 0.78rem;
    color: var(--pw-ink-weak);
}

.pw-rate__total strong {
    font-size: 1.5rem;
    line-height: 1;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--pw-ink);
}

/* ── Rail foot ─────────────────────────────────────────────────────────── */
.pw-sidelinks {
    margin: 0;
    padding: 0 2px;
}

/* ── Toast ─────────────────────────────────────────────────────────────────
   Placing a pixel fails for reasons the board cannot show (no charges, frozen,
   rate limited), so the message lands over the board rather than in the rail
   where a hand on the canvas would never look. */
.pw-toast {
    position: fixed;
    z-index: 80;
    left: 50%;
    bottom: 26px;
    transform: translateX(-50%);
    display: block;
    max-width: min(92vw, 460px);
    padding: 9px 16px;
    border: 1px solid var(--pw-glass-line);
    border-radius: 10px;
    background: rgb(11 13 18 / 0.94);
    box-shadow: 0 14px 34px -14px rgb(0 0 0 / 0.7);
    color: #f2f4fa;
    font-size: 0.82rem;
    line-height: 1.4;
    text-align: center;
    animation: pw-toast-in 0.2s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}

.pw-toast[hidden] {
    display: none;
}

@keyframes pw-toast-in {
    from { opacity: 0; transform: translate(-50%, 12px); }
    to { opacity: 1; transform: translate(-50%, 0); }
}

/* ══════════════════════════════════════════════════════════════════════════
   ARCHIVE INDEX
   ═══════════════════════════════════════════════════════════════════════ */
.pw-archive-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
    gap: 14px;
}

.pw-archive-card {
    display: block;
    overflow: hidden;
    border: 1px solid var(--pw-line);
    border-radius: 12px;
    background: var(--pw-surface);
    color: inherit;
    text-decoration: none;
    transition: border-color 0.13s, transform 0.13s;
}

.pw-archive-card:hover {
    border-color: hsl(var(--pw-accent));
    transform: translateY(-2px);
}

.pw-archive-card__img {
    display: block;
    width: 100%;
    aspect-ratio: 1;
    object-fit: contain;
    background: var(--pw-void);
    image-rendering: -moz-crisp-edges;
    image-rendering: crisp-edges;
    image-rendering: pixelated;
}

.pw-archive-card__meta {
    display: grid;
    gap: 3px;
    padding: 10px 12px 11px;
}

.pw-archive-card__title {
    font-size: 0.88rem;
    font-weight: 700;
    color: var(--pw-ink);
}

.pw-archive-card__stat {
    font-family: var(--pw-mono);
    font-variant-numeric: tabular-nums;
    font-size: 0.66rem;
    color: var(--pw-ink-weak);
}

/* ══════════════════════════════════════════════════════════════════════════
   ONE ARCHIVED CANVAS
   ═══════════════════════════════════════════════════════════════════════ */
/* A grid rather than a row, because the era rail underneath has to line up with the scrubber it
   annotates — sharing a column guarantees that, where stacking two rows only looks like it does. */
.pw-replay__controls {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    column-gap: 9px;
}

/* Native appearance stripped on purpose: the default track is a system blue
   pill that belongs to no other control on the page. */
.pw-replay__scrub {
    flex: 1 1 auto;
    min-width: 0;
    height: 20px;
    margin: 0;
    padding: 0;
    -webkit-appearance: none;
    appearance: none;
    background: transparent;
    cursor: pointer;
}

.pw-replay__scrub::-webkit-slider-runnable-track {
    height: 6px;
    border-radius: 999px;
    background: var(--pw-sunken);
    border: 1px solid var(--pw-line);
}

.pw-replay__scrub::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 14px;
    height: 14px;
    margin-top: -5px;
    border: 0;
    border-radius: 3px;
    background: hsl(var(--pw-accent));
}

.pw-replay__scrub::-moz-range-track {
    height: 6px;
    border-radius: 999px;
    background: var(--pw-sunken);
    border: 1px solid var(--pw-line);
}

.pw-replay__scrub::-moz-range-thumb {
    width: 14px;
    height: 14px;
    border: 0;
    border-radius: 3px;
    background: hsl(var(--pw-accent));
}

.pw-replay__scrub:focus-visible {
    outline: 2px solid hsl(var(--pw-accent));
    outline-offset: 3px;
}

/* Era boundaries under the scrubber. A twelve-week season on a 300px bar is featureless without
   them — these are the only moments where everybody's rate changed, so they are the landmarks. */
.pw-replay__eras {
    grid-column: 2;             /* under the scrubber, never under the buttons flanking it */
    position: relative;
    height: 26px;
    margin: 2px 0 4px;
}

/* A canvas whose season has not crossed an era boundary yet gets no rail, not an empty gap. */
.pw-replay__eras:empty {
    display: none;
}

.pw-era-tick {
    position: absolute;
    top: 0;
    transform: translateX(-50%);
    display: grid;
    justify-items: center;
    gap: 2px;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
    /* The labels sit close together on a short season; letting one overlap its neighbour is better than
       clipping it, and the tick itself is what carries the position. */
    white-space: nowrap;
}

.pw-era-tick i {
    width: 2px;
    height: 8px;
    border-radius: 1px;
    background: var(--era, var(--pw-ink-weak));
}

.pw-era-tick span {
    font-family: var(--pw-mono);
    font-size: 0.55rem;
    letter-spacing: 0.06em;
    color: var(--pw-ink-weak);
}

.pw-era-tick:hover span,
.pw-era-tick:focus-visible span {
    color: var(--pw-ink);
}

.pw-era-tick[data-era="RARE"]   { --era: #7bd66a; }
.pw-era-tick[data-era="EPIC"]   { --era: #3d9bff; }
.pw-era-tick[data-era="MYSTIC"] { --era: #ff4d4d; }
.pw-era-tick[data-era="FINAL"]  { --era: #ffd23f; }

/* ── Name tags over the board ──────────────────────────────────────────────
   Absolutely placed on the board by the script, anchored at the cell that
   changed. They must not take pointer events: the board underneath is the
   thing being watched, and a tag intercepting a hover would be a bug you
   only notice on a live canvas. */
.pw-tag {
    position: absolute;
    z-index: 3;
    transform: translate(-50%, -140%);
    padding: 2px 6px;
    border: 1px solid var(--pw-glass-line);
    border-radius: 6px;
    background: var(--pw-glass);
    backdrop-filter: blur(3px);
    font-family: var(--pw-mono);
    font-size: 0.62rem;
    line-height: 1.5;
    white-space: nowrap;
    color: var(--pw-glass-ink);
    pointer-events: none;
    opacity: 0;
}

/* The keyframes live in the script, not here. A tag is re-shown on an element
   that already ran the animation to its end, and restarting a CSS animation on
   the same element means removing a class, forcing a reflow and adding it back
   — which silently does nothing often enough that the tags simply stop
   appearing. Element.animate() has no such ambiguity: every call is a new
   animation. See tagFrame() in pixelWarsReplay.js. */

/* ── The names switch ──────────────────────────────────────────────────── */
.pw-replay__names {
    margin: 9px 0 0;
    font-size: 0.72rem;
    color: var(--pw-ink-weak);
}

.pw-replay__names label {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    cursor: pointer;
}

.pw-replay__names input {
    accent-color: hsl(var(--pw-accent));
    cursor: pointer;
}

.pw-replay__clock {
    margin: 11px 0 0;
    font-family: var(--pw-mono);
    font-variant-numeric: tabular-nums;
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--pw-ink);
}

.pw-replay__progress {
    margin: 3px 0 0;
    font-family: var(--pw-mono);
    font-variant-numeric: tabular-nums;
    font-size: 0.66rem;
    color: var(--pw-ink-weak);
}

/* ── Stats / facts list ────────────────────────────────────────────────────
   Shared by the archive summary and the admin fact sheet: label left, value
   right, one hairline per row. */
.pw-stats__list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.pw-stats__list li {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 6px 0;
    font-size: 0.79rem;
    color: var(--pw-ink-weak);
}

.pw-stats__list li + li {
    border-top: 1px solid var(--pw-line);
}

.pw-stats__list strong {
    flex: 0 0 auto;
    text-align: right;
    font-family: var(--pw-mono);
    font-variant-numeric: tabular-nums;
    font-size: 0.79rem;
    font-weight: 600;
    color: var(--pw-ink);
}

/* Same two-shadow trick as the swatches: the blob's colour can be the void. */
/* ── Event log ─────────────────────────────────────────────────────────────
   Fourteen fixed rows the script fills in place. Tabular numerals and a fixed
   time column matter more here than anywhere else on the page: the whole list
   redraws ten times a second while the replay runs, and a column that changes
   width with its contents turns that into a shimmer. */
.pw-log__list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.pw-log__row {
    display: grid;
    grid-template-columns: auto 10px minmax(0, 1fr) auto;
    align-items: center;
    gap: 7px;
    padding: 3px 0;
    font-family: var(--pw-mono);
    font-variant-numeric: tabular-nums;
    font-size: 0.68rem;
    line-height: 1.5;
}

.pw-log__row + .pw-log__row {
    border-top: 1px solid color-mix(in srgb, var(--pw-line) 55%, transparent);
}

/* The rows are built up front and hidden until the playhead has reached them.
   `display: grid` above beats the UA stylesheet's `[hidden] { display: none }`
   on specificity, so without this an unfilled row is a blank ruled line rather
   than nothing at all. Same story for the stat rows that only exist when the
   payload carries attribution. */
.pw-log__row[hidden],
.pw-stats__list li[hidden] {
    display: none;
}

.pw-log__time {
    color: var(--pw-ink-weak);
}

.pw-log__who {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--pw-ink);
}

.pw-log__at {
    color: var(--pw-ink-weak);
}

/* ── Rank table ────────────────────────────────────────────────────────────
   The bracket ladder. Rows are generated from the live tuning, so the count is
   whatever the config says — the styling must not assume ten of them, and must
   not assume the reader's own band is present at all. */
.pw-tiers__list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.pw-tiers__head,
.pw-tiers__row {
    display: grid;
    /* Fixed numeric columns so the two figures form real columns down the card;
       the band label takes whatever is left and is the only part allowed to wrap. */
    /* Wide enough for a four-character rate: the brackets are config, and a season
       tuned to pay +100 a day must not reflow the columns to fit. */
    grid-template-columns: minmax(0, 1fr) 5.5ch 5ch;
    align-items: baseline;
    gap: 10px;
    padding: 4px 0;
    font-family: var(--pw-mono);
    font-variant-numeric: tabular-nums;
    font-size: 0.74rem;
}

.pw-tiers__head {
    padding-bottom: 5px;
    border-bottom: 1px solid var(--pw-line);
    font-size: 0.6rem;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    white-space: nowrap;    /* a two-line heading over a one-line column reads as a broken row */
    color: var(--pw-ink-weak);
}

.pw-tiers__head span:not(:first-child),
.pw-tiers__pay,
.pw-tiers__bank {
    text-align: right;
}

.pw-tiers__row + .pw-tiers__row {
    border-top: 1px solid color-mix(in srgb, var(--pw-line) 55%, transparent);
}

.pw-tiers__band {
    color: var(--pw-ink-soft);
}

.pw-tiers__pay {
    font-weight: 600;
    color: var(--pw-ink);
}

.pw-tiers__bank {
    color: var(--pw-ink-weak);
}

/* The reader's own band. One row is highlighted at most, so this can afford to
   be loud — it is the answer to the question that brought them to the table. */
.pw-tiers__row[data-mine="true"] {
    margin-inline: -8px;
    padding-inline: 8px;
    border-radius: 6px;
    background: color-mix(in srgb, hsl(var(--pw-accent)) 13%, transparent);
    box-shadow: inset 2px 0 0 hsl(var(--pw-accent));
}

.pw-tiers__row[data-mine="true"] .pw-tiers__band,
.pw-tiers__row[data-mine="true"] .pw-tiers__bank {
    color: var(--pw-ink);
}

/* Rides inside the band cell rather than becoming a fourth column that only one
   row has — that would pull the marked row's figures out of line with the rest,
   which is the one row where alignment matters most. */
.pw-tiers__you {
    margin-inline-start: 6px;
    padding: 1px 4px;
    border-radius: 3px;
    vertical-align: 1px;
    background: hsl(var(--pw-accent));
    font-size: 0.5rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #fff;
}

.pw-tiers__note {
    margin-top: 9px;
}

.pw-chip {
    display: inline-block;
    width: 10px;
    height: 10px;
    margin-inline-end: 5px;
    vertical-align: -1px;
    border-radius: 2px;
    background: var(--swatch, #888);
    box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.25),
                0 0 0 1px var(--pw-line);
}

/* ── Contributors ──────────────────────────────────────────────────────── */
.pw-top__list {
    margin: 0;
    padding: 0;
    list-style: none;
    counter-reset: pw-rank;
}

/* Four columns, not three: the ::before counter below is a real grid item.
   The last two are fixed rather than auto because every row is its own grid —
   content-sized columns would land in a different place on every line. */
.pw-top__row {
    display: grid;
    grid-template-columns: 2.5ch minmax(0, 1fr) 5.5ch 5.5ch;
    align-items: baseline;
    gap: 8px;
    padding: 5px 0;
    font-size: 0.79rem;
}

.pw-top__row + .pw-top__row {
    border-top: 1px solid var(--pw-line);
}

/* The list is an <ol> and the order is the point, so the position is drawn
   back in after the browser's own numbering was reset away. */
.pw-top__row::before {
    counter-increment: pw-rank;
    content: counter(pw-rank);
    font-family: var(--pw-mono);
    font-variant-numeric: tabular-nums;
    font-size: 0.66rem;
    text-align: right;
    color: var(--pw-ink-weak);
}

.pw-top__who {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--pw-ink);
    font-weight: 600;
    text-decoration: none;
}

a.pw-top__who:hover {
    color: hsl(var(--pw-accent));
}

/* Admin sees full addresses, not display names — they get pasted straight
   into the reverse-a-painter box, so they must be readable character by
   character and must never be truncated. */
.pw-top__who--mono {
    font-family: var(--pw-mono);
    font-size: 0.7rem;
    font-weight: 400;
    white-space: normal;
    overflow-wrap: anywhere;
}

.pw-top__placed,
.pw-top__held {
    text-align: right;
    font-family: var(--pw-mono);
    font-variant-numeric: tabular-nums;
    font-size: 0.74rem;
}

.pw-top__placed {
    color: var(--pw-ink-weak);
}

.pw-top__held {
    color: var(--pw-ink);
    font-weight: 600;
}

/* ══════════════════════════════════════════════════════════════════════════
   ADMIN

   Plain and dense. Nothing here is being sold to anyone; the cards are narrow
   so the destructive controls stay inside one glance.
   ═══════════════════════════════════════════════════════════════════════ */
.pw-admin {
    max-width: 760px;
}

.pw-admin .pw-card {
    margin-bottom: 12px;
}

.pw-admin__facts .pw-card__title {
    font-family: var(--pw-mono);
    font-size: 0.76rem;
    font-weight: 500;
}

/* ── Usage chart ───────────────────────────────────────────────────────────
   Two single-series bars sharing the season's x-domain. The plot boxes give
   Chart.js a definite height; without one a responsive chart measures 0 and
   draws nothing. */
.pw-usage__label {
    margin: 10px 0 4px;
    font-family: var(--pw-mono);
    font-size: 0.58rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--pw-ink-weak);
}

.pw-usage__label:first-of-type {
    margin-top: 2px;
}

.pw-usage__plot {
    position: relative;
    height: 120px;
}

/* The same series as text — the no-JS and screen-reader form of the chart. */
.pw-usage__table {
    margin-top: 10px;
    font-size: 0.72rem;
    color: var(--pw-ink-weak);
}

.pw-usage__table summary {
    cursor: pointer;
}

.pw-usage__scroll {
    max-height: 240px;
    margin-top: 6px;
    overflow-y: auto;
}

.pw-usage__table table {
    width: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
}

/* tr > th, not bare th: Bulma's minireset carries th:not([align]) at 0-1-2,
   which outguns a 0-1-1 class rule regardless of load order. */
.pw-usage__table tr > th,
.pw-usage__table td {
    padding: 3px 6px;
    text-align: right;
    border-bottom: 1px solid color-mix(in srgb, var(--pw-line) 55%, transparent);
}

.pw-usage__table tr > th:first-child,
.pw-usage__table td:first-child {
    text-align: left;
    font-family: var(--pw-mono);
}

.pw-admin__tool .pw-hint {
    margin-top: 0;
    max-width: 78ch;
}

.pw-admin__form {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 8px;
    margin-top: 12px;
}

/* The address box takes whatever is left; the four coordinate boxes do not. */
.pw-admin__form .pw-input:not(.pw-input--num) {
    flex: 1 1 240px;
}

.pw-admin__row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 4px;
}

.pw-field {
    display: inline-grid;
    gap: 3px;
}

.pw-field > span {
    font-family: var(--pw-mono);
    font-size: 0.58rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--pw-ink-weak);
}

.pw-input {
    padding: 7px 10px;
    border: 1px solid var(--bulma-border);
    border-radius: 8px;
    background: var(--pw-sunken);
    color: var(--pw-ink);
    font-family: var(--pw-mono);
    font-size: 0.78rem;
    line-height: 1.3;
}

.pw-input:focus-visible {
    outline: 2px solid hsl(var(--pw-accent));
    outline-offset: 1px;
}

.pw-input--num {
    width: 6.5ch;
    font-variant-numeric: tabular-nums;
}

/* The drip breakdown's empty state. Not a hidden component: on a rank-only economy this is what most
   signed-in visitors see, so it gets a real line of type rather than an absent list. */
.pw-rate__none {
    margin: 4px 0 8px;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--pw-ink);
}

/* ── Economy editor ────────────────────────────────────────────────────────
   The one panel here that gets opened repeatedly during an event, so it is
   the one that gets room to breathe: labelled fields with their reasoning
   underneath, rather than the bare row the moderation tools use.          */
.pw-econ__badge {
    margin-left: 8px;
    padding: 2px 7px;
    border-radius: 99px;
    border: 1px solid currentColor;
    font-family: var(--pw-mono);
    font-size: 0.55rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    vertical-align: middle;
    color: hsl(var(--pw-warn));
}

.pw-econ__badge.is-quiet {
    color: var(--pw-ink-weak);
}

.pw-econ__form {
    margin-top: 14px;
}

.pw-econ__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 14px;
}

.pw-econ__cols {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 14px;
    margin-top: 16px;
}

.pw-field--wide .pw-input--num {
    /* The shared 6.5ch is sized for the wipe-a-region boxes; a base rate can be five digits. */
    width: 100%;
}

.pw-field small {
    font-size: 0.72rem;
    line-height: 1.4;
    color: var(--pw-ink-weak);
}

.pw-field code {
    padding: 1px 4px;
    border-radius: 4px;
    background: var(--pw-sunken);
    font-family: var(--pw-mono);
    font-size: 0.92em;
}

.pw-input--area {
    width: 100%;
    resize: vertical;
    /* Brackets are read as a column of numbers; proportional digits make that column ragged. */
    font-variant-numeric: tabular-nums;
}

.pw-econ__form .pw-admin__row {
    margin-top: 16px;
}

.pw-econ__reset {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 14px;
    padding-top: 14px;
    border-top: 1px solid var(--pw-line);
}

.pw-econ__reset .pw-hint {
    margin: 0;
}

/* ── Economy preview ───────────────────────────────────────────────────── */
.pw-econ-preview.is-preview {
    border-color: color-mix(in srgb, hsl(var(--pw-warn)) 55%, transparent);
}

.pw-econ-preview__scroll {
    /* The table must scroll inside its own box; the page never scrolls sideways. */
    overflow-x: auto;
    margin-top: 12px;
}

.pw-econ-table {
    width: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
    font-size: 0.82rem;
}

.pw-econ-table th,
.pw-econ-table td {
    padding: 7px 10px;
    text-align: right;
    white-space: nowrap;
    border-bottom: 1px solid var(--pw-line);
}

.pw-econ-table thead th {
    font-family: var(--pw-mono);
    font-size: 0.58rem;
    font-weight: 500;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--pw-ink-weak);
}

.pw-econ-table tbody th[scope="row"] {
    text-align: left;
    font-family: var(--pw-mono);
    font-weight: 500;
    color: var(--pw-ink);
}

.pw-econ-table tbody tr:last-child th,
.pw-econ-table tbody tr:last-child td {
    border-bottom: 0;
}

/* The two numbers anyone is actually here to read: where the drip ends up, and how deep the bank gets. */
.pw-econ-table .is-peak {
    font-weight: 700;
    color: hsl(var(--pw-good));
}

.pw-econ-table .is-bank {
    color: var(--pw-ink);
}

/* ── Dev harness (/dev/pixel-wars) ─────────────────────────────────────────
   Shares the admin furniture; only what a <select>, a checkbox row and the
   profile table need is added here.                                        */
select.pw-input {
    width: 100%;
    /* Native selects ignore the shared padding on some platforms unless the
       appearance is reset, which leaves them a different height from the text
       inputs sitting beside them in the same grid. */
    appearance: none;
    padding-right: 26px;
    background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                      linear-gradient(135deg, currentColor 50%, transparent 50%);
    background-position: right 13px top 55%, right 8px top 55%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
}

.pw-check {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 0.84rem;
    color: var(--pw-ink);
    cursor: pointer;
}

.pw-check input {
    width: 15px;
    height: 15px;
    accent-color: hsl(var(--pw-accent));
}

.pw-dev-table {
    /* Six columns ending in two buttons. Below this they squeeze until the actions clip, so let the
       container scroll instead — it already has overflow-x. */
    min-width: 760px;
}

.pw-dev-table th[scope="row"] {
    /* The label is the column a human actually reads, so it gets room before the 42-character address
       does; without a floor it wraps to four lines while the address sits on one. */
    white-space: normal;
    min-width: 170px;
}

.pw-dev-addr {
    font-family: var(--pw-mono);
    font-size: 0.62rem;
    color: var(--pw-ink-weak);
}

.pw-dev-table tr.is-current {
    /* Which profile you are currently signed in as — the one fact that decides what /pixel-wars will show
       you, and the easiest to lose track of after building four of them. */
    background: color-mix(in srgb, hsl(var(--pw-accent)) 12%, transparent);
}

.pw-dev-table tr.is-current th[scope="row"]::after {
    content: " ← you";
    font-family: var(--pw-mono);
    font-size: 0.6rem;
    letter-spacing: 0.08em;
    color: hsl(var(--pw-accent));
}

.pw-dev-actions {
    display: flex;
    gap: 6px;
    justify-content: flex-end;
}

.pw-dev-actions .pw-btn {
    padding: 4px 10px;
    font-size: 0.72rem;
}

/* ══════════════════════════════════════════════════════════════════════════
   LIGHT THEME

   Only the values that cannot come from Bulma. The accent drops in lightness
   because the dark-theme violet measures under 3:1 as text on white, and the
   board's frame gets darker and heavier — a near-black square on a white page
   needs a real edge, not the hairline that works against a dark one.
   ═══════════════════════════════════════════════════════════════════════ */
html.theme-light .pw {
    --pw-accent: 256 72% 54%;
    --pw-good: 152 60% 34%;
    --pw-warn: 36 88% 40%;
    --pw-bad: 356 66% 48%;
    --pw-danger: 356 68% 44%;
    --pw-frame: hsl(220 14% 76%);
    --pw-surface: #ffffff;
    --pw-sunken: var(--bulma-scheme-main-ter);
}

html.theme-light .pw-board {
    box-shadow: 0 1px 2px rgb(20 24 34 / 0.12), 0 10px 26px -16px rgb(20 24 34 / 0.5);
}

/* On white the halo bleeds pink well past the button; keep it tight. */
html.theme-light .pw-btn--danger:hover {
    box-shadow: 0 0 0 3px hsl(var(--pw-danger) / 0.2);
}

/* ══════════════════════════════════════════════════════════════════════════
   NARROW
   ═══════════════════════════════════════════════════════════════════════ */
@media screen and (max-width: 900px) {
    /* Board full width, rail underneath. Width is what bounds the square here,
       so the cap only matters in landscape — and there it needs a floor: the
       scripts never fit into less than 220px, and a shorter box would crop
       them. */
    .pw-stage {
        grid-template-columns: minmax(0, 1fr);
    }

    .pw-board {
        max-height: max(300px, calc(100vh - 180px));
    }

    /* Two across is enough to see all four cards without a long scroll, and
       the palette still gets a full-width row of its own below. */
    .pw-side {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        align-items: start;
    }

    .pw-palette-card,
    .pw-card--cta {
        grid-column: 1 / -1;
    }
}

@media screen and (max-width: 620px) {
    .pw-side {
        grid-template-columns: minmax(0, 1fr);
    }

    .pw-toast {
        bottom: 14px;
    }

    /* Play first. In source order the palette sits a whole screen below the
       board, so the phone re-deals: the head stays on top as written, then
       the balance, the board, the colour to paint it with — and only then
       everything that is reading rather than playing. The stage and the
       rail flatten into one column (the head stays a block, so its own
       spacing survives); scoped to :has(.pw-stage) so the between-seasons
       page — which has nothing to play — keeps its written order. */
    .pw:has(.pw-stage) {
        display: flex;
        flex-direction: column;
        gap: 12px;
    }

    .pw:has(.pw-stage) .pw-stage,
    .pw:has(.pw-stage) .pw-side {
        display: contents;
    }

    /* Every flattened child gets an explicit slot — a flex item left at
       order 0 would silently jump the queue. */
    .pw:has(.pw-stage) .pw-head       { order: 1; margin-bottom: 0; }
    .pw:has(.pw-stage) .pw-charges    { order: 2; }
    .pw:has(.pw-stage) .pw-canvasarea { order: 3; }
    .pw:has(.pw-stage) .pw-palette-card,
    .pw:has(.pw-stage) .pw-card--cta  { order: 4; }
    .pw:has(.pw-stage) .pw-rate       { order: 5; }
    .pw:has(.pw-stage) .pw-tiers      { order: 6; }
    .pw:has(.pw-stage) .pw-sidelinks  { order: 7; }
}

@media (prefers-reduced-motion: reduce) {
    .pw-live__dot,
    .pw-toast,
    .pw-rate__stop.is-live::before {
        animation: none;
    }

    .pw-meter__fill,
    .pw-archive-card,
    .pw-swatch {
        transition: none;
    }

    .pw-archive-card:hover,
    .pw-swatch:hover {
        transform: none;
    }
}
