/* Board */
#grid {
    margin: 0 auto;
    position: relative; /* positioning context for the #threatArrows overlay */
    display: flex;
    flex-direction: column;
    aspect-ratio: 1 / 1;
    height: 90vh;
    background-size: contain;
    overflow: hidden;
}
/* The Survival threat-arrow layer: one SVG over the whole board, above the
   pieces (z-index 1) but below the hover health readout (z-index 999). It never
   takes a pointer — drags pass straight through to the squares. */
#threatArrows {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 5;
    pointer-events: none;
}
/* Each arrow is a <g> of three shaft + head copies, pulsed as one via group
   opacity — which composites, and also means the shapes' overlap never
   darkens. The rust tone reads as scorched into the wood rather than painted
   over it; a pure signal-red sat on top of the textured theme like a UI
   sticker. The pulse tops out below full opacity on purpose: the grain should
   always show through.

   The three copies are the arrow's relief: a dark copy nudged down-right, a
   lit copy nudged up-left, and the face on top, so a sliver of each peeks out
   along opposite edges. That is the same top-left lamp the stone material and
   the button bevels answer to, done with two extra shapes instead of an SVG
   filter — feTurbulence-class filters raster on the CPU, which is a road this
   codebase has been down before (see raster.js). */
#threatArrows .threat {
    animation: arrowPulse 2s ease-in-out infinite;
}
#threatArrows line {
    stroke: #83301e;
    stroke-width: 0.1;
    stroke-linecap: round;
}
#threatArrows path {
    fill: #83301e;
}
#threatArrows .etchDark line { stroke: #3f1409; }
#threatArrows .etchDark path { fill: #3f1409; }
#threatArrows .etchLight line { stroke: #a85236; }
#threatArrows .etchLight path { fill: #a85236; }
/* The advisor's hint arrow: same etched construction on its own layer (a
   Survival settle clears the threat layer wholesale), in an antique brass
   that reads as counsel where the rust reads as danger. */
#hintArrows {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 5;
    pointer-events: none;
}
#hintArrows .hint {
    animation: arrowPulse 2s ease-in-out infinite;
}
#hintArrows line {
    stroke: #a8842a;
    stroke-width: 0.1;
    stroke-linecap: round;
}
#hintArrows path {
    fill: #a8842a;
}
#hintArrows .etchDark line { stroke: #4d3a0e; }
#hintArrows .etchDark path { fill: #4d3a0e; }
#hintArrows .etchLight line { stroke: #d4b054; }
#hintArrows .etchLight path { fill: #d4b054; }
/* The title screen's splash: the game's name over the board, in the same
   dirt-rubbed display face as everything else. It hangs above the pieces and
   the arrow layers, never takes a pointer (the board underneath stays
   playable), and sits dead centre — which on the standard starting position
   is the empty middle of the board, so it covers no piece until the player
   makes the armies fall. Sized against both axes so the word fits the board
   on the desktop (90vh) and narrow (100vw) layouts alike.

   The text below is the fallback face: at startup raster.js recuts the word
   from the white pieces' ivory (rasterizeTitleStone) and swaps in a bitmap,
   halo baked in. The font-size rule stays load-bearing either way — the
   bitmap is sized in em, so both versions track min(12vh, 15vw). */
#titleSplash {
    position: absolute;
    inset: 0;
    z-index: 6;
    display: none;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    font-size: min(12vh, 15vw);
    color: #fff;
    text-shadow: 0 0 4vh #000, 0 1vh 2vh #000, 0 0 1vh #000;
}
#titleSplash.open {
    display: flex;
}
/* The stone-cut title bitmap. 1em is the splash's own font-size — the same
   min(12vh, 15vw) the text wore — and the ratio (set by rasterizeTitleStone)
   is the bitmap's height over its font size, carrying the bake's halo/shadow
   padding, so the carved letters land at exactly the text version's size and
   keep tracking the viewport. The max-width guard covers the pad making the
   image a whisker wider than the word: contain shrinks it, never crops. */
#titleSplash img {
    height: calc(1em * var(--titleStoneRatio, 2));
    width: auto;
    max-width: 100%;
    object-fit: contain;
}
.row {
    flex-grow: 1;
    display: flex;
}
.row:nth-child(odd)>.square:nth-child(odd),
.row:nth-child(even)>.square:nth-child(even) {
    background-color: rgba(210, 210, 210, 0);
}
.row:nth-child(even)>.square:nth-child(odd),
.row:nth-child(odd)>.square:nth-child(even) {
    background-color: rgba(0, 0, 0, 0.5);
}
.square {
    width: 12.5%;
    aspect-ratio: 1 / 1;
    position: relative;
}
/* Hover highlight: the shared 3D bevel (recipe lives with the buttons in
   base.css). It sits on an overlay with static shadows and only its opacity
   transitions, which composites. The old white glow animated box-shadow
   itself, repainting the square — and, via its 30px outer bleed, the neighbors
   and their marble pieces — on every frame of the fade. */
.square::before {
    content: "";
    position: absolute;
    inset: 0;
    box-shadow:
        inset var(--bevelSize) var(--bevelSize) calc(var(--bevelSize) * 1.2) rgba(255, 227, 183, 0.65),
        inset calc(-1 * var(--bevelSize)) calc(-1 * var(--bevelSize)) calc(var(--bevelSize) * 1.2) rgba(15, 6, 0, 0.7),
        inset 0 0 calc(var(--bevelSize) * 5) rgba(255, 214, 150, 0.25);
    background: var(--bevelSheen);
    opacity: 0;
    transition: opacity 0.125s;
    pointer-events: none;
}
#grid .square:hover::before {
    opacity: 1;
}
/* A grabbed piece's legal destinations wear that same bevel for as long as the
   piece is held or picked (moveHints.js sets .move-target; input.js clears it on
   drop or cancel). Reusing the hover overlay means it composites exactly like
   the hover state — only opacity changes — so lighting a queen's whole reach
   costs no board repaint. The extra warm inset picks the held targets out from
   an incidental hover. */
#grid .square.move-target::before {
    opacity: 1;
    box-shadow:
        inset var(--bevelSize) var(--bevelSize) calc(var(--bevelSize) * 1.2) rgba(255, 227, 183, 0.65),
        inset calc(-1 * var(--bevelSize)) calc(-1 * var(--bevelSize)) calc(var(--bevelSize) * 1.2) rgba(15, 6, 0, 0.7),
        inset 0 0 calc(var(--bevelSize) * 6) rgba(255, 208, 140, 0.4);
}
.piece {
    width: 100%;
    aspect-ratio: 1 / 1;
    background-size: contain;
    background-position: center center;
    user-select: none;
    cursor: pointer;
    position: absolute;
    z-index: 1;
}
/* A king in check wears a stone frame around its square: ivory for the white
   king, obsidian for the black (--ringPaint), wearing away to nothing as its
   lives drain — what remains of the frame IS the health left. --ringDrain, set
   inline by checkIndicator.js, is how far around the frame has worn off;
   `from 45deg` starts the wear at the top of the right edge and runs it
   clockwise: right, bottom, left, top.

   The shape and the drain are both in the mask, which is three layers deep:
   a content-box/border-box pair excluded against each other keeps only the
   band the padding carves out (the classic gradient-ring trick), and the conic
   on top intersects the worn stretch away. Draining via the mask rather than
   the background is what lets the carved variant below work: paint, sheen and
   bevel shadows all vanish together where the frame has worn off, instead of
   leaving a ghost of chisel lighting hanging over bare board. mask-composite
   applies top-down, so the conic's `intersect` runs against the band the two
   layers below it produce. Nothing here animates — the frame only changes when
   the board settles.

   This replaced a per-square glow (a blurred, tinted silhouette of the king,
   masked from the piece art) that stepped yellow → orange → red with damage;
   the frame states the health directly instead of asking the player to rank
   three hues. Its .glow div and silhouette masks are gone with it. */
.square.in-check {
    --ringPaint: rgba(255, 248, 233, 0.92);
    --ringStone: var(--frameIvory, none);
}
.square.in-check.black-king {
    --ringPaint: rgba(18, 12, 8, 0.92);
    --ringStone: var(--frameObsidian, none);
}
/* Defuse's bomb wears the very same frame in a red no king ever bleeds. It
   never drains (--ringDrain stays 0): the urgency is the corner number, and
   the last two moves flash instead (animations.css). The stone variant
   re-fires the ivory band red — see the carved rules below. */
.square.lit-fuse {
    --ringPaint: rgba(224, 42, 30, 0.94);
    --ringStone: var(--frameIvory, none);
}
.square.in-check::after,
.square.lit-fuse::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2; /* over the piece art: a frame the king can't hide */
    pointer-events: none;
    padding: 7%; /* the frame's thickness */
    background: var(--ringPaint);
    mask: conic-gradient(from 45deg,
            transparent 0deg calc(var(--ringDrain, 0deg) - 4deg),
            #fff calc(var(--ringDrain, 0deg) + 4deg)),
        linear-gradient(#fff 0 0) content-box,
        linear-gradient(#fff 0 0);
    mask-composite: intersect, exclude, add;
}
/* With the stone material on, the frame is cut from the same ivory and
   obsidian as the pieces: raster.js bakes two band-shaped tiles through the
   piece material rig at startup (--frameIvory / --frameObsidian on :root) and
   the mask above trims them to the same geometry as the flat paint. The
   tile's own lighting sculpts the lips — no CSS bevel here on purpose: the
   shared box-shadow recipe's warm highlight is wider than this thin band and
   washed the obsidian frame out to tan along its lit edges. If the bake ever
   fails, --ringStone resolves to none and the flat --ringPaint underneath
   still draws the frame. body.carved is kept in step with the setting by
   settings.js. */
body.carved .square.in-check::after,
body.carved .square.lit-fuse::after {
    background-image: var(--ringStone);
    background-size: 100% 100%;
}
/* The ember frame's stone is the ivory band re-fired red: multiplying the
   tile with the red paint beneath it keeps every chisel highlight and shadow
   of the bake while the paint supplies the colour — no third tile to bake. */
body.carved .square.lit-fuse::after {
    background-blend-mode: multiply;
}
/* Threat used to be a red inset vignette on every attacking square (the old
   ".checking" class). It read as a scatter of lit squares with no direction, so
   it is gone: the threat is now an arrow drawn from attacker to king on the
   #threatArrows overlay above. Only opacity animates (arrowPulse), so it
   composites and never repaints the board. */
/* Lives counter, twice over. The always-on reading: a tiny number tucked
   into the square's top-right corner (just inside the frame band), fed by the
   data-health attribute checkIndicator.js keeps on the piece div. It steps
   aside while the big hover readout below is up. Ivory over a dark shadow so
   it reads on both square colours, and so it can't be mistaken for the tan
   coordinate labels in the other corners. */
.square.in-check .piece::after {
    content: attr(data-health);
    position: absolute;
    top: 8%;
    right: 9%;
    font-size: min(2.2vh, 2.6vw);
    line-height: 1;
    color: #f3ead9;
    text-shadow: 0 0 3px #000, 1px 1px 2px #000;
    transition: opacity 1s;
}
.square.in-check:hover .piece::after {
    opacity: 0;
}
/* And the big readout, revealed on hover — shared with Defuse's fuse count,
   which lives in the same <p> (the two modes never share a board). */
.square.in-check .piece>p,
.square.lit-fuse .piece>p {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 999;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 6vh;
    color: white;
    font-weight: 100;
    border-radius: 100%;
    opacity: 0%;
    transition: 1s;
}
.square.in-check:hover .piece>p,
.square.lit-fuse:hover .piece>p {
    opacity: 100%;
}

/* The bomb's moves-left count, in the same corner as the king's lives. */
.square.lit-fuse .piece::after {
    content: attr(data-fuse);
    position: absolute;
    top: 8%;
    right: 9%;
    font-size: min(2.2vh, 2.6vw);
    line-height: 1;
    color: #ffd9a8;
    text-shadow: 0 0 3px #000, 1px 1px 2px #000;
    transition: opacity 1s;
}
.square.lit-fuse:hover .piece::after {
    opacity: 0;
}

/* Floating "+N" score tags (scorePopup.js): spawned centred on a merged
   square, drifted upward and faded out by a Web Animation, then removed.
   Above even mid-flight pieces (inline z-index up to 200 in animate.js). */
.scorePopup {
    position: absolute;
    z-index: 300;
    font-size: 3vh;
    font-weight: bold;
    color: #ffe9b8;
    text-shadow: 0 0 6px #000, 1px 1px 3px #000;
    pointer-events: none;
    user-select: none;
    white-space: nowrap;
}

.num, .let {
    position: absolute;
    top: 2%;
    left: 2%;
    font-family: "Rubik Dirt", "Open Sans", "sans-serif";
    color: #b18c69CC;
    text-shadow: 0 0 4px #000, 1px 1px 4px #3F2820, -1px -1px 4px #3F2820;
}
.let {
    top: unset;
    left: unset;
    bottom: 2%;
    right: 5%;
}

@media screen and (max-width: 1011px) {
    #grid {
        margin: 0 auto;
        margin-top: 10vh;
        max-height: 100vw;
        max-width: 100vw;
        background-image: url(../assets/ui/wood.png);
        box-shadow: 0 0 50px #000;
    }
    .num, .let {
        font-size: 2vw;
    }
}
