/* ============================================================
   style-frame-overlays.css

   Applied design from board-layout-lab.html — user's tuned values
   exported as JSON v4, baked into production CSS.
   Source date: 2026-05-13

   Two layers:
     1. SPACING — overrides production @media (max-width: 760px)
        defaults with the values the user dialled in
     2. FRAME OVERLAYS — 9 decorative PNG layers anchored to
        header / player-rows / board-shell / viewport corners

   Loaded after style.css + style-bevels.css so it wins the cascade
   for matching specificity. Scoped to body[data-page="chess"] so
   other pages (blackjack/poker/index) are untouched.
   ============================================================ */

/* Hide frame overlays on >760 px viewports — the mobile design
   layout's anchor sizes/positions don't apply on desktop, so the
   overlays would either render at intrinsic PNG size (768×768) or
   collapse to 0×0 against hidden anchors. The desktop layout has
   its own decorative system (side panels). */
@media (min-width: 761px) {
  body[data-page="chess"] .chess-frame-deco { display: none; }
}


/* ============================================================
   Chapter-map cards — PNG frame backgrounds + number PNGs.

   Each chapter is rendered as a button with:
     - <img class="chapter-card-frame"> — chapter-{lock|unlock|select}.png
       (full card art incl. round number slot, target/crown chips,
       lock or jewel decoration depending on state)
     - <img class="chapter-card-num--white"> — N-white.png in the slot
     - <img class="chapter-card-num--pink">  — N-pink.png (selected only)
     - <span class="chapter-card-title">     — chapter name overlay

   Selected chapter's number pulses between -white and -pink variants
   via chapterNumPulse @keyframes (3 s cycle, ease-in-out).

   The PNG cards are ~3.4 : 1 aspect (1500×440-ish). The round number
   slot is roughly at left-center, ~12% from the left edge. Title
   slots into the free middle space — centred between the slot and
   the right-edge crown/target chips bake-in.
   ============================================================ */
/* All position + size values exposed as CSS variables so the lab
   (chapter-card-lab.html) can tweak them live without editing this
   file. Defaults below are the user-baked values from 2026-05-14. */
body[data-page="chess"] .chess-chapter-card,
.chapter-card-lab-demo {
  --chapter-num-left:    13.5%;
  --chapter-num-top:     50.4%;
  --chapter-num-width:   8.3%;
  --chapter-title-left:  25%;       /* sits flush against the right edge of the round number slot */
  --chapter-title-top:   33.7%;
  --chapter-title-fs:    18px;
  --chapter-title-align: left;
  /* --chapter-title-tx tracks alignment — the lab sets this in tandem
     with --chapter-title-align (0 for left, -50% for center, -100%
     for right) so the title's transform origin actually moves to the
     intended edge. */
  --chapter-title-tx:    0;
  --chapter-wins-left:   40%;
  --chapter-wins-top:    71%;
  --chapter-wins-fs:     17px;
  --chapter-stars-left:  76%;
  --chapter-stars-top:   71%;
  --chapter-stars-fs:    17px;
}
body[data-page="chess"] .chess-chapters-list .chess-chapter-card {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 3.4 / 1;
  border: 0;
  background: transparent;
  padding: 0;
  margin: 0;
  cursor: pointer;
  overflow: visible;
  transition: transform .18s ease, filter .18s ease;
}
body[data-page="chess"] .chess-chapters-list .chess-chapter-card:hover:not(:disabled) {
  transform: translateY(-1px);
  filter: brightness(1.04);
}
body[data-page="chess"] .chess-chapters-list .chess-chapter-card:active:not(:disabled) {
  transform: scale(.985);
}
body[data-page="chess"] .chess-chapters-list .chess-chapter-card:disabled {
  cursor: not-allowed;
  opacity: 1;  /* PNG art already conveys locked state */
}
body[data-page="chess"] .chapter-card-frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  pointer-events: none;
}
body[data-page="chess"] .chapter-card-num {
  position: absolute;
  left: var(--chapter-num-left);
  top: var(--chapter-num-top);
  width: var(--chapter-num-width);
  transform: translate(-50%, -50%);
  pointer-events: none;
}
body[data-page="chess"] .chapter-card-num--pink { opacity: 0; }
/* Selected chapter — the white & pink number variants alternate with
   a "dwell + bloom" rhythm: each variant stays fully visible for
   most of the cycle, then crossfades sharply through the other.
   The pink bloom adds a subtle scale + soft glow on appearance so the
   colour swap reads as a stylised heartbeat rather than fading to
   invisible (the user specifically called out the through-zero look
   in the previous iteration). */
body[data-page="chess"] .chess-chapter-card[data-chapter-state="select"] .chapter-card-num--white {
  animation: chapterNumWhiteCycle 4.6s ease-in-out infinite;
}
body[data-page="chess"] .chess-chapter-card[data-chapter-state="select"] .chapter-card-num--pink {
  opacity: 0;
  animation: chapterNumPinkBloom 4.6s ease-in-out infinite;
}
@keyframes chapterNumWhiteCycle {
  0%, 40%   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  50%       { opacity: 0; transform: translate(-50%, -50%) scale(.92); }
  60%, 100% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
@keyframes chapterNumPinkBloom {
  0%, 40%   { opacity: 0; transform: translate(-50%, -50%) scale(.92); filter: drop-shadow(0 0 0 transparent); }
  50%       { opacity: 1; transform: translate(-50%, -50%) scale(1.06); filter: drop-shadow(0 0 9px rgba(255, 120, 175, .65)); }
  60%, 100% { opacity: 0; transform: translate(-50%, -50%) scale(.92); filter: drop-shadow(0 0 0 transparent); }
}

/* Title — chapter name only. Deeper wine tone with subtle relief so
   it pops against the pink frame without the heavy plum-black look. */
body[data-page="chess"] .chapter-card-title {
  position: absolute;
  left: var(--chapter-title-left);
  top: var(--chapter-title-top);
  transform: translate(var(--chapter-title-tx, -50%), -50%);
  font-family: var(--display, "Unbounded", sans-serif);
  font-weight: 800;
  font-size: var(--chapter-title-fs);
  letter-spacing: -.015em;
  color: #5c0a32;
  line-height: 1.05;
  text-align: var(--chapter-title-align, left);
  text-shadow:
    0 1px 0 rgba(255, 255, 255, .62),
    0 0 1px rgba(255, 255, 255, .85);
  pointer-events: none;
  white-space: nowrap;
}
body[data-page="chess"] .chess-chapter-card[data-chapter-state="lock"] .chapter-card-title {
  color: rgba(82, 32, 56, .42);
  text-shadow: none;
}

/* Wins (cleared / total levels) + Stars (earned / max total).
   Each sits inside one of the two oval slots baked into the PNG —
   target icon for wins, crown icon for stars. */
body[data-page="chess"] .chapter-card-wins,
body[data-page="chess"] .chapter-card-stars {
  position: absolute;
  transform: translate(-50%, -50%);
  display: inline-flex;
  align-items: baseline;
  gap: 2px;
  font-family: var(--display, "Unbounded", sans-serif);
  letter-spacing: -.01em;
  color: #5c0a32;
  line-height: 1;
  pointer-events: none;
  white-space: nowrap;
  text-shadow:
    0 1px 0 rgba(255, 255, 255, .62),
    0 0 1px rgba(255, 255, 255, .85);
}
body[data-page="chess"] .chapter-card-wins {
  left: var(--chapter-wins-left);
  top:  var(--chapter-wins-top);
  font-size: var(--chapter-wins-fs);
}
body[data-page="chess"] .chapter-card-stars {
  left: var(--chapter-stars-left);
  top:  var(--chapter-stars-top);
  font-size: var(--chapter-stars-fs);
}
/* Count + index share the same font-size for a tidy, aligned look.
   Weight + opacity differentiate the two (bolder count, lighter +
   slightly dimmed total). */
body[data-page="chess"] .chapter-card-wins strong,
body[data-page="chess"] .chapter-card-stars strong {
  font-weight: 800;
  font-size: 1em;
}
body[data-page="chess"] .chapter-card-wins em,
body[data-page="chess"] .chapter-card-stars em {
  font-style: normal;
  font-weight: 700;
  font-size: 1em;
  opacity: .55;
}

/* Hide the legacy vertical dotted "path" between cards — user prefers
   the cleaner look without the connector. */
body[data-page="chess"] .chess-chapters-list::before {
  display: none;
  content: none;
}

@media (prefers-reduced-motion: reduce) {
  body[data-page="chess"] .chess-chapter-card[data-chapter-state="select"] .chapter-card-num--white,
  body[data-page="chess"] .chess-chapter-card[data-chapter-state="select"] .chapter-card-num--pink {
    animation: none;
  }
  body[data-page="chess"] .chess-chapter-card[data-chapter-state="select"] .chapter-card-num--pink {
    opacity: 1;
  }
}


/* ============================================================
   Player-row status pill — new icon + label layout (2026-05-14).

   Replaces the high-contrast state-jewel-active "Ваш ход" gradient
   with a quiet inline pill that's visible on BOTH player rows
   simultaneously. Active turn = character paw icon + clock time;
   waiting = hourglass icon + "Ожидает" (AI) or clock (duel).
   ============================================================ */
body[data-page="chess"] .player-row .player-row-status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* Bigger pill — height ~42 px to fit inside the 44 px avatar slot
     so the player-row card itself stays at 52 px (no growth). Left
     padding is tighter (2 px) so the icon sits flush against the
     pill's curved edge. */
  padding: 7px 16px 7px 2px;
  border-radius: 999px;
  background: rgba(255, 246, 250, .82);
  border: 1px solid rgba(216, 24, 121, .14);
  box-shadow: 0 2px 6px rgba(196, 50, 126, .08), inset 0 1px 0 rgba(255, 255, 255, .92);
  font-family: inherit;
  font-weight: 800;
  font-size: 15px;
  letter-spacing: -.005em;
  color: var(--plum-800, #4d1933);
  line-height: 1;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
body[data-page="chess"] .player-row .player-row-status-icon {
  display: inline-block;
  width: 28px;
  height: 28px;
  object-fit: contain;
  flex-shrink: 0;
  transform-origin: 50% 50%;
}
body[data-page="chess"] .player-row .player-row-status-label {
  display: inline-flex;
  align-items: center;
  height: 22px;
}

/* Hourglass icon (waiting state) rotates slowly in place — visual cue
   that the side is idle/waiting. Selector hits any src containing the
   wait-your-step slug, so paw icons stay still. */
body[data-page="chess"] .player-row .player-row-status-icon[src*="wait-your-step"] {
  animation: chessHourglassRotate 12s linear infinite;
}
@keyframes chessHourglassRotate {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  body[data-page="chess"] .player-row .player-row-status-icon[src*="wait-your-step"] {
    animation: none;
  }
}

/* Compress the row's internal gap so the bigger pill sits closer to
   the card edges. Row layout uses flex with gap; user spec was
   gap: 10px (default). Tighten to 6 so the bigger pill claims more
   of the row's interior without forcing the card to grow taller. */
body[data-page="chess"] .player-row {
  gap: 6px;
}
/* Active-turn pill — slightly warmer fill + soft rose border */
body[data-page="chess"] .player-row.is-active .player-row-status {
  background: linear-gradient(135deg, rgba(255, 232, 240, .96), rgba(255, 213, 230, .88));
  border-color: rgba(216, 24, 121, .28);
  color: var(--plum-900, #2a0f1f);
}
/* Check state — red-tinted to warn */
body[data-page="chess"] .player-row[data-status-kind="check"] .player-row-status {
  background: linear-gradient(135deg, rgba(255, 220, 220, .96), rgba(255, 195, 200, .92));
  border-color: rgba(216, 50, 70, .42);
  color: #7a1a26;
}


/* ============================================================
   Hint / Undo dock button icons — PNG replacements for SVG icons.
   ============================================================ */
body[data-page="chess"] .dock-actions .dock-btn-icon--png {
  width: 22px;
  height: 22px;
  object-fit: contain;
  display: block;
}


/* ============================================================
   Result-modal task crown — PNG replacement for the ti-crown SVG.
   We strip the original circular gold ring/glow chrome
   (style.css:10183 — bg gradient + box-shadow + border-radius)
   and let the crown-award PNG sit on its own, no decoration.
   The slot still occupies its grid cell so the layout doesn't
   reflow. Not-earned crowns are hidden via visibility.
   ============================================================ */
body[data-page="chess"] .chess-result-task-crown,
body[data-page="chess"] .chess-result-task[data-earned="true"] .chess-result-task-crown,
body[data-page="chess"] .chess-result-task[data-earned="false"] .chess-result-task-crown {
  background: transparent;
  box-shadow: none;
  border-radius: 0;
}
body[data-page="chess"] .chess-result-task-crown img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
body[data-page="chess"] .chess-result-task[data-earned="false"] .chess-result-task-crown {
  visibility: hidden;
}


/* ============================================================
   Result-modal confetti — falling petal celebration shown ONLY
   when overlay carries data-outcome="win" (player wins solo, or
   any player wins a duel). 16 spans with varied sizes (28-62 px),
   PNG variants, delays, drifts, rotations. Sits behind the
   result-card via z-index inside the overlay's stacking context.
   ============================================================ */
body[data-page="chess"] .chess-result-confetti {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 0;
  display: none;
}
body[data-page="chess"] .chess-result-overlay[data-outcome="win"] .chess-result-confetti {
  display: block;
}
body[data-page="chess"] .chess-result-confetti span {
  position: absolute;
  top: -10%;
  left: var(--x);
  width: var(--size, 40px);
  height: var(--size, 40px);
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  opacity: 0;
  will-change: transform, opacity;
  filter: drop-shadow(0 4px 12px rgba(196, 50, 126, .18));
  animation: chessResultLeafFall var(--dur, 6s) linear infinite;
  animation-delay: var(--delay, 0s);
}
body[data-page="chess"] .chess-result-confetti span:nth-child(5n + 1) {
  background-image: url("assets/chess/final-png/props/soft-petal-01.png");
}
body[data-page="chess"] .chess-result-confetti span:nth-child(5n + 2) {
  background-image: url("assets/chess/final-png/props/soft-petal-02.png");
}
body[data-page="chess"] .chess-result-confetti span:nth-child(5n + 3) {
  background-image: url("assets/chess/final-png/props/soft-petal-03.png");
}
body[data-page="chess"] .chess-result-confetti span:nth-child(5n + 4) {
  background-image: url("assets/chess/final-png/props/soft-petal-04.png");
}
body[data-page="chess"] .chess-result-confetti span:nth-child(5n + 5) {
  background-image: url("assets/chess/final-png/props/soft-petal-05.png");
}
@keyframes chessResultLeafFall {
  0%   { transform: translate3d(0, -10vh, 0) rotate(0deg); opacity: 0; }
  6%   { opacity: .82; }
  92%  { opacity: .82; }
  100% { transform: translate3d(var(--drift, 6vw), 112vh, 0) rotate(var(--rot, 360deg)); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  body[data-page="chess"] .chess-result-confetti span { animation: none; }
}


/* ============================================================
   Chess board stage — wallpaper-empty-1 backdrop replaces the cool
   pink gradient. Subtle warm overlay lifts contrast without washing
   out the bokeh texture.

   Board-shell drop shadow recoloured from cool dark-plum
   (46,14,32) to warm peach-gold (212,130,70) — reads as glow from
   above instead of grey weight. Only the OUTER ambient entries of
   the bevel-frame-board-luxe shadow stack are swapped via a
   variable override; the inner 9-band engraved frame stays as-is.
   ============================================================ */
body[data-page="chess"] .chess-board-stage {
  /* Slightly darker than the previous warm-cream wash — a soft dusty
     rose tint sits on top of wallpaper-empty-1 so the board has a
     warmer envelope to read against. */
  background:
    linear-gradient(180deg, rgba(140, 80, 95, .18), rgba(170, 100, 120, .22)),
    url("assets/chess/final-png/wallpaper/wallpaper-empty-1.jpeg") center / cover no-repeat;
}
body[data-page="chess"] .board-shell {
  --bevel-frame-board-luxe-shadow:
    /* outer ambient drop — soft rose (was peach-gold) */
    0 36px 72px rgba(220, 60, 130, .42),
    0 16px 32px rgba(200, 80, 140, .32),
    0 0 0 1px rgba(255, 255, 255, .92),
    /* === MULTI-BAND ENGRAVED FRAME (unchanged from style-bevels.css) === */
    inset 0 0 0 1px rgba(116, 18, 70, .56),
    inset 0 0 0 2px rgba(216, 24, 121, .32),
    inset 0 0 0 4px rgba(255, 255, 255, .96),
    inset 0 0 0 5px rgba(255, 184, 92, .82),
    inset 0 1px 0 4px rgba(255, 232, 168, 1),
    inset 0 2px 0 3px rgba(255, 248, 220, .68),
    inset 0 -1px 0 4px rgba(150, 80, 20, .82),
    inset 0 -2px 0 3px rgba(120, 60, 10, .56),
    inset 0 0 0 7px rgba(255, 250, 244, 1),
    inset 0 1px 0 6px rgba(255, 255, 255, 1),
    inset 0 -1px 0 6px rgba(180, 120, 90, .58),
    inset 0 0 0 8px rgba(255, 245, 232, .96),
    inset 0 -1px 0 7px rgba(196, 140, 100, .42),
    inset 0 0 0 9px rgba(216, 24, 121, .22),
    inset 0 1px 0 8px rgba(255, 144, 180, .42),
    inset 0 -1px 0 8px rgba(80, 12, 40, .68);
}


/* ============================================================
   Big falling leaves — background layer behind all content.

   8 large soft-petal PNGs (re-skinned as "leaves") drifting from
   top to bottom with horizontal sway + slow rotation. Sits at
   z-index: -1 so content visually covers it but it shows through
   the gaps between containers (around board frame, between dock
   buttons, etc.). Always visible incl. mobile (the smaller
   chess-atmosphere petals are hidden on mobile for perf — these
   bigger leaves replace them as the mobile atmosphere). Variant
   PNGs cycle via :nth-child to keep visual rhythm varied.
   ============================================================ */
body[data-page="chess"] .chess-leaves-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}
body[data-page="chess"] .chess-leaves-bg span {
  position: absolute;
  top: -12vh;
  left: var(--x);
  width: var(--size, 60px);
  height: var(--size, 60px);
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  opacity: 0;
  will-change: transform, opacity;
  animation: chessLeafFall var(--dur, 24s) linear infinite;
  animation-delay: var(--delay, 0s);
  filter: drop-shadow(0 4px 12px rgba(196, 50, 126, .14));
}
body[data-page="chess"] .chess-leaves-bg span:nth-child(5n + 1) {
  background-image: url("assets/chess/final-png/props/soft-petal-01.png");
}
body[data-page="chess"] .chess-leaves-bg span:nth-child(5n + 2) {
  background-image: url("assets/chess/final-png/props/soft-petal-02.png");
}
body[data-page="chess"] .chess-leaves-bg span:nth-child(5n + 3) {
  background-image: url("assets/chess/final-png/props/soft-petal-03.png");
}
body[data-page="chess"] .chess-leaves-bg span:nth-child(5n + 4) {
  background-image: url("assets/chess/final-png/props/soft-petal-04.png");
}
body[data-page="chess"] .chess-leaves-bg span:nth-child(5n + 5) {
  background-image: url("assets/chess/final-png/props/soft-petal-05.png");
}

@keyframes chessLeafFall {
  0%   { transform: translate3d(0, -12vh, 0) rotate(0deg); opacity: 0; }
  6%   { opacity: .55; }
  94%  { opacity: .55; }
  100% { transform: translate3d(var(--drift, 6vw), 112vh, 0) rotate(var(--rot, 360deg)); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  body[data-page="chess"] .chess-leaves-bg span { animation: none; opacity: 0; }
}

@media (max-width: 760px) {

  /* === SPACING — design values exposed as --lab-* CSS variables ====
     Defaults are normalised to a 412-px design viewport (Google Pixel
     9 Pro XL CSS viewport — the primary target device): every value
     is `(px / 412) * 100` vw, so anchors scale linearly with viewport
     and PNG overlays stay locked to relative positions across mobile
     aspect ratios.

     Pure-px values (element sizes, font sizes, 999-pill radii) stay
     as their px defaults because they shouldn't scale with viewport.

     The lab editor (board-layout-lab.html) tweaks these by writing
     to the same --lab-* names on document.documentElement, so all
     sliders override the defaults live without touching this file.
  */

  body[data-page="chess"] .site-header {
    min-height: var(--lab-header-height, 60px);
    padding: 0 var(--lab-header-pad-x, 3.519vw);   /* 14.5 px @ 412 */
    gap: var(--lab-header-gap, 1.942vw);            /* 8 px @ 412 */
  }
  body[data-page="chess"] .site-header .brand-avatar {
    width: var(--lab-header-avatar-size, 43px);
    height: var(--lab-header-avatar-size, 43px);
    border-radius: var(--lab-header-avatar-radius, 50%);
  }
  body[data-page="chess"] .site-header .chess-header-cta {
    padding: var(--lab-header-cta-pad-y, 0.485vw) var(--lab-header-cta-pad-x, 2.913vw);   /* 2 / 12 @ 412 */
    border-radius: var(--lab-header-cta-radius, 999px);
  }
  body[data-page="chess"] .site-header .chess-profile-pill {
    border-radius: var(--lab-header-balance-radius, 999px);
  }

  body[data-page="chess"] .chess-layout {
    gap: var(--lab-main-gap, 2.063vw);                                              /* 8.5 px @ 412 */
    padding: var(--lab-main-pad-top, 3.034vw) var(--lab-main-pad-x, 1.214vw) var(--lab-main-pad-bottom, 0);  /* 12.5 / 5 / 0 */
  }
  body[data-page="chess"] .chess-layout > .player-row--top {
    margin-top: var(--lab-gap-header-to-top, 1.699vw);    /* 7 px @ 412 */
    margin-bottom: var(--lab-gap-top-to-stage, 1.092vw);   /* 4.5 px @ 412 */
  }
  body[data-page="chess"] .chess-play-grid > .chess-board-stage > .player-row--bottom {
    margin-top: var(--lab-gap-stage-to-bottom, 3.155vw);   /* 13 px @ 412 */
    margin-bottom: var(--lab-gap-bottom-to-dock, 1.699vw);  /* 7 px @ 412 */
  }

  body[data-page="chess"] .chess-layout > .player-row,
  body[data-page="chess"] .chess-play-grid > .chess-board-stage > .player-row {
    padding: var(--lab-row-pad-y, 0) var(--lab-row-pad-x, 7.767vw);    /* 32 px @ 412 */
    border-radius: var(--lab-row-radius, 9.709vw);                      /* 40 px @ 412 */
  }
  /* Stage top padding has the +10 px breathing-room offset folded in.
     Lab slider --lab-stage-pad-top adjusts independently. */
  body[data-page="chess"] .chess-layout > .chess-play-grid > .chess-board-stage {
    padding:
      var(--lab-stage-pad-top, 4.005vw)              /* 16.5 px @ 412 */
      var(--lab-stage-pad, 1.578vw)                   /* 6.5 px @ 412 */
      var(--lab-stage-pad, 1.578vw);
    border-radius: var(--lab-stage-radius, 5.947vw);  /* 24.5 px @ 412 */
  }
  body[data-page="chess"] .chess-play-grid > .chess-board-stage > .board-shell {
    padding: var(--lab-shell-pad, 3.155vw);            /* 13 px @ 412 */
    border-radius: var(--lab-shell-radius, 2.549vw);   /* 10.5 px @ 412 */
  }
  body[data-page="chess"] .chess-play-grid > .chess-board-stage > .chess-board-dock {
    padding: var(--lab-dock-pad, 1.456vw);             /* 6 px @ 412 */
    border-radius: var(--lab-dock-radius, 4.854vw);    /* 20 px @ 412 */
  }
  body[data-page="chess"] .chess-board-dock .dock-actions button {
    padding-top: var(--lab-dock-btn-pad-y, 0.485vw);   /* 2 px @ 412 */
    padding-bottom: var(--lab-dock-btn-pad-y, 0.485vw);
    border-radius: var(--lab-dock-btn-radius, 4.126vw); /* 17 px @ 412 */
  }


  /* === Old branch decorations — disable (replaced by frame-top-23 /
     frame-bottom-22 overlays the user dialled in). The original
     hero-blossom-{left,right-down} pseudo-elements sit at the same
     corners but are misaligned with the new frame design. */
  body[data-page="chess"] .board-shell::before,
  body[data-page="chess"] .board-shell::after {
    display: none;
    content: none;
  }


  /* === Hint / Undo / Menu dock buttons — typography matches header
     balance-pill style (Nunito 800, 14 px) for visual harmony.

     Alignment strategy: every text-bearing child of .dock-btn-row
     becomes its own inline-flex with the SAME explicit height +
     line-height: 1, so the parent's `align-items: center` puts each
     glyph's bounding box centre at the same y-coordinate. No
     font-specific nudges needed — works regardless of font metrics. */
  body[data-page="chess"] .dock-actions [data-chess-hint],
  body[data-page="chess"] .dock-actions [data-chess-undo],
  body[data-page="chess"] .dock-actions .chess-sheet-trigger {
    gap: 5px;
    padding-top: 5px;
    padding-bottom: 5px;
  }
  body[data-page="chess"] .dock-actions .dock-btn-row {
    display: inline-flex;
    align-items: center;
    gap: 11px;
    line-height: 1;
    height: 22px;  /* common row height — anchors all child centres */
  }
  body[data-page="chess"] .dock-actions .dock-btn-row--menu {
    gap: 9px;
  }
  body[data-page="chess"] .dock-actions .dock-btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
    line-height: 1;
    height: 22px;
    color: var(--rose-600);
  }

  /* Cost pill — coin icon + number. Both children share the same
     inline-flex box-with-explicit-height so their geometric centres
     align exactly with the parent's .dock-btn-row centre line. */
  body[data-page="chess"] .dock-actions .dock-btn-cost {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 22px;
    /* Match header balance pill: inherit (= Nunito), 800, 14 px */
    font-family: inherit;
    font-weight: 800;
    font-size: 14px;
    line-height: 1;
    letter-spacing: -.005em;
    color: var(--plum-900);
  }
  body[data-page="chess"] .dock-actions .dock-btn-cost .ti-coin,
  body[data-page="chess"] .dock-actions .dock-btn-cost em {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 16px;       /* common glyph-box height — guarantees parity */
    line-height: 1;
  }
  body[data-page="chess"] .dock-actions .dock-btn-cost .ti-coin {
    font-size: 15px;
    color: #c89318;
    filter: drop-shadow(0 1px 0 rgba(255, 235, 180, .9));
  }
  body[data-page="chess"] .dock-actions .dock-btn-cost em {
    font-style: normal;
    font-variant-numeric: tabular-nums;
    font-size: inherit;
    min-width: 1ch;
  }

  /* Menu label "Меню" — same Nunito 800 14 px as the cost pill so all
     dock text reads with one voice. */
  body[data-page="chess"] .dock-actions .dock-btn-label {
    display: inline-flex;
    align-items: center;
    height: 22px;
    font-family: inherit;
    font-weight: 800;
    font-size: 14px;
    letter-spacing: -.005em;
    line-height: 1;
    color: var(--plum-900);
  }
  /* Exhausted / boss-blocked cues — recolour cost number + coin */
  body[data-page="chess"] .dock-actions [data-chess-hint][data-hint-exhausted="true"] .dock-btn-cost,
  body[data-page="chess"] .dock-actions [data-chess-hint][data-hint-blocked="boss"] .dock-btn-cost,
  body[data-page="chess"] .dock-actions [data-chess-undo][data-undo-blocked="boss"] .dock-btn-cost,
  body[data-page="chess"] .dock-actions [data-chess-undo][data-undo-exhausted="true"] .dock-btn-cost {
    color: var(--rose-600);
  }
  body[data-page="chess"] .dock-actions [data-chess-hint][data-hint-blocked="boss"] .dock-btn-cost .ti-coin,
  body[data-page="chess"] .dock-actions [data-chess-undo][data-undo-blocked="boss"] .dock-btn-cost .ti-coin {
    color: var(--rose-600);
    filter: none;
  }

  /* Disabled visual cue for exhausted undo button (cap still enforced
     in JS — toast informs the player). */
  body[data-page="chess"] .dock-actions [data-chess-undo][data-undo-exhausted="true"] {
    opacity: .55;
  }


  /* === Streak badge — hide on the compact mobile dock layout.

     Background: .chess-board-streak-badge is a child of
     .chess-board-dock. On mobile dock becomes position:relative
     (style.css:11734) so the badge's absolute coords (top:8 right:8)
     resolve against the DOCK — putting it directly over the Меню
     column of the new 3-button row. Floating it above the dock
     (`bottom: calc(100% + 6px)`) overlapped the bottom player-row
     instead because the gap between bot-row and dock is only ~13 px
     while the badge is 26 px tall.

     For the compact dock design, streak info is communicated through
     the player-row status pill ("Ваш ход / В зоне ×1.2") rather than
     a separate floating badge. Hide here.

     boss-flag is grid-slotted (grid-column: 1 / -1) inside the dock
     so it doesn't have this problem — leave its display logic to
     production CSS. */
  body[data-page="chess"] .chess-board-dock .chess-board-streak-badge {
    display: none;
  }


  /* === OVERLAYS ================================================= */

  /* Anchor parents must be position:relative for absolute overlays
     to position against them. Most already are; this is defensive. */
  body[data-page="chess"] .site-header,
  body[data-page="chess"] .player-row,
  body[data-page="chess"] .board-shell {
    position: relative;
  }

  /* Base overlay style — position centred on the (left, top) point,
     element scales to width%; height auto-derives from the PNG's
     natural aspect ratio. translate(-50%,-50%) replicates the lab's
     `x_offset - w/2` math without needing JS. */
  body[data-page="chess"] .chess-frame-deco {
    position: absolute;
    pointer-events: none;
    user-select: none;
    transform: translate(-50%, -50%);
    height: auto;
    display: block;
  }
  /* Body-level (frame anchor) overlays — fixed to viewport so they
     stay visible regardless of scroll. */
  body[data-page="chess"] .chess-frame-deco[data-anchor="frame"] {
    position: fixed;
  }


  /* --- Individual overlay positions (from lab JSON) -------------- */

  /* ov12 — btn-right-13 — header right side */
  body[data-page="chess"] .chess-frame-deco[data-frame="btn-right-13"] {
    width: 46%;
    left: 78%;
    top: 50%;
    z-index: 5;
  }

  /* ov9 — btn-right-14 — top player row, right */
  body[data-page="chess"] .chess-frame-deco[data-frame="btn-right-14"] {
    width: 43.5%;
    left: 78.5%;
    top: 50%;
    z-index: 5;
  }
  /* ov10 — btn-left-14 — top player row, left */
  body[data-page="chess"] .chess-frame-deco[data-frame="btn-left-14"] {
    width: 43.5%;
    left: 20.83%;
    top: 48.08%;
    z-index: 5;
  }

  /* ov7 — btn-right-7 — bottom player row, right */
  body[data-page="chess"] .chess-frame-deco[data-frame="btn-right-7"] {
    width: 45.5%;
    left: 77.78%;
    top: 49.5%;
    z-index: 5;
  }
  /* ov8 — btn-left-7 — bottom player row, left */
  body[data-page="chess"] .chess-frame-deco[data-frame="btn-left-7"] {
    width: 45.5%;
    left: 22%;
    top: 49.5%;
    z-index: 5;
  }

  /* ov6 — frame-top-23 — board shell top frame */
  body[data-page="chess"] .chess-frame-deco[data-frame="frame-top-23"] {
    width: 100.5%;
    left: 50%;
    top: 12%;
    z-index: 5;
  }
  /* ov1 — frame-bottom-22 — board shell bottom frame */
  body[data-page="chess"] .chess-frame-deco[data-frame="frame-bottom-22"] {
    width: 100%;
    left: 50%;
    top: 86.5%;
    z-index: 12;
  }

  /* ov14 — angle-lt-plant-5 — viewport top-left corner. z=5 matches
     the lab so the decoration paints OVER header content (it has
     transparent areas + pointer-events: none, so it doesn't block
     clicks on header buttons). */
  body[data-page="chess"] .chess-frame-deco[data-frame="angle-lt-plant-5"] {
    width: 57vw;
    left: 25.37vw;
    top: 13.28vh;
    z-index: 5;
  }
  /* ov13 — angle-rb-paw-4 — viewport bottom-right corner */
  body[data-page="chess"] .chess-frame-deco[data-frame="angle-rb-paw-4"] {
    width: 39.5vw;
    left: 80.79vw;
    top: 90.41vh;
    z-index: 5;
  }

}
