:root {
  --bg: #0d0d0f;
  --panel: rgba(22, 22, 26, 0.78);
  --line: rgba(255, 255, 255, 0.12);
  --text: #ece9e3;
  --muted: #a19d96;
  --accent: #ffd400;
  --pink: #ff5fa2;
  color-scheme: dark;
}

* {
  box-sizing: border-box;
}
[hidden] {
  display: none !important;
}

html,
body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.4 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  overflow: hidden;
  overscroll-behavior: none;
  -webkit-tap-highlight-color: transparent;
}

button {
  font: inherit;
  color: inherit;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.45em 1em;
  min-height: 40px;
  cursor: pointer;
  touch-action: manipulation;
}
button:hover {
  background: rgba(255, 255, 255, 0.12);
}
button[aria-pressed="true"] {
  border-color: var(--accent);
  background: rgba(255, 212, 0, 0.14);
}
button:focus-visible,
.stage:focus-visible,
input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
button.icon {
  width: 40px;
  padding: 0;
}

a {
  color: inherit;
}

/* The stage fills the window; the bar and the dock float over it. */
.stage {
  position: fixed;
  inset: 0;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  background: radial-gradient(120% 90% at 50% 38%, #1e1e24 0%, var(--bg) 70%);
}
.stage:focus-visible {
  outline-offset: -4px;
}
.stage canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
.stage-overlay {
  pointer-events: none;
}
.stage.is-pull canvas.stage-gl {
  cursor: ew-resize;
}
.stage canvas.stage-gl {
  cursor: grab;
}
.stage canvas.stage-gl:active {
  cursor: grabbing;
}

.progress {
  position: absolute;
  left: 0;
  top: 0;
  height: 2px;
  width: calc(var(--progress, 0) * 100%);
  background: var(--accent);
  opacity: 0;
  transition: opacity 0.3s;
}
.stage.is-loading .progress {
  opacity: 1;
}

/* A photoreal capture opens behind a veil (src/ui/open-veil.js): the stage blurred and dimmed under
   a small card with a bar and the megabytes so far; the veil lifts in a short fade once the capture
   is whole, the stage coming into focus as it goes. The card comes in once the wait has taken a
   moment. (The stage's own layers are blurred, not the veil's backdrop: no frosted glass, below.) */
.reach-ring {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 4;
  border: 2px solid var(--accent);
  border-radius: 50%;
  pointer-events: none;
}
.veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: rgba(13, 13, 15, 0.22);
  transition: opacity 0.22s ease-out;
}
.stage.is-veiled > :is(.stage-gl, .stage-overlay, .hotspots) {
  filter: blur(16px) brightness(0.8) saturate(0.8);
}
.stage.is-unveiling > :is(.stage-gl, .stage-overlay, .hotspots) {
  transition: filter 0.22s ease-out;
}
.veil.is-lifting {
  opacity: 0;
}
/* The toy's shelf picture, blurred where the toy will be, until the stage draws something. */
.veil-poster {
  position: absolute;
  left: 50%;
  top: 45%;
  width: 60%;
  transform: translate(-50%, -50%);
  filter: blur(16px) brightness(0.7) saturate(0.8);
  opacity: 0;
  transition: opacity 0.3s;
}
.veil-poster.is-in {
  opacity: 1;
}
.veil-card {
  position: absolute;
  left: 50%;
  top: 45%;
  width: min(16.5rem, calc(100% - 64px));
  padding: 12px 16px 9px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--panel);
  text-align: center;
  transform: translate(-50%, -50%);
  animation: veil-card 0.2s ease-out 0.25s both;
}
@keyframes veil-card {
  from {
    opacity: 0;
    transform: translate(-50%, calc(-50% + 6px));
  }
}
.veil-words {
  margin: 0 0 10px;
  overflow: hidden;
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.veil-bar {
  height: 4px;
  overflow: hidden;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.14);
}
.veil-bar > i {
  display: block;
  height: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.15s linear;
}
.veil-bytes {
  margin: 6px 0 0;
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.stage.is-veiled .progress {
  opacity: 0;
}

.message {
  position: absolute;
  left: 50%;
  top: 40%;
  transform: translate(-50%, -50%);
  width: min(26rem, calc(100% - 32px));
  padding: 1rem 1.2rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  text-align: center;
}
.message p {
  margin: 0 0 0.8rem;
}
/* (A Splash that isn't saved, with no network: clear of the stage's buttons, which show beside it.) */
.message[data-clear] {
  width: min(26rem, calc(100% - 144px));
}

.bar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: max(10px, env(safe-area-inset-top)) max(16px, env(safe-area-inset-right)) 10px
    max(16px, env(safe-area-inset-left));
  pointer-events: none;
  /* Even the muted link clears 4.5:1 over a white stage; the fade starts below the text. */
  background: rgba(13, 13, 15, 0.94);
}
.bar::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: 24px;
  background: linear-gradient(rgba(13, 13, 15, 0.94), transparent);
}
.is-fullscreen .bar {
  display: none;
}
.bar > * {
  pointer-events: auto;
}
.bar h1 {
  margin: 0;
  font-size: 17px;
  font-weight: 650;
  letter-spacing: 0.01em;
}
.bar h1 span {
  color: var(--accent);
}
.bar-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.pg-link {
  font-size: 13px;
  color: var(--muted);
  text-decoration: none;
  padding: 0 4px;
}
.pg-link:hover {
  color: var(--text);
}

.dock {
  position: fixed;
  z-index: 2;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 0 max(16px, env(safe-area-inset-right)) max(10px, env(safe-area-inset-bottom))
    max(16px, env(safe-area-inset-left));
  pointer-events: none;
}
.dock > * {
  pointer-events: auto;
}
.hint {
  margin: 0 auto 8px;
  width: fit-content;
  max-width: 100%;
  font-size: 13px;
  color: var(--muted);
  text-align: center;
  pointer-events: none;
}

.controls {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 auto 8px;
  max-width: 40rem;
  padding: 6px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 26px;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.controls {
  flex-wrap: wrap;
}
.save {
  position: relative;
}
.menu {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 0;
  display: grid;
  gap: 6px;
  padding: 8px;
  min-width: 13rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  z-index: 2;
}
.save .menu {
  left: auto;
  right: 0; /* (centered under Save it ran off a phone's right edge) */
}
.menu button {
  text-align: left;
  white-space: nowrap;
  border-radius: 10px;
}
/* A save with its choices beside it (the video's length; the GIF's motion, frames, and size). */
.menu-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.menu-row button {
  flex: 1 1 auto;
}
.menu-row select {
  min-height: 36px;
  font-size: 13px;
}
.fx {
  display: grid;
  gap: 6px;
  margin: 0 auto 8px;
  max-width: 40rem;
}
/* While the Effects row is open, a phone's dock shows it instead of the shelf. */
@media (max-width: 699px) {
  .dock.is-fx .finder,
  .dock.is-fx .shelf-find,
  .dock.is-fx .shelf,
  .dock.is-fx .shelf-empty {
    display: none;
  }
}
.fx-toggles {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
}
.fx-settings {
  display: grid;
  gap: 6px;
  max-height: 30vh;
  overflow-y: auto;
  border-radius: 16px;
}
.fx-settings:not(:has(> :not([hidden]))) {
  display: none;
}
.fx-params {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
  gap: 2px 14px;
  padding: 6px 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 16px;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.fx-slider,
.fx-axis,
.fx-colors {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 32px;
  font-size: 13px;
}
.fx-slider span,
.fx-axis span,
.fx-colors span {
  color: var(--muted);
  min-width: 4.8em;
}
.fx-slider input {
  flex: 1;
  min-width: 0;
  accent-color: var(--accent);
}
.fx-slider output {
  min-width: 3em;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.fx-axis {
  flex-wrap: wrap;
}
.fx-axis button {
  min-height: 28px;
  padding: 0.15em 0.7em;
  font-size: 13px;
}
.fx-colors input {
  width: 32px;
  height: 26px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: none;
}
.fx-slider[hidden],
.fx-colors[hidden] {
  display: none;
}
.fx-toggles button {
  background: var(--panel);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  min-height: 36px;
  padding: 0.35em 0.9em;
  font-size: 14px;
}
.fx-toggles select {
  font: inherit;
  font-size: 14px;
  color: inherit;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.35em 0.7em;
  min-height: 36px;
}
.splats {
  flex: 1 1 14rem;
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  padding-right: 10px;
}
.splats span {
  color: var(--muted);
  font-size: 14px;
}
.splats-track {
  position: relative;
  flex: 1;
  min-width: 0;
}
.splats-track input {
  width: 100%;
  margin: 0;
}
.splats-readout {
  flex: 0 0 7.5em;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 4px;
}
#splats-copies {
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1px 3px;
  font-size: 11px;
}
.splats input {
  min-width: 0;
  accent-color: var(--accent);
  height: 32px;
}
.splats output {
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  min-width: 3.5em;
  text-align: right;
  white-space: nowrap;
}

.shelf {
  list-style: none;
  margin: 0 auto;
  padding: 2px 2px 6px;
  display: flex;
  gap: 8px;
  max-width: 40rem;
  overflow-x: auto;
  scrollbar-width: none;
}
/* A flick on the row coasts as far as its speed carries it. ?old=shelf-snap: the row snaps to a card
   after each swipe, as Lite has it, which cut a flick's coast to about half (index.html marks the
   page html.shelf-snap). */
html.shelf-snap .shelf {
  scroll-snap-type: x proximity;
}
.shelf::-webkit-scrollbar {
  display: none;
}
.shelf li {
  scroll-snap-align: start;
}
.shelf button {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  width: 76px;
  padding: 6px 4px;
  border-radius: 14px;
  background: var(--panel);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.shelf img {
  width: 52px;
  height: 52px;
  object-fit: cover;
  border-radius: 10px;
}
.shelf span {
  font-size: 11.5px;
  line-height: 1.2;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.more {
  margin: 0 auto 8px;
  max-width: 40rem;
  max-height: 46vh;
  overflow-y: auto;
  padding: 8px 12px 10px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 18px;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  font-size: 14px;
}
.more-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
}
.more-head [role="tablist"] {
  display: flex;
  gap: 6px;
}
.more-head [role="tab"] {
  min-height: 34px;
  padding: 0.3em 0.9em;
  font-size: 14px;
}
.more-head [role="tab"][aria-selected="true"] {
  border-color: var(--accent);
  background: rgba(255, 212, 0, 0.14);
}
.more-head .icon {
  min-height: 34px;
  width: 34px;
}
.pane .row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 6px 0;
}
.pane .row > span {
  color: var(--muted);
  min-width: 4.5em;
}
.pane input[type="text"],
.pane select {
  flex: 1;
  min-width: 0;
  min-height: 36px;
  padding: 0.3em 0.8em;
  font: inherit;
  font-size: 14px;
  color: inherit;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line);
  border-radius: 999px;
}
.pane .toggles {
  flex-wrap: wrap;
}

/* The Toy sheet (src/ui/toy-panel.js): a kit toy's action, Alive, controls, and options. It
   scrolls within a third of the screen, so the toy keeps room above it on a phone. */
.toy-sheet {
  max-height: 34vh;
}
.toy-sheet h2 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.toy-sheet .row[hidden],
.toy-sheet .note[hidden] {
  display: none;
}
.toy-sheet .row {
  min-height: 36px;
}
.toy-sheet .row > span {
  min-width: 6em;
}
.toy-sheet input[type="range"] {
  flex: 1;
  min-width: 0;
  accent-color: var(--accent);
}
.toy-sheet output {
  min-width: 3em;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.toy-sheet .check-row {
  cursor: pointer;
}
.toy-sheet .check-row > span {
  color: var(--text);
}
.toy-sheet input[type="checkbox"] {
  width: 22px;
  height: 22px;
  margin: 0;
  accent-color: var(--accent);
}
.toy-sheet input[type="color"] {
  width: 44px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: none;
}
.toy-sheet .primary {
  border-color: var(--accent);
  background: rgba(255, 212, 0, 0.14);
}
/* Over the stage (src/ui/toy-stage.js): a kit toy's tool row, its slider, and its labels. */
.toy-tools {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 auto 6px;
  max-width: 40rem;
}
.toy-tools button {
  min-height: 36px;
  padding: 0.3em 0.9em;
  font-size: 14px;
  background: var(--panel);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.dock > .toy-tools {
  justify-content: flex-start;
  flex-wrap: nowrap;
  overflow-x: auto;
  max-width: 100%;
  margin: 0 0 8px;
}
.dock > .toy-tools button {
  flex: 0 0 auto;
}
.toy-tools .swatch-tool {
  box-shadow: inset 0 -4px 0 var(--tool-color);
}
.toy-tools .tinted-tool {
  border-color: var(--tool-tint);
}
.toy-tools button[aria-disabled="true"] {
  opacity: 0.45;
}
/* A capture's Splash tab, held in the bar until its rig is on (src/ui/toy-panel.js reserve). */
#toy-btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: default;
}
.toy-slider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 auto 6px;
  max-width: 24rem;
  padding: 4px 14px;
  font-size: 13px;
  color: var(--muted);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 999px;
}
.toy-slider input {
  flex: 1;
  accent-color: var(--accent);
}
/* A game or a song (src/ui/toy-game.js): the bar in the dock (main's game bar and song bar), and
   its panel on the Toy sheet. */
.game-bar {
  margin: 0 auto 6px;
  max-width: 28rem;
  padding: 6px 12px 8px;
  text-align: center;
  background: var(--panel);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--line);
  border-radius: 14px;
}
.game-bar-title {
  margin: 0;
  font-size: 13.5px;
  font-weight: 600;
  overflow-wrap: anywhere;
}
.game-bar-info {
  margin: 1px 0 4px;
  font-size: 12px;
  color: var(--muted);
}
.game-bar-info:empty {
  display: none;
}
/* A recording being heard: Stop beside the line that says how far it has got (src/ui/toy-game.js). */
.game-bar-info-line {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
}
.song-bar-stop {
  min-height: 0;
  height: 26px;
  margin: 0 0 3px;
  padding: 0 12px;
  font-size: 12px;
}
.game-bar-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 6px 12px;
}
.game-buttons {
  display: flex;
  gap: 4px;
}
.game-buttons button {
  display: grid;
  place-items: center;
  width: 38px;
  height: 34px;
  padding: 0;
}
.game-buttons .i-pause,
#game-play[data-playing="true"] .i-play {
  display: none;
}
#game-play[data-playing="true"] .i-pause {
  display: inline;
}
#game-play {
  border-color: var(--accent);
}
.game-bar-move {
  font-size: 12.5px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.song-bar-title {
  display: block;
  width: 100%;
  padding: 1px 4px;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  text-align: center;
  color: inherit;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
}
.song-bar-title:focus {
  border-color: var(--line);
  outline: none;
}
#song-loop[aria-pressed="true"] {
  color: #000;
  background: var(--accent);
  border-color: var(--accent);
}
.song-speed {
  height: 34px;
  font-size: 12.5px;
}
.song-seek {
  display: block;
  width: 100%;
  margin: 4px 0 0;
  accent-color: var(--accent);
}
.game-box {
  margin: 6px 0 2px;
}
.game-box .game-title {
  margin: 0;
  font-size: 12.5px;
}
/* Lane Audio piano: a recording being listened to, and its Stop. */
.song-heard-row {
  align-items: center;
}
.song-heard-row .song-heard {
  flex: 1;
  margin: 0;
  font-variant-numeric: tabular-nums;
}
.game-box textarea {
  width: 100%;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 12px;
}
.game-details summary {
  cursor: pointer;
  font-size: 13px;
  margin: 8px 0 2px;
}
.toy-legend {
  position: absolute;
  top: 64px;
  left: 12px;
  max-width: min(46vw, 16rem);
  max-height: 40vh;
  overflow: hidden;
  padding: 8px 10px;
  font-size: 12px;
  line-height: 1.35;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  pointer-events: none;
}
/* On a phone the labels run across the top, and the toy is framed below them (app.js, frameFree). */
@media (max-width: 699px) {
  .toy-legend {
    right: 12px;
    max-width: none;
    max-height: 24vh;
    overflow-y: auto;
    pointer-events: auto;
    font-size: 11.5px;
  }
}
/* The copied code's own buttons over the stage (the Splat mirror's Start camera): in the middle of
   the room above the dock, not of the whole page (Lite's stage fills it). */
.live-stage-button {
  top: var(--free-mid, 40%) !important;
}
.toy-legend-title {
  margin: 0 0 4px;
  font-weight: 600;
}
.toy-legend ul {
  margin: 0;
  padding: 0;
  list-style: none;
}
.toy-legend li.head {
  margin-top: 4px;
  font-weight: 600;
}
.toy-legend li.on {
  color: var(--accent);
}
.toy-legend li.dim {
  opacity: 0.5;
}
.toy-legend-ruler {
  display: block;
  height: 4px;
  margin: 2px 0 3px;
  background: var(--text);
  border-radius: 2px;
}

/* A PDF's words and a set's list, under a picture toy's media (src/ui/toy-media-text.js). */
.media-text-box {
  margin: 8px 0 2px;
  border: 1px solid var(--line);
  border-radius: 10px;
}
.media-text-box summary {
  padding: 7px 10px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.media-text-box[open] summary {
  border-bottom: 1px solid var(--line);
}
.media-text {
  max-height: 32vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 10px 12px;
  font-size: 14px;
  line-height: 1.5;
  user-select: text;
}
.media-text p {
  margin: 0 0 0.75em;
}
.media-text mark,
.media-found mark {
  background: rgba(255, 212, 0, 0.22);
  color: inherit;
  border-radius: 2px;
  box-shadow: inset 0 -2px 0 var(--accent);
}
.media-text-tools {
  padding: 8px 10px 0;
  border-top: 1px solid var(--line);
}
.media-text-tools form {
  flex: 1 1 12rem;
  min-width: 0;
}
.media-text-tools input[type="search"] {
  flex: 1 1 auto;
  min-width: 0;
}
.media-text-box .note {
  margin: 6px 10px 0;
}
.media-found {
  display: grid;
  gap: 4px;
  max-height: 24vh;
  overflow-y: auto;
  margin: 6px 0 8px;
  padding: 0 10px;
  list-style: none;
}
.media-found button {
  width: 100%;
  text-align: left;
  font-size: 13px;
}
.media-found-page {
  font-weight: 600;
}
.media-list {
  display: grid;
  gap: 4px;
  margin: 6px 0;
  padding: 0;
  list-style: none;
}
.media-list li {
  display: flex;
  align-items: center;
  gap: 8px;
}
.media-list li span {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Put it on a web page (src/ui/more.js): the code to copy. */
#embed-code {
  width: 100%;
  margin-top: 6px;
  padding: 6px 8px;
  font: 12px/1.4 ui-monospace, "SF Mono", Menlo, monospace;
  color: var(--text);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--line);
  border-radius: 8px;
  resize: vertical;
}
/* A kit toy's keyboard and input (src/ui/toy-input.js). */
.toy-input-title {
  margin: 10px 0 2px;
  font-weight: 600;
}
.toy-keys-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  max-height: 30vh;
  overflow-y: auto;
}
.toy-keys-buttons button {
  min-height: 34px;
  min-width: 34px;
  padding: 0.2em 0.6em;
  font-size: 13px;
}
.toy-keys-buttons button[aria-disabled="true"] {
  opacity: 0.45;
}
.toy-sheet form.row input[type="text"] {
  flex: 1;
}
.toy-pad canvas {
  display: block;
  width: min(100%, 240px, 18dvh);
  height: auto;
  margin: 6px 0;
  border-radius: 8px;
  touch-action: none;
}
.toy-pad .pad-pens {
  flex-wrap: wrap;
}
.toy-sheet .warning {
  margin: 6px 0;
  color: var(--pink);
}
.toy-sheet .note[hidden],
.toy-sheet .warning[hidden] {
  display: none;
}
.pane .note {
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.45;
  margin: 6px 0;
}
.pane h2 {
  font-size: 16px;
  margin: 4px 0;
}
.pane p {
  margin: 6px 0;
  line-height: 1.45;
}
.pane .howto {
  color: var(--text);
  font-weight: 600;
}
.qr {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.qr canvas {
  width: 132px;
  height: 132px;
  image-rendering: pixelated;
  border-radius: 8px;
}
.qr .note {
  flex: 1 1 10rem;
}

.finder {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 auto 6px;
  max-width: 40rem;
}
.chips {
  flex: 1;
  display: flex;
  gap: 6px;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.chips::-webkit-scrollbar {
  display: none;
}
.chip {
  flex: none;
  min-height: 32px;
  padding: 0.25em 0.8em;
  font-size: 13px;
  white-space: nowrap;
  background: var(--panel);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.chip[aria-expanded="true"] {
  border-color: var(--accent);
}
/* On a phone the finder stays one line (the October 21, 2026 review; the Operator's choice): Search
   pinned first, the shelf's names scrolling in the middle with Surprise me and Labs at their end
   (src/ui/shelf.js moves them there), and the gallery last, so about three names show instead of
   two, at the same height. The names fade at the edge they scroll on. */
@media (max-width: 600px) {
  .finder {
    gap: 4px;
  }
  #shelf-search-toggle {
    order: -1;
  }
  .finder > .chips {
    scroll-snap-type: x proximity;
    scroll-padding-inline: 4px;
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent);
  }
  .finder > .chips::after {
    content: "";
    flex: none;
    width: 16px; /* the last chip scrolls clear of the fade */
  }
  .finder > .chips .chip {
    scroll-snap-align: start;
  }
  .finder > .chip,
  .finder > .chips .chip {
    padding-inline: 0.65em;
  }
}
#shelf-search-toggle {
  font-size: 17px;
  line-height: 1;
}
.shelf-find {
  margin: 0 auto 6px;
  max-width: 40rem;
}
.shelf-find input {
  width: 100%;
  min-height: 36px;
  padding: 0.35em 0.9em;
  font: inherit;
  font-size: 16px; /* no zoom on focus in Safari */
  color: inherit;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 999px;
}
.shelf-empty {
  margin: 0 auto;
  max-width: 40rem;
  font-size: 13px;
  color: var(--muted);
}
.shelf button.is-away {
  opacity: 0.6;
}
.shelf button.is-away span::after {
  content: " ↗";
}
.shelf .thumb {
  width: 52px;
  height: 52px;
  border-radius: 10px;
  background: var(--line);
}

.credit {
  margin: 4px auto 0;
  max-width: 40rem;
  font-size: 11px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.credit b {
  color: var(--text);
  font-weight: 600;
}

.toast {
  position: fixed;
  left: 50%;
  top: calc(max(10px, env(safe-area-inset-top)) + 54px);
  transform: translateX(-50%);
  width: max-content;
  max-width: calc(100% - 32px);
  padding: 0.55rem 0.9rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  font-size: 14px;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  z-index: 4; /* over the panel and its sheets */
}
/* A toast with an action button (src/ui/toast-dismiss.js): the ✕ at its end (the 40 px circle of
   button.icon) and the swipe, which moves the toast by --toast-dx from its middle. */
.toast .toast-close {
  float: right;
  margin-left: 0.5rem;
}
.toast.swipeable {
  touch-action: pan-y;
  transform: translateX(calc(-50% + var(--toast-dx, 0px)));
  opacity: var(--toast-o, 1);
}
.toast.settling {
  transition:
    transform 0.2s ease-out,
    opacity 0.2s ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .toast.settling {
    transition: none;
  }
}

@media (min-width: 700px) {
  .controls,
  .fx,
  .more,
  .finder,
  .shelf-find,
  .shelf,
  .shelf-empty,
  .credit {
    max-width: 50rem;
  }
  .shelf button {
    width: 88px;
  }
  .shelf img {
    width: 60px;
    height: 60px;
  }
}

/* Record (src/ui/record.js): its button in the bar, the Share tab's row, the pill over the stage
   while it records, the countdown's big number, and the preview sheet. */
.rec-btn {
  position: fixed;
  right: max(16px, env(safe-area-inset-right));
  bottom: calc(var(--dock-h, 200px) + 6px);
  display: grid;
  place-items: center;
  background: var(--panel);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.rec-btn .i-stop,
.rec-btn[data-state="rec"] .i-rec,
.rec-btn[data-state="count"] .i-rec {
  display: none;
}
.rec-btn[data-state="rec"] .i-stop,
.rec-btn[data-state="count"] .i-stop {
  display: block;
}
.rec-btn[data-state="rec"] {
  border-color: #d62d2d;
}
.record-start {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.record-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #d62d2d;
}
.record-start.recording .record-dot {
  border-radius: 2px;
}
.pane .record-countdown {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  color: var(--muted);
  font-size: 14px;
}

.rec-pill {
  position: fixed;
  left: 50%;
  top: calc(max(10px, env(safe-area-inset-top)) + 54px);
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 8px;
  transform: translateX(-50%);
  padding: 5px 6px 5px 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 13.5px;
  white-space: nowrap;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  pointer-events: none; /* taps pass through to the toy, except on its button */
}
.rec-pill button {
  pointer-events: auto;
  min-height: 0;
  padding: 5px 12px;
  font-size: 13px;
}
.rec-dot {
  display: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #d62d2d;
}
.rec-pill.recording .rec-dot {
  display: block;
  animation: rec-blink 1s steps(2, start) infinite;
}
.rec-time {
  font-variant-numeric: tabular-nums;
}
.is-recording .toast {
  top: calc(max(10px, env(safe-area-inset-top)) + 100px);
}
@keyframes rec-blink {
  to {
    visibility: hidden;
  }
}

/* The countdown's number, big over the toy (never in the take: the take is the canvas). */
.rec-count {
  position: fixed;
  left: 0;
  right: 0;
  top: var(--free-mid, 50%);
  transform: translateY(-50%);
  z-index: 2;
  display: grid;
  place-items: center;
  pointer-events: none;
}
.rec-count span {
  display: grid;
  place-items: center;
  width: 148px;
  height: 148px;
  border-radius: 50%;
  background: rgb(0 0 0 / 55%);
  color: #fff;
  font-size: 92px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.rec-count span.pop {
  animation: rec-count-pop 0.45s ease-out;
}
@keyframes rec-count-pop {
  from {
    transform: scale(1.3);
    opacity: 0.4;
  }
}
@media (prefers-reduced-motion: reduce) {
  .rec-pill.recording .rec-dot,
  .rec-count span.pop {
    animation: none;
  }
}

.rec-preview {
  width: min(720px, calc(100vw - 32px));
  max-height: calc(100dvh - 32px);
  padding: 14px 16px 16px;
  background: #16161a;
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 16px;
  overflow: auto;
}
.rec-preview[open] {
  display: grid;
  gap: 10px;
}
.rec-preview::backdrop {
  background: rgb(0 0 0 / 55%);
}
.rec-preview-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.rec-preview h2 {
  margin: 0;
  font-size: 17px;
}
/* The player takes the take's own shape (tall on a phone), as big as fits. */
.rec-video {
  display: block;
  max-width: 100%;
  max-height: min(62vh, 560px);
  margin: 0 auto;
  background: #000;
  border-radius: 10px;
  object-fit: contain;
}
.rec-meta {
  margin: 0;
  color: var(--muted);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.rec-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.rec-actions .primary {
  border-color: var(--accent);
  background: rgba(255, 212, 0, 0.14);
}
#rec-discard {
  margin-left: auto;
}
.rec-confirm {
  display: grid;
  gap: 10px;
  padding: 12px;
  border: 1px solid var(--pink);
  border-radius: 12px;
}
.rec-confirm p {
  margin: 0;
  font-size: 14px;
}

/* The tilt lock (src/ui/tilt-lock.js): a round button above Record at the stage's right edge. */
.tilt-btn {
  position: fixed;
  right: max(16px, env(safe-area-inset-right));
  bottom: calc(var(--dock-h, 200px) + 54px);
  display: grid;
  place-items: center;
  background: var(--panel);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.tilt-btn .i-shut,
.tilt-btn[aria-pressed="true"] .i-open {
  display: none;
}
.tilt-btn[aria-pressed="true"] .i-shut {
  display: block;
}

/* Save image (src/ui/still.js): its row under Record on the Share tab. */
.pane .still-row .still-save {
  flex: none;
}
.pane .still-row select {
  flex: 1 1 0;
}
.pane .still-row #still-size {
  flex: 0 1 6.5em;
}

/* Save as PDF (src/ui/pdf.js): its line on the Share tab, and its dialog. */
.pane .pdf-line {
  flex: 1;
  margin: 0;
}
.pdfx {
  width: min(30rem, calc(100vw - 32px));
  max-height: calc(100dvh - 32px);
  padding: 0;
  background: #16161a;
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 16px;
  overflow: auto;
}
.pdfx::backdrop {
  background: rgb(0 0 0 / 55%);
}
.pdfx form {
  display: grid;
  gap: 12px;
  padding: 18px 18px 16px;
}
.pdfx h2 {
  margin: 0;
  font-size: 17px;
}
.pdfx-opt {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 10px;
  align-items: start;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  cursor: pointer;
}
.pdfx-opt:has(input:checked) {
  border-color: var(--accent);
  background: rgba(255, 212, 0, 0.08);
}
.pdfx-opt input {
  margin-top: 3px;
  accent-color: var(--accent);
}
.pdfx-opt > span {
  grid-column: 2;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.4;
}
.pdfx-moving {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
}
.pdfx-moving label {
  display: grid;
  gap: 2px;
  font-size: 12px;
  color: var(--muted);
}
.pdfx-moving select {
  font: inherit;
  font-size: 14px;
  color: inherit;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 4px 6px;
}
.pdfx-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
.pdfx-buttons .primary {
  border-color: var(--accent);
  background: rgba(255, 212, 0, 0.14);
}

/* Hands-on (src/ui/hands-on.js): ✋ above the tilt lock at the stage's right edge, ↺ beside it. */
.hands-btn {
  position: fixed;
  right: max(16px, env(safe-area-inset-right));
  bottom: calc(var(--dock-h, 200px) + 102px);
  display: grid;
  place-items: center;
  font-size: 18px;
  background: var(--panel);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.hands-btn.hands-reset {
  right: calc(max(16px, env(safe-area-inset-right)) + 48px);
}

/* The bottom panel's size (src/ui/dock-size.js): a handle at its top steps it from full to small (no
   shelf) to hidden (the handle and the credit alone). */
/* Over the panel's top edge, not in its flow: the panel, and the band the toy is framed in, keep
   their place. */
.dock-handle {
  position: absolute;
  top: -24px;
  left: 50%;
  transform: translateX(-50%);
  width: 56px;
  min-height: 22px;
  padding: 0;
  border: none;
  background: transparent;
}
.dock-handle span {
  display: block;
  margin: 0 auto;
  width: 36px;
  height: 5px;
  border-radius: 3px;
  background: var(--muted);
  opacity: 0.7;
}
.dock-handle:hover span {
  opacity: 1;
}
/* (The gallery opens over the stage whatever the panel's size: from the rail when the shelf is put away.) */
body:not(.gallery) .dock[data-size="small"] :is(.finder, #shelf-find, #shelf, #shelf-empty),
body:not(.gallery) .dock[data-size="hidden"] > :not(#dock-handle, #credit) {
  display: none;
}

/* The stage's buttons (src/ui/stage-buttons.js, and Hands-on, the tilt lock, and Record): one column
   at the stage's right edge, just above the dock; a hidden one takes no room. */
.stage-rail {
  position: fixed;
  right: max(16px, env(safe-area-inset-right));
  bottom: calc(var(--dock-h, 200px) + 6px);
  display: flex;
  /* From Record up; when they don't all fit between the bar and the panel (a sheet open, a
     landscape phone), the rest go on in a column to the left. */
  flex-flow: column-reverse wrap-reverse;
  align-items: flex-end;
  align-content: flex-start;
  gap: 4px;
  padding-top: 88px; /* Sound and How to play stay at the top when the remaining buttons wrap. */
  max-height: calc(100dvh - var(--dock-h, 200px) - 72px);
  pointer-events: none;
  z-index: 1; /* under the panel's menus (Save's), which open up over it */
}
/* Laid from the bottom: Record first, a toy's live input just above it (src/ui/toy-live.js), then up
   the page's order. */
.stage-rail > #hands-record {
  order: 0;
}
.stage-rail > .live-btn {
  order: 1;
}
.stage-rail > #tilt-toggle,
.stage-rail > #pop-toggle {
  order: 2; /* Pop out just above the lock */
}
.stage-rail > #keys-btn {
  order: 3; /* a piano's falling keys (src/ui/stage-buttons.js), just above Hands-on */
}
.stage-rail > .rail-row {
  order: 3;
}
.stage-rail > #spin-btn {
  order: 4;
}
.stage-rail > #home-btn {
  order: 5;
}
.stage-rail > #full-btn {
  order: 6;
}
.stage-rail > #help-btn {
  position: absolute;
  top: 44px;
  right: 0;
}
.stage-rail > #sound {
  position: absolute;
  top: 0;
  right: 0;
}
.stage-rail > #layers {
  order: 5;
}
.stage-rail > #detail-btn {
  order: 4;
}
.stage-rail > #labels-btn {
  order: 4; /* a capture's Labels (src/ui/hotspots.js), just above the turntable */
}
.is-fullscreen .stage-rail {
  max-height: calc(100dvh - var(--dock-h, 40px) - 24px);
}
/* The gallery from the rail (src/ui/shelf.js): only while the shelf is put away (the panel small or
   hidden, or full screen), just under full screen. */
.stage-rail > #rail-gallery {
  order: 7;
}
.dock[data-size="full"] ~ .stage-rail > #rail-gallery {
  display: none;
}
/* The fold button (src/ui/stage-buttons.js), on a phone and in full screen: just left of Record, out
   of the rail's flow, so Record stays lowest at the right. Folded, every other button goes invisible
   where it stands, so nothing moves and the fold button is the rail's one button, with a red dot
   while a microphone, camera, or recording is on. Where the rail wraps into columns, the gap between
   them keeps the fold button's place. */
.stage-rail > #rail-fold {
  position: absolute;
  right: 48px;
  bottom: 0;
  display: none;
}
.rail-fold .i-open,
.stage-rail.folded .rail-fold .i-fold {
  display: none;
}
.stage-rail.folded .rail-fold .i-open {
  display: block;
}
.rail-fold .rail-dot {
  position: absolute;
  top: 3px;
  right: 3px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #ff3b30;
  box-shadow: 0 0 0 2px var(--bg);
  display: none;
}
.stage-rail.folded:has(.live-btn[aria-pressed="true"], .rec-btn:not([data-state="off"])) .rail-dot {
  display: block;
}
.is-fullscreen .stage-rail > #rail-fold {
  display: grid;
}
.is-fullscreen .stage-rail {
  column-gap: 56px;
}
.is-fullscreen .stage-rail.folded > :not(#rail-fold) {
  visibility: hidden;
}
@media (max-width: 700px), (max-height: 500px) {
  .stage-rail > #rail-fold {
    display: grid;
  }
  .stage-rail {
    column-gap: 56px;
  }
  .stage-rail.folded > :not(#rail-fold) {
    visibility: hidden;
  }
  /* While More takes the room, the buttons step aside, as in the focused layout: they are for play,
     and they would wrap into a row over the toy. (Settings' Detail and splats opens their popover.) */
  .dock:has(> #more:not([hidden])) ~ .stage-rail {
    visibility: hidden;
  }
}
/* Hands-on's ↺ hangs to the left of ✋, out of the rail's flow, so switching Hands-on on moves no
   other button (the October 21, 2026 review). Where the rail wraps into a second column, the gap
   between the columns keeps its place for as long as the toy has Hands-on. */
.stage-rail .rail-row {
  position: relative;
  display: flex;
}
.stage-rail .rail-row > #hands-reset {
  position: absolute;
  top: 0;
  right: calc(100% + 8px);
}
.stage-rail:has(#hands-toggle:not([hidden])) {
  column-gap: 56px;
}
.stage-rail button {
  position: static;
  display: grid;
  place-items: center;
  font-size: 18px;
  background: var(--panel);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  pointer-events: auto;
}
.stage-rail button[hidden] {
  display: none;
}
/* On (the October 21, 2026 review): a solid yellow disc with a dark icon and a dark rim, so it reads
   over a light toy as well as a dark one; off stays the dark glass disc with a light icon. */
.stage-rail button[aria-pressed="true"],
.stage-rail button[aria-expanded="true"]:not(#rail-fold) {
  color: #141416;
  background: var(--accent);
  border-color: rgba(0, 0, 0, 0.55);
  box-shadow:
    0 0 0 2px rgba(255, 255, 255, 0.85),
    0 2px 8px rgba(0, 0, 0, 0.35);
}
.stage-rail button[aria-pressed="true"]:hover,
.stage-rail button[aria-expanded="true"]:not(#rail-fold):hover {
  background: #ffe24d;
}

/* A live input's button (src/ui/toy-live.js): its icon, and while the input runs a red dot at its
   corner, as a phone marks a microphone or camera in use. */
.stage-rail > .live-btn {
  position: relative;
}
.live-btn .live-dot {
  position: absolute;
  top: 3px;
  right: 3px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #ff3b30;
  box-shadow: 0 0 0 2px var(--bg);
  display: none;
}
.live-btn[aria-pressed="true"] .live-dot {
  display: block;
}
.stage-rail > .live-btn:disabled {
  opacity: 0.5;
  cursor: progress;
}
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* A toy's own controls stay over the stage when the dock is hidden. */
.toy-overlays {
  position: fixed;
  bottom: calc(var(--dock-h, 200px) + 6px);
  left: 12px;
  right: 72px;
  pointer-events: none;
  z-index: 1;
}
.toy-overlays > * {
  pointer-events: auto;
}
/* On a phone, left of the stage's buttons: the fold button beside Record, or a second column. */
@media (max-width: 700px), (max-height: 500px) {
  .toy-overlays {
    right: clamp(110px, calc(var(--rail-room, 0px) + 6px), 50vw);
  }
}
/* A toy's own credit on the stage (Video to 3D's clip, from the synced src/splashery/video3d/credit.js,
   which sets z-index 23 inline) sits with the stage's overlays, under the rail's popover. */
#v3d-source-credit {
  z-index: 3 !important;
}
.detail-popover {
  position: fixed;
  right: max(64px, calc(env(safe-area-inset-right) + 64px));
  width: min(21rem, calc(100vw - 80px));
  max-height: calc(100dvh - 16px);
  overflow-y: auto;
  padding: 12px;
  background: #16161a;
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: 0 8px 30px #0008;
  z-index: 4;
}
.detail-popover h2 {
  margin: 0;
  font-size: 15px;
}
.detail-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}
.detail-row select {
  flex: 1;
  min-width: 0;
  min-height: 40px;
  font: inherit;
  color: inherit;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 10px;
}
.detail-popover .splats {
  flex-wrap: wrap;
  padding: 0;
}
.detail-popover .splats input {
  flex-basis: 55%;
}
/* Copies (src/app.js): Remove a copy, the count, and Add a copy, with a line on what they cost. */
.copies-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}
.copies-row > span {
  color: var(--muted);
  font-size: 14px;
  flex-basis: 100%;
}
.copies-row button {
  flex: 1 1 auto;
  min-height: 44px;
  padding-inline: 0.7em;
  font-size: 14px;
  white-space: nowrap;
}
.copies-row button:disabled {
  opacity: 0.45;
  cursor: default;
}
.copies-row output {
  min-width: 2.2em;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.copies-row .note {
  flex-basis: 100%;
  margin: 0;
  color: var(--muted);
  font-size: 12.5px;
}

/* Keys named in How to play (src/ui/keys.js). */
kbd {
  padding: 0 0.35em;
  border: 1px solid var(--line);
  border-radius: 4px;
  font: inherit;
  font-size: 0.92em;
}

/* Paused (src/ui/pause.js): a chip at the top of the stage. */
.paused-chip {
  position: fixed;
  left: 50%;
  top: calc(max(10px, env(safe-area-inset-top)) + 54px);
  transform: translateX(-50%);
  min-height: 32px;
  padding: 0.3em 0.9em;
  font-size: 14px;
  background: var(--panel);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  z-index: 3;
}
.paused-chip[hidden] {
  display: none;
}

/* The gallery (src/ui/shelf.js): the shelf as one page of every toy's thumbnail, over the stage, as
   the Playground's gallery is. The chips, search, Surprise me, and Labs stay at its top. */
#shelf-gallery {
  display: grid;
  place-items: center;
  padding-inline: 0.65em;
}
.gallery-close {
  display: block;
  margin: 0 0 8px auto;
  min-height: 34px;
  font-size: 13px;
  background: var(--panel);
}
.gallery-close[hidden] {
  display: none;
}
body.gallery .dock {
  top: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  padding-top: max(12px, env(safe-area-inset-top));
  background: var(--bg);
  pointer-events: auto;
}
body.gallery .dock > :not(.gallery-close, .finder, #shelf-find, #shelf, #shelf-empty) {
  display: none;
}
body.gallery .dock > * {
  flex: none;
  width: 100%;
  max-width: 72rem;
  margin-inline: auto;
}
body.gallery .gallery-close {
  width: auto;
  margin-right: max(0px, (100% - 72rem) / 2);
}
body.gallery .shelf {
  flex: 1 1 auto;
  min-height: 0;
  position: relative;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(76px, 1fr));
  align-content: start;
  gap: 10px 8px;
  overflow-x: hidden;
  overflow-y: auto;
  scroll-snap-type: none;
}
body.gallery .shelf button {
  width: 100%;
  height: 100%;
  justify-content: flex-start;
}
body.gallery .shelf img,
body.gallery .shelf .thumb {
  width: 100%;
  max-width: 72px;
  height: auto;
  aspect-ratio: 1;
}
@media (min-width: 700px) {
  body.gallery .shelf {
    grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
    gap: 12px 8px;
  }
  body.gallery .shelf button {
    font-size: 12.5px;
  }
  body.gallery .shelf img,
  body.gallery .shelf .thumb {
    max-width: 96px;
  }
}

/* C17: Arcade furniture has its own space above and below the live playfield. */
.lite-arcade .arc-top {
  top: 6px;
}
.lite-arcade .arc-surface {
  overflow: hidden;
}
.lite-arcade .arc-stats {
  max-width: 54%;
}
.arcade-tools {
  display: flex;
  justify-content: center;
  gap: 6px;
}
.arcade-tools .arc-choices,
.arc-root.lite-arcade .arcade-tools .arc-choices {
  position: static;
  display: flex;
  flex-wrap: nowrap;
  gap: 6px;
  overflow-x: auto;
  pointer-events: auto;
}
.arcade-tools select {
  display: none;
  max-width: 100%;
}
.arcade-tools-floating {
  position: absolute;
  bottom: 0;
  left: 8px;
  right: 8px;
  margin: 0;
  max-width: none;
  pointer-events: auto;
}
@media (max-width: 600px) {
  .arcade-tools .arc-choices,
  .arc-root.lite-arcade .arcade-tools .arc-choices {
    display: none;
  }
  .arcade-tools select {
    display: block;
  }
}
/* Keep the touch pad inside the band beside the rail, including Strata's five buttons. */
@media (max-width: 600px) {
  .arc-root.lite-arcade .arc-key {
    width: 44px;
    height: 44px;
  }
  .arc-root.lite-arcade .arc-key.arc-fire {
    width: 54px;
  }
  .arc-root.lite-arcade .arc-pad-dir {
    grid-template-columns: repeat(3, 44px);
    grid-template-rows: repeat(2, 44px);
  }
}

/* No frosted glass. On a phone the blur under each panel is drawn again every frame the toy moves:
   the measuring Test's slow turn on an Adreno 830 had 29 to 31 late frames of 207 to 211 with it
   (Song landscape, the grape) and none without. The panels are a little more opaque instead.
   ?old=blur brings the glass back (index.html marks the page html.blur). */
html:not(.blur) {
  --panel: rgba(22, 22, 26, 0.88);
}
html:not(.blur) * {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

/* ---- The focused layout, the default (html.focus, src/ui/focus.js; ?ui=classic turns it off) -----
   The stage full-bleed and the toy framed above one bottom bar; a slim header with the toy's name
   and its credit; the stage's buttons in their column at the right edge, above the bar; every
   sheet from the bottom, at most about half the screen. Nothing here
   repaints over the moving stage each frame: the fades run only when the bars come and go. */
html.focus {
  --sheet-bg: #16161a;
}

/* The header: the toy's name (it opens the shelf) over its credit, and Share. */
html.focus .bar {
  align-items: center;
  gap: 4px;
  padding: max(0px, env(safe-area-inset-top)) max(8px, env(safe-area-inset-right)) 2px
    max(2px, env(safe-area-inset-left));
}
html.focus .bar::after,
html.focus .bar h1 {
  display: none;
}
html.focus.is-fullscreen .bar {
  display: flex;
}
.focus-id {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
#focus-name {
  align-self: flex-start;
  display: flex;
  align-items: center;
  gap: 2px;
  max-width: 100%;
  min-height: 44px;
  padding: 0 8px;
  border: none;
  border-radius: 12px;
  background: transparent;
  font-size: 17px;
  font-weight: 650;
}
#focus-name span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#focus-name svg {
  flex: none;
  color: var(--muted);
}
html.focus #credit {
  margin: -8px 0 2px;
  padding: 0 10px;
  max-width: none;
  font-size: 13px;
  line-height: 1.35;
}
html.focus #credit b {
  font-weight: 500;
}
html.focus #share {
  min-height: 44px;
}

/* The panel: no handle or hint in its flow; the sheets and a game's bar stack over the bottom bar. */
html.focus .dock {
  display: flex;
  flex-direction: column;
  padding: 0;
}
html.focus .dock-handle {
  display: none;
}
html.focus .dock > .controls {
  order: 1;
}

/* The bottom bar: one row, an icon over a short label. */
html.focus .controls {
  flex-wrap: nowrap;
  gap: 2px;
  margin: 0;
  max-width: none;
  padding: 4px max(6px, env(safe-area-inset-right)) max(4px, env(safe-area-inset-bottom))
    max(6px, env(safe-area-inset-left));
  border-width: 1px 0 0;
  border-radius: 0;
  background: rgba(16, 16, 19, 0.96);
  transition: opacity 0.5s;
}
html.focus .controls > button {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 54px;
  padding: 4px 0 3px;
  border: none;
  border-radius: 12px;
  background: transparent;
  font-size: 13px;
  line-height: 1.1;
  color: var(--muted);
}
html.focus .controls > button[aria-expanded="true"] {
  color: var(--accent);
  background: rgba(255, 212, 0, 0.1);
}
html.focus .controls > button svg {
  flex: none;
}
html.focus .controls > .splats {
  display: none; /* (Detail and splats, on the stage's column, shows the same slider) */
}

/* The sheets: full width over the bar, rounded at the top, a grip to swipe them down by. */
html.focus .dock > .more,
html.focus .focus-sheet {
  position: relative;
  max-width: none;
  max-height: min(50dvh, 26rem);
  margin: 0;
  padding: 18px max(12px, env(safe-area-inset-right)) 10px max(12px, env(safe-area-inset-left));
  border-width: 1px 0 0;
  border-radius: 18px 18px 0 0;
  background: var(--sheet-bg);
  overscroll-behavior: contain;
  animation: focus-sheet-in 0.18s ease-out;
}
html.focus .dock > .more::before {
  content: "";
  position: absolute;
  top: 7px;
  left: 50%;
  width: 36px;
  height: 4px;
  margin-left: -18px;
  border-radius: 2px;
  background: var(--muted);
  opacity: 0.6;
}
@keyframes focus-sheet-in {
  from {
    transform: translateY(32px);
    opacity: 0;
  }
}
html.focus .more-head {
  touch-action: none; /* (a swipe down on it closes the sheet) */
}
html.focus .more-head h2 {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
}
html.focus .more-head [role="tab"] {
  min-height: 44px;
  padding: 0.3em 0.75em;
  font-size: 14px;
}
html.focus .more-head [role="tablist"] {
  gap: 4px;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
html.focus .more-head .icon {
  flex: none;
  width: 44px;
  min-height: 44px;
}
html.focus .pane .note {
  font-size: 13px;
}
html.focus .pane button,
html.focus .pane select,
html.focus .pane input[type="text"],
html.focus .menu select,
html.focus .toy-sheet .row {
  min-height: 44px;
}
html.focus .chip {
  min-width: 44px;
}
html.focus .toy-sheet {
  max-height: min(50dvh, 26rem);
}

/* Effects: Splashery's row inside a sheet with a head; the sheet shows while the row does. */
#fx-sheet:has(> #fx[hidden]) {
  display: none;
}
html.focus #fx {
  margin: 0;
  max-width: none;
}
html.focus .fx-toggles {
  justify-content: flex-start;
}
html.focus .fx-toggles button,
html.focus .fx-toggles select {
  min-height: 44px;
  background: rgba(255, 255, 255, 0.06);
}
html.focus .fx-settings {
  max-height: none;
  overflow: visible;
}
html.focus .fx-params {
  background: rgba(255, 255, 255, 0.03);
}
html.focus .fx-slider,
html.focus .fx-axis,
html.focus .fx-colors {
  min-height: 40px;
}

/* The shelf's sheet: the chips, search, and one row of cards. */
html.focus #shelf-sheet .finder,
html.focus #shelf-sheet .shelf-find,
html.focus #shelf-sheet .shelf,
html.focus #shelf-sheet .shelf-empty {
  max-width: none;
}
html.focus .chip {
  min-height: 44px;
  font-size: 14px;
  background: rgba(255, 255, 255, 0.06);
}
html.focus .shelf {
  padding-bottom: 2px;
}
html.focus .shelf button {
  width: 84px;
  background: rgba(255, 255, 255, 0.05);
}
html.focus .shelf img,
html.focus .shelf .thumb {
  width: 60px;
  height: 60px;
}
html.focus .shelf span {
  font-size: 13px;
}
/* The gallery: the shelf's sheet over the whole page. */
html.focus body.gallery .dock {
  padding: max(12px, env(safe-area-inset-top)) 0 0;
}
html.focus body.gallery .dock > #shelf-sheet {
  display: flex !important;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
  padding-top: 0;
  border: none;
  background: none;
  animation: none;
}
html.focus body.gallery #shelf-sheet::before,
html.focus body.gallery #shelf-sheet > .more-head {
  display: none;
}
html.focus body.gallery #shelf-sheet > * {
  flex: none;
  width: 100%;
  max-width: 72rem;
  margin-inline: auto;
}
html.focus body.gallery #shelf-sheet > .shelf {
  flex: 1 1 auto;
}
html.focus body.gallery .gallery-close {
  width: auto;
  min-height: 44px;
  margin-right: max(0px, (100% - 72rem) / 2);
}

/* Views: the lab's tools, each a link that opens this toy again with it. */
.views-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px;
}
.views-list a {
  display: block;
  min-height: 44px;
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: 12px;
  text-decoration: none;
}
.views-list a[aria-current="true"] {
  border-color: var(--accent);
  background: rgba(255, 212, 0, 0.1);
}
.views-list b {
  display: block;
  font-size: 14px;
  font-weight: 600;
}
.views-list span {
  display: block;
  font-size: 13px;
  color: var(--muted);
}

/* Save and Playground ↗ at the top of More's Share tab. */
html.focus .focus-save-row #save,
html.focus .focus-save-row #open-pg {
  min-height: 44px;
}
html.focus .focus-save-row #open-pg {
  display: inline-flex;
  align-items: center;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  text-decoration: none;
  font-size: 14px;
}
/* Save's choices: over the stage above the More sheet (Save stays in view to close them). */
html.focus #save-list {
  position: fixed;
  left: max(8px, env(safe-area-inset-left));
  right: max(8px, env(safe-area-inset-right));
  bottom: calc(var(--dock-h, 64px) + 8px); /* (over the stage, just above the More sheet) */
  min-width: 0;
  max-height: calc(100dvh - var(--dock-h, 64px) - var(--top-h, 64px) - 16px);
  overflow-y: auto;
  background: var(--sheet-bg);
  box-shadow: 0 -6px 24px #0008;
  z-index: 5;
}
html.focus #save-list {
  font-size: 15px;
  line-height: 1.3;
}
html.focus #save-list button {
  min-height: 44px;
}
html.focus #save-list .menu-row:has(#gif-kind) > button {
  flex-basis: 100%;
}
html.focus #save-list .menu-row select {
  flex: 1 1 auto;
  min-height: 44px;
  font-size: 14px;
}
html.focus .detail-row select {
  min-height: 44px;
}
html.focus .detail-popover .icon {
  width: 44px;
  min-height: 44px;
}

/* The stage's buttons: the everyday layout's column at the right edge (Record lowest, wrapping into a
   second column when a sheet takes the room), just above the bar or the open sheet, each 44 px. */
html.focus .stage-rail {
  bottom: calc(var(--dock-h, 64px) + 8px);
  right: max(10px, env(safe-area-inset-right));
  padding-top: 96px; /* (Sound and How to play, at its top) */
  max-height: calc(100dvh - var(--dock-h, 64px) - var(--top-h, 64px) - 14px);
  transition: opacity 0.5s;
}
html.focus .stage-rail button {
  width: 44px;
  min-height: 44px;
}
html.focus .stage-rail > #help-btn {
  top: 48px;
}
html.focus .stage-rail > #rail-fold {
  right: 52px;
}
/* While the shelf, Views, or More takes the room, the buttons step aside (they are for play, and a
   third column would cover the toy); Toy and Effects keep them, wrapped into a second column. */
html.focus .dock:has(> #more:not([hidden]), > #shelf-sheet:not([hidden]), > #views-sheet:not([hidden])) ~ .stage-rail {
  visibility: hidden;
}
/* A QR Splash (src/app.js, frameFree): the buttons stay in one column whatever the room, so none sits
   on the code and the code keeps its size; where they run past the room (a sheet open on a phone),
   the column scrolls from Record up, its edge faded where more buttons wait. Sound and How to play
   join the column at its top, where they stood. ?old=qr-sheet-column: wrapped into columns. */
.stage-rail.qr-column {
  flex-wrap: nowrap;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  padding-top: 0;
  padding-left: 56px; /* (the fold button and Hands-on's ↺ hang left of the column) */
}
.stage-rail.qr-column > * {
  flex-shrink: 0;
}
html.focus:not(.old-full-btn) .dock:has(> #toy:not([hidden])) ~ .stage-rail.qr-column > #full-btn {
  position: static; /* (in the column, as with the sheet closed) */
}
.stage-rail.qr-column > #help-btn,
.stage-rail.qr-column > #sound {
  position: static;
  order: 8;
  margin-bottom: -4px; /* (no gap: as they stood over the column) */
}
.stage-rail.qr-column > #sound {
  order: 9;
}
html.focus .stage-rail.qr-column {
  padding-top: 0;
}
html.focus .stage-rail.qr-column > #help-btn,
html.focus .stage-rail.qr-column > #sound {
  margin-bottom: 0;
}
.stage-rail.qr-column[data-more="up"] {
  mask-image: linear-gradient(to bottom, transparent, #000 28px);
}
.stage-rail.qr-column[data-more="down"] {
  mask-image: linear-gradient(to top, transparent, #000 28px);
}
.stage-rail.qr-column[data-more="both"] {
  mask-image: linear-gradient(to bottom, transparent, #000 28px, #000 calc(100% - 28px), transparent);
}
/* A toy's slider and tools: left of the buttons and the fold button beside Record, as the hint. */
html.focus .toy-overlays {
  right: min(calc(var(--rail-w, 116px) + 6px), 50vw);
}
/* Detail and splats: left of the buttons, just above the bar. */
html.focus .detail-popover {
  top: auto !important;
  bottom: calc(var(--bar-h, 64px) + 8px);
  right: calc(max(10px, env(safe-area-inset-right)) + 56px);
  max-height: calc(100dvh - var(--bar-h, 64px) - var(--top-h, 64px) - 16px);
}
/* The hint: over the stage just above the bar for a few seconds when a toy opens, then gone. */
html.focus #hint {
  position: fixed;
  /* Centered in the room left of the stage's buttons (Record and the fold button beside it), over
     a toy's tools and slider. */
  left: max(12px, env(safe-area-inset-left));
  right: calc(var(--rail-w, 116px) + 6px);
  bottom: max(
    calc(var(--dock-h, 64px) + var(--toy-overlays-h, 0px) + 10px),
    calc(var(--arc-pad-top, 0px) + 6px) /* (above an Arcade game's touch pad) */
  );
  width: fit-content;
  margin: 0 auto;
  padding: 5px 12px;
  border-radius: 999px;
  background: rgba(13, 13, 15, 0.72);
  color: var(--text);
  opacity: 0;
  transition: opacity 0.6s;
}
html.focus #hint.is-shown {
  opacity: 1;
}
html.focus.rail-rows #hint {
  visibility: hidden;
}
/* (Where it would lie on a QR code, src/app.js hintClearOfCode: hidden, as over the rows.) */
html.focus #hint[data-on-code] {
  visibility: hidden;
}

/* What sits at the stage's top: under the header. */
html.focus .toy-legend {
  top: calc(var(--top-h, 64px) + 6px);
  right: 66px; /* (clear of the stage's buttons) */
}
html.focus [data-view-panel],
html.focus [data-measure] {
  top: calc(var(--top-h, 64px) + 6px) !important;
}
html.focus .toast,
html.focus .rec-pill,
html.focus .paused-chip {
  top: calc(var(--top-h, 64px) + 8px);
}
html.focus .is-recording .toast {
  top: calc(var(--top-h, 64px) + 54px);
}

/* Focus mode (src/ui/focus.js): after 5 s untouched, and at once in full screen, everything over the
   toy fades: the header with the toy's credit, the bar (and a game's or a song's), the stage's
   buttons, and the toy's own slider, tools, legend, and pill. Faded, they take no touches: the page
   finds where a touch landed, and one on a faded control brings them back without pressing it.
   ?old=focus-idle: the bar and the stage's buttons only (html.old-focus-idle). */
html.focus .bar,
html.focus .dock > .game-bar,
html.focus .dock > .toy-tools,
html.focus .toy-overlays,
html.focus .toy-legend,
html.focus .tap-state,
html.focus #v3d-source-credit {
  transition: opacity 0.5s;
}
html.focus.is-idle .controls,
html.focus.is-idle .stage-rail,
html.focus.is-idle:not(.old-focus-idle)
  :is(.bar, .dock > .game-bar, .dock > .toy-tools, .toy-overlays, #toy-legend, .tap-state, #hint, #v3d-source-credit) {
  opacity: 0;
}
html.focus.is-idle :is(.controls, .stage-rail),
html.focus.is-idle :is(.controls, .stage-rail) *,
html.focus.is-idle:not(.old-focus-idle) :is(.bar, .dock, .toy-overlays, #toy-legend, .tap-state, #v3d-source-credit),
html.focus.is-idle:not(.old-focus-idle) :is(.bar, .dock, .toy-overlays, #toy-legend, .tap-state, #v3d-source-credit) * {
  pointer-events: none !important;
}
/* ?focus=touch: a faint line at the foot of the screen, where a touch brings the controls back. */
html.focus.focus-touch body::after {
  content: "";
  position: fixed;
  left: 50%;
  bottom: max(8px, env(safe-area-inset-bottom));
  z-index: 5;
  width: 40px;
  height: 4px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.4);
  transform: translateX(-50%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s;
}
html.focus.focus-touch.is-idle body::after {
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  html.focus .dock > .more,
  html.focus .focus-sheet {
    animation: none;
  }
  /* The controls go and come back without the fade. */
  html.focus:not(.old-focus-idle)
    :is(.bar, .controls, .stage-rail, .dock > .game-bar, .dock > .toy-tools, .toy-overlays, .toy-legend, .tap-state, #v3d-source-credit),
  html.focus.focus-touch body::after {
    transition: none;
  }
}

/* With the Toy sheet open on a phone, the stage's buttons wrap into rows across the band above it,
   and the last of them, full screen and Layers, ran off the screen's left edge. Full screen stays:
   at the column's top, left of Sound. ?old=fullscreen-button: as before. */
@media (max-width: 700px), (max-height: 500px) {
  html.focus:not(.old-full-btn) .dock:has(> #toy:not([hidden])) ~ .stage-rail > #full-btn {
    position: absolute;
    top: 0;
    right: 52px;
  }
}

/* What a tap does now (src/ui/tap-state.js): a small pill at the lower left of the toy's band, clear
   of the stage's buttons, saying "Playing" (a ring filling), "Paused", "Zoomed in", or "Ready". A
   press on it pauses or goes on. It shows Space's pause too, in the chip's place. In the focused
   layout it takes the hint's place while it shows. */
.tap-state {
  position: fixed;
  left: max(12px, env(safe-area-inset-left));
  bottom: calc(var(--dock-h, 64px) + 8px);
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: calc(100vw - var(--rail-w, 116px) - 24px);
  min-height: 30px;
  padding: 0 12px 0 7px;
  background: rgba(13, 13, 15, 0.72);
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
}
.tap-state:hover {
  background: rgba(13, 13, 15, 0.86);
}
.tap-state:disabled {
  cursor: default;
}
.tap-state[hidden],
.tap-state [hidden],
.tap-pill .paused-chip {
  display: none;
}
.tap-state .tap-icon {
  display: inline-flex;
}
.tap-state .tap-sub {
  font-weight: 400;
  color: var(--muted);
}
.tap-state .tap-badge {
  padding: 3px 6px;
  border-radius: 999px;
  background: var(--accent);
  color: #111;
  font-size: 11px;
}
html.focus .tap-state {
  bottom: calc(var(--dock-h, 64px) + var(--toy-overlays-h, 0px) + 10px);
}
html.focus.tap-state-on #hint {
  opacity: 0;
}

/* The status ring (src/ui/status-ring.js): a round button the pill's height beside it (in its place
   when it's hidden), a 44 px target round a 30 px disc. Blue and filling while a job runs, a turning
   arc where it has no measure, red with "!" pulsing for a problem, a green check when done; its card
   opens above it. It fades with the pill in focus mode. */
.status-ring {
  position: fixed;
  left: max(5px, calc(env(safe-area-inset-left) - 7px));
  bottom: calc(var(--dock-h, 64px) + 1px);
  z-index: 3;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--text);
  transition: opacity 0.5s;
}
.status-ring:hover {
  background: transparent;
}
.status-ring:disabled {
  cursor: default;
}
html.focus .status-ring {
  bottom: calc(var(--dock-h, 64px) + var(--toy-overlays-h, 0px) + 3px);
}
html.focus.status-ring-on #hint {
  opacity: 0;
}
html.focus.is-idle:not(.old-focus-idle) .status-ring {
  opacity: 0;
  pointer-events: none !important;
}
.status-ring svg {
  overflow: visible;
}
.status-ring .sr-disc {
  fill: rgba(13, 13, 15, 0.72);
  stroke: rgba(255, 255, 255, 0.12);
}
.status-ring:hover .sr-disc {
  fill: rgba(13, 13, 15, 0.86);
}
.status-ring :is(.sr-track, .sr-arc, .sr-turn) {
  fill: none;
  stroke-width: 2.6;
}
.status-ring .sr-track {
  stroke: rgba(255, 255, 255, 0.22);
}
.status-ring .sr-arc,
.status-ring .sr-turn {
  stroke: #5ab8ff;
  stroke-linecap: round;
}
.status-ring .sr-pct {
  fill: currentColor;
  font: 700 9.5px/1 system-ui, sans-serif;
  text-anchor: middle;
  dominant-baseline: central;
}
.status-ring .sr-dots {
  fill: currentColor;
}
.status-ring :is(.sr-bang, .sr-check) {
  fill: none;
  stroke: currentColor;
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.status-ring .sr-dot {
  fill: currentColor;
}
.status-ring :is(.sr-spin, .sr-pct, .sr-dots, .sr-bang, .sr-dot, .sr-check, .sr-arc) {
  display: none;
}
.status-ring[data-state="working"][data-measured="true"] :is(.sr-arc, .sr-pct),
.status-ring[data-state="working"][data-measured="false"] :is(.sr-spin, .sr-dots),
.status-ring[data-state="error"] :is(.sr-bang, .sr-dot),
.status-ring[data-state="done"] :is(.sr-arc, .sr-check) {
  display: inline;
}
.status-ring .sr-spin {
  transform-origin: 15px 15px;
  animation: sr-turn 1.1s linear infinite;
}
@keyframes sr-turn {
  to {
    transform: rotate(360deg);
  }
}
.status-ring[data-state="error"] .sr-disc {
  fill: #c81e2a;
  stroke: #ff8a8f;
  transform-origin: 15px 15px;
  animation: sr-pulse 1.8s ease-in-out infinite;
}
.status-ring[data-state="error"] .sr-track {
  stroke: none;
}
@keyframes sr-pulse {
  50% {
    transform: scale(1.12);
    fill: #e5333f;
  }
}
.status-ring[data-state="done"] .sr-arc {
  stroke: #4cd471;
}
@media (prefers-reduced-motion: reduce) {
  .status-ring .sr-spin,
  .status-ring[data-state="error"] .sr-disc {
    animation: none;
  }
  .status-ring .sr-spin {
    transform: rotate(-90deg);
  }
  .status-ring {
    transition: none;
  }
}
.status-card {
  position: fixed;
  z-index: 6;
  width: max-content;
  max-width: min(320px, calc(100vw - 16px));
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: rgba(22, 22, 26, 0.96);
  color: var(--text);
  font-size: 14px;
  line-height: 1.35;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.45);
}
.status-card:focus {
  outline: none; /* (focus moves into it as it opens; its buttons show theirs) */
}
.status-card .sc-job + .sc-job {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}
.status-card p {
  margin: 0;
}
.status-card .sc-label {
  font-weight: 600;
}
.status-card .sc-text,
.status-card .sc-fix {
  color: var(--muted);
}
.status-card .sc-error {
  color: #ff8a8f;
}
.status-card .sc-error::before {
  content: "! ";
  font-weight: 700;
}
.status-card .sc-play {
  color: #7ee29a;
}
.status-card .sc-play::before {
  content: "▶ ";
}
.status-card .sc-bar {
  height: 4px;
  margin: 6px 0 2px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.16);
  overflow: hidden;
}
.status-card .sc-bar::before {
  content: "";
  display: block;
  width: var(--p, 0%);
  height: 100%;
  background: #5ab8ff;
}
.status-card .sc-actions {
  display: flex;
  gap: 8px;
  margin-top: 8px;
}
.status-card .sc-media {
  display: block;
  width: min(280px, calc(100vw - 42px));
  height: auto;
  aspect-ratio: 16 / 9;
  margin: 6px 0 0;
  border-radius: 6px;
  background: #000;
}
.status-card .sc-actions button {
  min-height: 36px;
  padding: 0 14px;
}
.status-live {
  position: fixed;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
/* (While the ring is on, the stage's loading bar and its problem message are in it.) */
html.status-ring-live .stage .progress,
html.status-ring-live #message[data-ring] {
  display: none;
}

/* A capture's labeled hotspots (src/ui/hotspots.js): numbered markers over the stage, the same size
   at any zoom, that take no touches themselves (a tap reaches the stage, which finds the marker), and
   the panel a tap on one opens, under the header at the left. */
.hotspots {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.hotspot {
  position: absolute;
  left: -12px;
  top: -12px;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  min-height: 0;
  padding: 0;
  border: 1.5px solid rgba(255, 255, 255, 0.92);
  border-radius: 50%;
  background: rgba(13, 13, 15, 0.82);
  color: #fff;
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
  pointer-events: none;
  will-change: transform, opacity;
}
.hotspot.is-active {
  border-color: var(--accent);
  background: var(--accent);
  color: #111;
}
.hotspot.is-behind {
  border-color: rgba(255, 255, 255, 0.5);
  box-shadow: none;
}
.hotspot-placed {
  border-color: var(--pink);
  color: var(--pink);
}
.hotspot-panel {
  position: fixed;
  top: 64px;
  left: max(12px, env(safe-area-inset-left));
  z-index: 3;
  width: min(22rem, calc(100vw - 90px));
  padding: 8px 10px 6px 12px;
  background: rgba(18, 18, 22, 0.92); /* (the markers under it don't show through) */
  border: 1px solid var(--line);
  border-radius: 14px;
  font-size: 14px;
  line-height: 1.4;
}
html.focus .hotspot-panel {
  top: calc(var(--top-h, 64px) + 6px);
}
.hotspot-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.hotspot-head h2 {
  flex: 1;
  margin: 0;
  font-size: 15px;
  font-weight: 600;
}
.hotspot-num {
  display: grid;
  place-items: center;
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--accent);
  color: #111;
  font-size: 12px;
  font-weight: 700;
}
.hotspot-panel button {
  min-height: 34px;
  width: 34px;
  padding: 0;
}
.hotspot-text {
  margin: 4px 0 2px;
  max-height: 30vh;
  overflow-y: auto;
}
.hotspot-steps {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  color: var(--muted);
  font-size: 12.5px;
}
.hotspot-edit {
  width: min(26rem, calc(100vw - 90px));
}
.hotspot-edit .hotspot-head button {
  width: auto;
  padding: 0 12px;
}
.hotspot-edit pre {
  margin: 6px 0 2px;
  white-space: pre-wrap;
  word-break: break-all;
  font-size: 11.5px;
  user-select: text;
  -webkit-user-select: text;
}
/* Focus mode: they stay while Labels is on (src/ui/focus.js). ?old=labels-focus: they fade with the
   other overlays. */
html.focus.old-labels-focus .hotspots {
  transition: opacity 0.5s;
}
html.focus.old-labels-focus.is-idle:not(.old-focus-idle) .hotspots {
  opacity: 0;
}
@media (prefers-reduced-motion: reduce) {
  html.focus.old-labels-focus .hotspots {
    transition: none;
  }
}

/* QR code's Splash tab, laid out for a phone (src/splashery/packs/qr.js, .qr-tidy; ?old=qr-code-panel
   leaves it as it was): one label column, controls 40 pixels tall or more, chip groups that wrap
   with even gaps, a heading for each section, and the sheet no wider than a reading column on a
   desk. */
#toy:has(.qr-tidy) .pane {
  max-width: 40rem;
}
#toy:has(.qr-tidy) .row:not(.check-row) > span:first-child {
  flex: 0 0 8.5em;
  min-width: 0;
}
#toy:has(.qr-tidy) .row,
#toy:has(.qr-tidy) .pane select,
#toy:has(.qr-tidy) .pane input[type="text"],
.qr-tidy input[type="password"] {
  min-height: 44px;
}
.qr-tidy input[type="password"] {
  flex: 1;
  min-width: 0;
  padding: 0.3em 0.8em;
  font: inherit;
  font-size: 14px;
  color: inherit;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line);
  border-radius: 999px;
}
#toy:has(.qr-tidy) input[type="color"] {
  width: 56px;
  height: 40px;
  padding: 2px;
  border-radius: 10px;
}
.qr-tidy .qr-head {
  margin: 18px 0 4px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
}
.qr-tidy .qr-sub {
  margin: 0 0 6px;
}
.qr-tidy .qr-chips,
.qr-tidy .button-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 8px 0;
}
.qr-tidy .qr-chips > button,
.qr-tidy .button-row > button {
  margin: 0;
  min-height: 44px;
  padding: 0.45em 0.95em;
}
.qr-tidy .qr-swatch {
  display: inline-block;
  width: 16px;
  height: 16px;
  margin: 0 7px 0 -2px;
  border-radius: 50%;
  vertical-align: -3px;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.4);
}
.qr-tidy .qr-top {
  align-items: flex-start;
}
.qr-tidy .qr-top > span {
  padding-top: 10px;
}
.qr-tidy textarea {
  flex: 1;
  min-width: 0;
  min-height: 88px;
  padding: 0.5em 0.8em;
  font: inherit;
  font-size: 14px;
  color: inherit;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line);
  border-radius: 14px;
}
.qr-tidy .warning,
.qr-tidy .note {
  margin: 6px 0;
}
