/* Stroke-order panel (js/stroke-order.js). Every colour comes from the title's own palette
   through the variables js/title.js writes on :root (the same variables app.css defaults),
   so the panel matches whichever title opened it. */
.stroke-order-dialog { color:var(--ink); background:var(--paper); border:1px solid var(--line); border-radius:12px; width:min(900px, calc(100vw - 24px)); max-height:90dvh; box-sizing:border-box; padding:20px; overflow:auto; }
.stroke-order-dialog::backdrop { background:rgba(var(--ink-rgb), .45); }
.stroke-order-dialog header { display:flex; align-items:center; justify-content:space-between; gap:12px; }
.stroke-order-dialog h2 { margin:0; font-size:1.4rem; }
.stroke-order-dialog button,.stroke-order-dialog select { min-height:44px; font:inherit; padding:8px 12px; border:1px solid var(--line); border-radius:8px; background:var(--panel); color:var(--ink); }
.stroke-order-dialog button:focus-visible,.stroke-order-dialog select:focus-visible { outline:3px solid var(--teal); outline-offset:2px; }
.stroke-order-dialog label { display:inline-flex; gap:8px; align-items:center; margin:5px; }
.stroke-order-dialog p { line-height:1.5; }
/* The panes area is one column by default: Watch, then the reserved point preview, then Practice
   (see the order rules below). A wide screen promotes it to three real columns so the preview is
   reserved BESIDE the pad. Either way the pad is a square that js/stroke-order.js sizes from the
   room the dialog really has, never a CSS-scaled drawing. */
.stroke-order-panes { display:grid; grid-template-columns:minmax(0,1fr); gap:16px; }
.stroke-order-panes canvas { display:block; width:100%; aspect-ratio:1; object-fit:contain; background:var(--panel); border:1px solid var(--line); border-radius:8px; box-sizing:border-box; }
.stroke-order-panes canvas[data-field="practice"] { touch-action:none; }
/* Wide: Watch | Practice | the reserved point preview. This is the layout that keeps the crop
   readable at the same time as the pad, which is what "simultaneous preview while writing" needs. */
@media(min-width:900px) { .stroke-order-panes { grid-template-columns:minmax(0,1fr) minmax(0,1fr) 220px; gap:20px; align-items:start; } }
.stroke-order-controls { display:flex; flex-wrap:wrap; gap:6px; margin-top:10px; }
.stroke-order-dialog [data-field="attribution"] { font-size:.85rem; overflow-wrap:anywhere; }
.stroke-order-dialog a { color:var(--accent-ink, var(--ink)); text-decoration:underline; }
@media(max-width:650px) { .stroke-order-dialog { padding:14px; } }

.stroke-order-practice { position:relative; }
.stroke-order-practice canvas[data-field="guide"] { position:absolute; inset:0; width:100%; height:100%; opacity:.18; pointer-events:none; }
.stroke-order-practice canvas[data-field="practice"] { position:relative; background:transparent; }

/* Vector lessons (Hanzi Writer): the two stage boxes for a title whose strokes are SVG paths.
   js/stroke-order.js gives the <svg> an explicit pixel size, because Hanzi Writer emits no
   viewBox — it draws in exactly the box it is handed, so the box has to be the box we show.
   touch-action:none keeps a finger or a stylus from scrolling the page while tracing a stroke. */
.stroke-order-vector { position:relative; background:var(--panel); border:1px solid var(--line); border-radius:8px; box-sizing:border-box; touch-action:none; }
.stroke-order-vector svg { display:block; touch-action:none; }
.stroke-order-vector svg * { touch-action:none; }

.stroke-order-dialog [hidden] { display:none !important; }
.stroke-order-dialog header { position:sticky; top:0; z-index:2; background:var(--paper); padding-bottom:8px; }

.stroke-order-announcement { position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%); white-space:nowrap; }

.stroke-order-panes .stroke-order-vector { width:min(100%, var(--stroke-order-pad-max, 100%)); aspect-ratio:1 / 1; margin-inline:auto; }

/* The practice pad's own detached point view, and the real pixel source behind it
   (JOB-20261015319). The panel is RESERVED layout: it is part of the dialog before the first stroke,
   so showing the point can never resize or move the pad. On a narrow screen it is a compact band
   placed immediately above Practice (order 1, Practice order 2), so the pad and the point it shows
   stay in one screen together; on a wide screen it is its own column beside the pad.
   The source div holds a faithful rasterisation of the pad's own <svg>; it is never visible, never
   focusable and never takes a pointer event. */
.stroke-order-panes > .stroke-order-point { order:1; display:grid; gap:6px; align-content:start; border:1px solid var(--line); border-radius:8px; background:var(--panel); padding:10px; }
.stroke-order-panes > section:nth-of-type(2) { order:2; }
.stroke-order-point-title { margin:0; font-size:.78rem; font-weight:800; letter-spacing:.06em; text-transform:uppercase; color:var(--ink); }
.stroke-order-point .writing-preview-frame { width:168px; max-width:100%; }
/* Wide: the natural order is Watch | Practice | preview, and the preview column is a plain vertical
   stack again. */
@media(min-width:900px) { .stroke-order-panes > .stroke-order-point { order:0; } .stroke-order-panes > section:nth-of-type(2) { order:0; } }
/* Narrow: the compact band — the real crop on the left, the labels on the right. */
@media(max-width:899px) {
  .stroke-order-panes > .stroke-order-point { grid-template-columns:132px minmax(0,1fr); gap:8px 10px; align-items:start; }
  .stroke-order-point .writing-preview-frame { grid-column:1; grid-row:1 / span 3; width:132px; margin:0; }
  .stroke-order-point-title { grid-column:2; grid-row:1; }
  .stroke-order-point .writing-preview-state { grid-column:2; grid-row:2; text-align:left; }
  .stroke-order-point .writing-preview-empty { grid-column:2; grid-row:3; font-size:.62rem; line-height:1.2; }
}
/* Short landscape (a phone on its side, or any dialog too short to stack the compact band above the
   pad — the frames root rejected, 393x852 -> 852x393 and 390x667 -> 667x390): the band moves BESIDE
   Practice and keeps its smallest readable crop, so the whole pad, the crop and its live state share
   the dialog window below the sticky header. The long hint only repeats the title and state, so it is
   dropped at this height; Watch falls to a second row and stays reachable through the dialog's own
   scroll. js/stroke-order.js measures this same window for the pad (applyPaneBudget, short layout). */
@media (orientation:landscape) and (max-height:520px) {
  .stroke-order-panes { grid-template-columns:minmax(0, 196px) minmax(0, 1fr); grid-template-rows:auto auto; gap:10px 14px; align-items:start; }
  .stroke-order-panes > .stroke-order-point { grid-column:1; grid-row:1; order:0; grid-template-columns:minmax(0, 1fr); gap:4px; padding:8px; }
  .stroke-order-point .writing-preview-frame { grid-column:1; grid-row:1; width:108px; margin:0 auto; }
  .stroke-order-point-title { grid-column:1; grid-row:2; }
  .stroke-order-point .writing-preview-state { grid-column:1; grid-row:3; text-align:left; }
  .stroke-order-point .writing-preview-empty { display:none; }
  .stroke-order-panes > section:nth-of-type(1) { grid-column:1 / -1; grid-row:2; order:0; }
  .stroke-order-panes > section:nth-of-type(2) { grid-column:2; grid-row:1; order:0; }
}
.stroke-order-mirror { position:absolute; visibility:hidden; pointer-events:none; background:var(--panel); }
.stroke-order-mirror-canvas { display:block; width:100%; height:100%; }
