  /* HINT LOCATION: a distinct-coloured OUTLINE around each hint-revealed region (static SVG layer drawn by
     render/board.js drawHintOutline — bright gold core + dark halo). Reads on ANY jewel colour, needs no
     animation/effect, so it stays visible on low-end / reduced-motion / battery-saver phones. */
  #board .hintOutlineLayer { position: absolute; left: 0; top: 0; pointer-events: none; z-index: 5; }
  /* in-play ambient juice: completion warmth (grows with solve progress) + combo edge-glow */
  #solveGlow { position: fixed; inset: 0; pointer-events: none; z-index: 4; opacity: 0; transition: opacity .5s ease;
    background: radial-gradient(circle at 50% 46%, rgba(255,210,120,.30), transparent 58%); }
  #comboGlow { position: fixed; inset: 0; pointer-events: none; z-index: 5; opacity: 0; transition: opacity .45s ease;
    background: radial-gradient(circle at 50% 60%, transparent 50%, rgba(255,196,92,.5) 118%); }
  #comboGlow.pulse { animation: comboPulse .4s ease-out; }
  @keyframes comboPulse { 0% { filter: brightness(1); } 35% { filter: brightness(1.7); } 100% { filter: brightness(1); } }
  #sealPraiseFx {
    position: fixed;
    z-index: 8;
    transform: translate(-50%, -50%);
    pointer-events: none;
    display: grid;
    place-items: center;
    min-width: 154px;
    min-height: 66px;
    filter: drop-shadow(0 7px 12px rgba(28,18,4,.38));
    animation: spfIn 1.55s cubic-bezier(.18,1.12,.3,1) both;
  }
  #sealPraiseFx .spfRing {
    position: absolute;
    width: 112px;
    height: 50px;
    border-radius: 50%;
    border: 2px solid rgba(255,231,126,.68);
    box-shadow: 0 0 13px rgba(255,211,88,.56), inset 0 0 10px rgba(255,255,255,.16);
    animation: spfRing 1.18s ease-out both;
  }
  #sealPraiseFx .spfGem {
    position: absolute;
    top: -9px;
    width: 24px;
    height: 24px;
    clip-path: polygon(50% 0, 88% 34%, 70% 100%, 30% 100%, 12% 34%);
    background:
      radial-gradient(circle at 38% 28%, rgba(255,255,255,.95) 0 11%, transparent 22%),
      linear-gradient(135deg, #fff5bf, #ffd35a 50%, #b86b12);
    box-shadow: 0 0 10px rgba(255,210,88,.74);
  }
  #sealPraiseFx .spfText {
    position: relative;
    padding: 15px 22px 13px;
    border-radius: 999px;
    background:
      linear-gradient(180deg, rgba(61,91,47,.88), rgba(34,65,35,.92)),
      radial-gradient(circle at 50% 0, rgba(255,235,135,.35), transparent 70%);
    border: 2px solid rgba(224,172,73,.82);
    color: #fff4c7;
    font-size: clamp(20px, 5vw, 28px);
    font-weight: 950;
    line-height: 1;
    white-space: nowrap;
    text-shadow: 0 2px 0 #4b2b0d, 0 0 9px rgba(255,225,118,.38);
  }
  #sealPraiseFx.tier5 .spfRing { width: 132px; border-color: rgba(255,238,148,.78); }
  #sealPraiseFx.tier8 .spfRing { width: 154px; border-color: rgba(255,248,188,.86); box-shadow: 0 0 18px rgba(255,226,108,.74), inset 0 0 12px rgba(255,255,255,.20); }
  #sealPraiseFx.elite .spfRing { border-color: rgba(221,190,255,.86); box-shadow: 0 0 18px rgba(175,100,255,.72), inset 0 0 12px rgba(255,255,255,.18); }
  #sealPraiseFx.elite .spfGem {
    background:
      radial-gradient(circle at 38% 28%, rgba(255,255,255,.96) 0 11%, transparent 22%),
      linear-gradient(135deg, #f1ddff, #b878f3 50%, #6d2db5);
    box-shadow: 0 0 12px rgba(186,114,255,.86);
  }
  #sealPraiseFx.elite .spfText {
    background:
      linear-gradient(180deg, rgba(62,49,91,.9), rgba(38,31,65,.94)),
      radial-gradient(circle at 50% 0, rgba(221,190,255,.38), transparent 70%);
    border-color: rgba(218,183,255,.86);
  }
  @keyframes spfIn {
    0% { opacity: 0; transform: translate(-50%, calc(-50% + 12px)) scale(.82); }
    14% { opacity: 1; transform: translate(-50%, -50%) scale(1.04); }
    68% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
    100% { opacity: 0; transform: translate(-50%, calc(-50% - 28px)) scale(.98); }
  }
  @keyframes spfRing {
    0% { opacity: 0; transform: scale(.5); }
    24% { opacity: .95; }
    100% { opacity: 0; transform: scale(1.7); }
  }
  /* "last piece" quiet cameo (fx/seal.js notifyLastPiece) — corner-anchored, no ring/text/sound, just a
     gentle fade so it never competes with the player's final placing stroke. Deliberately calmer than
     #sealPraiseFx: this is anticipation, not celebration (celebration stays reserved for the win sequence). */
  #lastPieceSha {
    position: fixed;
    z-index: 6;
    width: 46px;
    height: auto;
    transform: translate(-100%, -100%);
    pointer-events: none;
    filter: drop-shadow(0 4px 8px rgba(28,18,4,.3));
    animation: lastPieceIn 2s ease-out both;
  }
  @keyframes lastPieceIn {
    0% { opacity: 0; transform: translate(-100%, -100%) translateY(6px) scale(.9); }
    18% { opacity: 1; transform: translate(-100%, -100%) scale(1); }
    72% { opacity: 1; }
    100% { opacity: 0; }
  }
  body:not([data-screen="game"]) #solveGlow, body:not([data-screen="game"]) #comboGlow { opacity: 0 !important; }
  body:not([data-screen="game"]) #sealPraiseFx { display: none !important; }
  body:not([data-screen="game"]) #lastPieceSha { display: none !important; }
  html[data-motion="reduced"] { #comboGlow.pulse, #sealPraiseFx, #sealPraiseFx .spfRing { animation: none; } #solveGlow, #comboGlow { transition: none; } }
  html[data-motion="reduced"] #lastPieceSha { animation: none; opacity: .9; }
  /* glass specular: a thin top/left edge sheen, screen-blended → light catching the came edge.
     (No sparkle layer — the stained-glass character lives in the flowing reams of .cell.tex::before.) */
  .cell.paint::after, .cell.s1::after, .cell.s2::after, .cell.s3::after, .cell.s4::after, .cell.s5::after, .cell.sX::after {
    content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 1;
    background:
      linear-gradient(180deg, rgba(255,255,255,.28) 0%, rgba(255,255,255,.05) 9%, transparent 16%),
      linear-gradient(90deg,  rgba(255,255,255,.13) 0%, transparent 10%);
    mix-blend-mode: screen;
  }
  /* BACKLIGHT: a warm light pool UNDER the glass (z:2 — above the shards, BELOW the z:3 lead) that slowly
     WANDERS the board. Light reads as a source BEHIND the window: shards brighten via transmission as it
     drifts beneath them, while the lead stays a dark silhouette on top. An organic 2-D wander (not a
     single linear sweep) and ONE GPU-transformed layer (no per-cell animation → no jank). */
  #board .glassLight {
    position: absolute; inset: 0; z-index: 2; pointer-events: none; overflow: hidden; mix-blend-mode: screen;
    will-change: opacity;
    /* AMBIENT BASE: the whole window is gently backlit (daylight through the glass), not a single spotlight.
       Intensity dialed WAY down (2026-06-28) so this layer reads as a whisper of light, never as a visible
       translucent "panel" over the tiles. */
    background: radial-gradient(135% 112% at 50% 58%, rgba(255,245,214,.10) 0%, rgba(255,233,184,.05) 46%, rgba(255,223,162,.02) 74%, transparent 100%);
    animation: glassAmbient 13s ease-in-out infinite;
  }
  #board .glassLight::before {
    content: ""; position: absolute; width: 150%; height: 150%; left: -25%; top: -25%; will-change: transform, opacity;
    /* the SUN: a LARGE, SOFT bright zone (no hard core) that slowly drifts — broad light through the glass,
       not a pinpoint. Combined with the ambient base, the whole window glows and brightens where the sun is. */
    background: radial-gradient(circle at 50% 50%, rgba(255,244,206,.16) 0%, rgba(255,230,172,.10) 20%, rgba(255,218,150,.05) 40%, rgba(255,208,138,.02) 60%, transparent 76%);
    /* wander = where the broad light sits; pulse = MYSTICAL brightness — irregular swells, never steady */
    animation: glassSunWander 21s ease-in-out infinite, glassSunPulse 9.5s ease-in-out infinite;
  }
  @keyframes glassSunWander {
    0%,100% { transform: translate(-6%, -2%); }
    25% { transform: translate(10%, 6%); }
    50% { transform: translate(4%, 14%); }
    75% { transform: translate(-10%, 8%); }
  }
  /* irregular swells/dips at uneven times → a living, mysterious glow rather than a mechanical heartbeat.
     This (9.5s) and the ambient breath (13s) run at different periods, so the combined brightness rarely repeats. */
  @keyframes glassSunPulse { 0% { opacity: .5; } 14% { opacity: .92; } 29% { opacity: .62; } 46% { opacity: 1; } 60% { opacity: .58; } 74% { opacity: .86; } 88% { opacity: .66; } 100% { opacity: .5; } }
  @keyframes glassAmbient { 0%,100% { opacity: .78; } 40% { opacity: 1; } 70% { opacity: .86; } }
  html[data-motion="reduced"] { #board .glassLight { animation: none; } #board .glassLight::before { animation: none; opacity: .7; } }
  /* shape mode: a region placed but not yet a valid shape — warm-neutral tactile tile (no jewel yet),
     so sealing into a valid shape (→ .sN.locked) reads as a vivid pop. */
  .cell.paint { background: #c7b89c; box-shadow: inset 0 2px 0 rgba(255,255,255,.35), inset 0 -5px 9px rgba(0,0,0,.14); }
  /* shape mode palette: "All regions must be one of these shapes" — a raised reference card (depth kit). */
  .palette { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; align-items: center;
             max-width: 92vw; margin: 0 auto 4px; padding: 4px 12px; border-radius: 11px;
             background: var(--grad-card); border: 1px solid var(--line); box-shadow: var(--lift-sm), var(--sheen); }
  /* stage screen (2026-06-25): 2-row palette — label on its own top row, shapes on the wood bar below.
     The container is transparent; the wood status bar moved onto .palShapes (the bottom row). */
  #game .palette { flex-direction: column; flex-wrap: nowrap; gap: 3px; align-items: center;
                   width: min(86vw, 360px); max-width: none; aspect-ratio: auto;
                   background: none; border: none; box-shadow: none; padding: 0; margin: 2px auto 0; border-radius: 0; }
  #game .palShapes { display: flex; flex-wrap: nowrap; gap: clamp(6px,2vw,12px); align-items: center; justify-content: center;
                   width: 100%; aspect-ratio: 652 / 113; box-sizing: border-box; padding: 0 11%;
                   background: url('../../assets/stage/statusbar.webp') center / 100% 100% no-repeat; }
  .palLabel { text-align: center; font-size: 11px; font-weight: 800; color: #fff3d6; margin: 0;
    text-shadow: 0 1px 0 #3a2510, 0 1px 3px rgba(0,0,0,.6); }
  /* on the stage screen the label sits ABOVE the wood bar on the busy game canvas → give it its own dark pill
     so the cream text always reads (a flat brown ink had poor contrast). 2026-06-25. */
  #game .palLabel { color: #fff3d6; font-size: 12px; font-weight: 900; letter-spacing: .2px;
    background: rgba(48,30,14,.62); padding: 2px 13px 3px; border-radius: 999px;
    border: 1px solid rgba(255,226,150,.28); box-shadow: 0 2px 5px rgba(20,10,2,.35), inset 0 1px 0 rgba(255,240,200,.18);
    text-shadow: 0 1px 0 #2a1a0a, 0 1px 3px rgba(0,0,0,.7); }
  .shapeicon { display: grid; gap: 1.5px; }
  .shapeicon .ic { width: 11px; height: 11px; }
  .shapeicon .ic.on { background: linear-gradient(180deg, #ffe08a, #f0b43c);
    border-radius: 2px; box-shadow: inset 0 1px 0 rgba(255,255,255,.5), 0 1px 2px rgba(30,16,2,.5); }
  .shapeicon .ic.off { visibility: hidden; }
  .cell.bad::after { content: ""; position: absolute; inset: 0; background: rgba(255,77,87,.42); }
  /* shard pops into place AND lights up: grows in from small with a punchy overshoot + bright flash */
  @keyframes fillpop {
    0%   { transform: scale(.2);  opacity: 0; filter: brightness(3.4) drop-shadow(0 0 0 rgba(255,240,200,0)); }
    45%  { transform: scale(1.24); opacity: 1; filter: brightness(2)   drop-shadow(0 0 10px rgba(255,240,200,.95)); }
    72%  { transform: scale(.94);             filter: brightness(1.15) drop-shadow(0 0 4px rgba(255,240,200,.4)); }
    100% { transform: scale(1);   opacity: 1; filter: brightness(1)    drop-shadow(0 0 0 rgba(255,240,200,0)); }
  }

  #badge {
    position: fixed; z-index: 50; pointer-events: none;
    transform: translate(-50%, -150%);
    background: #0c0f17; color: #fff; border: 1px solid rgba(255,255,255,.18);
    padding: 4px 9px; border-radius: 999px; font-size: 14px; font-weight: 700;
    box-shadow: 0 4px 14px rgba(0,0,0,.5); display: none;
  }
  #badge.target-met { background: #15351f; border-color: var(--good); color: var(--good); }

  /* bottom action bar: margin-top:auto drops it to the floor of the game column — absorbs the dead
     space below the board and lands the controls in the thumb zone (raised card reads as a real bar) */
  .controls {
    display: flex; gap: 10px; justify-content: center;
    margin-top: auto;
    padding: 10px 16px;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 20px;
    box-shadow: 0 6px 16px rgba(90,76,40,.16), inset 0 1px 0 rgba(255,255,255,.6);
  }
  /* button system (base / .primary / .go / .danger / icon) lives in styles/ui.css — shared with the editor */
  .status { font-size: 13px; font-weight: 600; min-height: 18px; text-align: center; }
  body.reward-sequence #status { opacity: 0; }
  .status.ok { color: var(--good); }
  .status.warn { color: var(--bad); }
  .status.progress { color: var(--muted); }
  .legend { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; font-size: 12px; color: var(--muted); }
  .swatch { display: inline-flex; align-items: center; gap: 4px; }
  .dot { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }

  #win, #ask, #noDrags, #skipModal, #undoAsk {
    position: fixed; inset: 0; z-index: 60; display: none;
    align-items: center; justify-content: center; background: var(--scrim);
    backdrop-filter: none;   /* blur removed — backdrop-filter blur is a major jank source on mobile WebView */
  }
  /* stained-glass plaque: a thick black lead-came border (same language as the board) + a soft bezel */
  #win .card, #ask .card, #noDrags .card, #skipModal .card { background: var(--grad-card); border: 1px solid var(--line); border-radius: 22px; padding: 26px 30px; text-align: center; max-width: 320px; }
  #ask .card, #noDrags .card, #skipModal .card { box-shadow: var(--lift-lg), var(--sheen); }
  #skipModal.armLock .card { pointer-events: none; }
  #skipModal .skipUseBal { opacity: .85; font-weight: 800; }
  #win {
    /* near-opaque dim: the game board behind uses the SAME stone-frame art, so a transparent scrim let
       it bleed through and read as a doubled frame. Darken it so only the modal reads. */
    background:
      radial-gradient(circle at 50% 42%, rgba(255,238,150,.22), transparent 52%),
      rgba(10,15,8,.9);
    backdrop-filter: none;   /* near-opaque dim already covers the board; blur removed for mobile perf */
  }
