/*
 * The imageyFX-360 console.
 *
 * Built as a lighting desk rather than a settings page, because that is what
 * it is for: changing what a room full of people are looking at, quickly, in
 * the dark, without reading.
 *
 * Three decisions run through everything here.
 *
 * The desk is graphite, not black. Black is what is on stage - the canvas -
 * and a black console sitting under it would have no edge. Real desks are grey
 * for the same reason.
 *
 * The segment is the unit. This product is a circle divided into 360 parts and
 * its mark is a ring of 32; so meters, fader tracks and the pad grid are drawn
 * as discrete segments too. It also happens to be how hardware reads at a
 * glance in low light, which is the actual requirement.
 *
 * Colour is positional, not accented. The six macros take six stops of the
 * brand sweep in order, so the row of faders reproduces the ring unrolled. A
 * single accent colour would have been easier and would have told the eye
 * nothing about which fader it had grabbed.
 */

:root {
  /* --- the desk ---------------------------------------------------------- */
  --desk: #16181d;          /* console body */
  --desk-2: #1b1e25;        /* raised panels within it */
  --inset: #0b0c0f;         /* recessed wells: tracks, pad, meters */
  --rail: #22252e;          /* buttons at rest */
  --rail-hi: #2b2f3a;       /* buttons hovered */
  --edge: #313641;          /* hairlines */
  --edge-soft: #24272f;

  --ink: #e8eaf0;
  --dim: #8a90a0;           /* screen-printed labels */
  --faint: #5c6273;

  /* --- the sweep, in order ----------------------------------------------- */
  /* Six stops, magenta round to cyan. Assigned by position so a macro's
   * colour is a fact about which macro it is. */
  --s1: #ff2d95;
  --s2: #c13ae0;
  --s3: #8b5cf6;
  --s4: #6366f1;
  --s5: #38bdf8;
  --s6: #22d3ee;

  /* --- neon, one per layer ------------------------------------------------ */
  /*
   * The booth shows every layer at once, so each needs to be findable by
   * colour alone. These are picked for separation around the wheel rather than
   * for harmony with the brand sweep - eight sections that all look magenta
   * would be one long undifferentiated wall.
   */
  --n-circle: #ff2d95;    /* magenta - the brand's own */
  --n-lasers: #ff7a18;    /* orange */
  --n-grid:   #39ff14;    /* green */
  --n-spiro:  #00e5ff;    /* cyan */
  --n-shapes: #ffe600;    /* yellow */
  --n-logo:   #b026ff;    /* violet */
  --n-milk:   #ff1e56;    /* red */
  --n-text:   #00ffa3;    /* mint */

  /* --- states, each reserved to one meaning ------------------------------ */
  --live: #ff3b30;          /* recording, and nothing else */
  --armed: #ffb020;         /* armed, counting down, needs attention */

  /*
   * Window controls.
   *
   * Every other lit thing on this desk is neon, and neon means a place you can
   * go or a control that is doing something. Hide, undock and full screen are
   * neither - they act on the window, not the performance - so they are the
   * one thing here that reads as brushed metal rather than light.
   */
  --tool: #7f8ea6;
  --tool-lit: #cdd8e8;
  --ok: #34d399;

  /* --- metrics ------------------------------------------------------------ */
  --touch: 44px;            /* the floor for anything you press */
  --r: 7px;
  --r-lg: 12px;

  --label: 600 11px/1 var(--ui);
  --ui: -apple-system, BlinkMacSystemFont, 'Segoe UI', Inter, 'Helvetica Neue', Arial, sans-serif;
  /* Numbers are mono so a readout does not shuffle sideways as it counts. */
  --mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
}

/* ---------------------------------------------------------------- shell --- */

.console {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  background: var(--desk);
  border-top: 1px solid var(--edge);
  color: var(--ink);
  font: 13px/1.45 var(--ui);
  /* The canvas is the point. The desk takes the bottom of the screen and no
   * more, and only grows when a section genuinely needs the room. */
  max-height: 62vh;
  box-shadow: 0 -18px 44px rgba(0, 0, 0, .5);
  padding-bottom: env(safe-area-inset-bottom, 0);
}

.console[data-section="live"] { max-height: 46vh; }

/*
 * The idle desk used to shrink to 38vh, which was less than Live's own content
 * needs - so the reward for having nothing playing was a scrollbar. The splash
 * sizes itself against whatever room is left (see the container queries
 * below), so it does not need the desk to shrink on a large screen; only a
 * phone does, and that is handled at the breakpoint.
 */

.console[hidden] { display: none; }

.console__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* --- screen-printed labels ------------------------------------------------ */

.label {
  font: var(--label);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--dim);
}

.readout {
  font: 500 12px/1 var(--mono);
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------ transport --- */

:is(.console, .poolsheet, .splash) .transport {
  display: flex;
  align-items: center;
  gap: 11px;
  /* Tight vertical padding: the controls inside already carry the 44px touch
   * floor, so the padding is the only slack there is, and every pixel here is
   * one the decks below do not have. */
  padding: 6px 14px;
  border-bottom: 1px solid var(--edge-soft);
  background: var(--desk-2);
  flex-wrap: wrap;
}

/* --- the mode switch: the one authoritative control ----------------------- */

.mode-switch {
  display: inline-flex;
  background: var(--inset);
  border: 1px solid var(--edge);
  border-radius: var(--r);
  padding: 3px;
  gap: 3px;
}

.mode-switch button {
  /* The full touch floor, not the 36px this first had. This is the most
   * important control in the product and it is pressed in the dark. */
  min-height: var(--touch);
  padding: 0 18px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--dim);
  font: 600 12px/1 var(--ui);
  letter-spacing: .08em;
  text-transform: uppercase;
  cursor: pointer;
}

.mode-switch button[aria-pressed="true"] {
  background: linear-gradient(135deg, var(--s1), var(--s4));
  color: #fff;
}

.mode-switch button:not([aria-pressed="true"]):hover { color: var(--ink); }

/* --- transport buttons ---------------------------------------------------- */

.tbtn {
  min-width: var(--touch);
  min-height: var(--touch);
  display: inline-grid;
  place-items: center;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--rail);
  color: var(--ink);
  cursor: pointer;
  font-size: 15px;
}

.tbtn:hover { background: var(--rail-hi); }
.tbtn:active { transform: translateY(1px); }
.tbtn--play { background: var(--rail-hi); }

:is(.console, .poolsheet, .splash) .now {
  min-width: 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 1px;
  line-height: 1.25;
}

.now__title {
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.now__meta { display: flex; gap: 10px; align-items: center; }

/* ---------------------------------------------------------------- meter --- */

/*
 * A segmented bar, like the one on the desk this is pretending to be. The
 * segments are the product's own unit, and a stepped bar is readable from
 * across a room in a way a smooth one is not.
 */
:is(.console, .poolsheet, .splash) .meter {
  display: flex;
  gap: 2px;
  align-items: flex-end;
  height: 18px;
  padding: 3px;
  background: var(--inset);
  border: 1px solid var(--edge);
  border-radius: 4px;
}

:is(.console, .poolsheet, .splash) .meter i {
  width: 3px;
  height: 100%;
  background: var(--edge);
  border-radius: 1px;
  transition: background 60ms linear;
}

.meter i.on { background: var(--s5); }
.meter i.on.hot { background: var(--s1); }

@media (prefers-reduced-motion: reduce) {
  .meter i { transition: none; }
}

/* ----------------------------------------------------------- live decks --- */

.decks {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, .85fr) minmax(0, 1fr);
  gap: 1px;
  background: var(--edge-soft);
  flex: 1;
  min-height: 0;
}

.deck {
  background: var(--desk);
  padding: 8px 14px 8px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.deck__head {
  display: flex;
  align-items: center;
  min-height: 28px;
  justify-content: space-between;
  gap: 10px;
}

/* --- macro faders ---------------------------------------------------------- */

.macros {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 8px;
  flex: 1;
  min-height: 0;
}

.macro {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  min-width: 0;
}

.macro__name {
  font: var(--label);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/*
 * The fader.
 *
 * A range input rotated into a column, with the visible track drawn behind it
 * as discrete segments. The input keeps its own keyboard behaviour and its own
 * focus ring, which is the entire reason for doing it this way rather than
 * building a div and reimplementing arrow keys badly.
 */
.fader {
  position: relative;
  flex: 1;
  min-height: 70px;
  width: 100%;
  display: grid;
  place-items: center;
}

.fader__track {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column-reverse;
  gap: 2px;
  padding: 4px;
  background: var(--inset);
  border: 1px solid var(--edge);
  border-radius: var(--r);
  pointer-events: none;
}

.fader__track i {
  flex: 1;
  border-radius: 2px;
  background: var(--edge);
}

.fader__track i.on { background: var(--tint, var(--s3)); }

/* Waiting for a physical control to travel to the value. The desk shows this
 * rather than hiding it: a fader that is being ignored has to look ignored. */
.fader.pickup .fader__track i.on { background: var(--edge); box-shadow: inset 0 0 0 1px var(--tint); }

/*
 * A native vertical range.
 *
 * The first attempt rotated a horizontal input, which put the thumb outside
 * its own track: a rotated element's travel is still its width, and the width
 * of a fader column is not its height. `writing-mode: vertical-lr` makes the
 * browser lay the control out vertically for real, so the travel matches the
 * track and every native keyboard behaviour survives. `direction: rtl` is
 * what puts the maximum at the top, where up means more.
 */
.fader input {
  -webkit-appearance: none;
  appearance: none;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  writing-mode: vertical-lr;
  direction: rtl;
  background: transparent;
  margin: 0;
  cursor: ns-resize;
}

.fader input::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 26px;
  height: 14px;
  border-radius: 3px;
  background: linear-gradient(180deg, #f3f5f9, #b9bfcc);
  border: 1px solid #05070a;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .6);
  cursor: ns-resize;
}

.fader input::-moz-range-thumb {
  width: 26px;
  height: 14px;
  border-radius: 3px;
  background: linear-gradient(180deg, #f3f5f9, #b9bfcc);
  border: 1px solid #05070a;
}

.macro__value { font: 500 11px/1 var(--mono); color: var(--faint); }

/* --- the XY pad ------------------------------------------------------------ */

.pad {
  position: relative;
  flex: 1;
  min-height: 120px;
  border-radius: var(--r-lg);
  background: var(--inset);
  border: 1px solid var(--edge);
  touch-action: none;
  cursor: crosshair;
  overflow: hidden;
}

/* The same segment grid again, as the pad's ruling. */
.pad::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    repeating-linear-gradient(to right, var(--edge-soft) 0 1px, transparent 1px 12.5%),
    repeating-linear-gradient(to bottom, var(--edge-soft) 0 1px, transparent 1px 12.5%);
  opacity: .7;
}

.pad__dot {
  position: absolute;
  width: 22px;
  height: 22px;
  margin: -11px 0 0 -11px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 40%, #fff, var(--s2));
  box-shadow: 0 0 16px var(--s2);
  pointer-events: none;
}

.pad__axes {
  position: absolute;
  left: 8px; bottom: 6px; right: 8px;
  display: flex;
  justify-content: space-between;
  pointer-events: none;
}

/* --- punch and action buttons ---------------------------------------------- */

.punches {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px;
}

.punch {
  min-height: var(--touch);
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--rail);
  color: var(--dim);
  font: var(--label);
  letter-spacing: .08em;
  text-transform: uppercase;
  cursor: pointer;
  padding: 0 6px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.punch:hover { background: var(--rail-hi); color: var(--ink); }

/* Held. Bright, obviously temporary, and impossible to mistake for selected. */
.punch[data-held="true"] {
  background: var(--tint, var(--s3));
  border-color: var(--tint, var(--s3));
  color: #fff;
  box-shadow: 0 0 18px -2px var(--tint, var(--s3));
}

/*
 * Locked. Held, and staying held.
 *
 * Deliberately the held colour plus a steady outline rather than a colour of
 * its own: a locked punch is doing exactly what a held one does, and inventing
 * a third colour for it would say it was a third kind of thing. The ring is
 * what carries "this will not let go on its own", and it does not pulse -
 * something that is going to sit there for a whole section must not be the
 * brightest moving object on the desk.
 */
.punch[data-locked="true"] {
  outline: 2px solid var(--ink);
  outline-offset: -4px;
}

.punch[aria-pressed="true"] { color: var(--ink); border-color: var(--faint); }

.punch:disabled { opacity: .35; cursor: not-allowed; }

/* A button sitting beside a deck label takes the label's height rather than
 * the 44px floor. It is a secondary action, not something anybody reaches for
 * in the dark mid-set. */
.deck__head .punch {
  min-height: 28px;
  padding: 0 12px;
  font-size: 10px;
}

/* The Looks row is a shortlist, not something reached for in the dark, so it
 * gives up space before the punch buttons above it do. */
.deck--layers .punches:last-of-type .punch {
  min-height: 36px;
}

/* The Intensity slider added a row to the macros deck, which is what put the
 * last few pixels over. The faders give them back - they are still well above
 * the height at which a fader stops being usable. */

/* Permission buttons carry a whole word, so they wrap to as many columns as
 * fit rather than truncating "geometry" to "geomet...". */
.layer-card .punches {
  grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
}

/* --- record ----------------------------------------------------------------- */

:is(.console, .poolsheet, .splash) .rec {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: var(--touch);
  padding: 0 16px;
  border: 1px solid var(--live);
  border-radius: var(--r);
  background: transparent;
  color: var(--live);
  font: 600 12px/1 var(--ui);
  letter-spacing: .08em;
  text-transform: uppercase;
  cursor: pointer;
}

.rec:hover { background: rgba(255, 59, 48, .12); }

:is(.console, .poolsheet, .splash) .rec[data-state="recording"] {
  background: var(--live);
  color: #fff;
}

.rec[data-state="armed"] { border-color: var(--armed); color: var(--armed); }

.rec__dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: currentColor;
}

:is(.console, .poolsheet, .splash) .rec[data-state="recording"] .rec__dot {
  background: #fff;
  animation: pulse 1.6s ease-in-out infinite;
}

@keyframes pulse { 50% { opacity: .25; } }

@media (prefers-reduced-motion: reduce) {
  .rec[data-state="recording"] .rec__dot { animation: none; }
}

/* ------------------------------------------------------------------- nav --- */

:is(.console, .poolsheet, .splash) .nav {
  display: flex;
  border-top: 1px solid var(--edge);
  background: var(--desk-2);
}

:is(.console, .poolsheet, .splash) .nav button {
  flex: 1;
  min-height: var(--touch);
  padding: 10px 4px;
  border: 0;
  border-top: 2px solid transparent;
  background: transparent;
  color: var(--dim);
  font: var(--label);
  letter-spacing: .1em;
  text-transform: uppercase;
  cursor: pointer;
}

.nav button:hover { color: var(--ink); background: rgba(255, 255, 255, .03); }

:is(.console, .poolsheet, .splash) .nav button[aria-current="page"] {
  color: var(--ink);
  border-top-color: var(--s1);
  background: var(--desk);
}

/* ---------------------------------------------------------------- states --- */

/*
 * Idle disables performing, never navigating - and never the transport.
 *
 * Two gates, because they answer different questions.
 *
 *   needs-music   controls that manipulate a running performance: macros, the
 *                 pad, the punches. Meaningless with nothing playing.
 *   needs-source  controls that need somewhere for sound to come from. The
 *                 transport lives here, not above: play is how the music
 *                 starts, and disabling it because no music is playing is a
 *                 deadlock - a track picked and paused could never be started.
 *
 * Applied to containers so a whole deck goes quiet at once, and by
 * pointer-events rather than the disabled attribute on every control, so the
 * markup is not walked twice on every transition.
 */
.console[data-performing="false"] .needs-music,
.console[data-source="false"] .needs-source {
  opacity: .38;
  pointer-events: none;
}

.console[data-performing="false"] .needs-music *,
.console[data-source="false"] .needs-source * { cursor: default; }

/* ---------------------------------------------------------------- splash --- */

/*
 * The splash covers the canvas, not the desk.
 *
 * --console-h is written by the shell whenever the desk resizes. Centring in
 * the whole viewport instead would push the title and the three actions behind
 * the console, which is exactly the state somebody with no music playing needs
 * to be able to read.
 */
.splash {
  position: fixed;
  inset: 0 0 var(--console-h, 0px) 0;
  /* Queried against its own box below, because how much room the splash has
   * depends on the desk's height, not the window's - a tall section leaves it
   * a short strip on a perfectly tall screen. */
  container-type: size;
  container-name: splash;
  z-index: 30;
  display: grid;
  place-items: center;
  background: #000;
  opacity: 0;
  pointer-events: none;
  /* No transition here: the fade is driven frame by frame from the playback
   * clock so it stays locked to the 2500ms settle rather than running on a
   * second, slightly different timeline of the browser's own. */
}

.splash[data-shown="true"] { pointer-events: auto; }

.splash__inner {
  text-align: center;
  padding: 24px;
  max-width: 560px;
}

.splash__mark { width: min(38vw, 170px); height: auto; }


.splash__title {
  margin: 20px 0 6px;
  font: 300 clamp(26px, 5vw, 40px)/1.1 var(--ui);
  color: #fff;
}

.splash__title b { font-weight: 700; }
.splash__title span { color: var(--dim); font-weight: 400; }

.splash__hint {
  margin: 0 0 24px;
  color: var(--dim);
  font-size: 14px;
}

.splash__actions {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
}

.splash__actions button {
  min-height: var(--touch);
  padding: 0 20px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--rail);
  color: var(--ink);
  font: 600 13px/1 var(--ui);
  cursor: pointer;
}

.splash__actions button:first-child {
  background: linear-gradient(135deg, var(--s1), var(--s4));
  border-color: transparent;
  color: #fff;
}

.splash__actions button:hover { background: var(--rail-hi); }
.splash__actions button:first-child:hover { filter: brightness(1.1); }

.splash__notices {
  display: grid;
  gap: 7px;
  max-width: 520px;
  margin: 18px auto 0;
}

.splash__notices p { margin: 0; }

.splash__warning {
  padding: 8px 10px;
  border: 1px solid rgba(255, 122, 24, .34);
  border-radius: var(--r);
  background: rgba(255, 122, 24, .08);
  color: #c8cad1;
  font: 500 11px/1.45 var(--ui);
}

.splash__warning strong { color: #ffd2ad; font-weight: 700; }

.splash__requirements {
  color: var(--faint);
  font: 500 10px/1.4 var(--ui);
}


/* ------------------------------------------------------------------ focus --- */

/*
 * One visible ring everywhere. On a dark desk a default outline disappears, so
 * this is a light ring plus a dark halo that reads on any of the surfaces.
 */
:where(.console, .splash) :is(button, input, select, textarea, a, [tabindex]):focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
  border-radius: var(--r);
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ------------------------------------------------------------- responsive --- */

/* Tablet: the third deck drops under the other two rather than being squeezed
 * to a width where nothing in it is pressable. */
@media (max-width: 1000px) {
  .decks { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .deck--layers { grid-column: 1 / -1; }
  .console[data-section="live"] { max-height: 56vh; }
}

/* Phone: a bottom sheet. Nav moves to the bottom edge where a thumb is, the
 * decks stack, and the faders shorten - six full-height faders on a 390px
 * screen would each be 50px wide and unusable. */
@media (max-width: 700px) {
  .console { max-height: 72vh; }
  .console[data-section="live"] { max-height: 66vh; }
  .console[data-performing="false"][data-section="live"] { max-height: 46vh; }

  .decks { grid-template-columns: minmax(0, 1fr); }
  .deck--layers { grid-column: auto; }

  .fader { min-height: 76px; }
  .macros { gap: 6px; }
  .macro__name { font-size: 9px; letter-spacing: .06em; }

  .punches { grid-template-columns: repeat(4, minmax(0, 1fr)); }

  .transport { gap: 10px; padding: 10px 12px; }
  .now { order: 10; flex-basis: 100%; }

  .nav button { font-size: 9px; letter-spacing: .06em; padding: 8px 2px; }

  /*
   * Nine buttons will not share one row on a phone, and dropping the words
   * again would undo the point of having them. The tools take a row of their
   * own and split it three ways.
   */
  :is(.console) .nav { flex-wrap: wrap; }
  :is(.console) .nav__tools {
    flex: 1 0 100%;
    margin-left: 0;
    border-left: 0;
    border-top: 1px solid var(--edge);
  }
  :is(.console) .nav__tools .nav__icon { flex: 1; padding: 8px 4px; }

  .splash__mark { width: min(34vw, 120px); }
  .splash__title { margin-top: 14px; }
  .splash__hint { font-size: 13px; margin-bottom: 18px; }
  /* Two up rather than a single column: with four ways to start, a stacked
   * list is taller than the space the splash has on a phone. */
  .splash__actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
  }
  .splash__actions button { padding: 0 10px; font-size: 12px; }
  .splash__notices { margin-top: 14px; gap: 6px; }
  .splash__warning { padding: 7px 8px; font-size: 10px; }
  .splash__requirements { font-size: 9px; }
}

/* Short canvases need all first-screen essentials without scrolling. Kept
 * after the viewport rules so these container-sized decisions win on phones. */
@container splash (max-height: 620px) {
  .splash__mark { width: min(22cqw, 96px); }
  .splash__title { margin-top: 10px; font-size: clamp(20px, 4cqw, 28px); }
  .splash__hint { margin-bottom: 14px; }
}

@container splash (max-height: 520px) {
  .splash__inner { padding-block: 12px; }
  .splash__mark { width: min(20cqw, 72px); }
  .splash__title { margin: 8px 0 4px; font-size: clamp(19px, 4cqw, 25px); }
  .splash__hint { margin-bottom: 10px; font-size: 12px; }
  .splash__actions { gap: 6px; }
  .splash__actions button { min-height: 38px; padding: 0 10px; font-size: 11px; }
  .splash__notices { margin-top: 10px; gap: 4px; }
  .splash__warning { padding: 5px 7px; font-size: 9px; line-height: 1.3; }
  .splash__requirements { font-size: 8px; line-height: 1.25; }
}

@container splash (max-height: 420px) {
  .splash__inner { padding-block: 10px; }
  .splash__mark { display: none; }
  .splash__title { margin: 0 0 5px; font-size: clamp(18px, 3.4cqw, 24px); }
  .splash__hint { margin-bottom: 8px; font-size: 11px; }
  .splash__actions button { min-height: 32px; }
  .splash__notices { margin-top: 8px; gap: 3px; }
  .splash__warning { padding: 4px 7px; font-size: 9px; line-height: 1.25; }
  .splash__requirements { font-size: 8px; line-height: 1.2; }
}

/* A grab handle, so the poolsheet reads as one on a phone. */
.console__grip { display: none; }

@media (max-width: 700px) {
  .console__grip {
    display: block;
    width: 36px; height: 4px;
    margin: 7px auto 3px;
    border-radius: 2px;
    background: var(--edge);
  }
}

/* ------------------------------------------------------- the old panel --- */

/*
 * The way back to the full controls.
 *
 * The console does not yet cover everything the old panel does, so the gear
 * stays reachable and has to sit above the desk rather than behind it.
 */
/*
 * The tab sits at the left edge, vertically centred, which is where the
 * console's own content now is - it was landing on top of the first card in
 * every section. Moved to the right edge and lifted above the desk, where
 * nothing else lives.
 */
/*
 * The old panel and its gear are retired.
 *
 * Both are hidden by boot.js rather than deleted, so this phase stays
 * reversible, and this rule makes certain neither can be reached by a stray
 * z-index or a focus ring.
 */
#show, #panel { display: none !important; }


/* ------------------------------------------------------- the pool poolsheet --- */

/*
 * A sheet, not another panel in the console.
 *
 * Choosing what Auto may reach for is a sit-down job, not something done
 * mid-set, so it gets room and a scrim rather than being squeezed into the
 * desk beside the controls it is about.
 */
/* ------------------------------------------------------------- sign in --- */

/*
 * The one offer of an account, on load.
 *
 * A corner rather than the middle of the screen, and no backdrop. This
 * interrupts somebody who did not ask for it, on a page where everything
 * already works without an account - a dimmed overlay across a running
 * visualiser would say "you cannot use this yet", which is the opposite of
 * true. It sits beside the picture and waits.
 *
 * Top rather than bottom, over the picture rather than over the desk. The
 * bottom corner put it across the punch row, and covering controls somebody is
 * using is worse than covering the art they are watching - the art is still
 * there when the card goes, and a button under a card is a button that does
 * not work.
 */
.signin {
  position: fixed;
  right: 16px;
  top: 16px;
  z-index: 68;
  max-width: min(420px, calc(100vw - 32px));
  /* No inset and no backdrop: the rig behind stays clickable. */
}

.signin__panel {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px 18px;
  border: 1px solid var(--edge);
  border-radius: var(--r-lg);
  background: var(--desk-2);
  box-shadow: 0 18px 46px rgba(0, 0, 0, .55);
}

.signin__title {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ink);
}

.signin__body {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--dim);
}

.signin__row {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* The offer reads as the offer; dismissing is quiet but never hidden. */
.signin__go {
  width: 100%;
  text-align: center;
  white-space: normal;
  line-height: 1.4;
  padding: 9px 12px;
  border-color: var(--s6);
  color: var(--ink);
}

.signin__no {
  width: 100%;
  padding: 7px 12px;
  color: var(--dim);
  opacity: 0.8;
}

.signin__no:hover { opacity: 1; color: var(--ink); }

.signin__say:empty { display: none; }

.signin__say {
  margin: 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--dim);
}

@media (max-width: 620px) {
  .signin { right: 12px; left: 12px; top: 12px; max-width: none; }
}

.poolsheet {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: grid;
  place-items: center;
  padding: 3vh 3vw;
  background: rgba(4, 5, 8, .72);
}

.poolsheet[hidden] { display: none; }

.poolsheet__panel {
  width: min(980px, 100%);
  max-height: 94vh;
  display: flex;
  flex-direction: column;
  background: var(--desk);
  border: 1px solid var(--edge);
  border-radius: var(--r-lg);
  box-shadow: 0 30px 80px rgba(0, 0, 0, .6);
  overflow: hidden;
}

.poolsheet__head,
.poolsheet__tools {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--edge-soft);
  background: var(--desk-2);
  flex-wrap: wrap;
}

.poolsheet__head strong { flex: 1; min-width: 160px; }

.poolsheet__bulk { display: flex; gap: 6px; }
.poolsheet__bulk .punch { max-width: 110px; }

.poolsheet__search {
  flex: 1;
  min-width: 160px;
  min-height: var(--touch);
  padding: 0 12px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--inset);
  color: var(--ink);
  font: 13px var(--ui);
}

.poolsheet__search::placeholder { color: var(--faint); }

.poolsheet__list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 8px 16px 16px;
}

.poolsheet__group { margin-top: 14px; }

/* Keep each category title with its own cards. A sticky title detached from
 * the group while scrolling and covered the first row, making it look like a
 * broken floating title bar. */
.poolsheet__group-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
}

.poolsheet__group-head .punch { max-width: 62px; min-height: 30px; font-size: 10px; }

.poolsheet__rows {
  display: grid;
  gap: 6px;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}

.poolsheet__row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--touch);
  padding: 0 10px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--desk-2);
}

/* In the pool reads as lit; out of it recedes rather than disappearing, so the
 * list does not reflow as things are picked. */
.poolsheet__row[data-in="true"] { border-color: var(--s4); }
.poolsheet__row[data-in="false"] { opacity: .55; }

.poolsheet__name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.poolsheet__star {
  min-width: 32px;
  min-height: 32px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--faint);
  font-size: 16px;
  cursor: pointer;
  line-height: 1;
}

.poolsheet__star[aria-pressed="true"] { color: var(--armed); }
.poolsheet__star:hover { background: var(--rail); color: var(--ink); }

.poolsheet__problem {
  margin: 0;
  padding: 12px 16px;
  color: var(--armed);
  font-size: 13px;
  border-top: 1px solid var(--edge-soft);
}

@media (max-width: 700px) {
  .poolsheet { padding: 0; }
  .poolsheet__panel { max-height: 100vh; height: 100%; border-radius: 0; border: 0; }
  .poolsheet__rows { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------------ the fade --- */

/*
 * The settle, on the screen.
 *
 * --visual-gain is written every frame from the playback clock. Applied to the
 * canvases rather than to a wrapper because they are siblings in the body, and
 * kept off any transition so it stays locked to the 2500ms settle instead of
 * easing on a timeline of its own.
 *
 * Direct children of the body only. As a bare `canvas` rule this reached every
 * canvas on the page, so a preview drawn inside the interface faded out with
 * the performance - the shape previews in the pool sheet drew correctly and
 * rendered as empty cards, because the app was idle and the gain was zero. The
 * performance canvases are body children by construction; nothing in the
 * console is.
 */
body > canvas {
  /*
   * Three gains, multiplied.
   *
   * --visual-gain is the settle and belongs to the whole stack; --layer-gain
   * is one layer's own, written by the engine and ramped when a punch is let
   * go; --master-gain is a hand on the whole picture. Multiplying keeps them
   * independent: a punch fading out during the settle does not cancel the
   * settle, and none of the three has to know about the others.
   *
   * The master exists because the layers sum additively. Nine of them at once
   * clips to white, and the only way to pull that back used to be riding every
   * layer's own opacity - which fights the engine, because those are exactly
   * the controls the macros drive.
   */
  opacity: calc(var(--visual-gain, 1) * var(--layer-gain, 1) * var(--master-gain, 1));
}

/* ------------------------------------------------------------- create --- */

:is(.console, .poolsheet, .splash) .tools {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

:is(.console, .poolsheet, .splash) .tool {
  border: 1px solid var(--edge);
  border-radius: var(--r-lg);
  background: var(--desk-2);
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.tool__row { display: flex; gap: 8px; }
.tool__row .poolsheet__search { flex: 1; min-width: 0; }

.assets { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }

:is(.console, .poolsheet, .splash) .asset {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--desk-2);
  padding: 8px 12px;
}

.asset__name {
  flex: 1;
  min-width: 140px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.asset__actions { display: flex; gap: 6px; flex-wrap: wrap; }
/* Full touch floor. These were dropped to 34px to make the asset rows denser,
 * which quietly took Learn and Clear in Settings under it too - they are
 * ordinary controls people press, not labels. */
.asset__actions .punch { max-width: 130px; }

@media (max-width: 700px) {
  .asset__actions { width: 100%; }
  .asset__actions .punch { flex: 1; max-width: none; }
}

/* --------------------------------------------------- getting out of the way --- */

/*
 * Collapsed, the desk slides off the bottom rather than being removed.
 *
 * Kept in the DOM so nothing has to be rebuilt to bring it back, and so a
 * section that was open is still open when it returns.
 */
.console {
  transition: transform 180ms ease;
}

.console[data-collapsed="true"] {
  transform: translateY(110%);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .console { transition: none; }
}

/*
 * The window controls sit apart from the tabs.
 *
 * A hairline and a slightly sunken ground say these are not more places to go.
 * They were unlabelled glyphs next to seven worded tabs, which read as
 * decoration rather than as buttons - so they get words, and the colour keeps
 * them separable at a glance without a second look at the wording.
 */
:is(.console) .nav__tools {
  display: flex;
  flex: 0 0 max-content;
  margin-left: auto;
  border-left: 1px solid var(--edge);
  background: rgba(0, 0, 0, .22);
}

:is(.console) .nav__tools .nav__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex: 0 0 auto;
  min-width: var(--touch);
  min-height: var(--touch);
  padding: 10px 13px;
  border: 0;
  border-top: 2px solid transparent;
  background: transparent;
  color: var(--tool);
  font: var(--label);
  letter-spacing: .1em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
}

:is(.console) .nav__tools .nav__glyph { font-size: 14px; line-height: 1; }

:is(.console) .nav__tools .nav__icon:hover {
  color: var(--tool-lit);
  background: rgba(255, 255, 255, .06);
  border-top-color: var(--tool);
}

/*
 * The way back.
 *
 * Normally it lives inside the scrubber (see Shell.rehome), where it shares
 * that bar's fade and its generous hit area rather than fighting them - a
 * separate button down there was simply covered by the scrubber's invisible
 * full-width strip and took no clicks at all.
 *
 * These rules are the standalone case: no track loaded, so no scrubber, and it
 * has to fade and be reachable on its own.
 */
.console__restore {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: rgba(18, 20, 26, .8);
  color: var(--ink);
  font: 600 11px/1 var(--ui);
  letter-spacing: .1em;
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
}

.console__restore[hidden] { display: none; }

.console__chevron { font-size: 14px; line-height: 1; }

/* --- standing on its own -------------------------------------------------- */

body > .console__restore {
  position: fixed;
  right: 16px;
  bottom: max(14px, env(safe-area-inset-bottom));
  z-index: 44;
  min-height: var(--touch);
  padding: 0 16px;
  backdrop-filter: blur(14px);
  opacity: 0;
  transition: opacity 2.4s ease;
}

body > .console__restore:hover,
body > .console__restore:focus-visible {
  opacity: 1;
  transition-duration: .15s;
}

/* The same generous reach the scrubber uses: it sits at the very edge, which
 * is where a pointer is hardest to place. */
body > .console__restore::before {
  content: '';
  position: absolute;
  inset: -24px;
}

/* --- riding in the scrubber ------------------------------------------------ */

/*
 * Inside the bar it needs no fade of its own - the bar fades - and no z-index
 * argument, because it is a child of the thing that was covering it.
 */
.scrob .console__restore {
  position: static;
  flex: none;
  min-height: 34px;
  padding: 0 12px;
  opacity: 1;
  margin-left: 2px;
}

/*
 * Floating, it only comes forward when the desk is folded away.
 *
 * It is bottom-centred and the desk is bottom-anchored, so raising it
 * unconditionally put it over the navigation - and its deliberately generous
 * invisible hit area is a full-width strip, so it swallowed every click aimed
 * at the bottom row of the console.
 *
 * With the desk up it is not underneath any more, it is docked inside it. See
 * Shell.rehome: the same element, a different parent.
 */
.scrob[data-docked="false"] { z-index: 42; }

/* ------------------------------------------------ the docked transport --- */

/*
 * A strip across the foot of the desk, above the navigation.
 *
 * Floating, this is a card over the picture and everything about it is built
 * for that: fixed to the viewport, rounded, blurred, and fading out when it is
 * not wanted. Docked, it is part of the desk - so all of that comes off, and
 * what is left is the controls.
 */
.console__dock:empty { display: none; }

.console__dock .scrob {
  position: static;
  transform: none;
  width: auto;
  z-index: auto;
  border: 0;
  border-top: 1px solid var(--edge);
  border-radius: 0;
  background: var(--desk-2);
  backdrop-filter: none;
  box-shadow: none;
  /* Part of the furniture: no fading, and no waiting to be woken. */
  opacity: 1;
  pointer-events: auto;
  transition: none;
  padding: 8px 14px;
}

/* The invisible reach is for a bar at the edge of the screen. In here it would
 * only be a strip over the section above and the navigation below. */
.console__dock .scrob::before { display: none; }

/* It has the width of the desk now, so the rail can have the room. */
.console__dock .scrob-rail { flex: 1; }

/*
 * In the desk's colours rather than the old app's.
 *
 * The bar was built as an overlay for a blue-accented page and carries that
 * accent with it. Floating over the picture that is fine - it is chrome on
 * black. Sitting inside the desk it was the one blue thing among the neon, and
 * read as borrowed from somewhere else.
 */
.console__dock .scrob-play {
  border-color: var(--edge);
  background: var(--rail);
  color: var(--ink);
}

.console__dock .scrob-play:hover {
  border-color: var(--s1);
  color: var(--s1);
}

.console__dock .scrob-play.on,
.console__dock .scrob-play.on:hover {
  background: var(--s1);
  border-color: var(--s1);
  color: #12060c;
}

.console__dock .scrob-skip {
  border-color: var(--edge);
  color: var(--dim);
}

.console__dock .scrob-skip:hover { border-color: var(--s1); color: var(--ink); }

.console__dock .scrob-fill { background: var(--s1); }

.console__dock .scrob-knob {
  background: var(--ink);
  border-color: var(--s1);
}

/* The same tabular mono the rest of the desk reads its numbers in. */
.console__dock .scrob-time {
  font: 500 12px/1 var(--mono);
  font-variant-numeric: tabular-nums;
  color: var(--dim);
}

/* How hard Auto drives the macros. Auto only; hidden under User Set. */
:is(.console) .intensity {
  display: flex;
  align-items: center;
  gap: 10px;
}

:is(.console) .intensity[hidden] { display: none; }

:is(.console) .intensity input {
  flex: 1;
  min-width: 0;
  min-height: var(--touch);
  accent-color: var(--s1);
  background: transparent;
  cursor: ew-resize;
}

/* --------------------------------------------------------------- cues --- */

/*
 * Five places, not five identical buttons.
 *
 * Sized to fit inside a deck head, because these get pressed mid-set and
 * belong where the eye already is rather than in a panel you have to open.
 */
:is(.console) .cues {
  display: flex;
  gap: 4px;
  flex: 1;
  justify-content: flex-end;
  min-width: 0;
}

/*
 * A slot is the cue and the way to clear it, stacked.
 *
 * The cross sits under the button rather than on it. A corner badge on
 * something you press all set is something you eventually press by accident,
 * and the one action here that loses work should not share a target with the
 * one you use constantly.
 */
:is(.console) .cueslot {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

:is(.console) .cue__clear {
  min-height: 28px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 5px;
  background: transparent;
  color: var(--faint);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}

/* Quiet until reached for, then plainly destructive. */
:is(.console) .cue__clear:hover {
  border-color: var(--armed);
  color: var(--armed);
  background: rgba(255, 176, 32, .08);
}

:is(.console) .cue__clear:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 1px;
}

:is(.console) .cue {
  min-width: 32px;
  min-height: 28px;
  max-width: 88px;
  padding: 0 7px;
  border: 1px solid var(--edge);
  border-radius: 5px;
  background: var(--rail);
  color: var(--faint);
  font: var(--label);
  letter-spacing: .06em;
  text-transform: uppercase;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

:is(.console) .cue:hover { background: var(--rail-hi); color: var(--ink); }

/* A filled cue lights, so which ones you have is readable at a glance. */
:is(.console) .cue[data-filled="true"] {
  border-color: var(--s2);
  color: var(--ink);
  background: linear-gradient(180deg, rgba(193, 58, 224, .24), transparent);
}

:is(.console) .cues__revert {
  min-width: 30px;
  max-width: 30px;
  min-height: 28px;
  padding: 0;
  font-size: 13px;
  flex: 0 0 auto;
}

/* Bindings read as keys, because a binding of "," or "." set as small text is
 * a dot nobody can see - it looked unbound. */
:is(.console, .poolsheet) .keys { display: flex; gap: 5px; flex-wrap: wrap; }

:is(.console, .poolsheet) .key {
  display: inline-grid;
  place-items: center;
  min-width: 26px;
  padding: 2px 7px;
  border: 1px solid var(--edge);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: var(--inset);
  color: var(--ink);
  font: 500 11px/1.5 var(--mono);
  text-transform: none;
  letter-spacing: 0;
}

/* A cue Auto set. Marked, not hidden - what the mode does on your behalf
 * should be visible rather than something you discover later. */
:is(.console) .cue[data-auto="true"] {
  border-style: dashed;
}

/* The slot Auto just touched. A cue appearing with no signal is
 * indistinguishable from a bug. */
:is(.console) .cue[data-just] {
  animation: cue-flash 1.6s ease-out;
}

:is(.console) .cue[data-just="recall"] { border-color: var(--s5); }

@keyframes cue-flash {
  0% { background: var(--s2); color: #fff; }
  100% { background: var(--rail); }
}

@media (prefers-reduced-motion: reduce) {
  :is(.console) .cue[data-just] { animation: none; box-shadow: 0 0 0 2px var(--s2); }
}

/* ----------------------------------------------------- the control booth --- */

/*
 * Every control in the app on one screen, backlit.
 *
 * The glow is not decoration: --lit is how recently a knob moved, so the eye is
 * pulled to whatever the engine just touched and two hundred numbers read as
 * activity. Each tile carries only --v (where the value sits) and --lit (how
 * hot it is), so updating one is two property writes rather than a rebuild.
 */

:is(.console) .booth__note {
  margin: 6px 0 0;
  color: var(--dim);
  font-size: 13px;
  max-width: 70ch;
}

:is(.console) .booth__head {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 12px 0 5px;
  background: linear-gradient(var(--desk) 70%, transparent);
}

/* The heading takes the section's colour, so a layer can be found by glancing
 * rather than by reading. */
:is(.console) .booth__head .label {
  color: var(--tint);
  text-shadow: 0 0 12px color-mix(in srgb, var(--tint) 45%, transparent);
}

:is(.console) .booth {
  display: grid;
  gap: 7px;
  grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
}

:is(.console) .cell {
  --v: 0;
  --lit: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 9px 22px 7px 6px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--inset);
  cursor: ns-resize;
  overflow: hidden;
}

/* The backlight itself: a wash of the layer's colour behind the tile, at the
 * heat the knob is currently running. */
:is(.console) .cell::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--tint);
  opacity: calc(var(--lit) * 0.16);
  pointer-events: none;
}

:is(.console) .cell[data-driven="true"] { border-color: var(--edge-soft); }

/* --- the dial ------------------------------------------------------------- */

/*
 * A ring drawn with one conic gradient, which is a single paint rather than an
 * SVG per control - at two hundred of them that difference is the feature
 * working or the page stuttering. The sweep runs 250 degrees from lower left,
 * the way a real pointer knob does, so the ends are visibly ends.
 */
:is(.console) .cell__dial {
  position: relative;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background:
    conic-gradient(from 215deg,
      var(--tint) calc(var(--v) * 250deg),
      rgba(255, 255, 255, .07) calc(var(--v) * 250deg) 250deg,
      transparent 250deg);
  filter: drop-shadow(0 0 calc(var(--lit) * 7px) var(--tint));
}

/* The hole, which is what makes it a ring, and the face the pointer sits on. */
:is(.console) .cell__face {
  position: absolute;
  inset: 6px;
  border-radius: 50%;
  background: var(--desk-2);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05);
}

/* The pointer. Rotated to match the sweep, so the knob reads at a glance
 * without anybody having to compare arc lengths. */
:is(.console) .cell__face::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 3px;
  width: 2px;
  height: 8px;
  margin-left: -1px;
  border-radius: 1px;
  background: var(--tint);
  transform-origin: 50% 15px;
  transform: rotate(calc(-125deg + var(--v) * 250deg));
  opacity: calc(0.55 + var(--lit) * 0.45);
}

:is(.console) .cell__name {
  font: var(--label);
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--dim);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

:is(.console) .cell__value {
  font: 500 10px/1 var(--mono);
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}

/* Lit tiles bring their readout forward with them. */
:is(.console) .cell[style*="--lit"] .cell__value { color: var(--dim); }

/* --- input and lock -------------------------------------------------------- */

/*
 * The range covers the whole tile and is invisible. Dragging anywhere on a
 * knob adjusts it, which is how a knob should behave, and the native control
 * keeps its keyboard handling and its focus ring.
 */
:is(.console) .cell__input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: ns-resize;
}

:is(.console) .cell__input:disabled { cursor: default; }

:is(.console) .cell__input:focus-visible {
  opacity: 0;
}

:is(.console) .cell:focus-within {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/*
 * A full-height strip down the right edge of the tile.
 *
 * It began as an 18px dot in the corner, which looked right and was well under
 * the size anything gets to be if people are expected to hit it - especially
 * on a phone. Narrow is fine; short is not. The strip is the height of the
 * tile, so it is a real target, and the dot sits at the top of it where the
 * corner mark used to be.
 */
:is(.console) .cell__lock {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 20px;
  padding: 5px 0 0;
  border: 0;
  border-radius: 0 var(--r) var(--r) 0;
  background: transparent;
  color: var(--edge);
  font-size: 9px;
  line-height: 1;
  text-align: center;
  cursor: pointer;
  z-index: 2;
}

/*
 * Not available while Auto is driving. Dimmed rather than removed, so the
 * tile does not change shape when the mode does and a lock you already set
 * is still visible as a lock.
 */
:is(.console) .cell__lock:disabled { opacity: .35; cursor: default; }
:is(.console) .cell__lock:disabled:hover { background: transparent; }

:is(.console) .cell__lock:not(:disabled):hover { background: rgba(255, 255, 255, .05); }

:is(.console) .cell__lock:hover { color: var(--dim); }

/* Locked reads as held: the ring goes amber and the pointer stops glowing. */
:is(.console) .cell[data-locked="true"] { --tint: var(--armed); }
:is(.console) .cell[data-locked="true"] .cell__lock { color: var(--armed); }
:is(.console) .cell[data-locked="true"] { border-color: var(--armed); }

@media (prefers-reduced-motion: reduce) {
  /* The backlight is motion. Keep the state legible, drop the pulsing. */
  :is(.console) .cell::before { opacity: 0; }
  :is(.console) .cell__dial { filter: none; }
}

@media (max-width: 700px) {
  :is(.console) .booth { grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); }
  :is(.console) .cell__dial { width: 36px; height: 36px; }
}

/* --- the jump bar ----------------------------------------------------------- */

/*
 * Sticky, so it is reachable from anywhere in a wall of controls, and each
 * button carries its section's colour - which makes it double as the key to
 * what the colours mean.
 */
:is(.console) .booth__jump {
  position: sticky;
  top: 0;
  z-index: 3;
  display: flex;
  gap: 5px;
  flex-wrap: wrap;
  padding: 8px 0;
  margin-bottom: 2px;
  background: var(--desk);
}

:is(.console) .booth__go {
  min-height: 30px;
  padding: 0 11px;
  border: 1px solid color-mix(in srgb, var(--tint) 45%, var(--edge));
  border-radius: var(--r);
  background: color-mix(in srgb, var(--tint) 10%, var(--inset));
  color: var(--tint);
  font: var(--label);
  letter-spacing: .08em;
  text-transform: uppercase;
  cursor: pointer;
}

:is(.console) .booth__go:hover {
  background: color-mix(in srgb, var(--tint) 22%, var(--inset));
  box-shadow: 0 0 14px -4px var(--tint);
}

/* The section headings sit below the jump bar, so they need their own offset
 * or they would stick underneath it. */
:is(.console) .booth__head { top: 46px; }

/* The jump bar is sticky, so a heading scrolled to the top would arrive
 * underneath it. This is what scroll-margin is for - the browser does the
 * offset, rather than hand-rolled arithmetic that worked in one direction. */
:is(.console) .booth__anchor {
  height: 0;
  /* Clears the sticky jump bar, so a section lands below it rather than
   * underneath the thing that was clicked. */
  scroll-margin-top: 54px;
}

/* --- fader tiles ------------------------------------------------------------ */

/*
 * A knob for things that go round, a fader for things that go up.
 *
 * Hue and angle wrap - there is no top or bottom to them, and a fader with a
 * hard end on a wrapping value is a lie about the control. Size, opacity,
 * speed and position all have a floor, a ceiling and a meaningful middle,
 * which is exactly what a fader shows and a dial hides.
 */
:is(.console) .cell__fader {
  width: 100%;
  height: 42px;
  display: flex;
  align-items: flex-end;
  padding: 3px;
  border-radius: 4px;
  background: rgba(255, 255, 255, .05);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .5);
  overflow: hidden;
}

:is(.console) .cell__fill {
  display: block;
  width: 100%;
  height: calc(var(--v) * 100%);
  min-height: 2px;
  border-radius: 3px;
  background: var(--tint);
  box-shadow: 0 0 calc(var(--lit) * 12px) var(--tint);
}

/* A cap at the top of the travel, so the position reads as a fader rather
 * than as a bar chart. */
:is(.console) .cell__fill::after {
  content: '';
  display: block;
  height: 3px;
  background: #fff;
  opacity: calc(0.35 + var(--lit) * 0.65);
}

/* --- state tiles ------------------------------------------------------------ */

/*
 * Presets, palettes and switches. There is no travel to draw, so drawing a
 * dial at full would be a control that means nothing - these are readouts, and
 * the backlight carries the signal instead. For a preset switching, that is
 * the most noticeable thing Auto does.
 */
:is(.console) .cell__state {
  width: 100%;
  height: 42px;
  border-radius: 4px;
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--tint) calc(12% + var(--lit) * 40%), transparent),
      transparent);
  border: 1px solid color-mix(in srgb, var(--tint) 35%, var(--edge));
}

:is(.console) .cell--state .cell__value {
  color: var(--ink);
  font-size: 11px;
}

/* Layers hidden because they are off in Setup. Amber, because it is a state
 * somebody chose and can undo, not a fault. */
:is(.console) .booth__off { color: var(--armed); }

/* ====================================================== Render Studio === */

/*
 * A room, not a sheet.
 *
 * The pool editor is a dialog over the desk because it answers a question
 * about what is on the desk. This is not that: while a render runs, the app is
 * not a performance instrument - the clock is being stepped by the encoder -
 * so the desk underneath would be lying about what its controls do. It covers
 * everything, and the way out is a labelled button rather than a dismissal.
 */
.studio {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  flex-direction: column;
  background: var(--desk);
  color: var(--ink);
}

.studio[hidden] { display: none; }

.studio__head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--edge);
  background: var(--desk-2);
  flex: none;
}

.studio__title { display: flex; align-items: baseline; gap: 10px; }
.studio__title strong { font-size: 15px; letter-spacing: .02em; }

.studio__state {
  flex: 1;
  font: var(--label);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--dim);
}

.studio__body {
  flex: 1;
  min-height: 0;
  display: grid;
  /* Settings are read once and left alone; the queue is watched. So the queue
   * gets the room, and on a narrow window it gets the top of the scroll. */
  grid-template-columns: minmax(260px, 340px) minmax(0, 1fr);
  gap: 0;
}

.studio__panel {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px 18px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.studio__panel--queue { border-left: 1px solid var(--edge); }

.studio__note {
  margin: 0;
  max-width: 62ch;
  color: var(--dim);
  font-size: 13px;
}

/*
 * A grid rather than a wrapping row: four sizes laid out by flex left 4K
 * stranded on a line of its own, which reads as a different kind of option
 * rather than the fourth of four.
 */
.studio__chips {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
  gap: 6px;
}

.studio__chips .punch { min-width: 0; }

/* ---------------------------------------------------------- agent HUD --- */

/*
 * What the agent is doing, while it is doing it.
 *
 * Bottom left, small, out of the way: this is a visualiser and the picture is
 * the point. It exists because half a minute of an agent thinking looks
 * exactly like half a minute of an agent having crashed.
 */
.agent-hud {
  position: fixed;
  left: 16px;
  bottom: 16px;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: min(60vw, 560px);
  padding: 7px 12px;
  border-radius: 999px;
  background: rgba(8, 9, 12, .72);
  backdrop-filter: blur(6px);
  border: 1px solid rgba(255, 255, 255, .12);
  color: var(--ink);
  font: var(--label);
  letter-spacing: .06em;
  text-transform: uppercase;
  pointer-events: none;
  animation: agent-hud-in .2s ease both;
}

@keyframes agent-hud-in {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: none; }
}

/*
 * A dot that travels while there is work, and holds still when there is not.
 *
 * Indeterminate on purpose. The stages of an analysis take wildly different
 * lengths and a bar that jumps from a fifth to nine tenths is a lie told to
 * look reassuring - so this says "working" and says nothing about how long.
 */
.agent-hud::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--s6);
  flex: 0 0 auto;
}

.agent-hud[data-busy="true"]::before {
  animation: agent-hud-pulse 1.1s ease-in-out infinite;
}

@keyframes agent-hud-pulse {
  0%, 100% { opacity: .25; transform: scale(.8); }
  50% { opacity: 1; transform: scale(1.25); box-shadow: 0 0 12px var(--s6); }
}

@media (prefers-reduced-motion: reduce) {
  .agent-hud { animation: none; }
  .agent-hud[data-busy="true"]::before { animation: none; opacity: 1; }
}

/* ------------------------------------------------------- agent bridge --- */

/*
 * Visible on purpose. An agent working the rig through this is typing into a
 * box a human can watch, and reading an answer the human can read too.
 */
.agent-bridge__in {
  width: 100%;
  min-height: 68px;
  padding: 8px 10px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--inset);
  color: var(--ink);
  font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  resize: vertical;
}

.agent-bridge__in:focus-visible { outline: 2px solid var(--s1); outline-offset: 1px; }

/*
 * The line that says what came back, and the three things to do with it.
 */
.agent-bridge__head {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: -6px;
}

.agent-bridge__sum {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--dim);
  font: 11px/1.4 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

.agent-bridge__act {
  flex: 0 0 auto;
  min-height: var(--touch);
  padding: 3px 9px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--inset);
  color: var(--dim);
  font: 11px/1.4 inherit;
  cursor: pointer;
}

.agent-bridge__act:hover { color: var(--ink); border-color: var(--s6); }
.agent-bridge__act:focus-visible { outline: 2px solid var(--s1); outline-offset: 1px; }

.agent-bridge__out {
  margin: 0;
  max-height: 320px;
  overflow: auto;
  padding: 10px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--inset);
  color: var(--dim);
  font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  white-space: pre-wrap;
  word-break: break-word;
}

/*
 * Folded, not shortened.
 *
 * Height only. The text inside is the whole reply either way, because an agent
 * that can only read the page reads this element - clipping what a person sees
 * must never change what a machine gets.
 */
.agent-bridge__out--clipped {
  max-height: 72px;
  /*
   * Scrollable, not hidden - and that is the whole point of this rule.
   *
   * `overflow: hidden` clips the text out of the accessibility tree as well as
   * out of view, and an agent that can only read that tree is precisely who
   * this panel is for. Folding the answer away from a person was quietly
   * taking it away from a machine. `auto` keeps every character reachable
   * while showing the same few lines.
   */
  overflow: auto;
}

/* Told apart without reading a word of it. */
.agent-bridge__out[data-ok="true"] { color: var(--ink); border-color: var(--s6); }
.agent-bridge__out[data-ok="false"] { color: #ffb4c4; border-color: #b3123a; }

/* ---------------------------------------------------------- cue line --- */

/*
 * The agent's set, drawn against the track.
 *
 * Sits under the mode switch because that is where "who is driving" is already
 * answered - this is the rest of that sentence: for how much longer, and how
 * far in.
 */
.cueline {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  margin-top: 6px;
}

.cueline__track {
  position: relative;
  flex: 1 1 auto;
  height: 14px;
  border-radius: 7px;
  background: var(--inset);
  border: 1px solid var(--edge);
  overflow: hidden;
}

.cueline__cue {
  position: absolute;
  top: 3px;
  width: 2px;
  height: 6px;
  margin-left: -1px;
  border-radius: 1px;
  background: var(--s6);
  opacity: 0.5;
  transition: opacity 0.2s linear, height 0.12s ease-out, top 0.12s ease-out;
}

/* Behind us now. Still drawn - the shape of the whole set is the point. */
.cueline__cue[data-fired="true"] { opacity: 1; background: var(--s1); }

/* The moment one fires. Brief and unmissable, then back into the row. */
.cueline__cue[data-hit="true"] {
  top: 1px;
  height: 10px;
  opacity: 1;
  background: var(--ink);
  box-shadow: 0 0 6px 1px var(--s1);
}

.cueline__head {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 1px;
  background: var(--ink);
  opacity: 0.75;
}

.cueline__label {
  flex: 0 0 auto;
  color: var(--dim);
  font: 11px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-variant-numeric: tabular-nums;
}

/* A set is worth watching; it is not worth a seizure. */
@media (prefers-reduced-motion: reduce) {
  .cueline__cue { transition: none; }
  .cueline__cue[data-hit="true"] { box-shadow: none; }
}

/* ------------------------------------------------------ agent control --- */

/*
 * Who has the desk, while they have it.
 *
 * Sits inside the mode switch because that is where somebody looks to find out
 * what is driving, and the honest answer while an agent is working is neither
 * of the two buttons. Red for the same reason the takeover card is: this is
 * about authority, not about the picture.
 */
.mode-switch__agent {
  display: inline-flex;
  align-items: center;
  padding: 0 10px;
  border-radius: var(--r);
  font: var(--label);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #fff;
  background: #b3123a;
  box-shadow: 0 0 16px -4px #ff2d5f;
  white-space: nowrap;
}

/* The two buttons stay reachable - pressing one is how you take it back - but
 * they stop claiming to be the current state while somebody else has it. */
.mode-switch[data-agent="true"] button { opacity: .5; }

/* ------------------------------------------------------------- words --- */

/*
 * The Text layer's copy, in the booth above the controls that dress it.
 *
 * Every other layer draws whatever its preset says; this is the one whose
 * content somebody has to type, so it sits above the grid rather than in it -
 * choosing a typeface for copy that does not exist is the wrong order to meet
 * the layer in.
 */
/* ---------------------------------------------------------- text slots --- */

/*
 * Five layers of type, each with its own words.
 *
 * Stacked rather than tabbed: they are cast together and read together, and a
 * tab strip would hide four fifths of the thing you are trying to compose.
 */
.textslots {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0 0 12px;
}

.textslots__slot { display: flex; flex-direction: column; gap: 6px; }

.textslots__head {
  display: flex;
  align-items: center;
  gap: 8px;
}

.textslots__head .label { flex: 1 1 auto; }

/* Quiet until wanted: removing a layer is not the common action. */
.textslots__drop,
.textslots__add {
  flex: 0 0 auto;
  max-width: 190px;
  font-size: 11px;
  padding: 4px 10px;
}

.textslots__drop { opacity: 0.55; }
.textslots__drop:hover { opacity: 1; }

.textslots__add { align-self: flex-start; }

/* The slot's own wordlist sits flush inside it. */
.textslots__slot .words { margin: 0; }

.words {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 0 12px;
  padding: 12px;
  border: 1px solid var(--edge);
  border-radius: var(--r-lg);
  background: var(--desk-2);
}

.words__head { display: flex; align-items: center; gap: 8px; }

.words__entry,
.words__text {
  flex: 1;
  min-width: 0;
  min-height: 34px;
  padding: 0 10px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--inset);
  color: var(--ink);
  font: inherit;
}

.words__entry:focus-visible,
.words__text:focus-visible { outline: 2px solid var(--s1); outline-offset: 1px; }

.words__add { flex: 0 0 auto; max-width: 90px; }

.words__list { display: flex; flex-direction: column; gap: 6px; }

.words__item { display: flex; align-items: center; gap: 6px; }

/* The selected word is the one the controls below are editing, so it has to be
 * obvious which row the rest of the panel belongs to. */
.words__item.on .words__text {
  border-color: var(--s1);
  box-shadow: 0 0 0 1px var(--s1) inset;
}

.words__kill { flex: 0 0 auto; max-width: 40px; min-width: 34px; padding: 0; }

/*
 * Where the music goes.
 *
 * The first thing in the room, because arriving from a link that is the only
 * question - and a dashed rectangle reading "drop a file" is a button to most
 * people, so it is one.
 */
.studio__drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 18px 12px;
  border: 1.5px dashed var(--edge);
  border-radius: var(--r-lg);
  background: var(--inset);
  cursor: pointer;
  text-align: center;
  transition: border-color .14s ease, background .14s ease;
}

.studio__drop:hover,
.studio__drop:focus-visible { border-color: var(--s1); background: var(--desk-2); }
.studio__drop:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

/* Lit while a file is over it, so the target is unmistakable mid-drag. */
.studio__drop[data-over="true"] {
  border-color: var(--s1);
  border-style: solid;
  background: var(--desk-2);
  box-shadow: 0 0 22px -6px var(--s1) inset;
}

.studio__drop-lead { font-size: 13px; color: var(--ink); }
.studio__drop-say { font-size: 12px; color: var(--dim); }

/* ------------------------------------------------------ agent control --- */

/*
 * Said once, large, and then gone.
 *
 * A picture that starts moving on its own is indistinguishable from a fault,
 * and somebody who does not know an agent is driving will go looking for the
 * bug. Red because this is the one message on the desk that is about who is in
 * charge rather than about the picture.
 *
 * It does not linger. A banner that stayed would be a permanent lie the moment
 * the agent stopped, and a visualiser is the last thing that wants something
 * parked over it.
 */
.agent-claim {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  font: 700 clamp(28px, 7vw, 88px)/1 var(--face, system-ui, sans-serif);
  letter-spacing: .16em;
  text-align: center;
  color: #ff2d2d;
  text-shadow: 0 0 30px rgba(255, 45, 45, .65), 0 2px 0 rgba(0, 0, 0, .6);
  background: radial-gradient(ellipse at center,
              rgba(120, 0, 0, .38) 0%, rgba(0, 0, 0, 0) 62%);
  animation: agent-claim 2.6s ease forwards;
}

@keyframes agent-claim {
  0%   { opacity: 0; transform: scale(1.06); }
  12%  { opacity: 1; transform: scale(1); }
  62%  { opacity: 1; }
  100% { opacity: 0; }
}

/* Motion is the wrong thing to insist on; the message is not. */
@media (prefers-reduced-motion: reduce) {
  .agent-claim { animation: agent-claim-fade 2.6s linear forwards; }
  @keyframes agent-claim-fade {
    0%, 70% { opacity: 1; }
    100% { opacity: 0; }
  }
}


/*
 * A control you can see and cannot use yet.
 *
 * Dimmed rather than hidden, because the point of showing it is to say what a
 * subscription buys - a control that is not there answers nobody's question.
 * The cursor says it is not a dead button so much as one that is waiting for
 * something, and the buttons carry :disabled so it is not a lie to a screen
 * reader either.
 */
.studio__chips[data-locked="true"] { opacity: .45; }
.studio__chips[data-locked="true"] .punch { cursor: not-allowed; }

/* ------------------------------------------------------- the way out --- */

/*
 * A plate bolted to the bottom of the settings column.
 *
 * Deliberately a different object from everything above it. The controls in
 * this column set up a render; this does not, and dressing it as one more
 * settings block would have it read as another thing to decide. So it is
 * hardware: a brushed panel screwed to the chassis with the two exits engraved
 * on it, the way the ways out of a real machine are labelled.
 *
 * Sits in the space the column already has spare rather than claiming any -
 * `margin-top: auto` pushes it to the floor and it takes only the height its
 * own text needs.
 */
.plate {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px 13px 13px;
  border-radius: 5px;
  /* Brushed, lit from above: a light top edge and a dark bottom one is the
   * whole of what makes a flat rectangle read as a raised piece of metal. */
  background:
    linear-gradient(180deg, #2a2e37 0%, #23262e 42%, #1e2129 100%);
  border: 1px solid #12141a;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .07),
    inset 0 -1px 0 rgba(0, 0, 0, .5),
    0 2px 5px rgba(0, 0, 0, .45);
  position: relative;
}

/*
 * Four bolts, one per corner.
 *
 * Drawn as background dots rather than four elements: they are the only reason
 * the panel reads as fixed to something rather than floating on it, and they
 * are not worth four nodes in the DOM to say so.
 */
.plate::before {
  content: "";
  position: absolute;
  inset: 6px;
  border-radius: 3px;
  pointer-events: none;
  /* A lit crown over a dark seat, which is the whole of what makes a dot read
   * as a countersunk screw head rather than as a full stop. */
  background-image:
    radial-gradient(circle at 45% 40%, #6b7284 0 1.9px, #3a3f4b 2px 2.7px, #0d0f13 2.9px 3.6px, transparent 3.7px),
    radial-gradient(circle at 45% 40%, #6b7284 0 1.9px, #3a3f4b 2px 2.7px, #0d0f13 2.9px 3.6px, transparent 3.7px),
    radial-gradient(circle at 45% 40%, #6b7284 0 1.9px, #3a3f4b 2px 2.7px, #0d0f13 2.9px 3.6px, transparent 3.7px),
    radial-gradient(circle at 45% 40%, #6b7284 0 1.9px, #3a3f4b 2px 2.7px, #0d0f13 2.9px 3.6px, transparent 3.7px);
  background-repeat: no-repeat;
  background-position: left top, right top, left bottom, right bottom;
}

/* Engraved, not printed: cut into the metal and catching light on the lower
 * edge of the stroke. */
.plate__title {
  font: var(--label);
  letter-spacing: .22em;
  text-transform: uppercase;
  color: #9aa1b2;
  text-shadow: 0 1px 0 rgba(255, 255, 255, .06);
  border-bottom: 1px solid rgba(0, 0, 0, .45);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .045);
  padding-bottom: 8px;
}

.plate__item {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.plate__lead {
  font-size: 13px;
  color: var(--ink);
  text-shadow: 0 1px 0 rgba(0, 0, 0, .55);
}

.plate__say {
  font-size: 12px;
  line-height: 1.35;
  color: var(--dim);
}

.plate__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.plate__go {
  flex: 0 0 auto;
  max-width: none;
  min-height: 32px;
  padding: 0 12px;
}

/* The chosen one is lit, the same way a chosen tab is. */
.studio__chips .punch[data-on="true"] {
  color: var(--ink);
  border-color: var(--s1);
  background: color-mix(in srgb, var(--s1) 16%, transparent);
}

.studio__select {
  width: 100%;
  max-width: 340px;
  min-height: var(--touch);
  padding: 8px 10px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--rail);
  color: var(--ink);
  font: inherit;
}

.studio__queue-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.studio__queue-head .label { flex: none; }
.studio__queue-head .readout { flex: 1; color: var(--dim); }
.studio__queue-head .punch { flex: 0 0 auto; max-width: 180px; }

/* Start is the one button in here that begins minutes of work: it is armed. */
.studio__go[data-on="false"]:not(:disabled) {
  border-color: var(--s1);
  color: var(--ink);
}

.studio__go[data-on="true"] {
  border-color: var(--armed);
  color: var(--armed);
}

.studio__rows { display: flex; flex-direction: column; gap: 8px; }

.studio__job {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 10px 12px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--desk-2);
}

.studio__job[data-state="rendering"] { border-color: var(--s1); }
.studio__job[data-state="done"] { border-color: var(--ok, #2ecc71); }
.studio__job[data-state="failed"] { border-color: var(--armed); }
.studio__job[data-state="cancelled"] { opacity: .6; }

.studio__job-top { display: flex; align-items: center; gap: 10px; }
.studio__job-name { flex: none; }
.studio__job-top .label { flex: 1; }
.studio__job-top .punch { flex: 0 0 auto; max-width: 130px; }

.studio__bar {
  height: 4px;
  border-radius: 2px;
  background: var(--rail);
  overflow: hidden;
}

.studio__bar-fill {
  height: 100%;
  width: 0;
  background: var(--s1);
  transition: width 160ms linear;
}

.studio__job[data-state="done"] .studio__bar-fill { background: var(--ok, #2ecc71); }
.studio__job[data-state="failed"] .studio__bar-fill { background: var(--armed); }

.studio__job-message { font-size: 12px; color: var(--dim); }

.studio__foot {
  flex: none;
  padding: 12px 18px;
  border-top: 1px solid var(--edge);
  background: var(--desk-2);
}

@media (prefers-reduced-motion: reduce) {
  .studio__bar-fill { transition: none; }
}

@media (max-width: 900px) {
  /* One column, queue first: it is the thing being watched. */
  .studio__body { grid-template-columns: minmax(0, 1fr); }
  .studio__panel--queue { order: -1; border-left: 0; border-bottom: 1px solid var(--edge); }
}

/* --- the pool sheet's picture view ---------------------------------------- */

/*
 * Offered only where there is something to look at - logo artwork and the
 * shape figures. A grid effect or a laser rig is a behaviour over time, and a
 * wall of identical grey squares standing in for one is worse than a list.
 */
/*
 * The only view. There is no list any more.
 *
 * Wider tiles than the old picture view used, because this is now how every
 * pool is chosen from rather than an alternative to reading names: at 110px a
 * caption of any length was an ellipsis, which put the reading back.
 */
.poolsheet__tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(136px, 1fr));
  gap: 12px;
}

.poolsheet__tile {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

/* Lifts toward the pointer, so a grid of ninety reads as ninety things you can
 * press rather than as a contact sheet. */
.poolsheet__tile .poolsheet__frame {
  transition: transform .14s ease, border-color .14s ease, box-shadow .14s ease,
              opacity .14s ease;
}

.poolsheet__tile:hover .poolsheet__frame {
  transform: translateY(-2px);
  border-color: var(--faint);
}

@media (prefers-reduced-motion: reduce) {
  .poolsheet__tile .poolsheet__frame { transition: none; }
  .poolsheet__tile:hover .poolsheet__frame { transform: none; }
}

.poolsheet__frame {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 1;
  padding: 8px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  /* A chequer, so a transparent logo reads as transparent rather than as a
   * white one on a white card or an invisible one on a dark card. */
  background-color: #14161c;
  background-image:
    linear-gradient(45deg, #1c1f27 25%, transparent 25%, transparent 75%, #1c1f27 75%),
    linear-gradient(45deg, #1c1f27 25%, transparent 25%, transparent 75%, #1c1f27 75%);
  background-size: 14px 14px;
  background-position: 0 0, 7px 7px;
  cursor: pointer;
}

.poolsheet__frame:disabled { cursor: default; }

/*
 * In the pool, and obviously so from across the room.
 *
 * A one-pixel inset ring was enough when this was a small optional view beside
 * a list. As the only view it is a wall of similar squares, and "which of
 * these ninety am I using" has to be answerable at a glance - so a chosen tile
 * gets a ring with a lit edge and the ones left out drop well back.
 */
.poolsheet__tile[data-in="true"] .poolsheet__frame {
  border-color: var(--s1);
  box-shadow: 0 0 0 2px var(--s1) inset, 0 0 16px -4px var(--s1);
}

.poolsheet__tile[data-in="false"] .poolsheet__frame {
  opacity: .38;
  filter: saturate(.5);
}

.poolsheet__tile[data-in="false"]:hover .poolsheet__frame {
  opacity: .7;
  filter: none;
}

.poolsheet__frame:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

.poolsheet__art {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* Sits over the corner of the artwork, so favouriting never means hunting. */
.poolsheet__star--tile {
  position: absolute;
  top: 4px;
  right: 4px;
  z-index: 1;
  min-width: 30px;
  min-height: 30px;
  border-radius: var(--r);
  background: rgba(0, 0, 0, .55);
}

.poolsheet__caption {
  font: var(--label);
  letter-spacing: .04em;
  color: var(--dim);
  text-align: center;
  /* Two lines rather than an ellipsis: the caption is the only thing that says
   * which effect this is, so truncating it defeats the tile. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.25;
  text-transform: none;
}

.poolsheet__tile[data-in="true"] .poolsheet__caption { color: var(--ink); }

/* ------------------------------------------------------------- the plan --- */

/*
 * Shown in two places: Settings, and inline in Create where somebody meets the
 * gate. The second is the one that matters - the moment you want to generate
 * something is the moment "what does this cost" is a live question, and
 * sending people to another screen to answer it is how a subscription flow
 * loses them.
 */
.plan { display: flex; flex-direction: column; gap: 10px; }

/*
 * `hidden` has to win here.
 *
 * The UA rule is `[hidden] { display: none }` at the same specificity as any
 * class rule, so `.plan__actions { display: flex }` later in this sheet simply
 * beat it - and Upgrade and Manage or cancel were offered to somebody signed
 * out, which is the one audience that must never see them.
 */
.plan [hidden] { display: none !important; }

.plan__strip {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}

.plan__tier-now { font-size: 15px; }
.plan__strip .readout { color: var(--dim); }

/*
 * The allowance as a channel meter rather than a progress bar, because that is
 * what "unlimited under fair use" actually describes: a scale with a headroom
 * mark on it. You may run past the line. It goes hot, and keeps working.
 */
.plan__meter {
  position: relative;
  height: 6px;
  border-radius: 3px;
  background: var(--rail);
  overflow: hidden;
}

.plan__meter-fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--s1);
  transition: width 200ms ease;
}

.plan__strip[data-state="hot"] ~ .plan__meter .plan__meter-fill { background: var(--armed); }
.plan__strip[data-state="owner"] ~ .plan__meter .plan__meter-fill { background: var(--ok, #2ecc71); }

.plan__note { margin: 0; max-width: 62ch; color: var(--dim); font-size: 13px; }

.plan__actions { display: flex; gap: 8px; flex-wrap: wrap; }
.plan__actions .punch { max-width: 260px; }

.plan__tiers {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 10px;
}

.plan__tier {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--desk-2);
}

/* One recommendation, made once. Three cards all shouting is no
 * recommendation at all. */
.plan__tier--pick { border-color: var(--s1); }

.plan__tier-head { display: flex; align-items: baseline; gap: 8px; }
.plan__tier-name { flex: 1; font-size: 14px; }
.plan__tier-price { font: 500 18px/1 var(--mono); color: var(--ink); }
.plan__tier-price i { font: var(--label); font-style: normal; color: var(--dim); }
.plan__tier-what { margin: 0; font-size: 12px; color: var(--dim); }

/* Locked tools stay legible. Somebody deciding whether to subscribe should be
 * able to read exactly what they would get - a hidden row is not an argument
 * for paying, and a greyed one that names itself is. */
:is(.console) .tool[data-locked="true"] { opacity: .55; }
:is(.console) .tool[data-locked="true"] .deck__head::after {
  content: 'needs a plan';
  font: var(--label);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--armed);
}

/* An <a> styled as a button. Still a link, so it opens in a new tab, can be
 * copied, and reads as a destination to a screen reader - which is what the
 * legal pages are. */
:is(.console) a.punch {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}

:is(.console) a.punch:hover { text-decoration: underline; }

/* --- input bindings ------------------------------------------------------- */

/*
 * Forty-odd bindings, laid out as cards rather than as a column.
 *
 * Each was a full-width row, so setting one meant scrolling past all the
 * others - and on a wide screen the row was mostly empty, with Learn stranded
 * at the far right a long way from the name it belonged to. A card keeps the
 * name, its keycaps and its buttons together, which is the thing being read.
 */
:is(.console) .binds {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 8px;
}

:is(.console) .bind {
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  padding: 10px 12px;
}

:is(.console) .bind .asset__name { min-width: 0; }

/* Buttons share the width of the card rather than each taking a fixed size,
 * so Learn and Clear read as one control group. */
:is(.console) .bind .asset__actions { width: 100%; }
:is(.console) .bind .asset__actions .punch { flex: 1; max-width: none; }

@media (max-width: 700px) {
  :is(.console) .binds { grid-template-columns: minmax(0, 1fr); }
}

/* ------------------------------------------------ premium asset workspace --- */

.poolsheet__copy {
  margin: 0;
  padding: 11px 16px;
  border-bottom: 1px solid var(--edge-soft);
  color: var(--dim);
  background: linear-gradient(90deg, rgba(176, 38, 255, .08), transparent 55%);
  font-size: 12.5px;
  line-height: 1.55;
}

.poolsheet__generator {
  padding: 12px 16px 2px;
  background: var(--desk);
}

.poolsheet__generator:empty { display: none; }
.poolsheet__generator .create-tool { margin: 0; }
.poolsheet__restore { max-width: 150px; }

.poolsheet__add-frame {
  display: grid;
  place-items: center;
  align-content: center;
  gap: 7px;
  border-style: dashed;
  color: var(--dim);
  background: rgba(255, 255, 255, .025);
}

.poolsheet__add-frame:hover { color: var(--ink); border-color: var(--s1); }
.poolsheet__plus { font: 300 40px/1 var(--ui); color: var(--s1); }
.poolsheet__add-copy { font: var(--label); letter-spacing: .04em; }

.poolsheet__asset-actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px;
}

.poolsheet__asset-actions .punch {
  min-width: 0;
  min-height: 34px;
  padding: 5px 7px;
  font-size: 10px;
}

.poolsheet__remove { color: #fda4af; }

.pool-generator-cta {
  position: relative;
  overflow: hidden;
  border-color: rgba(176, 38, 255, .45) !important;
  background:
    radial-gradient(circle at 92% 10%, rgba(176, 38, 255, .18), transparent 38%),
    linear-gradient(135deg, rgba(42, 22, 57, .72), var(--desk-2)) !important;
}

.pool-generator-cta__actions { display: flex; gap: 8px; flex-wrap: wrap; }
.pool-generator-cta__actions .punch { flex: 1; max-width: 220px; }

.create-premium { isolation: isolate; }

.create-hero {
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 18px;
  min-height: 150px;
  padding: 22px;
  overflow: hidden;
  border: 1px solid rgba(176, 38, 255, .35);
  border-radius: 18px;
  background:
    radial-gradient(circle at 82% 18%, rgba(176, 38, 255, .28), transparent 34%),
    radial-gradient(circle at 64% 118%, rgba(40, 224, 190, .15), transparent 42%),
    linear-gradient(135deg, #171321 0%, #101218 62%, #14151d 100%);
  box-shadow: 0 20px 46px rgba(0, 0, 0, .25), inset 0 1px rgba(255, 255, 255, .04);
}

.create-hero::after {
  content: '';
  position: absolute;
  width: 210px;
  height: 210px;
  right: -72px;
  top: -90px;
  border: 1px solid rgba(255, 255, 255, .08);
  border-radius: 50%;
  box-shadow: 0 0 0 26px rgba(176, 38, 255, .035),
              0 0 0 58px rgba(176, 38, 255, .025);
}

.create-hero__copy { position: relative; z-index: 1; max-width: 620px; }
.create-hero__copy h2 { margin: 8px 0 7px; font-size: clamp(21px, 3vw, 31px); line-height: 1.08; }
.create-hero__eyebrow { display: flex; align-items: center; gap: 8px; font: var(--label); color: #d7b7ff; letter-spacing: .08em; }
.create-hero__spark { color: #62f7db; text-shadow: 0 0 12px rgba(98, 247, 219, .8); }
.create-hero__usage {
  position: relative;
  z-index: 1;
  max-width: 250px;
  padding: 9px 12px;
  border: 1px solid rgba(255, 255, 255, .1);
  border-radius: 999px;
  background: rgba(5, 7, 12, .48);
  color: var(--dim);
  font-size: 11px;
  text-align: center;
  backdrop-filter: blur(10px);
}

.create-section-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 2px 2px 0; }
.create-section-head strong { font-size: 14px; }
.create-section-head--library { margin-top: 4px; }

.create-tools { grid-template-columns: repeat(2, minmax(260px, 1fr)) !important; }
.create-tool {
  position: relative;
  overflow: hidden;
  border-color: rgba(255, 255, 255, .09) !important;
  background: linear-gradient(145deg, rgba(28, 30, 39, .98), rgba(17, 19, 26, .98)) !important;
  box-shadow: 0 12px 28px rgba(0, 0, 0, .16);
  transition: border-color .28s ease, box-shadow .28s ease, background .28s ease;
}

.create-tool::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  opacity: .7;
  background: linear-gradient(90deg, var(--s1), var(--s4), transparent);
}

.create-tool[data-tool="textblock"]::before { background: linear-gradient(90deg, #ff6bd6, #b026ff, transparent); }
.create-tool[data-tool="shapes"]::before { background: linear-gradient(90deg, #55f6d2, #4ba3ff, transparent); }
.create-tool[data-tool="packs"]::before { background: linear-gradient(90deg, #ffc857, #ff6b6b, transparent); }
.create-tool__identity { display: flex; align-items: center; gap: 9px; }
.create-tool__cost {
  flex: none;
  padding: 5px 7px;
  border: 1px solid rgba(98, 247, 219, .2);
  border-radius: 999px;
  background: rgba(98, 247, 219, .07);
  color: #a9eadc;
  letter-spacing: .07em;
  white-space: nowrap;
}
.create-tool__icon {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 1px solid rgba(255, 255, 255, .1);
  border-radius: 9px;
  background: rgba(255, 255, 255, .045);
  color: #e7d2ff;
  font-size: 13px;
}

.create-tool__feedback { display: flex; align-items: center; gap: 8px; min-height: 20px; }
.create-tool__activity { display: none; align-items: center; gap: 4px; }
.create-tool__activity i { width: 5px; height: 5px; border-radius: 50%; background: var(--s1); animation: createPulse .9s ease-in-out infinite; }
.create-tool__activity i:nth-child(2) { animation-delay: .14s; }
.create-tool__activity i:nth-child(3) { animation-delay: .28s; }
.create-tool[data-state="generating"] { border-color: rgba(176, 38, 255, .72) !important; box-shadow: 0 0 28px rgba(176, 38, 255, .14); }
.create-tool[data-state="generating"] .create-tool__activity { display: flex; }
.create-tool[data-state="success"] { border-color: rgba(64, 226, 164, .82) !important; box-shadow: 0 0 26px rgba(64, 226, 164, .14); }
.create-tool[data-state="success"] .create-tool__go { color: #b9ffe3; border-color: rgba(64, 226, 164, .6); }
.create-tool[data-state="error"] { border-color: rgba(251, 113, 133, .65) !important; }

@keyframes createPulse {
  0%, 100% { transform: translateY(0); opacity: .35; }
  50% { transform: translateY(-4px); opacity: 1; }
}

.create-assets {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 10px;
}

:is(.console) .create-asset {
  align-items: stretch;
  padding: 0;
  overflow: hidden;
  flex-wrap: nowrap;
  flex-direction: column;
  border-radius: 14px;
  background: linear-gradient(145deg, var(--desk-2), #11131a);
}

.create-asset__preview {
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background-color: #12151c;
  background-image:
    linear-gradient(45deg, #1b1f28 25%, transparent 25%, transparent 75%, #1b1f28 75%),
    linear-gradient(45deg, #1b1f28 25%, transparent 25%, transparent 75%, #1b1f28 75%);
  background-size: 16px 16px;
  background-position: 0 0, 8px 8px;
  border-bottom: 1px solid var(--edge-soft);
}

.create-asset__preview img,
.create-asset__preview canvas { width: 100%; height: 100%; object-fit: contain; }
.create-asset__cover { display: flex; flex-direction: column; align-items: center; gap: 8px; color: var(--dim); }
.create-asset__cover span { font-size: 34px; color: var(--s1); }
.create-asset__body { display: flex; flex: 1; flex-direction: column; gap: 10px; padding: 12px; }
.create-asset__body .asset__actions { margin-top: auto; }
.create-asset__body .asset__actions .punch { flex: 1; min-width: 84px; }

@media (max-width: 760px) {
  .create-hero { align-items: flex-start; flex-direction: column; min-height: 0; padding: 18px; }
  .create-tool__head { align-items: flex-start; }
  .create-tool__cost { white-space: normal; text-align: right; line-height: 1.25; }
  .create-hero__usage { max-width: none; text-align: left; }
  .create-tools { grid-template-columns: 1fr !important; }
  .poolsheet__asset-actions { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  .create-tool { transition: none; }
  .create-tool__activity i { animation: none; opacity: 1; }
}
/* Discoverable controls and a compact performance desk. */
.booth__filters { display:flex; flex-wrap:wrap; gap:8px; margin:12px 0; }
.booth__filters input, .booth__filters select { min-width:140px; flex:1; min-height:44px; background:var(--desk); color:var(--ink); border:1px solid var(--edge); border-radius:8px; padding:8px; }
.cell__select { position:absolute; inset:0 0 26px; width:100%; height:calc(100% - 26px); opacity:0; cursor:pointer; }
.cell__select:disabled { cursor:default; }
.cell:focus-within { outline:2px solid var(--ink); outline-offset:2px; }
.cell__select option { background:#171a21; color:white; }
.live__compact-toggle { width:100%; min-height:44px; border:0; border-bottom:1px solid var(--edge); background:var(--desk); color:var(--dim); cursor:pointer; }
.section--live[data-compact="true"] .deck--pad, .section--live[data-compact="true"] .deck--layers { display:none; }
.section--live[data-compact="true"] .decks { display:block; }
.section--live[data-compact="true"] .deck--macros { padding:8px 14px; }
.section--live[data-compact="true"] .macro { max-height:95px; }
.section--live[data-compact="true"] .fader { height:64px; }
.settings__advanced summary { cursor:pointer; min-height:44px; padding:12px; border:1px solid var(--edge); border-radius:8px; }
.settings__advanced > div { padding:12px 0; }
.splash__demo-error { color:#fbbf24; max-width:400px; }

.looks__toolbar { display:flex; gap:8px; flex-wrap:wrap; margin:12px 0; }
.looks__toolbar input { min-height:44px; min-width:180px; flex:1; color:var(--ink); background:var(--desk); border:1px solid var(--edge); border-radius:8px; padding:8px 12px; }
.starter__grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:12px; margin:12px 0 24px; }
.starter__card { min-width:0; display:flex; flex-direction:column; gap:10px; padding:12px; border:1px solid var(--edge); border-radius:12px; background:var(--desk-2); }
.starter__card img { width:100%; height:auto; aspect-ratio:16/9; object-fit:cover; background:#000; border-radius:8px; }
.starter__card select { min-height:44px; color:var(--ink); background:var(--desk); border:1px solid var(--edge); border-radius:8px; padding:8px; }
.starter__title { font-size:16px; }
.look__thumbnail { border-radius:8px; object-fit:cover; }

/* Live recording settings remain beside the record button at narrow widths. */
.live-recording { position: relative; flex-shrink: 0; }
/*
 * Small words, full-size target.
 *
 * This read as a 24px-high strip of 11px text, which is a fine way to look
 * like a secondary action and a poor way to be hit with a thumb - `--touch` is
 * the floor for anything you press, and it applies to the quiet controls as
 * much as the loud ones. The text stays 11px; the box around it grows, and
 * most of that growth is transparent padding rather than visible weight.
 */
.live-recording__options {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch);
  font-size: 11px;
  padding: 0 8px;
  margin-top: 0;
  background: none;
  border: 0;
  cursor: pointer;
  color: #b9b4c9;
}

.live-recording__options:hover { color: var(--ink); }
.live-recording__choices { position: fixed; inset: 0; margin: auto; height: max-content; max-height: calc(100vh - 32px); overflow: auto; width: min(320px, 85vw); padding: 20px; border: 1px solid #484052; border-radius: 10px; background: #17131f; box-shadow: 0 8px 28px #0008; }
.live-recording__choices::backdrop { background: #0009; }
.live-recording__close { margin-top: 16px; padding: 8px 18px; color: #fff; background: #552468; border: 1px solid #9865ad; border-radius: 6px; cursor: pointer; }
.live-recording__choice { display: flex; align-items: center; gap: 8px; margin: 5px 0 10px; color: #eee8f7; cursor: pointer; }
.live-recording__choices p { margin: 8px 0 0; color: #b9b4c9; line-height: 1.45; }
.live-recording__choices .live-recording__error { color: #ffb0b0; }

/*
 * Ask - the console's own question box.
 *
 * A backdrop, because unlike the sign-in offer this one genuinely is modal:
 * it is asking about a thing that is about to happen and there is no sensible
 * answer to "what does the panel behind mean while I decide". The rig itself
 * keeps drawing - it is a different canvas and this never touches it - so the
 * picture on the projector is unaffected by somebody naming a Look.
 */
.ask {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  padding: 16px;
  background: rgba(6, 8, 12, 0.62);
}

.ask__panel {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: min(440px, 100%);
  max-height: calc(100vh - 32px);
  overflow: auto;
  padding: 18px 20px;
  border: 1px solid var(--edge);
  border-radius: var(--r-lg);
  background: var(--desk-2);
  box-shadow: 0 22px 54px rgba(0, 0, 0, .6);
}

.ask__title {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--ink);
}

.ask__body {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--dim);
  white-space: pre-line;
}

.ask__label {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dim);
}

.ask__field {
  width: 100%;
  box-sizing: border-box;
  padding: 9px 11px;
  font: inherit;
  font-size: 13px;
  color: var(--ink);
  background: var(--desk);
  border: 1px solid var(--edge);
  border-radius: var(--r);
  resize: vertical;
}

.ask__field:focus {
  outline: none;
  border-color: var(--s6);
}

/* The primary sits at the end, where the thumb is on a phone and where the
 * eye finishes on a desktop. */
.ask__row {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  flex-wrap: wrap;
  margin-top: 2px;
}

.ask__row .punch { min-width: 104px; text-align: center; }

.ask__yes { border-color: var(--s6); color: var(--ink); }

/* Destructive answers look destructive. Trash is recoverable and is not
 * marked; deleting for good is. */
.ask__yes--danger {
  border-color: #a3454f;
  color: #ffd9dd;
  background: #2a1419;
}

@media (max-width: 480px) {
  .ask__row { flex-direction: column-reverse; }
  .ask__row .punch { width: 100%; }
}

/* A punch that is in reads as in, the same as the layer punches do. */
.asset__actions .punch[data-punched="true"] {
  border-color: var(--s6);
  color: var(--ink);
  background: #12303a;
}

/* A switch reads beside its own words, not as a lonely box under a label. */
.ask__switch {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 13px;
  color: var(--ink);
}

.ask__check {
  width: 18px;
  height: 18px;
  accent-color: var(--s6);
  flex: none;
}

/* A select is a field like any other, minus the browser's own chrome. */
select.ask__field { appearance: none; padding-right: 28px; cursor: pointer; }

/* A locked punch is a different state from a held one: held is a finger on a
 * button, locked is something you walked away from and have to click off. */
.asset__actions .punch[data-locked="true"] {
  border-color: #f0b429;
  color: #ffe9b8;
  background: #35270c;
}
