/* ===========================================================
   Tagda Timer — reset, layout skeleton, background, timer
   =========================================================== */

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }

/* The UA rule for [hidden] is display:none at the lowest specificity, so any
   `#id { display: ... }` silently defeats it. Overlays live behind `hidden`,
   so that has to be unbeatable or they stay on screen forever. */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  font-family: var(--font-ui);
  background: var(--bg);
  color: var(--text);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overscroll-behavior: none;
  touch-action: manipulation;
  /* Holding the screen to start a solve is a long press, and a long press on
     text is a text selection: on a phone the digits turned blue and a
     copy/paste bubble appeared over the timer. `user-select` alone is not
     enough -- iOS Safari needs the prefixed property, and the callout is a
     separate switch again. */
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
}
/* Everything you press to time a solve. Text you might genuinely want to copy
   -- the scramble, the drawers, anything you can type into -- is exempted
   below, so this costs nothing anywhere it matters. */
#app, #timer-zone, #timer-core, #timer-display, #time-main, #time-penalty,
#solving-label, #inspect-readout, #timer-avgs, #timer-hint, #last-delta,
#panel-cube, #mascot, #dock-bottom, #tile-float-layer {
  -webkit-user-select: none;
  user-select: none;
}
.scramble, #drawer, #palette, .sh-box, #recon-root, input, textarea {
  -webkit-user-select: text;
  user-select: text;
}

button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
svg { display: block; }
svg[viewBox] { fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
/* That icon rule is inherited by <text>, which then renders as a stroked
   outline with no fill — unreadable. Text inside an SVG is type, not an icon. */
svg text { fill: currentColor; stroke: none; }
/* ...and it was inherited by every shape in every chart too, which is a
   subtler version of the same bug: a 1.7px currentColor outline on histogram
   bars, on heatmap cells, and — most visibly — a hard box drawn around the
   trend plot by its own transparent hover-catcher. Charts are drawings, not
   icons: they state their own stroke, so start them from none. Anything that
   wants a line (.axis-line, .spark-*, .hm-divider) sets it in its own rule. */
.chart-card svg, #mini-trend { stroke: none; stroke-width: 1; }
a { color: inherit; text-decoration: none; }

/* =================== FOCUS ===================
   This app is driven from the keyboard -- space to time, and a dozen single-key
   shortcuts -- but it had no focus styles of its own at all, so keyboard users
   got whatever ring the browser happened to draw, which on a dark custom
   surface is usually a low-contrast smudge and differs per browser.

   :focus-visible, not :focus, so a mouse click on a button does not leave a
   ring behind it. The double ring is what makes it survive every theme: the
   accent reads on dark surfaces, the dark halo reads where the accent is pale.
   Nothing here fires during a solve -- the timer is driven by keydown on the
   document, not by a focused control. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--bg) 70%, transparent);
}
/* The scramble is focusable only so it can be read out and copied; a ring
   around a whole paragraph of notation is noise. */
.scramble:focus-visible { outline-offset: 4px; box-shadow: none; }
/* Inputs that were given `outline: none` still need to say where focus is. */
select.inp:focus-visible, input.inp:focus-visible, textarea.inp:focus-visible,
#pal-input:focus-visible, #manual-input:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
::selection { background: var(--accent); color: var(--on-accent); }

kbd {
  font-family: var(--font-mono); font-size: .78em; font-weight: 600;
  padding: .16em .45em; border-radius: 6px;
  background: color-mix(in srgb, var(--text) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--text) 16%, transparent);
  border-bottom-width: 2px; line-height: 1.4; white-space: nowrap;
}

/* scrollbars */
* { scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
*::-webkit-scrollbar { width: 9px; height: 9px; }
*::-webkit-scrollbar-thumb { background: var(--border); border-radius: 99px; border: 2px solid transparent; background-clip: content-box; }
*::-webkit-scrollbar-thumb:hover { background: var(--text-faint); background-clip: content-box; }
*::-webkit-scrollbar-track { background: transparent; }

/* =================== BACKGROUND LAYERS =================== */
#bg-root { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }

#bg-shader, #bg-media {
  position: absolute; inset: -2%;
  width: 104%; height: 104%;
  transform: scale(var(--bg-scale));
  filter: blur(var(--bg-blur)) saturate(var(--bg-sat));
  transition: filter var(--dur-slow) var(--ease), opacity var(--dur-slow) var(--ease), transform 1.2s var(--ease);
}
#bg-media { background-size: cover; background-position: center; background-repeat: no-repeat; }
#bg-media video { width: 100%; height: 100%; object-fit: cover; }
#bg-shader[hidden], #bg-media[hidden] { display: none; }

#bg-veil {
  position: absolute; inset: 0;
  background: var(--bg);
  opacity: var(--bg-dim);
  transition: opacity var(--dur-slow) var(--ease), background var(--dur) var(--ease);
}
#bg-vignette {
  position: absolute; inset: 0;
  background: radial-gradient(120% 90% at 50% 45%, transparent 32%, color-mix(in srgb, var(--bg) 85%, transparent) 100%);
}

/* inspection perimeter ring */
#inspect-ring {
  position: fixed; inset: 0; width: 100%; height: 100%;
  z-index: 60; pointer-events: none; opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
#inspect-ring.on { opacity: 1; }
#inspect-ring-rect {
  fill: none; stroke: var(--accent-2); stroke-width: 3;
  width: calc(100% - 6px); height: calc(100% - 6px);
  filter: drop-shadow(0 0 12px currentColor);
  transition: stroke 500ms linear;
}

#flash {
  position: fixed; inset: 0; z-index: 61; pointer-events: none;
  background: var(--flash-color, #fff); opacity: 0;
}
#flash.pulse { animation: flashPulse 420ms var(--ease); }
@keyframes flashPulse { 0% { opacity: .0 } 12% { opacity: .30 } 100% { opacity: 0 } }

/* =================== APP GRID =================== */
#app {
  position: relative; z-index: 10;
  height: 100dvh;
  min-width: 0; max-width: 100vw; overflow: hidden;
  display: grid;
  grid-template-rows: auto auto 1fr;
  gap: var(--gap);
  padding: var(--gap);
  padding-bottom: max(var(--gap), env(safe-area-inset-bottom));
}

/* Focus mode: everything but the timer leaves, and it leaves the instant you
   press down — not when inspection or the solve finally starts. `body.focus`
   is the single switch for that; `body.inspecting` only ever means "the
   countdown is on screen", so turning focus mode off does not take the
   inspection visuals with it. */
body.focus .fade-layer,
body.focus #topbar,
body.focus .panel,
body.focus #creator-chip {
  opacity: 0;
  filter: blur(9px);
  transform: scale(.985);
  pointer-events: none;
  transition: opacity 220ms var(--ease), filter 220ms var(--ease), transform 220ms var(--ease);
}
body.focus #bg-veil { opacity: calc(var(--bg-dim) + .34); }
body.focus #bg-shader, body.focus #bg-media { filter: blur(calc(var(--bg-blur) + 5px)) saturate(.25); }
body.focus #app { grid-template-rows: 0 0 1fr; }

/* A relay is the one case where the scramble zone keeps its row in focus mode.
   Everything in it still goes — the notation, the tools, the preview — but the
   rail stays, because which of five puzzles you are on cannot be read off the
   clock, and getting it wrong costs the whole attempt. The zone itself is
   un-faded rather than the rail, since a child cannot undo its parent's
   opacity. */
body.focus.relay #app { grid-template-rows: 0 auto 1fr; }
body.focus.relay #scramble-zone {
  opacity: 1; filter: none; transform: none; pointer-events: none;
}
body.focus.relay #scramble-zone > *:not(#relay-rail) {
  opacity: 0;
  filter: blur(9px);
  transition: opacity 220ms var(--ease), filter 220ms var(--ease);
}
/* the timer digits sit behind the inspection countdown, so hide them there */
body.inspecting #timer-display { opacity: 0; }

#topbar, .panel, .fade-layer, #creator-chip {
  transition: opacity 420ms var(--ease) 40ms, filter 420ms var(--ease) 40ms, transform 460ms var(--ease-back) 40ms;
}

/* =================== TOP BAR =================== */
#topbar {
  display: flex; align-items: center; gap: var(--gap);
  min-height: 44px; min-width: 0;
}
.topbar-left { display: flex; align-items: center; min-width: 0; flex: 0 0 auto; }
.brand { display: flex; align-items: center; gap: 9px; user-select: none; flex: 0 0 auto; }
/* An <img> now, not an icon font glyph — it must not inherit the stroke rules
   that style the rest of the SVG icons. */
.brand-mark {
  width: 36px; height: 36px; flex: none;
  object-fit: contain; display: block;
  filter: drop-shadow(0 3px 8px rgba(0,0,0,.5));
}
.brand-text {
  font-family: var(--font-ui); font-weight: 600; font-size: 1.18rem;
  letter-spacing: -.03em; color: var(--text);
}
.brand-text b { color: var(--accent); font-weight: 800; }

.topbar-center {
  display: flex; align-items: center; gap: 8px; margin: 0 auto;
  min-width: 0; flex: 0 1 auto; overflow: hidden;
}
.topbar-right { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }

/* `margin: 0 auto` on a flex row centres the pickers in the space *left over*
   between the wordmark and the icon cluster -- and those two are nowhere near
   the same width (155px against 480px), so the row landed 163px left of the
   page's centre while the SCRAMBLE label and the scramble under it sat dead
   centre. Three lines that should share one axis, and two of them disagreed
   with the one you actually read.

   Giving both outer groups the same flex rule makes them the same width, and
   a centred thing between two equal weights is centred on the page. It is
   done in flexbox rather than a three-column grid on purpose: when the bar is
   too narrow for two 480px sides, `min-width: max-content` freezes the icon
   cluster at its own width and flexbox hands the rest to the left group, so
   the row slides back off centre by however much is missing instead of
   overlapping the icons -- which is exactly what the equivalent grid does.
   Perfect above ~1530px, and progressively less wrong below it.

   Only above 1040px: below that the bar wraps and the centre group becomes
   its own scrolling row, where being centred is not the question. */
@media (min-width: 1041px) {
  .topbar-left, .topbar-right { flex: 1 1 0; min-width: max-content; }
  .topbar-right { justify-content: flex-end; }
  .topbar-center { margin: 0; }
}

/* =================== SCRAMBLE =================== */
#scramble-zone {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  min-width: 0; padding-top: 6px;
}
.scramble-label {
  font-size: .64rem; font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
  color: var(--text-faint);
}
#scramble-wrap {
  display: flex; align-items: center; gap: 10px;
  width: min(1320px, 95%);
  min-width: 0;
  /* Symmetric room for the hover tools, so a wide one-line scramble never
     runs underneath them and the text stays optically centred. */
  padding: 0 92px;
}
@media (max-width: 760px) { #scramble-wrap { padding: 0; } }
.scramble {
  font-family: var(--font-mono);
  font-size: calc(clamp(1.15rem, 2.45vw, 1.95rem) * var(--scramble-scale) * var(--scramble-boost, 1));
  font-weight: 600; line-height: 1.6; letter-spacing: .012em;
  text-align: center; color: var(--text);
  padding: 13px 20px; border-radius: var(--radius-sm);
  cursor: pointer; outline: none;
  transition: background var(--dur-fast) var(--ease);
  word-spacing: .32em;
  white-space: pre-wrap;
  background: color-mix(in srgb, var(--text) 4%, transparent);
  border: 1px solid color-mix(in srgb, var(--border) 55%, transparent);
  flex: 1 1 auto; min-width: 0;
}
/* A 3x3 scramble breaking after 19 of its 21 moves reads terribly. When the
   text is one logical line, keep it on one visual line and let JS shrink the
   type just enough to fit. */
.scramble.oneline { white-space: nowrap; overflow: hidden; }
.scramble:hover { background: color-mix(in srgb, var(--text) 6%, transparent); }
.scramble.multiline { text-align: left; font-size: calc(clamp(.8rem, 1.35vw, 1.05rem) * var(--scramble-scale) * var(--scramble-boost, 1)); }
.scramble.long { font-size: calc(clamp(.8rem, 1.45vw, 1.08rem) * var(--scramble-scale) * var(--scramble-boost, 1)); }
/* Two lines of notation at the one-line leading read as one solid block with a
   crack through it. When the fit gives up and lets the text wrap (or the
   scramble carries its own line breaks), it gets the room to be two lines:
   more leading, tighter word spacing so a line is a phrase rather than a
   scatter of letters, and balanced breaks so the second line is not two
   moves long. */
.scramble.multiline, .scramble.wrapped {
  line-height: 1.8;
  word-spacing: .2em;
  padding-top: 14px; padding-bottom: 14px;
}
.scramble.wrapped { text-wrap: balance; text-align: center; }
.scramble.multiline { text-wrap: initial; }
.scramble .m { display: inline-block; transition: color var(--dur-fast); }

/* A race hold: the room is between scrambles, or you have finished one the
   rest of the room has not. It occupies the scramble's box so the page does
   not jump, and looks as unlike a scramble as it can — proportional type,
   dimmer, no monospace grid — because the whole point of it is that there is
   nothing here to turn. */
.scramble.race-hold {
  font-family: inherit;
  font-size: clamp(.92rem, 1.5vw, 1.12rem);
  font-weight: 500; letter-spacing: 0; word-spacing: normal;
  line-height: 1.5; text-wrap: balance;
  color: var(--text-dim);
  cursor: default;
  background: color-mix(in srgb, var(--text) 3%, transparent);
  border-style: dashed;
}
.scramble.race-hold:hover { background: color-mix(in srgb, var(--text) 3%, transparent); }
/* Something has to say this is a live wait rather than a stuck screen. A slow
   breath on the text is enough, and it is the kind of motion that has no
   business overriding a reduced-motion preference. */
@media (prefers-reduced-motion: no-preference) {
  .scramble.race-hold { animation: race-hold-breathe 2.4s ease-in-out infinite; }
}
@keyframes race-hold-breathe { 50% { opacity: .62; } }

/* Taken out of the flex flow: in-flow they add their width to the row and shove
   the scramble off-centre relative to the timer below it. */
#scramble-tools {
  position: absolute; right: 0; top: 50%;
  transform: translateY(-50%);
  display: flex; gap: 2px;
  opacity: 0; transition: opacity var(--dur) var(--ease);
}
#scramble-zone:hover #scramble-tools { opacity: 1; }
/* On a touch screen these used to be `display: none`, which left no way at all
   to reach the previous scramble, the copy button, or custom scrambles — the
   keyboard shortcuts they double for do not exist on a phone. Out of the
   absolute corner and into a plain centred row under the notation, always on,
   because there is no hover to reveal them with. */
@media (hover: none), (max-width: 760px) {
  #scramble-tools {
    position: static; transform: none;
    opacity: 1; justify-content: center;
    gap: 4px; margin-top: 2px;
  }
  /* Always on here, so they have to be findable: --text-faint on the aurora
     was barely there. */
  #scramble-tools .ghost-btn:not(:hover) { color: var(--text-dim); }
  /* The tools no longer sit in the row, so the reserved room for them goes too. */
  #scramble-wrap { flex-wrap: wrap; justify-content: center; padding: 0; }
}

#case-label {
  font-size: .75rem; font-weight: 600; letter-spacing: .13em; text-transform: uppercase;
  color: var(--accent-2);
  padding: 3px 11px; border-radius: 99px;
  background: color-mix(in srgb, var(--accent-2) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent-2) 26%, transparent);
}

/* =================== LEARN MODE =================== */
/* Sits under the case label, and is the only thing on the timer screen that is
   allowed to say more than a word or two -- the algorithm has to be readable at
   a glance from arm's length, so it is set in the mono face at scramble size
   rather than as UI text. */
#learn-bar {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  margin-top: 10px;
}

#learn-progress {
  display: flex; align-items: center; gap: 14px;
  font-size: .72rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-faint);
}
#learn-progress b { font-weight: 700; margin-right: 4px; }
.lp-seg.new b      { color: var(--text-dim); }
.lp-seg.learning b { color: var(--warn); }
.lp-seg.mature b   { color: var(--ok); }
.lp-seg.due b      { color: var(--accent); }
#btn-learn-exit { font-size: 1rem; line-height: 1; padding: 2px 7px; }

#learn-alg {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  justify-content: center;
  padding: 9px 16px; border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
}
.la-tag {
  font-size: .68rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent);
}
.la-alg {
  font-family: var(--font-mono); font-size: 1.05rem; letter-spacing: .02em;
  color: var(--text);
}

#learn-actions { display: flex; align-items: center; gap: 12px; min-height: 22px; }
#learn-verdict { font-size: .78rem; color: var(--text-dim); }
#learn-verdict.lv-pass { color: var(--ok); }
#learn-verdict.lv-hold { color: var(--warn); }
#learn-verdict.lv-fail { color: var(--danger); }
#learn-verdict.lv-done { color: var(--text-faint); }

/* Zen mode hides the furniture; the algorithm is furniture. */
body.zen #learn-bar { display: none; }

/* =================== STAGE =================== */
/* Third track is an intentionally empty spacer that mirrors the sidebar, so the
   timer sits in the middle of the SCREEN rather than the middle of whatever
   space the sidebar left over. */
#stage {
  display: grid;
  grid-template-columns:
    minmax(0, var(--sidebar-w)) minmax(0, 1fr) minmax(0, var(--sidebar-w));
  gap: var(--gap);
  min-height: 0;
  align-items: stretch;
}
/* A rail that is switched off does not keep its column. The remaining children
   fall into the remaining tracks in DOM order, which is why the right rail is
   written after the timer in index.html. */
#stage[data-left="off"]  { grid-template-columns: minmax(0, 1fr) minmax(0, var(--sidebar-w)); }
#stage[data-right="off"] { grid-template-columns: minmax(0, var(--sidebar-w)) minmax(0, 1fr); }
#stage[data-sidebar="off"],
#stage[data-left="off"][data-right="off"] { grid-template-columns: minmax(0, 1fr); }

/* left column: the times list, which is the thing you scroll */
#sidebar {
  display: flex; flex-direction: column; gap: var(--gap);
  min-height: 0; min-width: 0;
  font-size: calc(1rem * var(--sidebar-text));
  /* The creator chip is pinned to the bottom-left corner, on top of this
     column. Stop the panels short of it instead of letting the two share
     pixels. */
  padding-bottom: var(--chip-clearance);
}
/* Stats sits above the times list, so expanding it eats into a list that can
   scroll rather than growing off the bottom of the column. It may take at most
   two thirds of the sidebar; past that the trend chart gives up height first. */
/* `flex: 0 0 auto` matters: as a shrinkable item with min-height 0, the flex
   algorithm handed all the height to the greedy times list below and collapsed
   the expanded panel to nothing. It takes its content height, capped. */
#panel-times { flex: 1 1 auto; min-height: 0; }

/* Right column: the numbers. Nothing in here scrolls, so it is free to sit
   opposite the times list rather than stacked on top of it, and the creator
   chip is not down this side — no clearance needed. */
#sidebar-right {
  display: flex; flex-direction: column; gap: var(--gap);
  min-height: 0; min-width: 0;
  font-size: calc(1rem * var(--sidebar-text));
}
/* ...except that the preview is down this side, in the corner, on top of it.
   Stop the rail short of it the same way the left one stops short of the
   creator chip. Only while the preview is actually parked in that corner: drag
   it anywhere else (`data-placed`) or switch it off (`body.no-cube`) and the
   rail gets its full height back.

   Above the mobile breakpoint only: below it the rail is a short tile at the
   bottom of the screen and the preview floats clear above it. */
/* Nothing reserves rail space for the preview any more. It used to, from a
   constant that could only ever be a guess at the widget's height, and it cost
   the solve list that height permanently whether or not the two were anywhere
   near each other. The preview moves aside instead, and only when it actually
   collides — see placePreview() in tiles.js. */
#panel-stats { flex: 0 1 auto; min-height: 0; }

/* =================== TIMER =================== */
#timer-zone {
  position: relative; z-index: 5;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-width: 0; user-select: none;
}
#timer-core {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; min-width: 0;
}

/* The top bar and the scramble eat the top of the grid, so a timer centred in
   its own row lands visibly above the middle of the screen. Pin it to the
   viewport instead — it stays put while everything around it fades in and out.
   Only above the mobile breakpoint: below it the sidebar owns the bottom third
   of the screen and true centring would sit the digits underneath it. */
@media (min-width: 861px) {
  #timer-core, #inspect-readout {
    position: fixed; left: 50%; top: 50%;
    transform: translate(-50%, -50%);
  }
}

/* ---------------- virtual cube ----------------
   csTimer's arrangement: the cube sits dead centre of the screen and the clock
   is parked against the right edge of the centre column, out of its way. */
#vcube-wrap { width: min(56vh, 46%); aspect-ratio: 1; flex: none; }
#vcube-holder, #vcube-holder twisty-player { width: 100%; height: 100%; }
body[data-input="virtual"] #panel-cube { display: none; }
body[data-input="virtual"] #timer-display { font-size: min(var(--timer-size), 5.5vw); }
@media (min-width: 861px) {
  body[data-input="virtual"] #vcube-wrap {
    position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%);
  }
  body[data-input="virtual"] #timer-core {
    position: absolute; left: auto; right: 0; top: 50%; transform: translateY(-50%);
    align-items: flex-end; max-width: 27%;
  }
  body[data-input="virtual"] #timer-hint { text-align: right; text-wrap: balance; }
}
@media (max-width: 860px) {
  #vcube-wrap { width: min(80vw, 40vh); }
  body[data-input="virtual"] #timer-core,
  body[data-input="virtual"] #inspect-readout { position: relative; left: auto; top: auto; transform: none; }
}

#timer-display {
  position: relative;
  font-family: var(--font-timer);
  font-size: var(--timer-size);
  font-weight: var(--timer-weight);
  letter-spacing: var(--timer-track);
  line-height: .95;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "zero" 1;
  display: flex; align-items: baseline; justify-content: center;
  color: var(--t-idle);
  transition: color 120ms linear, transform 200ms var(--ease-back), opacity var(--dur);
  will-change: transform;
  text-shadow: 0 0 var(--timer-glow) color-mix(in srgb, var(--accent) 65%, transparent);
}
#timer-display.state-holding { color: var(--t-holding); transform: scale(.972); }
#timer-display.state-ready   { color: var(--t-ready);   transform: scale(1.03); }
#timer-display.state-running { color: var(--t-running); }
#timer-display.state-inspect { color: var(--warn); }
/* Hiding the time means hiding it. At .12 opacity the digits were still
   legible on every theme, so the setting did nothing but dim the thing it was
   asked to remove. The word takes its place, in the same box, so the layout
   does not jump when the solve starts. */
#timer-display.hidden-digits #time-main,
#timer-display.hidden-digits #time-penalty { visibility: hidden; }

#solving-label {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  display: none;
  font-family: var(--font-ui);
  font-size: clamp(1.1rem, 3.6vw, 2.4rem);
  font-weight: 700; letter-spacing: .38em; text-indent: .38em;
  text-transform: uppercase;
  color: var(--t-running);
  text-shadow: none;
  white-space: nowrap;
  opacity: .8;
}
#timer-display.hidden-digits #solving-label { display: block; }

#time-penalty {
  font-size: .3em; font-weight: 700; letter-spacing: 0;
  margin-left: .18em; color: var(--danger); align-self: center;
}

#inspect-readout {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  pointer-events: none;
}
#inspect-num {
  font-family: var(--font-timer); font-weight: 800;
  font-size: clamp(5rem, 22vw, 16rem); line-height: .9;
  color: var(--warn); font-variant-numeric: tabular-nums;
  text-shadow: 0 0 40px color-mix(in srgb, var(--warn) 45%, transparent);
  /* No pulsing. The number is the thing you are reading under pressure and it
     needs to hold still. */
}
#inspect-hint {
  font-size: .74rem; letter-spacing: .34em; text-transform: uppercase;
  color: var(--text-dim); font-weight: 600;
}

#timer-hint {
  /* --text-dim, not --text-faint: the faint tone sank into the aurora behind it,
     and on a phone this line is the only instruction for starting a solve. */
  font-size: .78rem; color: var(--text-dim); letter-spacing: .06em;
  transition: opacity var(--dur) var(--ease);
  height: 1.2em;
}
body.focus #timer-hint, body.inspecting #timer-hint { opacity: 0; }
/* The markup carries both wordings until updateHint() replaces the lot; this
   is which of them this device was going to end up with anyway. */
#timer-hint .hint-coarse { display: none; }
@media (pointer: coarse) {
  #timer-hint .hint-fine { display: none; }
  #timer-hint .hint-coarse { display: inline; }
}

/* =================== RUNNING AVERAGES =================== */
/* Hangs under the timer block rather than sitting inside it. In the flow it
   added its own height to #timer-core, and because that box is centred on the
   viewport by its own half-height, every pixel it gained pushed the digits up —
   the timer no longer sat where it always had. Out of the flow it cannot move
   anything: #timer-core measures exactly what it did before this row existed. */
#timer-avgs {
  position: absolute; top: 100%; left: 50%;
  transform: translateX(-50%);
  margin-top: 10px;
  display: flex; align-items: center; gap: 26px;
  white-space: nowrap;
  font-family: var(--font-mono);
  transition: opacity var(--dur) var(--ease);
  pointer-events: none;
}
#timer-avgs[hidden] { display: none; }
/* Before the twelfth solve there is no ao12 to print. */
#timer-avgs.solo .sep, #timer-avgs.solo > span:last-child { display: none; }
#timer-avgs > span { display: flex; align-items: baseline; gap: 10px; }
#timer-avgs b {
  font-size: .84rem; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  color: var(--text-faint);
}
/* Sized off the timer itself, so it stays in proportion at every window width
   and follows the timer size slider instead of pinning itself to one number. */
#timer-avgs i {
  font-style: normal; font-weight: 700; color: var(--text);
  font-size: clamp(1.4rem, calc(var(--timer-size) * .22), 2.6rem);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
}
#timer-avgs .sep {
  width: 1px; height: 26px; align-self: center;
  background: color-mix(in srgb, var(--text) 22%, transparent);
}
/* Nothing but the timer during an attempt — same rule the hint follows. */
body.focus #timer-avgs, body.inspecting #timer-avgs,
#timer-display.state-running ~ #timer-avgs { opacity: 0; }

/* pace ghost */
#pace-ghost {
  position: relative; width: min(420px, 60vw); height: 3px;
  border-radius: 99px; background: color-mix(in srgb, var(--text) 9%, transparent);
  overflow: visible;
}
#pace-fill {
  height: 100%; width: 0%; border-radius: 99px;
  background: var(--ok);
  box-shadow: 0 0 14px color-mix(in srgb, var(--ok) 70%, transparent);
  transition: background 200ms linear, box-shadow 200ms linear;
}
#pace-ghost.behind #pace-fill { background: var(--danger); box-shadow: 0 0 14px color-mix(in srgb, var(--danger) 70%, transparent); }
#pace-label {
  position: absolute; top: 8px; right: 0;
  font-size: .62rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--text-faint);
}

#last-delta {
  font-family: var(--font-mono); font-size: .82rem; font-weight: 600;
  padding: 3px 10px; border-radius: 99px;
  animation: popIn 420ms var(--ease-back);
}
/* The inspection countdown is drawn over the top of the timer block, and this
   chip sits exactly where the word INSPECTION goes — the two printed on top of
   each other. It reports the last solve, which is not something you need while
   the clock for the next one is already running. */
body.inspecting #last-delta, body.focus #last-delta { display: none; }
#last-delta.better { color: var(--ok); background: color-mix(in srgb, var(--ok) 13%, transparent); }
#last-delta.worse  { color: var(--danger); background: color-mix(in srgb, var(--danger) 13%, transparent); }
@keyframes popIn { from { opacity:0; transform: translateY(6px) scale(.92); } to { opacity:1; transform:none; } }

/* PB shockwave.

   Animating width and height meant re-laying-out a 190vmax box on every frame
   of the celebration, alongside the confetti. The ring is now drawn at its
   final size and scaled up, which the compositor handles on its own thread —
   and it is centred with a negative margin rather than a transform, so the
   transform is free for the animation to use. */
.shockwave {
  position: fixed; left: 50%; top: 50%; z-index: 55;
  /* 150vmax is comfortably past the corners from the centre of any viewport;
     bigger than that is just a bigger texture for the compositor to hold.
     No will-change: the running transform animation gets its own layer either
     way, and asking for one early only makes that texture live longer. */
  width: 150vmax; height: 150vmax; margin: -75vmax 0 0 -75vmax;
  border-radius: 50%; border: 5px solid var(--accent);
  pointer-events: none;
  animation: shock 900ms cubic-bezier(.16,.9,.3,1) forwards;
}
@keyframes shock {
  0%   { transform: scale(.012); opacity: .92; }
  55%  { opacity: .38; }
  100% { transform: scale(1); opacity: 0; }
}

#confetti {
  position: fixed; inset: 0; z-index: 70; pointer-events: none;
  width: 100%; height: 100%; opacity: 0;
}
#confetti.on { opacity: 1; }

/* =================== RESPONSIVE =================== */
@media (max-width: 1080px) {
  #stage { grid-template-columns: minmax(0, min(var(--sidebar-w), 200px)) 1fr minmax(0, min(var(--sidebar-w), 200px)); }
  #stage[data-left="off"]  { grid-template-columns: 1fr minmax(0, min(var(--sidebar-w), 200px)); }
  #stage[data-right="off"] { grid-template-columns: minmax(0, min(var(--sidebar-w), 200px)) 1fr; }
  #stage[data-sidebar="off"],
  #stage[data-left="off"][data-right="off"] { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 860px) {
  #app {
    gap: 10px;
    padding: 10px;
    padding-top: max(10px, env(safe-area-inset-top));
    padding-left: max(10px, env(safe-area-inset-left));
    padding-right: max(10px, env(safe-area-inset-right));
    padding-bottom: max(10px, env(safe-area-inset-bottom));
  }

  /* The timer takes the whole top of the stage and every pixel of slack in it,
     so the digits sit in the middle of the space they have rather than at the
     top of it. This is what was wrong with the inspection countdown: #stage's
     first row was `auto`, so the timer block was only as tall as itself and
     the number was pinned to the top of the screen with the rest of the page
     empty below it. `1fr` plus the centring #timer-zone already does puts it
     back in the middle, and it stays there however tall the scramble is. */
  #stage, #stage[data-sidebar="off"], #stage[data-left="off"], #stage[data-right="off"] {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr) auto;
  }
  #timer-zone { grid-column: 1 / -1; grid-row: 1; }
  /* Focus mode already empties the two rows above the stage. The tiles below
     the timer fade out but keep their track, which left the countdown centred
     in the top two thirds of the screen rather than in the screen. They are at
     opacity 0 with no pointer events by then, so collapsing the track costs
     nothing and the number lands dead centre. */
  body.focus #stage { grid-template-rows: minmax(0, 1fr) 0; }

  /* The two rails become two small tiles side by side under the timer, rather
     than two full-width panels stacked on top of each other — stacked, they ate
     the bottom half of the screen and squeezed the timer into what was left.
     Figures on the left, times on the right: the reverse of the desktop rails,
     and placed explicitly here precisely because DOM order (times, timer,
     stats) would otherwise put them the other way round. */
  #sidebar-right { grid-column: 1; grid-row: 2; }
  #sidebar       { grid-column: 2; grid-row: 2; flex-direction: column; padding-bottom: 0; }
  /* A height rather than a cap. Sized by their contents, the two tiles were
     124px tall on the first frame, 176 once the panels had been filled in and
     287 once a session's solves arrived — so the timer above them moved twice
     on every load, which was most of the layout shift a phone actually sees.
     34vh is where they end up, so 34vh is what they start at. */
  #sidebar, #sidebar-right { min-height: 0; min-width: 0; height: 34vh; }
  #sidebar > .panel, #sidebar-right > .panel { min-width: 0; }
  #panel-times { flex: 1 1 auto; min-height: 0; }
  #panel-stats { flex: 1 1 auto; min-height: 0; }
  /* With one of them switched off the other has no reason to stay half width. */
  #stage[data-left="off"] #sidebar-right,
  #stage[data-right="off"] #sidebar { grid-column: 1 / -1; }

  /* Expanded, the figures need the full width, and the times list stands down
     for as long as they have it. Two half-width tiles cannot both be useful —
     the stat grid alone is taller than a phone's whole lower third. The list is
     back the moment the stats collapse, and the full Statistics drawer is on
     the chart button either way. */
  #stage:has(#panel-stats[data-collapsed="false"]) #sidebar { display: none; }
  #stage:has(#panel-stats[data-collapsed="false"]) #sidebar-right {
    grid-column: 1 / -1; height: 46vh;
  }

  .brand-text { display: none; }
  .topbar-center { gap: 5px; }
  /* Below this the tiles own the bottom of the screen, and a row hanging off
     the end of the timer block would land on top of them. The same two figures
     are in the stats peek a few pixels away. */
  #timer-avgs { display: none; }
  /* The hint stays. It used to go with them, which left a touch screen with no
     statement anywhere of how to start a solve — and unlike the averages it is
     not repeated somewhere else on the page. */
  #timer-hint { display: block; font-size: .72rem; }
  #timer-core { gap: 6px; }
}

/* The session picker used to be dropped here, on the grounds that S and the
   command palette still reach it. Neither of those is true without a keyboard,
   so it goes only where there is one — the pickers scroll sideways below 620px
   (components.css), which is what makes keeping it affordable. */
@media (hover: hover) and (max-width: 560px) {
  #session-seg { display: none; }
}

/* ---- phones ---- */
@media (max-width: 560px) {
  /* Density, and it is not cosmetic. At desktop padding the times panel's own
     chrome — 18px of padding each side, three 12px gaps, a head, a column row
     and a footer — added up to more than the height the tile had, so the solve
     list was allotted exactly zero pixels and the session looked empty on a
     phone. Everything below buys that list its height back. */
  :root { --pad: 11px; --gap: 8px; }
  .panel { gap: 7px; }
  /* "SCRAMBLE" over an unmistakable row of notation is a label a phone cannot
     afford, and neither is 1.6 lines of leading on three lines of it. */
  .scramble-label { display: none; }
  .scramble { padding: 9px 11px; line-height: 1.45; }
  /* A floor, so the list is never squeezed out of existence again: two or three
     solves are visible whatever else is on screen, and it scrolls from there. */
  #hist-list { min-height: 58px; }

  /* Room for the scramble that is coming.
     "Warming up scrambler…" is one line; a 3x3 wraps to two here, so the whole
     stage under it dropped 38px the moment the first scramble landed. The
     reservation lasts until showScramble() takes `warming` off, and 85px is
     what two fitted lines measure across phone widths (80px at 360, 96px at
     560). An event whose scramble does fit on one line (2x2, pyraminx) pays
     for it with a box this size until that scramble arrives. */
  .scramble.warming { min-height: 85px; }
}

/* ---- landscape on a phone ----
   ~380px of height total: the stacked column cannot have four rows of chrome
   and a timer. Drop to the timer and the scramble, and let the drawers (which
   are full-screen here) carry the rest. */
@media (max-width: 940px) and (max-height: 500px) and (orientation: landscape) {
  #app { grid-template-rows: auto auto 1fr; gap: 6px; padding: 6px; }
  body.focus #app { grid-template-rows: 0 0 1fr; }
  /* #panel-cube is dropped too, but that rule has to live in components.css —
     its `display: flex` is declared there, and an equally specific rule in the
     earlier file loses no matter what media query it sits in. */
  #sidebar, .scramble-label, #timer-hint { display: none; }
  /* The times list is gone, so nothing is sharing the row — the figures may as
     well have the width rather than sit in a half-tile with the other half
     empty. (Not the data-attribute rules above: the list is hidden here by this
     stylesheet, not by the setting those attributes track.) */
  /* `height: auto` puts back what the portrait block above reserves: on its
     side the times list is gone, so the figures are the only tile and they are
     as tall as they are. */
  #sidebar-right { grid-column: 1 / -1; height: auto; max-height: 44vh; }
  /* min(), not a flat size: --timer-size is written as an inline style from the
     size slider, so it cannot be overridden from a stylesheet at all — and
     should not be. Capping it against the viewport height keeps whatever the
     user chose, right up to the point where the digits would not fit. */
  #timer-display { font-size: min(var(--timer-size), 13vh); }
  #inspect-num { font-size: min(clamp(5rem, 22vw, 16rem), 24vh); }
}

/* zen mode — hide chrome permanently */
body.zen #topbar, body.zen #sidebar, body.zen #sidebar-right,
body.zen #panel-cube, body.zen #creator-chip,
body.zen #dock-bottom, body.zen #tile-float-layer { display: none; }
body.zen #app { grid-template-rows: auto 1fr; }
body.zen #stage { grid-template-columns: minmax(0, 1fr); }

/* ---------------- Scramble of the Day ----------------

   The same subtraction zen mode does, taken one step further, plus a bar of
   its own. Written as its own block rather than folded into the `body.zen`
   selector above because the two are not the same idea: zen is a preference
   you toggle and forget, this is a mode you are in for one scramble and then
   leave, and it has chrome of its own that zen must never grow. */
/* `#panel-cube` is deliberately NOT in this list. The cube preview is not
   chrome — it is how you read the scramble, and a scramble you cannot check
   yourself against is exactly the thing you do not want on the one attempt
   that counts. It was hidden in the first version of this window and that was
   simply wrong. */
body.sotd #topbar, body.sotd #sidebar, body.sotd #sidebar-right,
body.sotd #creator-chip,
body.sotd #dock-bottom, body.sotd #tile-float-layer,
body.sotd #now-playing, body.sotd #goal-line, body.sotd #learn-bar,
/* Prev / next / copy / paste-your-own. All four are wrong in here: three of
   them step the timer onto a scramble that is not today's, which is the one
   thing this window exists to prevent, and the fourth is a button for a
   scramble you are supposed to be reading, not collecting. */
body.sotd #scramble-tools,
/* Your running ao5 and ao12 belong to the practice session this window is not
   part of. One official attempt has no average, and a rolling mean of twelve
   ordinary solves sitting under the one that counts invites you to read the
   attempt as though it were the thirteenth of them. */
body.sotd #timer-avgs,
/* Same for the last-solve delta: there is nothing here for it to be a delta
   from. */
body.sotd #last-delta,
/* The bar overhead already says what this scramble is, so the standing
   "Scramble" caption underneath it is the label repeated twice. */
body.sotd .scramble-label { display: none; }
body.sotd #app { grid-template-rows: auto 1fr; }
body.sotd #stage { grid-template-columns: minmax(0, 1fr); }

/* Room for the bar, so the scramble does not start underneath it. */
body.sotd #scramble-zone { padding-top: 58px; }

/* The scramble is the reason the window exists, so it is given the size it
   could never have had with a sidebar either side of it.

   Applied as a multiplier on the existing three size tiers rather than as a
   flat font-size, which is the version that had to be thrown away: a fixed
   2rem is fine for a 3x3 and runs a 5x5 scramble straight off the screen,
   because the tiers exist precisely to stop that. Boosting them keeps the
   tier that was chosen for this scramble and only spends the room the
   missing sidebars just freed up. `--scramble-scale` is the reader's own
   size preference and stays untouched underneath it. */
body.sotd { --scramble-boost: 1.35; --sotd-board-w: min(360px, 30vw); }

/* Nothing is shifted sideways, and both attempts to do so are worth recording
   because each looked reasonable and each was worse than this.

   Shifting only the timer put it in the middle of the free space while the
   scramble stayed on the middle of the viewport, so the two things the window
   exists to show sat on two different axes. Shifting both — padding the
   scramble by the column's width — lined them up, but paid for it out of the
   scramble: a third of its width went to making room, and the line-fitter
   duly set it SMALLER inside the window than outside, which is the opposite
   of the point.

   So the column is placed under the scramble instead of beside it (js/
   dailyui.js measures where that is), and nothing needs to move: the scramble
   keeps the full width, the timer stays centred on the screen where it always
   is, and the board sits in the empty left-hand region beside the digits. */
body.sotd #scramble-wrap { max-width: min(1100px, 92vw); }
/* The `long` tier exists because a 21-move scramble in a normal layout has to
   share the screen with a dock, a tile row and two sidebars. In here it shares
   the screen with nothing, so it starts from the ordinary tier and lets
   fitScrambleToLine shrink or wrap it from there — which it will, because that
   is the one thing that function guarantees. The clamp is repeated rather than
   factored out on purpose: this rule is "use the other tier", and writing it
   as anything else would hide that. */
body.sotd #scramble-text.long {
  font-size: calc(clamp(1.15rem, 2.45vw, 1.95rem) * var(--scramble-scale) * var(--scramble-boost, 1));
}
/* The hold message is the only thing on screen in the states where it shows,
   so it is set to be read from where you actually sit rather than at the size
   a line of prose gets when a whole app is competing with it. Not boosted via
   --scramble-boost: that multiplier is on the notation tiers, and this is
   deliberately not notation. */
body.sotd #scramble-text.race-hold { font-size: clamp(1.05rem, 1.9vw, 1.4rem); }

/* The preview keeps its ordinary bottom-right corner. It was briefly moved up
   under the bar to dodge the board when the board was a bottom sheet; with the
   board in a left column there is nothing to dodge, and the corner it has
   everywhere else in the app is the one place people already look for it. */

/* Both of these are markup in index.html at all times — see the comment
   there — so they are display:none by default and shown only in the window.
   `[hidden]` still wins on the board, which is how "submit a time to see it"
   is expressed without a second class to keep in sync. */
/* The timer is NOT dimmed once you have submitted. An earlier version faded
   it to 12% so the board could sit on top of it, and the result was that the
   time you had just done — the single number you most want to read at that
   moment — was the least legible thing on screen. Moving the board out to its
   own column removed the reason for the fade rather than tuning it. */

#sotd-bar, #sotd-board { display: none; }
body.sotd #sotd-bar { display: flex; }
body.sotd #sotd-board:not([hidden]) { display: block; }

/* =================== HOLD / ARM INDICATOR =================== */
/* Sits under the digits and is the one piece of arming feedback that survives
   focus mode and inspection, where everything else is hidden. */
#hold-bar {
  position: relative;
  width: min(180px, 34vw); height: 2px;
  border-radius: 99px; overflow: hidden;
  background: color-mix(in srgb, var(--text) 10%, transparent);
}
#hold-fill {
  height: 100%; width: 0%; border-radius: 99px;
  background: color-mix(in srgb, var(--t-holding) 80%, transparent);
}
#hold-bar.ready #hold-fill { background: color-mix(in srgb, var(--t-ready) 85%, transparent); }

/* The inspection countdown is the only readable thing while arming, so it
   carries the state colour too. */
body.holding #inspect-num { color: var(--t-holding); text-shadow: 0 0 40px color-mix(in srgb, var(--t-holding) 45%, transparent); }
body.armed   #inspect-num { color: var(--t-ready);   text-shadow: 0 0 44px color-mix(in srgb, var(--t-ready) 55%, transparent); }
body.armed   #inspect-hint::after { content: ' — release'; color: var(--t-ready); }

/* =================== FEWEST MOVES ===================
   An hour and a written solution, where the hold-to-start timer normally is.
   Everything the timer would otherwise offer is taken away rather than left
   sitting there inert: there is no spacebar start, no inspection, no pace
   ghost and no running ao5 in an event whose round is three attempts long. */
body.fmc #timer-hint,
body.fmc #hold-bar,
body.fmc #pace-ghost,
body.fmc #timer-avgs,
body.fmc #manual-entry,
body.fmc #stackmat-bar { display: none !important; }

/* The averages an FMC session is actually scored on are the single and the
   mean of 3 (WCA 9b4a). A rolling ao12 of move counts is not a statistic
   anybody keeps, and a column of them invites reading the session as a speed
   session that happens to be printed in the wrong unit. */
body.fmc .stat[data-k="ao5"],
body.fmc .stat[data-k="ao12"],
body.fmc .stat[data-k="ao50"],
body.fmc .stat[data-k="ao100"],
body.fmc .bst[data-b="ao5"],
body.fmc .bst[data-b="ao12"],
body.fmc .bst[data-b="ao50"],
body.fmc .bst[data-b="ao100"],
body.fmc .stats-peek span:has(#peek-ao5),
body.fmc .stats-peek span:has(#peek-ao12),
body.fmc .stats-peek span:has(#peek-ao50) { display: none; }

.fmc-zone {
  width: min(680px, 92vw);
  display: flex; flex-direction: column; align-items: stretch; gap: 10px;
  user-select: text;
}

.fmc-idle { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.fmc-start {
  font: 600 1.05rem/1 var(--font-ui, inherit);
  padding: 14px 28px; border-radius: 14px; cursor: pointer;
  color: var(--bg); background: var(--accent); border: 0;
  box-shadow: 0 10px 30px color-mix(in srgb, var(--accent) 35%, transparent);
  transition: transform 120ms var(--ease-back), filter 120ms linear;
}
.fmc-start:hover { transform: translateY(-1px); filter: brightness(1.06); }
.fmc-start:active { transform: translateY(1px); }
.fmc-hint { margin: 0; font-size: .82rem; color: var(--text-faint); text-align: center; }

.fmc-live { display: flex; flex-direction: column; gap: 8px; }
.fmc-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.fmc-acts { display: flex; gap: 8px; align-items: center; }
/* Submit is the whole point of the screen, and inherited .ghost-btn styling
   put it on screen as 11px of faint grey beside a 34px clock — there, and
   invisible. It is the primary action here and is dressed as one; Abandon
   stays quiet, but quiet still has to be readable. */
.fmc-acts #fmc-submit {
  font-size: .92rem; font-weight: 700; padding: 8px 18px; border-radius: 10px;
  color: var(--bg); background: var(--accent); border-color: transparent;
}
.fmc-acts #fmc-submit:hover { filter: brightness(1.07); }
.fmc-acts #fmc-abandon {
  font-size: .82rem; padding: 8px 14px; border-radius: 10px;
  color: var(--text-dim);
}
.fmc-acts #fmc-abandon:hover { color: var(--danger); border-color: var(--danger); }
.fmc-clock {
  font-family: var(--font-timer); font-variant-numeric: tabular-nums;
  font-size: 2.1rem; font-weight: 700; line-height: 1;
  color: var(--t-idle);
}
.fmc-clock.warn   { color: var(--warn); }
.fmc-clock.danger { color: var(--danger); }

.fmc-box {
  width: 100%; resize: vertical; min-height: 64px;
  font: 500 1rem/1.5 var(--font-mono, ui-monospace, monospace);
  letter-spacing: .04em;
  padding: 10px 12px; border-radius: 12px;
  color: var(--text); background: color-mix(in srgb, var(--surface) 82%, transparent);
  border: 1px solid var(--border);
}
.fmc-box:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.fmc-notes { min-height: 46px; font-size: .88rem; letter-spacing: 0; opacity: .9; }

.fmc-readout {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px;
  font-size: .85rem; min-height: 1.4em;
}
.fmc-count { font-weight: 700; font-variant-numeric: tabular-nums; }
.fmc-etm { color: var(--text-faint); font-variant-numeric: tabular-nums; }
.fmc-etm.near { color: var(--warn); }
.fmc-etm.over { color: var(--danger); font-weight: 700; }
.fmc-state.good { color: var(--ok); }
.fmc-state.bad  { color: var(--danger); }
.fmc-why { flex: 1 1 100%; color: var(--text-faint); font-size: .8rem; }
.fmc-why.bad  { color: var(--danger); }
.fmc-why.warn { color: var(--warn); }

/* The solution as it is read back in the times list's menu. */
.pop-solution {
  max-width: 320px; padding: 4px 10px 8px;
  font: 500 .82rem/1.45 var(--font-mono, ui-monospace, monospace);
  color: var(--text-faint); white-space: pre-wrap; word-break: break-word;
}
