/* capchatthing widget.
 *
 * Keeps the grammar people already recognise — a trigger bar with a state tile,
 * a card with a stage and one control, a quiet utility row — and puts the
 * effort into how it feels in the hand: controls with weight, states that land
 * on the stage where the eye already is, and nothing that moves unless the
 * person did something. */

.cap-shell,
.cap,
.cap-trigger {
  /* Scoped to the widget, never :root — a third-party widget must not repaint
     the host page's scrollbars, canvas or form controls. */
  color-scheme: light;

  --cap-blue: #4d7cfe;
  --cap-blue-deep: #3a63e0;
  --cap-ink: #262b3a;
  --cap-body: #4b5166;
  --cap-mute: #9aa1b4;
  --cap-faint: #c6cbd6;
  --cap-line: #e6e9f0;
  --cap-well: #f1f3f8;
  --cap-ok: #1fa971;
  --cap-bad: #ef4b4f;
  /* Deliberately not themed: its only consumer is .cap-mark, which is appended
     to the stage and therefore always sits on the generated puzzle image, never
     on a themed surface. */
  --cap-teal: #3ee0be;
  --cap-font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;

  /* surfaces & effects — light defaults; the dark block below overrides these */
  --cap-surface: #fff;
  --cap-tile-bg: #fff;
  --cap-tile-border: #c3c9d8;
  --cap-socket: #e3e7ee;
  --cap-ok-text: #17835a;
  --cap-fill: #e3ebff; --cap-fill-ok: #dcf4e8; --cap-fill-bad: #fde3e4;
  --cap-btn-off-bg: #e3e8f3; --cap-btn-off-text: #a4acbf;
  --cap-result-bg: rgba(255, 255, 255, .74);
  --cap-skel-a: #eef0f5; --cap-skel-b: #e2e6ee;
  --cap-ring: inset 0 0 0 1px rgba(20, 26, 44, .06);
  --cap-inset: inset 0 1px 3px rgba(20, 26, 44, .09);
  --cap-shadow-bar: 0 1px 2px rgba(20, 26, 44, .05), 0 8px 22px rgba(20, 26, 44, .12);
  --cap-shadow-card: 0 1px 2px rgba(20, 26, 44, .06), 0 18px 44px rgba(20, 26, 44, .24);
}

/* ── dark mode: system preference, unless the host has chosen explicitly ──
 *
 * PAIRED EDIT. Every dark rule below exists twice: once under
 * [data-theme="dark"] for an explicit choice, and once under
 * :root:not([data-theme="light"]) inside a prefers-color-scheme query for the
 * system default. Both are needed — the :not() is what lets an explicit light
 * choice win on a dark OS. Four pairs in this file; change one, change its twin,
 * or an explicit toggle silently renders differently from a system preference:
 *
 *   this token block                        (here, and in the @media below)
 *   .cap__hints img
 *   .cap-cell[data-piece="1"] .cap-dot
 *
 * light-dark() would collapse these, but it is not widely available until late
 * 2026 and old browsers fall back to the light branch. For public CAPTCHA
 * traffic that tail is exactly who we cannot afford to render wrong. */
:root[data-theme="dark"] .cap-shell,
:root[data-theme="dark"] .cap,
:root[data-theme="dark"] .cap-trigger {
    color-scheme: dark;

    --cap-blue: #6b93ff; --cap-blue-deep: #4d7cfe;
    --cap-ink: #e8ebf2; --cap-body: #c3c8d4; --cap-mute: #8b92a3; --cap-faint: #5f6678;
    --cap-line: #2c3240; --cap-well: #262b36;
    --cap-ok: #2ec48a; --cap-bad: #f25d61;
    --cap-surface: #1c2029; --cap-tile-bg: #262b36; --cap-tile-border: #3a4152;
    --cap-socket: #2e3440; --cap-ok-text: #5fd9a6;
    --cap-fill: rgba(107, 147, 255, .18); --cap-fill-ok: rgba(46, 196, 138, .18); --cap-fill-bad: rgba(242, 93, 97, .18);
    --cap-btn-off-bg: #2c3240; --cap-btn-off-text: #6b7385;
    --cap-result-bg: rgba(28, 32, 41, .78);
    --cap-skel-a: #262b36; --cap-skel-b: #2f3541;
    --cap-ring: inset 0 0 0 1px rgba(255, 255, 255, .10);
    --cap-inset: inset 0 1px 3px rgba(0, 0, 0, .35);
    --cap-shadow-bar: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 22px rgba(0, 0, 0, .35);
    /* Over a dark page a heavy ambient shadow reads as a vignette, not as
       height. Depth in dark comes from the lighter surface and the edge ring. */
    --cap-shadow-card: 0 1px 2px rgba(0, 0, 0, .4), 0 18px 44px rgba(0, 0, 0, .32);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .cap-shell,
  :root:not([data-theme="light"]) .cap,
  :root:not([data-theme="light"]) .cap-trigger {
    color-scheme: dark;

    --cap-blue: #6b93ff; --cap-blue-deep: #4d7cfe;
    --cap-ink: #e8ebf2; --cap-body: #c3c8d4; --cap-mute: #8b92a3; --cap-faint: #5f6678;
    --cap-line: #2c3240; --cap-well: #262b36;
    --cap-ok: #2ec48a; --cap-bad: #f25d61;
    --cap-surface: #1c2029; --cap-tile-bg: #262b36; --cap-tile-border: #3a4152;
    --cap-socket: #2e3440; --cap-ok-text: #5fd9a6;
    --cap-fill: rgba(107, 147, 255, .18); --cap-fill-ok: rgba(46, 196, 138, .18); --cap-fill-bad: rgba(242, 93, 97, .18);
    --cap-btn-off-bg: #2c3240; --cap-btn-off-text: #6b7385;
    --cap-result-bg: rgba(28, 32, 41, .78);
    --cap-skel-a: #262b36; --cap-skel-b: #2f3541;
    --cap-ring: inset 0 0 0 1px rgba(255, 255, 255, .10);
    --cap-inset: inset 0 1px 3px rgba(0, 0, 0, .35);
    --cap-shadow-bar: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 22px rgba(0, 0, 0, .35);
    /* Over a dark page a heavy ambient shadow reads as a vignette, not as
       height. Depth in dark comes from the lighter surface and the edge ring. */
    --cap-shadow-card: 0 1px 2px rgba(0, 0, 0, .4), 0 18px 44px rgba(0, 0, 0, .32);
  }
}

/* Single source of truth for width: trigger, skeleton and card all read it, so
   the widget never changes width between states or between modes, wherever it
   is embedded. Override --cap-width on the host to resize the whole thing. */
.cap-shell { --cap-width: 348px; display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }

/* One ring for everything focusable — trigger, buttons, tiles and the slider
   handle all live inside the shell. The offset lifts it clear of the element, so
   a blue handle or button gets its ring on the surface behind it rather than on
   its own blue. Token-driven, so it follows a host brand override for free. */
.cap-shell :focus-visible {
  outline: 2px solid var(--cap-blue);
  outline-offset: 2px;
}
/* max-width matters as much as width: .cap-trigger and .cap are already
   guarded, so the slot was the one element that could push a narrow phone
   into horizontal scroll. The widget ships no media queries by design —
   a host resizes it by overriding --cap-width — but it should still not
   overflow a host that never thought about it. */
.cap-slot { width: var(--cap-width); max-width: 100%; }

/* ── trigger bar ──────────────────────────────────────────────────────── */

.cap-trigger {
  display: flex; align-items: center; gap: 13px;
  width: var(--cap-width); max-width: 100%; height: 58px; padding: 0 15px 0 14px;
  background: var(--cap-surface); border: 1px solid var(--cap-line); border-radius: 8px;
  box-shadow: var(--cap-shadow-bar);
  font: 500 15px/1 var(--cap-font); color: var(--cap-ink);
  cursor: pointer; text-align: left;
  transition: border-color .18s ease, box-shadow .18s ease, transform .12s ease;
}
.cap-trigger:hover { border-color: var(--cap-tile-border); }
.cap-trigger:active { transform: translateY(1px); }
.cap-trigger[disabled] { cursor: default; transform: none; }
.cap-trigger[disabled]:hover { border-color: var(--cap-line); box-shadow: var(--cap-shadow-bar); }
.cap-trigger[hidden] { display: none; }

.cap-trigger__tile {
  position: relative; flex: none; width: 30px; height: 30px;
  border: 1.5px solid var(--cap-tile-border); border-radius: 8px; background: var(--cap-tile-bg);
  display: grid; place-items: center;
  transition: border-color .18s ease, background .22s ease, transform .22s cubic-bezier(.3, 1.4, .5, 1);
}
.cap-trigger:hover .cap-trigger__tile { border-color: var(--cap-blue); }
.cap-trigger__tile svg {
  position: absolute; width: 18px; height: 18px; opacity: 0;
  fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round;
  transition: opacity .18s ease;
}
.cap-trigger[data-state="busy"] .cap-trigger__tile { border-color: var(--cap-blue); color: var(--cap-blue); }
.cap-trigger[data-state="busy"] .cap-trigger__spin { opacity: 1; animation: cap-spin .8s linear infinite; }
.cap-trigger[data-state="done"] .cap-trigger__tile { background: var(--cap-ok); border-color: var(--cap-ok); color: #fff; transform: scale(1.06); }
.cap-trigger[data-state="done"] .cap-trigger__check { opacity: 1; }
.cap-trigger[data-state="done"] .cap-trigger__check path { stroke-dasharray: 26; animation: cap-draw .4s ease-out both; }
.cap-trigger[data-state="error"] .cap-trigger__tile { border-color: var(--cap-bad); color: var(--cap-bad); }
.cap-trigger[data-state="error"] .cap-trigger__cross { opacity: 1; }

.cap-trigger__text { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cap-trigger[data-state="done"] .cap-trigger__text { color: var(--cap-ok-text); }
.cap-trigger[data-state="error"] .cap-trigger__text { color: var(--cap-bad); }

.cap-trigger__brand, .cap__brand {
  display: flex; align-items: center; gap: 5px;
  font: 600 10.5px/1 var(--cap-font); letter-spacing: .09em; color: var(--cap-faint);
}
.cap-trigger__brand svg, .cap__brand svg { width: 14px; height: 14px; }

@keyframes cap-spin { to { transform: rotate(360deg); } }
@keyframes cap-draw { from { stroke-dashoffset: 26; } to { stroke-dashoffset: 0; } }

/* ── pull trigger ─────────────────────────────────────────────────────── */
/* trigger: 'pull' — the checkbox bar's alternative: a card with a handle the
   visitor pulls to the right. Same width, surface, hairline, radius, shadow
   and type as .cap-trigger, so the widget keeps its footprint whichever
   trigger a template picks. Only tokens are used, so it follows the dark
   blocks above without a rule of its own. */
.cap-pull {
  width: var(--cap-width); max-width: 100%;
  background: var(--cap-surface); border: 1px solid var(--cap-line); border-radius: 8px;
  box-shadow: var(--cap-shadow-bar);
  padding: 12px 12px 10px;
  font: 400 15px/1.3 var(--cap-font); color: var(--cap-ink);
  user-select: none; -webkit-user-select: none;
}
.cap-pull__track {
  position: relative; height: 48px;
  background: var(--cap-well); border-radius: 8px; overflow: hidden;
}
.cap-pull__handle {
  position: absolute; top: 4px; left: 4px;
  width: 64px; height: 40px; padding: 0;
  display: grid; place-items: center;
  background: var(--cap-blue); color: #fff;
  border: 0; border-radius: 6px;
  box-shadow: 0 1px 3px rgba(20, 26, 44, .25);
  cursor: grab; touch-action: none;
}
.cap-pull--snap .cap-pull__handle { transition: left .28s cubic-bezier(.2, .8, .3, 1); }
.cap-pull[data-state="dragging"] .cap-pull__handle { cursor: grabbing; }
.cap-pull__handle:hover { background: var(--cap-blue-deep); }
.cap-pull__handle[disabled] { cursor: default; }
.cap-pull__handle svg {
  position: absolute; width: 18px; height: 18px; opacity: 0;
  fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round;
  transition: opacity .18s ease;
}
.cap-pull__handle .cap-pull__chev { width: 10px; height: 16px; opacity: 1; }
.cap-pull[data-state="busy"] .cap-pull__chev { opacity: 0; }
.cap-pull[data-state="busy"] .cap-pull__spin { opacity: 1; animation: cap-spin .8s linear infinite; }
.cap-pull[data-state="done"] .cap-pull__handle { background: var(--cap-ok); }
.cap-pull[data-state="done"] .cap-pull__chev { opacity: 0; }
.cap-pull[data-state="done"] .cap-pull__check { opacity: 1; }
.cap-pull[data-state="done"] .cap-pull__check path { stroke-dasharray: 26; animation: cap-draw .4s ease-out both; }
.cap-pull[data-state="error"] .cap-pull__handle { background: var(--cap-bad); }

/* The label sits clear of the handle at rest and fades as the handle crosses
   it; while the check runs it is centred and quiet. */
.cap-pull__label {
  position: absolute; top: 0; left: 100px; height: 48px; line-height: 48px;
  white-space: nowrap; pointer-events: none;
  font-weight: 500; color: var(--cap-ink);
}
.cap-pull__label span { font-weight: 400; color: var(--cap-mute); }
.cap-pull[data-state="busy"] .cap-pull__label,
.cap-pull[data-state="done"] .cap-pull__label { left: 0; right: 0; text-align: center; padding-left: 40px; font-weight: 400; color: var(--cap-mute); }
.cap-pull[data-state="done"] .cap-pull__label { color: var(--cap-ok-text); }
.cap-pull[data-state="error"] .cap-pull__label { color: var(--cap-bad); }
.cap-pull__foot { margin: 10px 0 0 2px; font-size: 12.5px; line-height: 1.3; letter-spacing: .01em; color: var(--cap-mute); }

/* ── modal placement ──────────────────────────────────────────────────── */
/* placement: 'modal' — the slot lives on a scrim over the page instead of
   under the trigger. The card is the card; nothing is added around it, and
   its own close button is the way out. Inside the shell on purpose, so the
   tokens reach it. */
.cap-scrim {
  position: fixed; inset: 0; z-index: 2147483000;
  display: flex;
  padding: 16px;
  overflow: auto; -webkit-overflow-scrolling: touch;
  background: rgba(20, 26, 44, .45);
}
.cap-scrim[hidden] { display: none; }
/* margin:auto, not align-items:center — centred when the card fits, and
   scrollable from its top edge when a short viewport cannot hold it. */
.cap-scrim .cap-slot { margin: auto; }

/* ── card ─────────────────────────────────────────────────────────────── */

.cap {
  position: relative;
  width: var(--cap-width); max-width: 100%;
  font: 400 14px/1.45 var(--cap-font); color: var(--cap-body);
  background: var(--cap-surface); border-radius: 8px;
  box-shadow: var(--cap-shadow-card);
  user-select: none; -webkit-user-select: none;
}
/* Caret pointing back at the tile it came from. */
.cap::before {
  content: ""; position: absolute; top: -6px; left: 22px;
  border: 6px solid transparent; border-top: 0; border-bottom-color: var(--cap-blue);
}
/* caret: false — a template whose card does not sit under a trigger bar (a
   modal, a page-level card) has nothing for the caret to point at. */
.cap-shell--nocaret .cap::before { display: none; }
.cap__rule { height: 3px; border-radius: 8px 8px 0 0; background: linear-gradient(90deg, var(--cap-blue), var(--cap-blue-deep)); }

.cap__head { display: flex; align-items: center; gap: 10px; padding: 15px 16px 12px; }
.cap__title { flex: 1; font-size: 15px; font-weight: 500; color: var(--cap-ink); letter-spacing: -.004em; text-align: center; }
.cap__hints { display: flex; gap: 5px; align-items: center; }
.cap__hints img {
  width: 26px; height: 26px; padding: 4px; display: block;
  background: var(--cap-well); border-radius: 7px;
  box-shadow: var(--cap-ring);
}
/* PAIRED EDIT — twin in the @media block below. */
:root[data-theme="dark"] .cap__hints img { background: #dde0e6; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .cap__hints img { background: #dde0e6; }
}

.cap-slot--animating { overflow: hidden; transition: height .3s cubic-bezier(.2, .8, .2, 1); }

.cap__stage {
  position: relative; margin: 0 16px; border-radius: 6px; overflow: hidden;
  background: var(--cap-well); box-shadow: var(--cap-ring);
}
.cap__stage > img { display: block; width: 100%; }

.cap__action { padding: 14px 16px 0; }
.cap__action:empty { display: none; }

.cap__note { padding: 10px 16px 4px; font-size: 12.5px; min-height: 26px; color: var(--cap-mute); }
.cap__note:empty { padding-top: 6px; }
.cap__note[data-state="ok"] { color: var(--cap-ok); }
.cap__note[data-state="bad"] { color: var(--cap-bad); }

/* ── slider ───────────────────────────────────────────────────────────── */

.cap-piece {
  position: absolute; pointer-events: none;
  filter: drop-shadow(0 3px 5px rgba(0, 0, 0, .38));
}
/* strip mode: two layers over one stage. The upper halves never move; the layer
   of lower halves slides behind them and is transparent above the cut. */
.cap-strip-base { display: block; width: 100%; }
.cap-strip-slide { position: absolute; top: 0; left: 0; height: 100%; width: auto; max-width: none; }
/* The strip's thumbnail is landscape, so it cannot use the square hint tile. */
.cap__hints img.cap-hint--wide { width: 40px; height: 28px; object-fit: contain; }

/* The slider drives the piece from its track, so the piece itself ignores the
   pointer. Drag mode is the opposite — the piece is the control. */
.cap-piece--drag { pointer-events: auto; cursor: grab; touch-action: none; }
.cap-piece--held { cursor: grabbing; filter: drop-shadow(0 6px 10px rgba(0, 0, 0, .45)); }

.cap-track {
  position: relative; height: 46px; border-radius: 23px;
  background: var(--cap-well); box-shadow: var(--cap-inset);
}
.cap-track__hint {
  position: absolute; inset: 0; padding-left: 54px;
  display: grid; place-items: center;
  font-size: 13px; color: var(--cap-mute); pointer-events: none;
  transition: opacity .15s ease;
}
.cap-track--moving .cap-track__hint { opacity: 0; }
.cap-track__fill {
  position: absolute; inset: 0 auto 0 0; width: 0;
  background: var(--cap-fill); border-radius: 23px;
  transition: background .2s ease;
}
.cap-track__handle {
  position: absolute; top: 0; left: 0; width: 66px; height: 46px;
  display: grid; place-items: center; cursor: grab; touch-action: none;
  border-radius: 23px;
  background: linear-gradient(180deg, #5d89ff, #3f6df0);
  box-shadow: 0 3px 8px rgba(63, 109, 240, .42), inset 0 1px 0 rgba(255, 255, 255, .28);
  transition: transform .12s ease, background .2s ease, box-shadow .2s ease;
}
.cap-track__handle:hover { transform: scale(1.03); }
.cap-track__handle:active { cursor: grabbing; transform: scale(1.03); }
.cap-track__handle svg {
  position: absolute; width: 20px; height: 20px; opacity: 0;
  fill: none; stroke: #fff; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round;
  transition: opacity .15s ease;
}
.cap-track__handle .cap-i-arrow { opacity: 1; }
.cap--ok .cap-track__handle { background: linear-gradient(180deg, #36cc8c, #1fa971); box-shadow: 0 3px 8px rgba(31, 169, 113, .38); }
.cap--ok .cap-track__handle .cap-i-arrow { opacity: 0; }
.cap--ok .cap-track__handle .cap-i-check { opacity: 1; }
.cap--ok .cap-track__fill { background: var(--cap-fill-ok); }
.cap--bad .cap-track__handle { background: linear-gradient(180deg, #f56b6e, #e8383d); box-shadow: 0 3px 8px rgba(239, 75, 79, .38); }
.cap--bad .cap-track__handle .cap-i-arrow { opacity: 0; }
.cap--bad .cap-track__handle .cap-i-cross { opacity: 1; }
.cap--bad .cap-track__fill { background: var(--cap-fill-bad); }

/* ── button ───────────────────────────────────────────────────────────── */

.cap-btn {
  width: 100%; height: 46px; border: 0; border-radius: 8px;
  font: 500 15px/1 var(--cap-font); color: #fff; cursor: pointer;
  background: linear-gradient(180deg, #5d89ff, #3f6df0);
  box-shadow: 0 3px 8px rgba(63, 109, 240, .32);
  transition: transform .12s ease, box-shadow .18s ease, background .2s ease;
}
.cap-btn:hover { box-shadow: 0 5px 14px rgba(63, 109, 240, .4); }
.cap-btn:active { transform: translateY(1px); }
.cap-btn[disabled] { background: var(--cap-btn-off-bg); color: var(--cap-btn-off-text); box-shadow: none; cursor: default; transform: none; }

/* ── rotate dial ──────────────────────────────────────────────────────── */

.cap-dial {
  position: relative; display: grid; place-items: center;
  padding: 18px 0 10px; cursor: grab; touch-action: none;
}
.cap-dial--turning { cursor: grabbing; }

/* The disc and the knob turn as one element, so the knob orbits the rim and
   reads as the dial's current position. A bare circle gives no clue that it can
   be turned at all — go-captcha answers that with a slider underneath, but a
   slider collapses the two-axis drag back to one axis and takes the trajectory
   data with it. A knob keeps the motion and supplies the affordance. */
.cap-dial__turn {
  position: relative; display: grid; place-items: center;
  width: 64%; max-width: 200px;
}
.cap-dial__disc {
  display: block; width: 100%; border-radius: 50%;
  /* The ring is the one styling idea worth taking from go-captcha: it reads as
     a dial you turn, not a picture that happens to be round. */
  /* A circle, so its height is knowable before the image decodes. Without this
     the dial is zero tall until load and the card snaps. */
  aspect-ratio: 1;
  box-shadow: 0 0 0 6px var(--cap-socket), 0 6px 18px rgba(0, 0, 0, .28);
  user-select: none; -webkit-user-select: none; -webkit-user-drag: none;
}
.cap-dial__knob {
  position: absolute; top: -9px; left: 50%; width: 22px; height: 22px;
  margin-left: -11px; border-radius: 50%;
  background: linear-gradient(180deg, #5d89ff, #3f6df0);
  box-shadow: 0 0 0 3px var(--cap-surface), 0 2px 6px rgba(0, 0, 0, .42),
              inset 0 1px 0 rgba(255, 255, 255, .3);
}
.cap-dial__hint {
  display: flex; align-items: center; gap: 5px;
  margin-top: 12px; font-size: 12.5px; color: var(--cap-mute);
  pointer-events: none;
}
.cap-dial__hinticon { width: 15px; height: 15px; }
/* The hint has said its piece the moment the dial moves. */
.cap-dial--touched .cap-dial__hint { opacity: 0; }

/* ── click markers (icon-order) ───────────────────────────────────────── */

.cap-mark {
  position: absolute; width: 28px; height: 28px; margin: -14px 0 0 -14px;
  border: 2.5px solid var(--cap-teal); border-radius: 50%;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, .28), 0 2px 8px rgba(0, 0, 0, .35);
  pointer-events: none;
}
.cap-mark__n {
  position: absolute; right: -8px; bottom: -8px; width: 17px; height: 17px;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--cap-teal); color: #0b2b24;
  font: 700 10px/1 var(--cap-font);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
}

/* ── boards (gobang, match3) ──────────────────────────────────────────── */

.cap-board {
  display: grid; gap: 8px; padding: 12px;
  background: var(--cap-well); border-radius: 8px;
  box-shadow: inset 0 1px 3px rgba(20, 26, 44, .07);
}
.cap-cell {
  position: relative; aspect-ratio: 1; border: 0; padding: 0; background: none;
  display: grid; place-items: center; border-radius: 10px; cursor: pointer;
  transition: transform .12s ease;
}
.cap-cell:hover { transform: translateY(-1px); }

.cap-dot {
  width: 80%; height: 80%; border-radius: 50%;
  background: var(--cap-socket);
  box-shadow: inset 0 1.5px 3px rgba(20, 26, 44, .13);
  transition: transform .15s cubic-bezier(.3, 1.3, .5, 1), box-shadow .15s ease;
}
.cap-cell[data-piece] .cap-dot {
  background: radial-gradient(circle at 36% 30%, color-mix(in srgb, var(--piece) 62%, #fff) 0%, var(--piece) 68%);
  box-shadow: 0 3px 6px rgba(20, 26, 44, .24), inset 0 -3px 5px rgba(0, 0, 0, .16), inset 0 2px 3px rgba(255, 255, 255, .3);
}
.cap-cell[aria-pressed="true"] .cap-dot {
  transform: scale(1.12);
  box-shadow: 0 0 0 3px var(--cap-surface), 0 0 0 5px var(--cap-blue), 0 8px 14px rgba(20, 26, 44, .3);
}
/* The server names piece colours for a light board; the navy one has no
   contrast on a dark well, so dark mode remaps it. */
/* PAIRED EDIT — twin in the @media block below. */
:root[data-theme="dark"] .cap-cell[data-piece="1"] .cap-dot { --piece: #9aa3c2 !important; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .cap-cell[data-piece="1"] .cap-dot { --piece: #9aa3c2 !important; }
}

.cap-tile {
  background: var(--cap-tile-bg); font-size: 60px; line-height: 1; overflow: hidden;
  box-shadow: 0 1px 2px rgba(20, 26, 44, .06), 0 2px 6px rgba(20, 26, 44, .07);
  transition: transform .14s ease, box-shadow .14s ease;
}
.cap-tile:hover { box-shadow: 0 2px 4px rgba(20, 26, 44, .08), 0 6px 12px rgba(20, 26, 44, .1); }
.cap-tile[aria-pressed="true"] {
  transform: translateY(-2px);
  box-shadow: 0 0 0 2px var(--cap-blue), 0 6px 14px rgba(20, 26, 44, .16);
}
.cap-tile svg { width: 78%; height: 78%; display: block; filter: drop-shadow(0 2px 2px rgba(20, 26, 44, .16)); }
.cap-tile--swap { animation: cap-bump .26s cubic-bezier(.3, 1.4, .5, 1); }
@keyframes cap-bump { 40% { transform: scale(1.12); } }

/* ── result on the stage ──────────────────────────────────────────────── */

.cap-result {
  position: absolute; inset: 0; z-index: 2;
  display: grid; place-content: center; justify-items: center; gap: 9px;
  background: var(--cap-result-bg);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
}
.cap-result__icon {
  width: 54px; height: 54px; border-radius: 50%; display: grid; place-items: center;
}
.cap-result--ok .cap-result__icon { background: var(--cap-ok); box-shadow: 0 8px 20px rgba(31, 169, 113, .38); }
.cap-result--bad .cap-result__icon { background: var(--cap-bad); box-shadow: 0 8px 20px rgba(239, 75, 79, .36); }
.cap-result__icon svg {
  width: 26px; height: 26px;
  fill: none; stroke: #fff; stroke-width: 2.8; stroke-linecap: round; stroke-linejoin: round;
}
.cap-result__text { font: 500 14px/1 var(--cap-font); color: var(--cap-ink); }

/* ── footer ───────────────────────────────────────────────────────────── */

.cap__foot {
  display: flex; align-items: center; gap: 2px;
  margin-top: 6px; padding: 8px 10px 10px; border-top: 1px solid var(--cap-line);
}
.cap__foot button {
  width: 32px; height: 32px; border: 0; padding: 0; background: none; cursor: pointer;
  display: grid; place-items: center; border-radius: 7px; color: var(--cap-mute);
  transition: color .15s ease, background .15s ease;
}
.cap__foot button:hover { color: var(--cap-ink); background: var(--cap-well); }
.cap__foot svg { width: 18px; height: 18px; }
.cap__brand { margin-left: auto; padding-right: 6px; }

/* ── skeleton ─────────────────────────────────────────────────────────── */

.cap-skeleton { padding: 16px 16px 18px; }
.cap-skeleton__line, .cap-skeleton__stage, .cap-skeleton__bar {
  background: linear-gradient(90deg, var(--cap-skel-a) 0%, var(--cap-skel-b) 40%, var(--cap-skel-a) 80%);
  background-size: 300% 100%; border-radius: 6px;
}
.cap-skeleton__line { height: 13px; width: 58%; margin: 4px auto 18px; }
.cap-skeleton__stage { height: 188px; margin-bottom: 16px; }
.cap-skeleton__bar { height: 46px; border-radius: 23px; }

/* ── motion: only in answer to something the person did ───────────────── */

@media (prefers-reduced-motion: no-preference) {
  .cap:not(.cap--swap) { animation: cap-in .2s ease-out; }
  /* Contents fade instead, so a skeleton-to-card swap changes what is inside
     the shell without the shell itself flashing again. */
  .cap--swap > .cap__head, .cap--swap > .cap__stage, .cap--swap > .cap__action {
    animation: cap-fade .18s ease-out;
  }
  @keyframes cap-in { from { opacity: 0; transform: scale(.99); } }

  .cap-result { animation: cap-fade .18s ease-out; }
  .cap-result__icon { animation: cap-pop .32s cubic-bezier(.3, 1.5, .5, 1); }
  @keyframes cap-fade { from { opacity: 0; } }
  @keyframes cap-pop { from { transform: scale(.5); opacity: 0; } }

  .cap-mark { animation: cap-pop .2s cubic-bezier(.3, 1.5, .5, 1); }

  /* A slow breath on the knob until the dial is first touched: enough to read as
     operable, not enough to nag. */
  .cap-dial:not(.cap-dial--touched) .cap-dial__knob { animation: cap-knob 2.4s ease-in-out infinite; }
  @keyframes cap-knob { 50% { transform: scale(1.16); } }
  .cap-dial__hint { transition: opacity .2s ease; }

  .cap--shake { animation: cap-shake .4s; }
  @keyframes cap-shake {
    20% { transform: translateX(-7px); } 40% { transform: translateX(6px); }
    60% { transform: translateX(-4px); } 80% { transform: translateX(3px); }
  }

  .cap-skeleton__line, .cap-skeleton__stage, .cap-skeleton__bar { animation: cap-sheen 1.1s linear infinite; }
  @keyframes cap-sheen { from { background-position: 150% 0; } to { background-position: -150% 0; } }
}

/* ── narrow viewports ─────────────────────────────────────────────────────
 *
 * The widget ships no breakpoints for sizing — a host resizes it by overriding
 * --cap-width — but internal chrome is a different matter. On a 288px card,
 * 16px of padding either side is a tenth of the playable width, and gobang's
 * 5x5 cells fall to 43px, just under the 44px anyone can reliably hit.
 * Reclaiming the chrome is what buys those cells back. */
@media (max-width: 400px) {
  .cap__head { padding: 13px 12px 10px; }
  .cap__stage { margin: 0 12px; }
  .cap__action { padding: 12px 12px 0; }
  .cap__note { padding: 8px 12px 4px; }
  .cap-skeleton { padding: 14px 12px 16px; }
  .cap-board { gap: 6px; padding: 10px; }
  /* The glyph has to shrink with its cell or it overflows a 3x3 at this width. */
  .cap-tile { font-size: 44px; }
  .cap-dial { padding: 12px 0 8px; }
}

/* Touch pointers, at any width: the footer icons are 32px, which is fine for a
   cursor and too small for a thumb. This keys on the input device rather than
   the viewport, because a small window on a desktop does not need bigger
   targets and a large tablet does. */
@media (pointer: coarse) {
  .cap__foot button { width: 42px; height: 42px; }
  .cap__foot { padding: 6px 8px 8px; }
  .cap-track__handle { width: 72px; }
}
