/* ==========================================================================
   3D LOGIC GRID — page styles
   --------------------------------------------------------------------------
   Only what the board itself needs. The framed play stage, the sidebar, the
   buttons and the palette all come from the shared unified-3d-game.css;
   nothing here re-declares them. The HUD floats over the WebGL stage rather
   than taking layout space from it, so #game-area keeps its full size and the
   cube is never squeezed by its own controls.

   Corners are square throughout, matching the newspaper scheme.
   ========================================================================== */

#game-area {
  position: relative;
  z-index: 1;
  width: 100%;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  height: 100%;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 0;
  cursor: grab;
  /* Stage v2 sky behind the cube: indigo air warming into a violet horizon
     glow, then clamped into the plinth tone below the horizon line so the
     backdrop reads as one place (sky → table), not a stripe stack. */
  background: linear-gradient(180deg, #07060c 0%, #120f1e 30%, #241d3e 58%, #393063 72%, #2a2150 100%);
  touch-action: none;
}

html[data-theme='light'] #game-area {
  background: linear-gradient(180deg, #f8f2e7 0%, #f1e6d2 34%, #e0c6a6 58%, #4a3f6b 78%, #3a3153 100%);
}

#game-area canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
}

/* ── HUD shell ───────────────────────────────────────────────────────── */

.l3d-hud {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  display: block;
}

/* Only the controls take pointer events. The strips themselves are layout
   containers and must never intercept: on a wide screen the rail is a
   full-height column down the left edge, and letting the container take
   clicks made the board unclickable underneath it at 1024 px. */
.l3d-chip,
.l3d-mode,
.l3d-overview,
.l3d-iconbtn,
.l3d-cluesbtn,
.l3d-hintcard,
.l3d-hintcard-close {
  pointer-events: auto;
}

.l3d-hud-top {
  position: absolute;
  top: var(--spacing-sm, 8px);
  left: var(--spacing-sm, 8px);
  right: var(--spacing-sm, 8px);
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-sm, 8px);
}

.l3d-hud-bottom {
  position: absolute;
  left: var(--spacing-sm, 8px);
  right: var(--spacing-sm, 8px);
  bottom: var(--spacing-sm, 8px);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-sm, 8px);
  /* Wrapping is the safety net: whatever the compaction below does, a row
     that cannot fit must never be laid out past its own container, because
     past the container is the site sidebar. */
  flex-wrap: wrap;
  row-gap: 6px;
}

.l3d-hud-bottom .l3d-modes,
.l3d-hud-bottom .l3d-progress,
.l3d-hud-bottom .l3d-zoom {
  flex: 0 1 auto;
}

.l3d-hud-bottom .l3d-progress { min-width: 0; }

/* One tap to the clue list. On a phone the sidebar is a drawer that opens on
   the statistics, four cards above the clues; this is the shortcut past them. */
.l3d-cluesbtn {
  display: none;
  align-items: center;
  gap: 6px;
  flex: 1 1 auto;
  min-width: 0;
  max-width: 12rem;
  min-height: 40px;
  padding: 4px 10px;
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-xs, 12px);
  font-weight: var(--font-weight-bold, 700);
  letter-spacing: 0.02em;
  color: var(--news-ink);
  background: var(--news-paper);
  border: 1px solid var(--news-rule);
  border-radius: 0;
  box-shadow: var(--shadow-hard);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.l3d-cluesbtn::before {
  content: "\2630";
  font-size: 13px;
}

.l3d-cluesbtn:hover { background: var(--news-highlight); }
.l3d-cluesbtn:focus-visible { outline: 2px solid var(--news-accent); outline-offset: 2px; }



.l3d-hud-bottom .l3d-status {
  flex: 1 1 auto;
  min-width: 0;
  /* Never let the status line wrap: it would set the height of the whole
     control bar and, on a short landscape stage, swallow the board. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Face rail ───────────────────────────────────────────────────────── */

.l3d-rail {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  align-items: stretch;
}

.l3d-chip {
  position: relative;
  display: flex;
  align-items: center;
  gap: 7px;
  min-height: 38px;
  padding: 6px 12px 8px;
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-xs, 12px);
  font-weight: var(--font-weight-bold, 700);
  letter-spacing: 0.02em;
  color: var(--news-ink);
  background: var(--news-paper);
  border: 1px solid var(--news-rule);
  border-radius: 0;
  box-shadow: var(--shadow-hard);
  cursor: pointer;
  transition: transform var(--transition-fast, 120ms) ease, background var(--transition-fast, 120ms) ease;
}

.l3d-chip:hover {
  background: var(--news-highlight);
}

.l3d-chip.is-active {
  background: var(--news-ink);
  color: var(--news-paper);
  border-color: var(--news-ink);
}

.l3d-chip.is-active .l3d-chip-num {
  color: var(--news-paper);
  border-color: var(--news-paper);
}

.l3d-chip-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 17px;
  height: 17px;
  border: 1px solid currentColor;
  font-size: 10px;
  line-height: 1;
  flex: 0 0 auto;
}

.l3d-chip-meter {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  background: var(--news-rule-light);
  overflow: hidden;
}

.l3d-chip-meter > span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--news-accent);
  transition: width var(--transition-base, 200ms) ease;
}

.l3d-chip.is-done .l3d-chip-meter > span {
  background: var(--news-correct);
}

.l3d-chip:focus-visible {
  outline: 2px solid var(--news-accent);
  outline-offset: 2px;
}

.l3d-chip-label {
  white-space: nowrap;
}

.l3d-overview {
  min-height: 38px;
  padding: 6px 12px;
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-xs, 12px);
  font-weight: var(--font-weight-bold, 700);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--news-ink);
  background: var(--news-paper);
  border: 1px solid var(--news-rule);
  border-radius: 0;
  box-shadow: var(--shadow-hard);
  cursor: pointer;
}

.l3d-overview:hover { background: var(--news-highlight); }
.l3d-overview-short { display: none; }

/* ── Marking mode ────────────────────────────────────────────────────── */

.l3d-modes {
  display: flex;
  border: 1px solid var(--news-rule);
  background: var(--news-paper);
  box-shadow: var(--shadow-hard);
}

.l3d-mode {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 38px;
  padding: 6px 13px;
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-xs, 12px);
  font-weight: var(--font-weight-bold, 700);
  letter-spacing: 0.02em;
  color: var(--news-ink-light);
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
}

.l3d-mode + .l3d-mode {
  border-left: 1px solid var(--news-rule-light);
}

.l3d-mode:hover { background: var(--news-highlight); }

.l3d-mode.is-active {
  background: var(--news-ink);
  color: var(--news-paper);
}

.l3d-mode-glyph {
  font-size: 15px;
  line-height: 1;
}

.l3d-mode:focus-visible {
  outline: 2px solid var(--news-accent);
  outline-offset: -2px;
}

/* ── Status line, progress, zoom ─────────────────────────────────────── */

.l3d-status {
  max-width: min(46ch, 60%);
  padding: 5px 10px;
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-xs, 12px);
  color: var(--news-ink-light);
  background: color-mix(in srgb, var(--news-paper) 88%, transparent);
  border-left: 3px solid var(--news-accent);
}

.l3d-status[data-tone="error"] {
  color: var(--news-incorrect);
  border-left-color: var(--news-incorrect);
}

.l3d-progress {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 190px;
}

.l3d-progress-track {
  flex: 1 1 auto;
  height: 8px;
  background: var(--news-rule-light);
  border: 1px solid var(--news-rule);
  overflow: hidden;
}

.l3d-progress-fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--news-accent);
  transition: width var(--transition-base, 200ms) ease;
}

.l3d-cellinfo {
  display: none;
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding: 4px 10px;
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-xs, 12px);
  font-weight: var(--font-weight-semibold, 600);
  color: var(--news-ink);
  background: var(--news-paper);
  border-left: 3px solid var(--news-accent);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Only when the captions are gone. */
.l3d-hud.l3d-no-captions .l3d-cellinfo { display: block; }

.l3d-progress-label {
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: 11px;
  font-weight: var(--font-weight-bold, 700);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--news-ink-light);
  white-space: nowrap;
}

.l3d-zoom {
  display: flex;
  gap: 4px;
}

.l3d-iconbtn {
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 17px;
  line-height: 1;
  color: var(--news-ink);
  background: var(--news-paper);
  border: 1px solid var(--news-rule);
  border-radius: 0;
  box-shadow: var(--shadow-hard);
  cursor: pointer;
}

.l3d-iconbtn:hover { background: var(--news-highlight); }

.l3d-iconbtn:focus-visible {
  outline: 2px solid var(--news-accent);
  outline-offset: 2px;
}

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

.l3d-hintcard {
  position: absolute;
  left: 50%;
  bottom: 56px;
  transform: translateX(-50%);
  width: min(520px, calc(100% - 24px));
  padding: var(--spacing-md, 16px) var(--spacing-md, 16px) var(--spacing-sm, 8px);
  background: var(--news-paper);
  border: 1px solid var(--news-rule);
  border-top: 4px solid var(--news-accent);
  box-shadow: var(--shadow-hard-lg);
  z-index: 6;
  /* The card sits over the lower rows of the cube. A hint names a cell and
     points at it; if the card swallows the tap, the player is told the answer
     and then told off for not entering it. Let taps fall through to the board
     and re-enable them only on the controls that need them. */
  pointer-events: none;
}

.l3d-hintcard[hidden] { display: none; }

.l3d-hintcard .l3d-hintcard-close,
.l3d-hintcard .l3d-quote-n { pointer-events: auto; }

.l3d-hintcard.is-visible {
  animation: l3d-rise 220ms ease-out;
}

@keyframes l3d-rise {
  from { opacity: 0; transform: translate(-50%, 10px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

.l3d-hintcard-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.l3d-hintcard-kicker {
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: 10px;
  font-weight: var(--font-weight-bold, 700);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--news-accent);
}

.l3d-hintcard-close {
  width: 30px;
  height: 30px;
  font-size: 19px;
  line-height: 1;
  color: var(--news-ink-light);
  background: transparent;
  border: 1px solid var(--news-rule-light);
  border-radius: 0;
  cursor: pointer;
}

.l3d-hintcard-close:hover { background: var(--news-highlight); }

.l3d-hintcard-title {
  margin: 6px 0 4px;
  font-family: var(--news-font-display, Georgia, serif);
  font-size: var(--font-size-lg, 18px);
  font-weight: 900;
  line-height: 1.25;
  color: var(--news-ink);
}

.l3d-hintcard-detail {
  margin: 0 0 8px;
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-sm, 13px);
  line-height: 1.55;
  color: var(--news-ink-light);
}

.l3d-quotes {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--news-rule-light);
}

.l3d-quotes li {
  display: flex;
  gap: 8px;
  padding: 6px 0;
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-xs, 12px);
  line-height: 1.5;
  color: var(--news-ink-light);
  border-bottom: 1px solid var(--news-rule-light);
}

.l3d-quote-n {
  flex: 0 0 auto;
  font-weight: var(--font-weight-bold, 700);
  color: var(--news-accent);
}

/* ── Toast ───────────────────────────────────────────────────────────── */

.l3d-toast {
  position: absolute;
  top: 52px;
  left: 50%;
  transform: translate(-50%, -6px);
  max-width: min(520px, calc(100% - 24px));
  padding: 8px 14px;
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-sm, 13px);
  font-weight: var(--font-weight-semibold, 600);
  color: var(--news-paper);
  background: var(--news-ink);
  border-left: 4px solid var(--news-accent);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--transition-fast, 120ms) ease, transform var(--transition-fast, 120ms) ease;
  z-index: 7;
}

.l3d-toast.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}

.l3d-rotate {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  max-width: min(42ch, calc(100% - 24px));
  padding: 10px 14px;
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-xs, 12px);
  line-height: 1.5;
  text-align: center;
  color: var(--news-ink);
  background: var(--news-paper);
  border: 1px solid var(--news-rule);
  box-shadow: var(--shadow-hard);
  z-index: 5;
  pointer-events: none;
}

.l3d-rotate[hidden] { display: none; }

.l3d-toast[data-tone="error"] { border-left-color: var(--news-incorrect); }
.l3d-toast[data-tone="success"] { border-left-color: var(--news-correct); }

/* ── Accessible grid mirror ──────────────────────────────────────────── */

.l3d-mirror {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
  z-index: 8;
}

/* Keyboard users who Tab into the mirror get a real, visible grid. */
.l3d-mirror:focus-within {
  position: absolute;
  inset: 0;
  width: auto;
  height: auto;
  margin: 0;
  padding: var(--spacing-md, 16px);
  overflow: auto;
  clip: auto;
  clip-path: none;
  white-space: normal;
  background: var(--news-paper);
  border: 2px solid var(--news-ink);
}

.l3d-mirror-title {
  margin: 0 0 6px;
  font-family: var(--news-font-display, Georgia, serif);
  font-size: var(--font-size-md, 16px);
  font-weight: 900;
}

.l3d-mirror-table {
  border-collapse: collapse;
  margin-bottom: var(--spacing-md, 16px);
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-xs, 12px);
}

.l3d-mirror-table th,
.l3d-mirror-table td {
  border: 1px solid var(--news-rule-light);
  padding: 2px 6px;
  text-align: left;
  color: var(--news-ink);
}

.l3d-mirror-cell {
  width: 100%;
  min-width: 44px;
  min-height: 34px;
  font-size: 15px;
  color: var(--news-ink);
  background: var(--news-paper);
  border: 0;
  border-radius: 0;
  cursor: pointer;
}

.l3d-mirror-cell[data-state="yes"] { color: var(--news-correct); font-weight: 700; }
.l3d-mirror-cell[data-state="no"] { color: var(--news-incorrect); }
.l3d-mirror-cell:focus-visible { outline: 2px solid var(--news-accent); outline-offset: -2px; }

/* ── Clue list ───────────────────────────────────────────────────────── */

#clues-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 15rem;
  overflow-y: auto;
}

.clue-row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  padding: 7px 6px;
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-sm, 13px);
  line-height: 1.5;
  color: var(--news-ink);
  border-bottom: 1px solid var(--news-rule-light);
  cursor: pointer;
}

.clue-row.is-active {
  background: var(--news-highlight);
  box-shadow: inset 3px 0 0 var(--news-accent);
}

.clue-row:hover,
.clue-row:focus-visible {
  background: var(--news-highlight);
  outline: none;
}

.clue-row:focus-visible {
  box-shadow: inset 0 0 0 2px var(--news-accent);
}

.clue-index {
  flex: 0 0 auto;
  font-weight: var(--font-weight-bold, 700);
  color: var(--news-accent);
}

.clue-text { flex: 1 1 auto; }

/* ── Feedback pill (sidebar) ─────────────────────────────────────────── */

#feedback {
  display: none;
  margin-top: var(--spacing-md, 16px);
  padding: var(--spacing-sm, 8px) var(--spacing-md, 16px);
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-sm, 13px);
  font-weight: var(--font-weight-semibold, 600);
  text-align: center;
  border-radius: 0;
}

#feedback.is-visible { display: block; }
#feedback.correct { background: rgba(28, 124, 63, 0.14); color: var(--news-correct); border: 1px solid var(--news-correct); }
#feedback.incorrect { background: rgba(179, 38, 30, 0.14); color: var(--news-incorrect); border: 1px solid var(--news-incorrect); }
#feedback.partial { background: rgba(11, 110, 153, 0.12); color: var(--news-accent); border: 1px solid var(--news-accent); }

/* ── Error panel ─────────────────────────────────────────────────────── */

.logic-error-panel {
  max-width: 32rem;
  margin: 5rem auto;
  padding: 2rem;
  text-align: center;
  background: rgba(179, 38, 30, 0.12);
  border: 1px solid var(--news-incorrect);
  box-shadow: var(--shadow-hard);
}

.logic-error-panel h2 {
  margin-bottom: var(--spacing-md, 16px);
  font-size: var(--font-size-xl, 22px);
  color: var(--news-incorrect);
}

.logic-error-panel p {
  margin-bottom: var(--spacing-lg, 24px);
  color: var(--news-ink);
}

.logic-warning-icon {
  margin-bottom: 1rem;
  font-size: 3rem;
}

/* ── Win modal extras ────────────────────────────────────────────────── */

.logic-modal-title {
  margin-bottom: var(--spacing-md, 16px);
  font-family: var(--news-font-display, Georgia, serif);
  font-size: var(--font-size-2xl, 26px);
  font-weight: 900;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--news-ink);
}

.l3d-win-stats {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px 14px;
  margin: 0 0 var(--spacing-md, 16px);
  text-align: left;
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-sm, 13px);
}

.l3d-win-stats dt {
  color: var(--news-ink-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 11px;
  font-weight: var(--font-weight-bold, 700);
  align-self: center;
}

.l3d-win-stats dd {
  margin: 0;
  color: var(--news-ink);
  font-weight: var(--font-weight-semibold, 600);
}

.l3d-win-note {
  margin: 0;
  color: var(--news-ink-light);
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-xs, 12px);
  line-height: 1.5;
  text-align: left;
}

.logic-modal-copy {
  margin-bottom: var(--spacing-lg, 24px);
  color: var(--news-ink-light);
}

/* ── Narrow viewports ────────────────────────────────────────────────── */

/* A finger needs a bigger target than a mouse. */
@media (hover: none) and (pointer: coarse) {
  .l3d-chip,
  .l3d-overview,
  .l3d-mode { min-height: 44px; }
  .l3d-iconbtn { width: 44px; height: 44px; }
  .l3d-hintcard-close { width: 44px; height: 44px; }
  .clue-row { min-height: 44px; align-items: center; }
}

/* ── Middling widths ────────────────────────────────────────────────── */

/* Above the mobile breakpoint the site's fixed sidebar takes 300 px, so a
   window of 769-1100 px leaves a stage barely 400-800 px wide — narrower than
   the mobile layout it replaces. The labelled control bar needs about 690 px;
   below that it used to be laid out past the end of its own container, which
   put the zoom pair and OVERVIEW on top of the sidebar, where they could not
   be tapped at all. Icons only, in the same shape as the short-window case. */
@media (max-width: 1240px) {
  .l3d-mode-text { display: none; }
  /* One row, and the progress bar yields before anything wraps. Above the
     mobile breakpoint the fixed sidebar leaves a stage narrower than a
     phone's, so this band needs the same discipline. */
  .l3d-hud-bottom { flex-wrap: nowrap; }
  .l3d-hud-bottom .l3d-progress { flex: 1 1 60px; min-width: 0; }
  .l3d-status { max-width: 20ch; }
  .l3d-mode-glyph { font-size: 17px; }
  .l3d-zoom .l3d-iconbtn { display: none; }
  /* A short word that fits, rather than a fixed square with the label pushed
     out of it. `clientWidth === scrollWidth` is then something a test can
     assert, instead of a clipped word that only looks intentional. */
  .l3d-overview-long { display: none; }
  .l3d-overview-short { display: inline; }
  .l3d-overview { padding: 3px 8px; }
  .l3d-status { max-width: 22ch; }
}

/* ── Narrow ─────────────────────────────────────────────────────────── */

/* A wrapped face rail is what pushed the board under the HUD on narrow
   screens: three chip rows claimed a third of the stage and the cube was
   fitted into what was left, which left cells the player could not reach. The
   rail scrolls sideways instead, so it is always exactly one row tall. The
   same single row is what keeps the rail clear of the shared timer card in
   the header, which floats over the top-right of the stage. */
@media (max-width: 900px) {
  .l3d-rail {
    flex: 1 1 auto;
    min-width: 0;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .l3d-rail::-webkit-scrollbar { display: none; }
  /* The shared header floats a timer card over the top-right of the stage, so
     the rail keeps its chips out of that corner by ending its scroll content
     early rather than by stacking order, which does not help a visual
     collision. */
  .l3d-rail { padding-right: 132px; }
  .l3d-chip { flex: 0 0 auto; }
  .l3d-chip-label { max-width: 44vw; overflow: hidden; text-overflow: ellipsis; }
  .l3d-chip { padding: 5px 9px 8px; }
}

@media (max-width: 768px) {
  /* The status sentence never changes, so on a phone its slot goes to the
     thing the player actually reaches for: one tap to the clues, scrolled
     past the four statistics cards the drawer opens on. */
  .l3d-status { display: none; }
  .l3d-cluesbtn { display: inline-flex; order: 2; }
  .l3d-hud-bottom { flex-wrap: wrap; row-gap: 6px; right: 80px; }
  .l3d-hud-bottom .l3d-modes { order: 1; }
  .l3d-hud-bottom .l3d-zoom { order: 3; margin-left: auto; }
  .l3d-hud-bottom .l3d-progress { order: 4; flex: 1 1 100%; min-width: 0; }
  
.l3d-cellinfo {
  display: none;
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding: 4px 10px;
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-xs, 12px);
  font-weight: var(--font-weight-semibold, 600);
  color: var(--news-ink);
  background: var(--news-paper);
  border-left: 3px solid var(--news-accent);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Only when the captions are gone. */
.l3d-hud.l3d-no-captions .l3d-cellinfo { display: block; }

.l3d-progress-label { font-size: 10px; }
  .l3d-hintcard { bottom: 76px; }
  .l3d-toast { top: 52px; font-size: var(--font-size-xs, 12px); }
}

/* Below about 430 px three named chips need ~540 px of a 260-330 px rail, so
   the third face can never be shown whole. The chips already carry a number,
   and each face prints its own pair title on the cube, so the words are the
   redundant half at this size. */
@media (max-width: 700px) {
  .l3d-chip-label { display: none; }
  .l3d-rail { gap: 3px; }
  .l3d-chip { padding: 6px 8px 8px; }
  .l3d-chip-num { width: 18px; height: 18px; font-size: 11px; }
}

@media (max-width: 420px) {
  .l3d-mode { padding: 6px 10px; }
  .l3d-mode-text { display: none; }
  .l3d-mode-glyph { font-size: 17px; }
  .l3d-hud-top { top: 6px; left: 6px; right: 6px; }
  .l3d-hud-bottom { left: 6px; right: 74px; bottom: 6px; }
  /* There is not room for the zoom pair and the overview button beside the
     help button at this width. Pinch and double-tap both zoom, so the pair
     goes and the overview stays. */
  .l3d-zoom .l3d-iconbtn { display: none; }
  /* Clues and Overview become glyphs so the whole row fits without wrapping. */
  .l3d-cluesbtn {
    flex: 0 0 auto;
    width: 44px;
    min-width: 44px;
    max-width: 44px;
    padding: 4px 0;
    overflow: hidden;
    text-indent: -999px;
  }
  .l3d-cluesbtn::before { text-indent: 0; margin: 0 auto; }
  /* A short word that fits, rather than a fixed square with the label pushed
     out of it. `clientWidth === scrollWidth` is then something a test can
     assert, instead of a clipped word that only looks intentional. */
  .l3d-overview-long { display: none; }
  .l3d-overview-short { display: inline; }
  .l3d-overview { padding: 4px 8px; }
}

/* ── Short ──────────────────────────────────────────────────────────── */

/* "Short" means landscape, not merely a small window: at 560 px of height the
   shared framed stage plus its own HUD stops leaving room for a board. */

/* In landscape a phone is wide and only a few hundred pixels tall, and the
   shared framed stage floors at 320 px — taller than the viewport, which puts
   the whole board below the fold. Lower that floor here, and buy the board
   back the height the HUD was taking: one compact strip top and bottom. */
@media (max-height: 560px) {
  /* The shared framed stage floors at 320 px, which is taller than a phone in
     landscape — the whole board ends up below the fold. Lower the floor and
     buy the height back from the HUD: one compact row top and bottom. */
  #main-content.game-canvas-container {
    min-height: 140px;
  }
  .l3d-hud-top { top: 4px; left: 4px; right: 4px; }
  /* `right` must still clear the help button, which floats over the
     bottom-right corner at every size. Dropping it to 4 px here is what put
     OVERVIEW back underneath the FAB in landscape. */
  .l3d-hud-bottom { left: 4px; right: 76px; bottom: 4px; gap: 6px; flex-wrap: nowrap; }
  .l3d-status { display: none; }
  
.l3d-cellinfo {
  display: none;
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding: 4px 10px;
  font-family: var(--news-font-body, system-ui, sans-serif);
  font-size: var(--font-size-xs, 12px);
  font-weight: var(--font-weight-semibold, 600);
  color: var(--news-ink);
  background: var(--news-paper);
  border-left: 3px solid var(--news-accent);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Only when the captions are gone. */
.l3d-hud.l3d-no-captions .l3d-cellinfo { display: block; }

.l3d-progress-label { display: none; }
  .l3d-hud-bottom .l3d-modes,
  .l3d-hud-bottom .l3d-progress,
  .l3d-hud-bottom .l3d-zoom { order: 0; flex: 0 0 auto; }
  .l3d-hud-bottom .l3d-progress { flex: 1 1 40px; min-width: 40px; }
  .l3d-hud-bottom .l3d-zoom { margin-left: 0; }
  /* `!important` because the coarse-pointer block above raises every control
     to 44 px for a finger. On a landscape phone that is the difference
     between a board you can reach and no board at all — the stage is about
     150 px tall, and two 44 px strips would take most of it. */
  .l3d-chip { min-height: 30px !important; padding: 2px 8px 5px; font-size: 11px; }
  .l3d-iconbtn { width: 30px !important; height: 30px !important; font-size: 14px; }
  .l3d-overview { min-height: 30px !important; padding: 3px 9px; font-size: 11px; }
  .l3d-mode { min-height: 30px !important; padding: 3px 9px; }
  .l3d-hintcard { bottom: 52px; max-height: 40vh; overflow-y: auto; }
  .l3d-toast { top: 44px; }
}

/* A short window is almost always also a narrow one for this page, because
   the site's fixed sidebar takes 300 px of it. There is no room for labelled
   controls in what is left, so the bottom bar becomes icons only: two mode
   glyphs, the overview, and whatever is left over goes to the progress bar.
   Without this the row overflowed its own container and OVERVIEW ended up
   drawn on top of the sidebar, where it could not be tapped at all. */
@media (max-height: 560px) {
  .l3d-mode-text { display: none; }
  .l3d-mode-glyph { font-size: 17px; }
  .l3d-zoom .l3d-iconbtn { display: none; }
  /* Forced to a fixed square rather than merely shrunk: the label is plain
     text inside the button, and a row that cannot shrink overflows its
     container and lands on top of the site sidebar, where it cannot be
     tapped. `overflow: hidden` hides the text the glyph replaces. */
  .l3d-overview {
    width: 32px !important;
    min-width: 32px !important;
    max-width: 32px !important;
    padding: 3px 0 !important;
    overflow: hidden;
    text-indent: -999px;
  }
  .l3d-overview::before {
    content: "\25A3";
    display: block;
    font-size: 15px;
    line-height: 1;
    text-indent: 0;
  }
  .l3d-hud-bottom .l3d-progress { flex: 1 1 30px; min-width: 30px; }
}

/* ── Sidebar on a phone ─────────────────────────────────────────────── */

/* On a phone the sidebar is a drawer, and everything above the clue list has
   to be scrolled past before a clue can be read. Tightening the cards is the
   difference between the first clue being a flick away and a long scroll. */
@media (max-width: 768px) {
  .game-sidebar.logic-sidebar { padding: var(--spacing-md, 16px) var(--spacing-md, 16px) var(--spacing-lg, 24px); }
  .game-sidebar.logic-sidebar .game-sidebar-section.card { padding: var(--spacing-sm, 8px) var(--spacing-md, 16px); }
  .game-sidebar.logic-sidebar .game-sidebar-name { font-size: var(--font-size-lg, 18px); }
  .game-sidebar.logic-sidebar .sidebar-stats { gap: 4px; }
  .game-sidebar.logic-sidebar .sidebar-stat-item { padding: 4px 6px; }
  .game-sidebar.logic-sidebar .sidebar-howto { max-height: 12rem; overflow-y: auto; }
}

/* ── Reduced motion ──────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  .l3d-hintcard.is-visible { animation: none; }
  .l3d-chip-meter > span,
  .l3d-progress-fill,
  .l3d-toast { transition: none; }
}

body.ui-reduce-motion .l3d-hintcard.is-visible { animation: none; }
body.ui-reduce-motion .l3d-chip-meter > span,
body.ui-reduce-motion .l3d-progress-fill,
body.ui-reduce-motion .l3d-toast { transition: none; }
