/* Orbace Sudoku — Su-Pu replay v2 additions (Su-Pu Replay v2, Phase 1).
 * Namespaced under .osr-v2, which js/orbace-supu-replay-v2.js applies to the
 * board, controls and move-list elements it owns.
 *
 * ISOLATION RULE — enforced by test/replay-core.test.mjs, not just asserted:
 * EVERY selector in this file must begin with `.osr-v2`. css/orbace-supu-
 * replay.css (v1) still styles every live Su-Pu page and both stylesheets
 * load together on the lab page; a bare `.osr-mv` or `.og-cell` rule here
 * would silently restyle v1's boards. Add a rule, keep the prefix.
 *
 * This file adds ONLY what v2 introduces — trial digits, promoted values,
 * contradiction rings, pins, and the four new move-list row kinds. Fills,
 * notes, hints, mistakes and erases keep v1's treatment untouched, inherited
 * from css/orbace-grid.css and css/orbace-supu-replay.css.
 */

/* ---- Palette additions ------------------------------------------------
 * --trial is a FOURTH hue, deliberately far from tea (committed fills),
 * note blue and amber (mistakes), and with no existing meaning in the
 * Orbace palette to collide with. Violet reads as "provisional".
 * A refuted trial line uses --seal, NOT amber: a contradiction is the
 * technique succeeding, not a player error, and amber means "you made a
 * mistake" everywhere else on the site. That distinction is the difference
 * between a replay that teaches and one that scolds.
 * Declared on .osr-v2 rather than :root so nothing outside a v2 replay can
 * pick these up. */
.osr-v2 {
  --trial: #6D4AA8;
  --trial-deep: #553A85;
  --trial-tint: rgba(109, 74, 168, .10);
}

/* ---- Board: trial digit ----------------------------------------------
 * Occupies the same slot as a filled digit (ONE per cell, unlike a note),
 * but italic + a branch-tinted cell + a dotted underline, so it can never
 * be read as a committed value. Base look here (italic, underline,
 * position); actual color comes from one of the 8 `.oglab-trial-cN`
 * classes below, keyed by the owning branch's own sequence number
 * (js/orbace-replay-core.js's trialColorClass()) — the SAME class names
 * su-pu-capture.html / su-pu-lab.html use for the live board, so a replay
 * always shows the identical color for the identical branch. Superseded
 * 2026-09-20: this used to be a plain violet + one alternate hue (`.tb2`)
 * for "first attempt vs. retry" — already wrong the moment a third+
 * distinct branch existed. */
.osr-v2 .og-cell.trial {
  font-weight: 600; font-style: italic;
}
.osr-v2 .og-cell.trial::after {
  content: ""; position: absolute; left: 26%; right: 26%; bottom: 14%; height: 2px;
  background: currentColor; opacity: .5; border-radius: 2px;
}
.osr-v2 .og-cell.trial.oglab-trial-c0 { color: #6D4AA8; background: rgba(109, 74, 168, .10); }
.osr-v2 .og-cell.trial.oglab-trial-c1 { color: #B8477F; background: rgba(184, 71, 127, .10); }
.osr-v2 .og-cell.trial.oglab-trial-c2 { color: #2E7D9A; background: rgba(46, 125, 154, .10); }
.osr-v2 .og-cell.trial.oglab-trial-c3 { color: #8A6420; background: rgba(138, 100, 32, .10); }
.osr-v2 .og-cell.trial.oglab-trial-c4 { color: #A8342E; background: rgba(168, 52, 46, .10); }
.osr-v2 .og-cell.trial.oglab-trial-c5 { color: #3D5FA8; background: rgba(61, 95, 168, .10); }
.osr-v2 .og-cell.trial.oglab-trial-c6 { color: #8A3D6B; background: rgba(138, 61, 107, .10); }
.osr-v2 .og-cell.trial.oglab-trial-c7 { color: #55708A; background: rgba(85, 112, 138, .10); }
.osr-v2 .og-cell .oglab-trial-branchnum {
  position: absolute; top: 1px; right: 2px; font-size: 8px; font-weight: 700;
  font-style: normal; line-height: 1; opacity: .75; pointer-events: none;
}

/* A value promoted from a confirmed trial path reads as a normal committed
   value (it IS one), but keeps a violet footer rule so the replay still shows
   where the digit came from. */
.osr-v2 .og-cell.promoted::after {
  content: ""; position: absolute; left: 20%; right: 20%; bottom: 8%; height: 2px;
  background: var(--trial); opacity: .55;
}

/* ---- Board: current step ----------------------------------------------
 * Which cell(s) the step currently on screen actually touched, kept in
 * sync with the move list's own .osr-mv.cur row via stepCells() in the JS
 * (2026-09-10 UI feedback: "the cell move and the row move are easily out
 * of sync" — the old check only ever read a single `payload.cell_index`,
 * which most non-VALUE_SET event types don't carry their cell reference
 * under at all). A ring, not a fill, for the same reason .contradiction
 * below is a ring: it needs to stay visible over whatever the cell already
 * shows (a mistake, a trial digit, a promoted value) instead of fighting it
 * for the same pixels. Placed before .contradiction in source order so
 * that rule's own ring — the same specificity — wins the rare case where a
 * step's touched cell is also a contradiction cell; that signal matters
 * more. v1's board never gets this class at all (only a v2 board does), so
 * this rule is a pure addition, nothing to collide with there. */
.osr-v2 .og-cell.justplaced {
  box-shadow: inset 0 0 0 2.5px #C9A24B !important;
}

/* ---- Board: contradiction -------------------------------------------
 * The cells that killed a refuted line — the teaching moment. A ring, not a
 * fill, so it layers over whatever the cell already shows instead of fighting
 * it for a background. */
.osr-v2 .og-cell.contradiction {
  box-shadow: inset 0 0 0 2.5px var(--seal) !important;
  background: rgba(196, 30, 58, .10) !important;
  animation: osrV2Contra 1.1s ease-in-out infinite;
}
@keyframes osrV2Contra {
  0%, 100% { box-shadow: inset 0 0 0 2.5px var(--seal); }
  50%      { box-shadow: inset 0 0 0 2.5px var(--seal), inset 0 0 0 6px rgba(196, 30, 58, .20); }
}
@media (prefers-reduced-motion: reduce) {
  .osr-v2 .og-cell.contradiction { animation: none; }
}

/* ---- Board: pinned symbol -------------------------------------------
 * Top-left corner glyph, seal vermilion, deliberately off the digit's optical
 * centre so it never fights a value or a note for the same pixels. */
.osr-v2 .og-pin {
  position: absolute; top: 1px; left: 2px; line-height: 1; pointer-events: none;
  font-size: max(9px, calc(var(--og-cell) * .30)); color: var(--seal);
  text-shadow: 0 0 2px #FFF, 0 0 2px #FFF;
}
.osr-v2 .og-cell.haspin { box-shadow: inset 0 0 0 1.5px rgba(196, 30, 58, .30); }

/* ---- Move list: bounded height (UAT feedback, 2026-09-16) ------------
 * `.osr-list` is applied only to the move-list element (mount() in
 * js/orbace-supu-replay-v2.js), distinct from `.osr-v2` alone which also
 * lands on the board — without a separate class there'd be no way to
 * target "the list, not the board" from here. js sets max-height inline
 * (syncListHeight(), matched to the board's own rendered height — the
 * board is responsive, so no fixed value here would track it); this rule
 * is what actually makes that cap scroll instead of just clipping. */
.osr-v2.osr-list { overflow-y: auto; }

/* ---- Move list: new row kinds ---------------------------------------
 * .osr-mv's base styling comes from css/orbace-supu-replay.css and is reused
 * as-is; only the kinds v1 has no concept of are defined here. */
.osr-v2 .osr-mv.trial { border-left-color: var(--trial); }
.osr-v2 .osr-mv.trial .osr-mv-label { color: var(--trial-deep); }

/* A refuted line is a RESULT, not a failure. Seal vermilion — the same hue
   the technique-hint ring already uses — rather than mistake amber. */
.osr-v2 .osr-mv.refuted { border-left-color: var(--seal); background: rgba(196, 30, 58, .05); }
.osr-v2 .osr-mv.refuted .osr-mv-label { color: var(--seal); }
.osr-v2 .osr-mv.confirmed { border-left-color: var(--tea); background: rgba(62, 107, 79, .07); }
.osr-v2 .osr-mv.confirmed .osr-mv-label { color: var(--tea-deep); }

/* A merge is neither a proof (confirmed's tea) nor a dead end (refuted's
   seal) nor a giveup (abandoned's plain trial hue) — its own sage/celadon
   hue (2026-09-20, Priority 1 fix) so it reads as its own distinct outcome
   next to whichever of those rows follows it. */
.osr-v2 .osr-mv.merged { border-left-color: var(--celadon-d); background: rgba(138, 168, 148, .10); }
.osr-v2 .osr-mv.merged .osr-mv-label { color: var(--celadon-d); }

/* Unsupported/unrecognized resolution — deliberately NOT given a hue of its
   own (no colorClass is ever set for it, see describe()): a "stop safely"
   row should look inert, not confidently colored for an outcome this
   renderer doesn't actually understand. */
.osr-v2 .osr-mv.unsupported { border-left-color: var(--mid); background: rgba(107, 99, 90, .06); }
.osr-v2 .osr-mv.unsupported .osr-mv-label { color: var(--mid); }

/* Nested trial rows (v3) indent by depth so a sub-path visibly sits under
   its parent (unchanged, still depth-based — `--trial-depth`, set by
   orbace-supu-replay-v2.js from the branch stack). UAT feedback
   (2026-09-20): COLOR is no longer depth-based (superseded
   `.tdepth-1/2/3` below) — every trial/refuted/confirmed row is now
   colored by its own OWNING BRANCH's sequence number via one of the same
   8 `.oglab-trial-cN` classes the board uses (js/orbace-supu-replay-v2.js
   sets `info.colorClass`), so a branch reads as the same distinct color
   everywhere, not just "different once nested". This also means a
   REFUTED/CONFIRMED row's branch-color now applies at every depth,
   including the root — previously only depth 1+ got any override at all;
   depth 0 always fell through to the plain red/refuted or green/confirmed
   base color below regardless of which root branch it was. */
.osr-v2 .osr-mv.trial,
.osr-v2 .osr-mv.refuted,
.osr-v2 .osr-mv.confirmed,
.osr-v2 .osr-mv.merged {
  margin-left: calc(var(--trial-depth, 0) * 14px);
}
.osr-v2 .osr-mv.trial.oglab-trial-c0, .osr-v2 .osr-mv.refuted.oglab-trial-c0, .osr-v2 .osr-mv.confirmed.oglab-trial-c0, .osr-v2 .osr-mv.merged.oglab-trial-c0 { border-left-color: #6D4AA8; }
.osr-v2 .osr-mv.trial.oglab-trial-c0 .osr-mv-label { color: #6D4AA8; }
.osr-v2 .osr-mv.trial.oglab-trial-c1, .osr-v2 .osr-mv.refuted.oglab-trial-c1, .osr-v2 .osr-mv.confirmed.oglab-trial-c1, .osr-v2 .osr-mv.merged.oglab-trial-c1 { border-left-color: #B8477F; }
.osr-v2 .osr-mv.trial.oglab-trial-c1 .osr-mv-label { color: #B8477F; }
.osr-v2 .osr-mv.trial.oglab-trial-c2, .osr-v2 .osr-mv.refuted.oglab-trial-c2, .osr-v2 .osr-mv.confirmed.oglab-trial-c2, .osr-v2 .osr-mv.merged.oglab-trial-c2 { border-left-color: #2E7D9A; }
.osr-v2 .osr-mv.trial.oglab-trial-c2 .osr-mv-label { color: #2E7D9A; }
.osr-v2 .osr-mv.trial.oglab-trial-c3, .osr-v2 .osr-mv.refuted.oglab-trial-c3, .osr-v2 .osr-mv.confirmed.oglab-trial-c3, .osr-v2 .osr-mv.merged.oglab-trial-c3 { border-left-color: #8A6420; }
.osr-v2 .osr-mv.trial.oglab-trial-c3 .osr-mv-label { color: #8A6420; }
.osr-v2 .osr-mv.trial.oglab-trial-c4, .osr-v2 .osr-mv.refuted.oglab-trial-c4, .osr-v2 .osr-mv.confirmed.oglab-trial-c4, .osr-v2 .osr-mv.merged.oglab-trial-c4 { border-left-color: #A8342E; }
.osr-v2 .osr-mv.trial.oglab-trial-c4 .osr-mv-label { color: #A8342E; }
.osr-v2 .osr-mv.trial.oglab-trial-c5, .osr-v2 .osr-mv.refuted.oglab-trial-c5, .osr-v2 .osr-mv.confirmed.oglab-trial-c5, .osr-v2 .osr-mv.merged.oglab-trial-c5 { border-left-color: #3D5FA8; }
.osr-v2 .osr-mv.trial.oglab-trial-c5 .osr-mv-label { color: #3D5FA8; }
.osr-v2 .osr-mv.trial.oglab-trial-c6, .osr-v2 .osr-mv.refuted.oglab-trial-c6, .osr-v2 .osr-mv.confirmed.oglab-trial-c6, .osr-v2 .osr-mv.merged.oglab-trial-c6 { border-left-color: #8A3D6B; }
.osr-v2 .osr-mv.trial.oglab-trial-c6 .osr-mv-label { color: #8A3D6B; }
.osr-v2 .osr-mv.trial.oglab-trial-c7, .osr-v2 .osr-mv.refuted.oglab-trial-c7, .osr-v2 .osr-mv.confirmed.oglab-trial-c7, .osr-v2 .osr-mv.merged.oglab-trial-c7 { border-left-color: #55708A; }
.osr-v2 .osr-mv.trial.oglab-trial-c7 .osr-mv-label { color: #55708A; }

.osr-v2 .osr-mv.pin { border-left-color: var(--seal); }
.osr-v2 .osr-mv.pin .osr-mv-label { color: var(--seal); }
.osr-v2 .osr-mv.undo { border-left-color: var(--mid); }
.osr-v2 .osr-mv.undo .osr-mv-label { color: var(--mid); }

/* A true text note is a full-width PROSE row, not a label + coordinate row —
   this is the "row by row presentation" the requirement asks for. */
.osr-v2 .osr-mv.text {
  display: block; border-left-color: var(--ink); background: #FFFDF6;
  font-family: var(--sans); font-size: 13px; padding: 8px 10px;
}
.osr-v2 .osr-mv.text .osr-mv-label {
  display: block; min-width: 0; margin-bottom: 2px;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--mid);
}
/* A post-hoc annotation-layer note is violet-tinted so it is never mistaken
   for something the player wrote during the solve. */
.osr-v2 .osr-mv.text.ann { background: #F4F1FA; border-left-color: var(--trial); }
.osr-v2 .osr-mv.text.ann .osr-mv-label { color: var(--trial-deep); }

/* ---- Trial series readout -------------------------------------------- */
.osr-v2-series {
  display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
  margin-top: 8px; font-size: 12px; color: var(--mid);
}
.osr-v2-badge {
  font-family: var(--mono); font-size: 11px; padding: 2px 7px; border-radius: 20px;
  border: 1px solid var(--trial); color: var(--trial-deep);
}
/* UAT feedback (2026-09-20): the badge is now always colored by the
   branch it's naming (js/orbace-supu-replay-v2.js's renderSeriesReadout()
   sets one of these), superseding the dead `.b2`/`.closed` modifiers
   (never actually applied by any current JS — removed, not kept
   alongside). */
.osr-v2-badge.oglab-trial-c0 { border-color: #6D4AA8; color: #6D4AA8; }
.osr-v2-badge.oglab-trial-c1 { border-color: #B8477F; color: #B8477F; }
.osr-v2-badge.oglab-trial-c2 { border-color: #2E7D9A; color: #2E7D9A; }
.osr-v2-badge.oglab-trial-c3 { border-color: #8A6420; color: #8A6420; }
.osr-v2-badge.oglab-trial-c4 { border-color: #A8342E; color: #A8342E; }
.osr-v2-badge.oglab-trial-c5 { border-color: #3D5FA8; color: #3D5FA8; }
.osr-v2-badge.oglab-trial-c6 { border-color: #8A3D6B; color: #8A3D6B; }
.osr-v2-badge.oglab-trial-c7 { border-color: #55708A; color: #55708A; }
.osr-v2-series s { opacity: .5; }

/* ---- W6 accessibility pass (2026-09-13) --------------------------------
 * This file had zero focus/motion-preference styling before this pass —
 * see js/orbace-supu-replay-v2.js's own W6 comments for the full audit.
 * Every rule here still obeys the .osr-v2 isolation rule at the top of
 * this file. */

/* Move-list rows are now real keyboard targets (role="button", tabindex);
   give them a visible, on-brand focus ring instead of relying on whatever
   default outline the browser happens to draw over this custom background. */
.osr-v2 .osr-mv:focus-visible {
  outline: 2px solid var(--trial); outline-offset: -2px;
}

/* The interactive (lab, click-to-annotate) board's roving keyboard cursor —
   see setFocusedCell() in the JS. Board-level :focus-visible covers the
   single real tab stop; .kbd-focus is the per-cell virtual-cursor ring
   tracked via aria-activedescendant, which needs its own visible marker
   since the cell itself never receives real DOM focus. */
.osr-v2.og-board:focus-visible { outline: 2px solid var(--trial); outline-offset: 2px; }
.osr-v2 .og-cell.kbd-focus { box-shadow: inset 0 0 0 2.5px var(--trial) !important; }

/* ---- Story block (Package C/D wiring, 2026-09-24) ---------------------
 * Read-only render of a published Su-Pu's "Shape the replay" story
 * (title/opening question/ordered chapters/takeaway) — orbace-supu-
 * replay-v2.js's own renderStory(). Own namespace (.osr-story-*), not a
 * reuse of su-pu-capture.html's .spcap-story-preview-* — that CSS isn't
 * loaded on the public /su-pu/:id page this block also needs to render
 * on, and this file's own isolation rule (Suite 5 above) requires every
 * selector here start with .osr-v2 regardless. Visual language matches
 * osr-explain (orbace-supu-replay.css) rather than inventing a new card
 * style: same ivory background, same border/radius, same 13px body copy. */
.osr-v2.osr-story {
  margin-top: 14px; max-width: min(540px, 92vw); padding: 14px 16px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--ivory);
}
.osr-v2.osr-story .osr-story-title {
  font-family: var(--serif); font-weight: 600; font-size: 17px; color: var(--ink); margin-bottom: 4px;
}
.osr-v2.osr-story .osr-story-question {
  font-style: italic; color: var(--tea-deep); font-size: 13px; margin: 0 0 8px;
}
.osr-v2.osr-story .osr-story-chapters {
  list-style: none; margin: 0 0 8px; padding: 0; display: flex; flex-direction: column; gap: 6px;
}
.osr-v2.osr-story .osr-story-chapters li { font-size: 13px; color: var(--ink); }
.osr-v2.osr-story .osr-story-badge {
  display: inline-block; font-family: var(--mono); font-size: 10.5px; letter-spacing: .04em;
  padding: 1px 7px; border-radius: 20px; margin-right: 6px; color: var(--mid); background: var(--rice);
}
.osr-v2.osr-story .osr-story-badge-explain { color: var(--tea-deep); background: rgba(62, 107, 79, .10); }
.osr-v2.osr-story .osr-story-badge-question { color: var(--amber-deep); background: rgba(201, 134, 43, .10); }
.osr-v2.osr-story .osr-story-badge-outcome { color: var(--seal); background: rgba(196, 30, 58, .08); }
.osr-v2.osr-story .osr-story-takeaway {
  font-size: 13px; color: var(--ink); border-top: 1px solid var(--line); padding-top: 8px; margin: 0;
}
