/* Stream 1 console.
   Dark by default: this gets used in a dim booth at the back of a room, and a
   white screen there is genuinely blinding. */
:root {
  /* One insert row. Structure, not paint: the rack's height is counted in
     these, so a look must not touch it. */
  --ins-row:   19px;

  --bg:        #0d1014;
  --panel:     #151a20;
  --panel2:    #1b2129;
  --line:      #262e38;
  --line2:     #333d4a;
  --text:      #e6ebf2;
  --dim:       #8a95a3;
  --dimmer:    #5d6774;
  --accent:    #4ea3e8;
  /* The selected channel, taken from the logo — its purples run #7040f0 to
     #b040f0 and this is the middle of them. Deliberately not --accent: the
     blue is everywhere, and a selection that shares a colour with every
     button and link is not a selection you can find at a glance. */
  --pick:      #8a45f0;
  --pick-wash: rgba(138, 69, 240, .13);
  --pick-edge: rgba(138, 69, 240, .40);
  --accent2:   #2d7fc4;
  --good:      #46c27a;
  --warn:      #e8b84f;
  --hot:       #e8674f;
  --solo:      #e8c44f;
  --mute:      #e8674f;
  --radius:    7px;
  --strip-w:   92px;
  /* The meters are a canvas, which no stylesheet can reach, so their colours
     are handed to the drawing code through these. The values here are what
     was hardcoded in it: an unlit well and reference lines that read as a
     dim scratch across it. */
  --meter-bg:   #06090c;
  --meter-line: rgba(255, 255, 255, .13);
  /* The equaliser is a canvas too. Its grid was white at five per cent: a
     scratch on black, and nothing at all on paper. */
  --eq-grid:    rgba(255, 255, 255, .05);
  --eq-axis:    rgba(255, 255, 255, .15);
  --eq-curve:   #a06bff;
  --eq-label:   #4a5563;
  /* Behind an engaged HP, GATE or COMP. Was written into the rule, so on a
     light desk it stayed navy with purple text on it and could not be read. */
  --pill-on:    #1d3547;
  /* Surfaces, named rather than spelled out, because a change of look is
     mostly a change to these. They were seventeen near-identical darks
     scattered through the file, and a theme cannot reach those. The state
     colours above are deliberately left alone: a clipping meter means the
     same thing whatever the desk looks like. */
  --control:   #0a0e12;
  --well:      #0d1217;
  --well-deep: #06090c;
  --row-on:    #17222c;
  --panel3:    #1b222b;
}

* { box-sizing: border-box; }

html, body {
  margin: 0; height: 100%;
  background: var(--bg); color: var(--text);
  font: 13px/1.4 -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}
body { display: flex; flex-direction: column; overflow: hidden; }

button, select, input {
  font-family: inherit; font-size: inherit; color: inherit;
}
button { cursor: pointer; }

/* ---------------------------------------------------------------- topbar */
.topbar {
  display: flex; align-items: center; gap: 18px;
  padding: 9px 14px;
  background: linear-gradient(180deg, var(--panel3), var(--well));
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}
/* Wide enough for a church's name, capped so a long interface name underneath
   cannot push the rest of the header off to the right.
   
   The basis matters: with `auto` the box sized to whatever was left after the
   rest of the header, which was 166px, and "Afliacion Apostolic Church" needs
   208. A church's own name being cut short in its own header is a poor way to
   greet them. It still shrinks below this on a narrow window. */
.brand {
  display: flex; align-items: center; gap: 10px;
  /* The floor is the point: without one the rest of the header squeezes this
     down to whatever is left, which was 173px, and the name is cut off again.
     260 leaves 216 for the text once the badge and gap are taken out, which
     holds a name the length of "Afliacion Apostolic Church". */
  flex: 0 1 300px; min-width: 260px; max-width: 340px;
}
.brandtext { min-width: 0; overflow: hidden; flex: 1 1 auto; }
/* The logo, not a letterform. Cropped to its tile so it fills the badge:
   the file the app icon is built from carries a transparent margin, which is
   Apple's grid and would make this sit noticeably smaller than it should. */
.mark {
  width: 34px; height: 34px; border-radius: 8px;
  flex: 0 0 auto; display: block; object-fit: contain;
}
.brandtext { min-width: 0; }
/* The way to another church's desk, and to starting one. It was a button with
   a transparent border and a chevron the colour of punctuation, so it read as
   a label — Aziel asked for a feature that was already here and behind it. */
.projectname {
  display: flex; align-items: center; gap: 6px;
  background: var(--panel2); border: 1px solid var(--line2); border-radius: 5px;
  font-weight: 600; font-size: 14px; padding: 3px 8px; color: var(--text);
  max-width: 230px; cursor: pointer;
}
.projectname:hover { border-color: var(--accent); background: var(--well); }
.projectname #projectNameText {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.projectname .chev { color: var(--dim); font-size: 11px; flex: 0 0 auto; }
.projectname:hover .chev { color: var(--accent); }
/* One line. It used to wrap to three with a real interface running, which
   pushed the church's name above it into an ellipsis and made the whole
   corner look broken. Clipped with the full text on the hover instead. */
.sub {
  font-size: 10px; color: var(--dim); padding-left: 6px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 100%;
}
.sub .ok { color: var(--good); }
.sub .bad { color: var(--hot); }

/* loudness panel */
.loudness {
  flex: 1; display: flex; flex-direction: column; gap: 5px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 7px 12px; min-width: 320px;
}
.lufsrow { display: flex; align-items: center; gap: 14px; }
.lufsbig { display: flex; align-items: baseline; gap: 5px; min-width: 132px; flex: 0 0 auto; }
.lufsval { font-size: 26px; font-weight: 650; font-variant-numeric: tabular-nums; letter-spacing: -.5px; }
.lufsunit { font-size: 11px; color: var(--dim); }
.lufslabel { font-size: 10px; color: var(--dimmer); margin-left: 4px; }
.lufsbar {
  flex: 1; height: 9px; border-radius: 5px; position: relative;
  background: var(--control); border: 1px solid var(--line); overflow: hidden;
}
.lufsfill {
  position: absolute; left: 0; top: 0; bottom: 0; width: 0%;
  background: linear-gradient(90deg, #2f7a4f, var(--good));
  transition: width .08s linear, background .2s;
}
.lufsfill.over { background: linear-gradient(90deg, #9a6b28, var(--warn)); }
.lufsfill.hot  { background: linear-gradient(90deg, #a03a2a, var(--hot)); }
.lufstarget {
  position: absolute; top: -2px; bottom: -2px; width: 2px;
  background: var(--accent); z-index: 2; left: 50%;
  box-shadow: 0 0 5px var(--accent);
}
.lufssmall { display: flex; gap: 12px; }
.lufssmall div { text-align: right; }
.lufssmall b {
  display: block; font-size: 13px; font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.lufssmall span { font-size: 9px; color: var(--dimmer); text-transform: uppercase; letter-spacing: .5px; }
.targetrow { display: flex; align-items: center; gap: 10px; }
.advice {
  font-size: 11px; color: var(--dim); flex: 1; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.advice.good { color: var(--good); }
.advice.warn { color: var(--warn); }
.advice.hot  { color: var(--hot); }

.minisel, .minibtn {
  background: var(--panel2); border: 1px solid var(--line2);
  border-radius: 5px; padding: 3px 7px; font-size: 11px; color: var(--text);
}
.minibtn:hover, .minisel:hover { border-color: var(--accent); }

.widthctl {
  display: flex; align-items: center; gap: 5px; flex: 0 0 auto;
}
.widthctl .wlabel {
  font-size: 9px; text-transform: uppercase; letter-spacing: .5px;
  color: var(--dimmer); margin-right: 2px;
}
.widthctl button {
  width: 22px; height: 22px; border-radius: 5px; padding: 0;
  background: var(--panel2); border: 1px solid var(--line2);
  color: var(--text); font-size: 13px; line-height: 1;
}
.widthctl button:hover { border-color: var(--accent); }
.widthctl input[type=range] {
  width: 96px; height: 3px; -webkit-appearance: none; appearance: none;
  background: var(--line2); border-radius: 2px;
}
.widthctl input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none; width: 12px; height: 12px; border-radius: 50%;
  background: #6a7583; border: 1px solid #808c9b;
}
.widthctl input[type=range]:focus { outline: none; }

.topactions { display: flex; gap: 7px; }
.topbtn {
  background: var(--panel2); border: 1px solid var(--line2);
  border-radius: var(--radius); padding: 8px 14px; font-weight: 500;
}
.topbtn:hover { border-color: var(--accent); background: #212a34; }

/* ------------------------------------------------- the connection button
   Colour first, words second. The judgement has to survive being glanced at
   from across a room by somebody who is mid service and not reading. */
.linkbtn {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 11px 6px 9px;
}
.linkarcs { width: 19px; height: 15px; flex: 0 0 auto; }
.linkarcs path {
  fill: none; stroke: var(--dimmer); stroke-width: 1.9;
  stroke-linecap: round; transition: stroke .25s;
}
.linkarcs circle { fill: var(--dimmer); transition: fill .25s; }
.linklabel { font-size: 11px; color: var(--dim); letter-spacing: .2px; }

.linkbtn.good .a0, .linkbtn.good .a1, .linkbtn.good .a2 { stroke: var(--good); fill: var(--good); }
.linkbtn.good .a3 { stroke: var(--good); }
.linkbtn.good .linklabel { color: var(--good); }

.linkbtn.fair .a0, .linkbtn.fair .a1, .linkbtn.fair .a2 { stroke: var(--warn); fill: var(--warn); }
.linkbtn.fair .linklabel { color: var(--warn); }

.linkbtn.poor .a0, .linkbtn.poor .a1 { stroke: var(--hot); fill: var(--hot); }
.linkbtn.poor .linklabel { color: var(--hot); }
/* Trouble is the one state that should catch an eye that was not looking. */
.linkbtn.poor { border-color: var(--hot); }
.linkbtn.poor .linkarcs { animation: linkpulse 1.6s ease-in-out infinite; }
@keyframes linkpulse { 50% { opacity: .35; } }

.linkbtn.unknown .a0 { stroke: var(--dim); fill: var(--dim); }
.linkbtn.none { opacity: .55; }

.linkverdict {
  font-size: 15px; font-weight: 600; margin: 4px 0 10px;
  display: flex; align-items: center; gap: 8px;
}
.linkverdict .dot { width: 10px; height: 10px; border-radius: 50%; background: var(--dimmer); }
.linkverdict.good .dot { background: var(--good); }
.linkverdict.fair .dot { background: var(--warn); }
.linkverdict.poor .dot { background: var(--hot); }
.linkadvice {
  margin-top: 12px; padding: 10px 12px; border-radius: var(--radius);
  background: var(--panel2); border: 1px solid var(--line2);
  font-size: 12px; line-height: 1.55; color: var(--text);
}
.linkadvice b { color: var(--accent); }

/* A number, its name, and why it is worth knowing — the last of those is not
   decoration. "42 ms" means nothing to an operator on a Sunday morning. */
#linkNumbers .routerow {
  display: grid; grid-template-columns: 105px 66px 1fr;
  gap: 4px 10px; align-items: baseline; padding: 5px 0;
}
#linkNumbers .routerow + .routerow { border-top: 1px solid var(--line); }
#linkNumbers .rlabel { color: var(--text); font-weight: 500; }
#linkNumbers b { font-variant-numeric: tabular-nums; }
#linkNumbers .hint { font-size: 10.5px; line-height: 1.45; margin: 0; }
@media (max-width: 560px) {
  #linkNumbers .routerow { grid-template-columns: 1fr auto; }
  #linkNumbers .hint { grid-column: 1 / -1; }
}

/* ------------------------------------------------------------- scene bar */
.viewbar {
  display: flex; align-items: center; gap: 12px; flex: 0 0 auto;
  padding: 6px 14px; background: var(--well);
  border-bottom: 1px solid var(--line);
}
.scenebar {
  display: flex; gap: 7px; flex: 1; min-width: 0;
  overflow-x: auto; scrollbar-width: thin;
}
.scenechip {
  background: var(--panel2); border: 1px solid var(--line2);
  border-radius: 20px; padding: 5px 15px; font-size: 12px; white-space: nowrap;
}
.scenechip:hover { border-color: var(--accent); }
.scenechip.active {
  background: var(--accent2); border-color: var(--accent); color: #fff; font-weight: 500;
}

/* --------------------------------------------------------------- console */
.console { flex: 1; display: flex; min-height: 0; }

/* The one scroller on the desk. Channels first, then the master and its
   buses, so the whole mixer is a single row you walk along — which is how
   Logic reads, and the only layout that works unchanged on a phone. */
/* `display: flex` below beats the browser's own [hidden] rule, so say it
   explicitly — otherwise hiding the desk to show the church picker leaves
   both on screen at once. */
.strips[hidden] { display: none; }
.strips {
  flex: 1; display: flex; gap: 1px; align-items: stretch;
  /* auto rather than hidden: on a short window the buttons at the foot of a
     strip were simply cut off with no way to reach them. */
  overflow-x: auto; overflow-y: auto;
  padding: 8px; background: var(--control); scrollbar-width: thin;
}

/* Stretch, not flex-start.
 *
 * A strip's fader area is `flex: 1` with no height of its own, so it fills
 * whatever it is given. Given nothing to fill — which is what flex-start
 * means here — it grows to its content instead and the strip ends up a
 * thousand pixels tall in a six-hundred pixel window. That happened the
 * moment the master moved inside this scroller and is what "the strips are
 * super long" meant.
 *
 * min-height: 0 so a tall strip can still shrink to the scroller rather than
 * pushing it open; the flex default of min-height:auto refuses to. */
.chanarea {
  flex: 0 0 auto; display: flex; gap: 1px;
  align-items: stretch; min-height: 0;
}
/* Tall enough for everything on a strip, which is what decides whether the
   buttons at its foot can be reached at all.
   
   The container below scrolls when a strip will not fit, and that only works
   while a strip insists on being its full height — a strip squeezed to less
   than its contents clips them against its own `overflow: hidden` instead,
   and the container sees nothing to scroll because the strip claims to fit.
   At 250px that is exactly what happened: mute and solo were cut off with the
   equaliser open on a short window and there was no way to reach them.
   Aziel, 2026-10-14.
   
   So this has to stay at or above what a strip actually needs. If a control
   is ever added to one, this number goes up with it. */
.strip { min-height: 334px; }

/* Sits immediately after the last channel rather than at the right edge of
   the window — the same place Logic puts Stereo Out. The rule beneath it is
   the only thing marking the boundary, and it is enough. */
.master {
  flex: 0 0 auto; display: flex; gap: 10px;
  padding: 0 8px 0 10px; margin-left: 8px;
  border-left: 1px solid var(--line);
}
.mastergroups, .masterbuses { display: flex; gap: 1px; }
.mastergroups:empty { display: none; }

/* Group strips sit inline with the channels they contain, at the position of
   their first member, so a group reads as a folder rather than as something
   parked off to one side. */
.strip.isgroup {
  background: var(--panel3); border-color: var(--line2);
}
.strip.isgroup .snum { color: var(--dim); }
.strip.isgroup.collapsed { border-color: var(--accent2); }

.ghead { display: flex; align-items: center; gap: 3px; }
.gtri {
  background: none; border: none; padding: 0 2px; font-size: 10px;
  color: var(--dim); line-height: 1; flex: 0 0 auto;
}
.gtri:hover { color: var(--accent); }

/* A member strip carries its group's colour down the left edge, so it is
   obvious at a glance what belongs to what. */
.strip.member { border-left-width: 3px; }
.strip.lastmember { margin-right: 9px; }

/* Membership badges on a channel strip. */
.grouptags {
  display: flex; flex-wrap: wrap; gap: 2px; justify-content: center;
  min-height: 11px;
}
.gtag {
  font-size: 8px; font-weight: 700; line-height: 1;
  padding: 2px 4px; border-radius: 3px; color: #0d1014;
}

/* Assignment buttons in the channel editor. */
.grouppick { display: grid; grid-template-columns: repeat(2, 1fr); gap: 5px; }
.gpick {
  display: flex; align-items: center; gap: 6px;
  background: var(--well); border: 1px solid var(--line2);
  border-radius: 5px; padding: 5px 7px; font-size: 11px; text-align: left;
}
.gpick:hover { border-color: var(--accent); }
.gpick .gdot {
  width: 9px; height: 9px; border-radius: 2px; flex: 0 0 auto;
  background: #333d4a;
}
.gpick.on { background: #1d2b38; border-color: var(--accent2); color: #fff; }
.gpick .gname { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------------------------------------------------------------- strips */
.strip {
  flex: 0 0 var(--strip-w); width: var(--strip-w);
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 6px 5px;
  display: flex; flex-direction: column; gap: 5px;
  position: relative; overflow: hidden;
}
.strip.isbus { background: var(--panel2); flex-basis: 104px; width: 104px; }
/* After .isbus, so a selected bus is washed too rather than losing to it. */
.strip.selected {
  border-color: var(--pick);
  box-shadow: 0 0 0 1px var(--pick-edge) inset;
  background: linear-gradient(180deg, var(--pick-wash), transparent 62%),
              var(--panel);
}
.strip.selected.isbus {
  background: linear-gradient(180deg, var(--pick-wash), transparent 62%),
              var(--panel2);
}
.strip .colorbar { height: 3px; border-radius: 2px; flex: 0 0 auto; }
.strip .sname {
  background: transparent; border: none; text-align: center; width: 100%;
  font-size: 11px; font-weight: 600; padding: 2px 0; border-radius: 4px;
  text-overflow: ellipsis; overflow: hidden; white-space: nowrap;
}
.strip .sname:focus { outline: none; background: var(--well); }
.strip .snum { font-size: 9px; color: var(--dimmer); text-align: center; letter-spacing: .5px; }

/* The input badge is a button now. It has to keep reading as a label — it is
   information first — while being obviously pressable once the pointer is
   near it, which is what the underline on hover is for. */
/* The group's badge is the same two-line shape but not a button — it says
   how many channels this fader rides and which, and there is nothing to
   press. Same rules, or the row comes out twelve pixels short and every
   fader below it sits twelve pixels high. */
.strip button.snum.insrc,
.strip .snum.gnum {
  display: block; width: 100%; padding: 1px 0;
  background: none; border: none; cursor: pointer;
  font: inherit; font-size: 9px; letter-spacing: .5px;
  color: var(--dimmer);
}
/* A strip listening to the interface's own output. Amber, because it is not
   broken — somebody may be recording the room feed on purpose — but it must
   never be mistaken for a microphone. */
.strip button.snum.insrc .innum,
.strip .snum.gnum .innum { display: block; }
/* The interface's own name for the channel, under the number rather than
   instead of it. Clipped rather than wrapped: MIC/LINE/HIZ 1 is wider than a
   strip and a second line would push every fader down. */
.strip button.snum.insrc .inlbl,
.strip .snum.gnum .inlbl {
  display: block; font-size: 8px; line-height: 1.25; opacity: .72;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.strip button.snum.insrc.isreturn .inlbl { color: var(--warn); opacity: 1; }
.strip button.snum.insrc.isreturn .inlbl::after { content: " \21ba"; }
.strip button.snum.insrc:hover {
  color: var(--text); text-decoration: underline; text-underline-offset: 2px;
}

/* The channel number, in the corner the way Logic does it. Out of the flow so
   it cannot push the name or the meters around, and not clickable so it never
   steals a press meant for the strip. */
.strip { position: relative; }
/* The channel's own number, top left.
 *
 * It sits over the colour bar and the name field, so it needs to read against
 * both — a bare grey digit on a bright colour bar is legible at 10px and gone
 * at 8. The chip is what makes it survive being shrunk. */
.strip .chnum {
  position: absolute; top: 4px; left: 4px; z-index: 2;
  font-size: 10px; font-weight: 700; line-height: 1;
  color: var(--dim); font-variant-numeric: tabular-nums;
  background: rgba(6, 9, 12, .78); border-radius: 3px; padding: 2px 3px;
  pointer-events: none;
}
.strip.selected .chnum { color: var(--pick); }

.procrow { display: flex; gap: 3px; justify-content: center; }
.pill {
  flex: 1; font-size: 8px; text-transform: uppercase; letter-spacing: .3px;
  padding: 2px 0; border-radius: 3px; text-align: center;
  background: var(--well); border: 1px solid var(--line); color: var(--dimmer);
}
.pill.on { background: var(--pill-on); border-color: var(--accent2); color: var(--accent); }

.faderarea { flex: 1; display: flex; gap: 5px; min-height: 90px; }
.meterwrap {
  flex: 0 0 15px; background: var(--well-deep); border: 1px solid var(--line);
  border-radius: 3px; overflow: hidden; position: relative;
}
.meterwrap canvas { display: block; width: 100%; height: 100%; }
/* The numbered scale between meter and fader.
 *
 * Narrow strips lose it rather than squash it: at the smallest widths there
 * is no room for numbers and a cramped, unreadable column is worse than a
 * plain fader. `.tight` is set on the rack below 84px, where the button
 * labels already shorten to M and S. */
.faderscale {
  flex: 0 0 17px; position: relative; pointer-events: none;
  /* Left grey on purpose, at his call. These are a ruler — they are read
     while somebody is looking at something else, and a scale carrying the
     same colour as the things it measures competes with them. */
  font-size: 8px; line-height: 1; color: var(--dimmer);
  font-variant-numeric: tabular-nums;
}
/* Inset by half a cap, top and bottom, to match the thumb's real travel.
   Keep in step with the cap height in the fader rules below. */
.faderscale .fmarks {
  position: absolute; left: 0; right: 0; top: 10px; bottom: 10px;
}
.faderscale .fmark {
  position: absolute; right: 0; width: 100%;
  display: flex; align-items: center; gap: 2px;
  transform: translateY(-50%);
}
.faderscale .ftick { flex: 0 0 3px; height: 1px; background: var(--line); }
.faderscale .fnum  { flex: 1; text-align: right; }
.faderscale .fmark.unity .ftick { background: var(--dim); width: 5px; flex-basis: 5px; }
.faderscale .fmark.unity .fnum  { color: var(--text); font-weight: 600; }
.faderscale .fmark.gone { display: none; }
.faderscale.nofit { visibility: hidden; }
.tight .faderscale { display: none; }

/* The only part of a strip that gives when there is less room, which is right
   — everything else is a fixed control. The floor stops it giving all of it:
   a fader a few pixels tall cannot be mixed on, and a strip that has crushed
   its fader to nothing is one whose other controls are about to be clipped. */
.faderwrap {
  flex: 1; min-height: 96px;
  position: relative; display: flex; justify-content: center;
}
input[type=range].fader {
  -webkit-appearance: none; appearance: none;
  writing-mode: vertical-lr; direction: rtl;
  width: 26px; height: 100%; background: transparent; margin: 0;
}
/* A slot, not a groove: narrow, dark and recessed, so the cap sits in it. */
input[type=range].fader::-webkit-slider-runnable-track {
  width: 4px; border-radius: 2px;
  background: linear-gradient(90deg, var(--well-deep), var(--well) 55%, var(--well-deep));
  border: 1px solid var(--panel3);
  box-shadow: inset 0 0 3px rgba(0,0,0,.9);
}

/* The cap.
 *
 * A console fader cap, drawn rather than imitated: a slab with a brushed
 * highlight along its top edge, a darker underside, and one bright line
 * across the middle. That line is the working part — it is what you read
 * against the numbers beside it, and without it a cap is a blob you have to
 * guess the centre of. Everything else is there to make the line easy to
 * find: the shading gives it an edge to sit on, the shadow lifts it off the
 * slot so it does not merge into the meter behind.
 */
input[type=range].fader::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 30px; height: 19px; margin-left: -13px;
  border-radius: 4px;
  background:
    /* the reading line, dead centre — the brightest thing on the strip */
    linear-gradient(180deg, transparent 8px, #ffffff 8px, #ffffff 9px, transparent 9px),
    /* brushed metal across the cap */
    repeating-linear-gradient(90deg, rgba(255,255,255,.07) 0 1px, transparent 1px 3px),
    /* the body: a hard break at the centre, bright above, near-black below */
    linear-gradient(180deg, #9aa7b8 0%, #7c8896 40%, #232a33 43%, var(--well) 100%);
  border: 1px solid #a7b2c2;
  border-bottom-color: var(--well-deep);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.45),
    inset 0 -1px 0 rgba(0,0,0,.85),
    0 2px 6px rgba(0,0,0,.75);
}
input[type=range].fader:focus { outline: none; }
input[type=range].fader:focus::-webkit-slider-thumb,
input[type=range].fader:active::-webkit-slider-thumb {
  border-color: var(--accent);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.28),
    inset 0 -1px 0 rgba(0,0,0,.5),
    0 0 0 1px rgba(78,163,232,.35),
    0 2px 6px rgba(0,0,0,.6);
}

/* The gain readout is an input, but should read as a label until you touch it
   — a strip covered in boxes is harder to scan than one covered in numbers. */
.gainval {
  display: block; width: 100%;
  text-align: center; font-size: 10px; font-variant-numeric: tabular-nums;
  color: var(--dim); padding: 1px 0;
  background: transparent; border: 1px solid transparent; border-radius: 4px;
  font-family: inherit; cursor: text;
}
.gainval:hover { border-color: var(--line2); color: var(--text); }
.gainval:focus {
  outline: none; background: var(--well);
  border-color: var(--accent); color: var(--text);
}
/* There for its height, not to be seen or touched. visibility rather than
   display so the box stays and the row still measures. */
.ghost { visibility: hidden; pointer-events: none; }

/* A group is a DCA: it rides faders and carries no audio, so where every
   other strip has an Output this says what it is instead. */
.dcatag { cursor: help; opacity: .72; }
/* The group's own two-line badge: how many channels, then which. */
.gnum { cursor: default; }
.gnum .inlbl { font-variant-numeric: tabular-nums; }
.dcatag .outname { letter-spacing: .6px; }

.panrow { display: flex; align-items: center; justify-content: center; gap: 4px; }
.panrow label { font-size: 9px; color: var(--dimmer); }

/* ------------------------------------------------------------ the pan dial
   Driven by how far you drag, not by how wide the strip is — so it is the
   same to set at 64 px as at 180. See makePanDial() for why that matters. */
.pandial {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 2px 4px 1px; border-radius: 7px; border: 1px solid transparent;
  background: none; cursor: ns-resize; touch-action: none;
  -webkit-user-select: none; user-select: none;
}
.pandial:hover { background: var(--panel3); }
.pandial:focus-visible { outline: none; border-color: var(--pick); }
/* Bigger than the control strictly needs, because it is carrying the mark as
   well as the value — at thirty-four the disc, the scribed ring and the dot
   were all fighting for the same few pixels.
   
   Sized to the strip rather than fixed: the strips narrow to 64px on the
   width control, and a fixed 56 would have burst out of them. */
/* Fixed, not a percentage. width:100% inside .pandial — a flex column with
   no width of its own — resolved against nothing useful and blew the strip
   out until the whole desk scaled down to fit. 52 is chosen to still sit
   inside the narrowest strip the width control allows, which is 64. */
.parcs { width: 52px; height: 52px; display: block; }
.ptrack {
  fill: none; stroke: var(--line2); stroke-width: 2.2; stroke-linecap: round;
}
.pfill {
  fill: none; stroke: var(--pick); stroke-width: 2.4; stroke-linecap: round;
}
.ptop { stroke: var(--dimmer); stroke-width: 1.5; stroke-linecap: round; }
/* The disc, the scribed ring and the dot — the logo's own parts. One per
   strip rather than dozens per panel, which is what makes it a mark rather
   than wallpaper. */
.pbody   { stroke: rgba(0, 0, 0, .55); stroke-width: 1; }
.pscribe { fill: none; stroke: rgba(0, 0, 0, .5); stroke-width: .9; }
.pdot    { fill: #fff; }
.pnub { stroke: #fff; stroke-width: 1.8; stroke-linecap: round; }
.pandial:hover .pnub, .pandial:focus-visible .pnub { stroke: #fff; }
.pval {
  font-size: 9px; font-variant-numeric: tabular-nums; letter-spacing: .2px;
  color: var(--pick); font-weight: 600; line-height: 1;
}
/* Dead centre is the resting state and should look like one, not like a
   setting somebody chose. */
.pandial.centred .pval { color: var(--dimmer); font-weight: 500; }
.pandial.centred .pnub { stroke: rgba(255, 255, 255, .75); }
.pandial.centred .pdot { fill: rgba(255, 255, 255, .55); }

/* A stereo pair balances rather than pans, and the two are different jobs. */
.pandial.balance .pfill { stroke: var(--solo); }
.pandial.balance .pval  { color: var(--solo); }

/* Narrow strips: keep the dial, drop it a size. It is the control that needed
   fixing at this width, so it is the last thing that should shrink away. */
.tight .parcs { width: 28px; height: 28px; }
.tight .pval { font-size: 8px; }

.btnrow { display: flex; gap: 3px; flex: 0 0 auto; }
.btnrow .sbtn { flex: 1; }
.sbtn {
  padding: 5px 0; font-size: 10px; font-weight: 600;
  min-width: 0; overflow: hidden; white-space: nowrap;
  background: var(--panel2); border: 1px solid var(--line2);
  border-radius: 4px; letter-spacing: .3px;
}
.sbtn.mute.on { background: var(--mute); border-color: var(--mute); color: #1a0d0a; }
.sbtn.solo.on { background: var(--solo); border-color: var(--solo); color: #1a1505; }
.sbtn.edit { flex: 0 0 auto; width: 100%; }
.sbtn.edit:hover { border-color: var(--accent); }

/* ---------------------------------------------------------------- detail */
.detail {
  flex: 0 0 auto; background: var(--panel); border-top: 1px solid var(--line);
  max-height: 44vh; overflow-y: auto;
}
.detail-empty { padding: 14px; text-align: center; color: var(--dimmer); font-size: 12px; }
.detail-head {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 14px; border-bottom: 1px solid var(--line); position: sticky; top: 0;
  background: var(--panel); z-index: 3;
}
.dot { width: 11px; height: 11px; border-radius: 50%; flex: 0 0 auto; }
.dname {
  background: transparent; border: 1px solid transparent; border-radius: 5px;
  font-weight: 600; font-size: 15px; padding: 3px 7px; width: 190px;
}
.dname:hover { border-color: var(--line2); }
.dname:focus { outline: none; border-color: var(--accent); background: var(--well); }

.modules {
  display: grid; gap: 10px; padding: 12px 14px;
  grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
  align-items: start;
}
.module {
  background: var(--panel2); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 9px 11px;
}
.module.wide { grid-column: span 2; min-width: 340px; }
@media (max-width: 900px) { .module.wide { grid-column: span 1; min-width: 0; } }
.mhead {
  display: flex; align-items: center; justify-content: space-between;
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .6px; color: var(--dim); margin-bottom: 9px;
}
.tog {
  background: var(--well); border: 1px solid var(--line2); border-radius: 4px;
  padding: 2px 10px; font-size: 10px; text-transform: uppercase; color: var(--dimmer);
}
.tog.on { background: var(--good); border-color: var(--good); color: #04150c; font-weight: 600; }

.knobs { display: grid; grid-template-columns: repeat(auto-fit, minmax(62px, 1fr)); gap: 8px; }
.knob { text-align: center; }
.knob .kname {
  font-size: 9px; color: var(--dimmer); text-transform: uppercase;
  letter-spacing: .4px; margin-bottom: 3px;
}
.knob .kval {
  font-size: 11px; font-variant-numeric: tabular-nums; color: var(--text);
  margin-top: 2px;
}
.knob input {
  width: 100%; height: 3px; -webkit-appearance: none; appearance: none;
  background: var(--line); border-radius: 2px;
}
.knob input::-webkit-slider-thumb {
  -webkit-appearance: none; width: 13px; height: 13px; border-radius: 50%;
  background: #6a7583; border: 1px solid #808c9b;
}
.knob input:focus { outline: none; }
.knob input:focus::-webkit-slider-thumb { background: var(--accent); border-color: var(--accent); }

.grmeter {
  height: 5px; background: var(--well-deep); border: 1px solid var(--line);
  border-radius: 3px; overflow: hidden; margin-bottom: 8px;
}
.grfill {
  height: 100%; width: 0%; background: linear-gradient(90deg, var(--warn), var(--hot));
  transition: width .06s linear;
}

/* The EQ takes the whole width: it is the thing you spend the most time in. */
.module.eqmodule { grid-column: 1 / -1; }
.eqtools { display: flex; align-items: center; gap: 6px; }
#eqCanvas {
  /* Set by the grip below it and remembered between sessions. 300px is what
     it was before anybody could change it. */
  width: 100%; height: var(--eq-height, 300px); display: block;
  background: linear-gradient(180deg, var(--well-deep), var(--well-deep));
  border: 1px solid var(--line);
  border-radius: 6px; touch-action: none; cursor: crosshair;
}
/* The handle for making the curve taller. Deliberately a full-width strip
   rather than a corner: this is grabbed in a dark room, often in a hurry. */
.eqgrip {
  height: 10px; margin: 2px 0 0; cursor: ns-resize;
  border-radius: 0 0 6px 6px;
  background: linear-gradient(180deg, var(--line), transparent);
}
.eqgrip:hover { background: linear-gradient(180deg, var(--dim), transparent); }

/* In the panel the desk already says which channel and which church, right
   above it. In a window of its own there is nothing else on screen at all,
   and two of these open at once is how somebody edits the wrong church. */
.eqwhere { display: none; }
body.eqpopped .eqwhere {
  display: inline; color: var(--dim); font-size: 11px; letter-spacing: .2px;
}

/* The equaliser once it is in a window of its own: it should fill it. */
body.eqpopped { margin: 0; padding: 10px; background: var(--bg); }
body.eqpopped #modEQ { height: calc(100vh - 20px); display: flex;
                       flex-direction: column; }
body.eqpopped #eqCanvas { flex: 1 1 auto; height: auto; min-height: 120px; }
body.eqpopped .eqgrip { display: none; }

.eqhint {
  font-size: 10px; color: var(--dimmer); padding: 6px 2px 8px;
  text-align: center; letter-spacing: .2px;
}
.eqbands {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: 6px;
}
.eqband {
  background: var(--well); border: 1px solid var(--line);
  border-radius: 5px; padding: 6px 7px;
}
.eqband.active { border-color: var(--line2); }
.eqband.soloed { border-color: var(--solo); box-shadow: 0 0 0 1px var(--solo) inset; }
.eqband .bhead {
  display: flex; align-items: center; gap: 5px;
  font-size: 9px; color: var(--dimmer); text-transform: uppercase; margin-bottom: 5px;
}
.eqband .bdot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
.eqband .bnum { flex: 1; font-weight: 600; }
.eqband .bmini {
  background: var(--control); border: 1px solid var(--line2); border-radius: 3px;
  font-size: 9px; padding: 1px 4px; color: var(--dim);
}
.eqband .bmini.on { background: var(--solo); border-color: var(--solo); color: #1a1505; }
.eqband select {
  width: 100%; background: var(--control); border: 1px solid var(--line2);
  border-radius: 4px; font-size: 10px; padding: 2px 4px; margin-bottom: 4px;
}
.eqband select:focus { outline: none; border-color: var(--accent); }
.eqband .brow {
  display: flex; justify-content: space-between; font-size: 10px;
  font-variant-numeric: tabular-nums; padding: 1px 0; color: var(--dim);
}
.eqband .brow b { color: var(--text); font-weight: 500; }

/* A number you can type, that still reads as a value rather than a form
   field until you go near it. Same idea as the fader readouts. */
.eqband .brow .bval {
  width: 74px; text-align: right;
  background: none; border: 1px solid transparent; border-radius: 4px;
  padding: 1px 4px; margin: -1px -4px;
  font: inherit; font-variant-numeric: tabular-nums;
  color: var(--text); font-weight: 500;
}
.eqband .brow .bval:hover { border-color: var(--line, #2a3340); }
.eqband .brow .bval:focus {
  outline: none; border-color: var(--accent, #4ea3e8);
  background: rgba(0,0,0,.35); color: #fff;
}
.eqband.off { opacity: .45; }

/* Where a strip goes. Quiet until it is not the stream, because the ordinary
   case is the one nobody needs reminding of. */
.outsel {
  display: flex; align-items: center; justify-content: center; gap: 3px;
  width: 100%; margin-top: 3px; padding: 2px 3px;
  background: rgba(78,163,232,.10); border: 1px solid rgba(78,163,232,.30);
  border-radius: 4px; font-size: 9px; color: var(--accent, #4ea3e8);
  font-family: inherit; cursor: pointer; overflow: hidden;
}
.outsel:hover { background: rgba(78,163,232,.20); }
.outsel .outarrow { opacity: .7; flex: 0 0 auto; }
.outsel .outname  { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.outsel.none {
  background: rgba(255,255,255,.04); border-color: var(--line);
  color: var(--dimmer);
}
.tight .outsel .outarrow { display: none; }

/* A stereo pair balances where a mono channel pans. Marked, because the
   control looks identical and does something different. */
.panrow.balance label { color: var(--accent, #4ea3e8); font-weight: 600; }

/* ---------------------------------------------------------------- modals */
.modal {
  position: fixed; inset: 0; background: rgba(4,7,10,.72);
  display: grid; place-items: center; z-index: 50; padding: 18px;
  backdrop-filter: blur(3px);
}
.modal[hidden] { display: none; }
.sheet {
  background: var(--panel); border: 1px solid var(--line2);
  border-radius: 11px; padding: 20px; width: min(520px, 100%);
  max-height: 86vh; overflow-y: auto;
  box-shadow: 0 18px 50px rgba(0,0,0,.6);
}
.sheet h2 { margin: 0 0 6px; font-size: 17px; }
.hint { color: var(--dim); font-size: 12px; margin: 0 0 14px; line-height: 1.5; }
.field { margin-bottom: 13px; }
.field.row { display: flex; gap: 11px; }
.field.row > div { flex: 1; }
.field label {
  display: flex; align-items: center; justify-content: space-between;
  font-size: 10px; text-transform: uppercase;
  letter-spacing: .5px; color: var(--dim); margin-bottom: 5px;
}
.field label .minibtn { text-transform: none; letter-spacing: 0; }
.field select, .sceneform input, #newSceneName {
  width: 100%; background: var(--panel2); border: 1px solid var(--line2);
  border-radius: 6px; padding: 8px 10px;
}
.field select:focus, .sceneform input:focus { outline: none; border-color: var(--accent); }
.sceneform { display: flex; gap: 8px; margin-bottom: 12px; }
.sceneform input { flex: 1; }
.fadeopt { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; font-size: 12px; }
.fadeopt label { color: var(--dim); white-space: nowrap; }
.fadeopt input { flex: 1; }
.fadeopt span { font-variant-numeric: tabular-nums; color: var(--dim); min-width: 58px; }
.scenelist { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.scenerow {
  display: flex; align-items: center; gap: 9px;
  background: var(--panel2); border: 1px solid var(--line);
  border-radius: 6px; padding: 8px 11px;
}
.scenerow.active { border-color: var(--accent); }
.scenerow .nm { flex: 1; font-weight: 500; }
.modalactions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
button.primary {
  background: var(--accent2); border: 1px solid var(--accent);
  border-radius: 6px; padding: 8px 15px; font-weight: 600; color: #fff;
}
button.primary:hover { background: var(--accent); }
/* Any unclassed button in a sheet: without this they fall back to the browser
   default light background while inheriting light text, and vanish.
   
   `button:not([class])` rather than a list of places buttons happen to be.
   The list was the bug: it had to be extended every time a sheet gained a
   button, and the failure for forgetting is a white bar with invisible text,
   which is what happened the next time one was added. Buttons with a class
   of their own are untouched and style themselves. */
/* It happened again on 2026-10-05, exactly as the paragraph above predicted:
   the device buttons moved out of .sheet and into the settings drawer, fell
   off this list, and rendered as pale bars with near-invisible text. A plain
   `button:not([class])` would end this for good, but it would also catch the
   width buttons in the top bar and the few others that are sized by their
   own container, so the list stays — and so does this note, since the next
   new panel will do it a fourth time. */
.sheet button:not([class]), .drawerbody button:not([class]),
.modalactions button:not(.primary), .closebtn, .scenerow button,
.growactions button {
  background: var(--panel2); border: 1px solid var(--line2);
  border-radius: 6px; padding: 8px 15px; color: var(--text);
}
.growactions button { padding: 5px 12px; font-size: 11px; }
.growactions button:hover { border-color: var(--accent); }
.scenerow button { padding: 4px 11px; font-size: 11px; }
.closebtn { width: 100%; margin-top: 15px; }
.routing {
  font-size: 11px; color: var(--dim); background: var(--well);
  border: 1px solid var(--line); border-radius: 6px;
  padding: 10px 12px; margin-bottom: 13px; line-height: 1.7;
}
.routing b { color: var(--text); font-weight: 600; }
.error {
  background: #2d1614; border: 1px solid #7a3228; color: #f0a99e;
  border-radius: 6px; padding: 9px 12px; font-size: 12px; margin-top: 11px;
}

.toast {
  position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%);
  background: var(--panel2); border: 1px solid var(--accent);
  border-radius: 7px; padding: 10px 20px; z-index: 90; font-size: 13px;
  box-shadow: 0 8px 26px rgba(0,0,0,.5);
}
.toast[hidden] { display: none; }

/* The diagnostics log. Dense on purpose — it is read by scanning. */
.difflist { max-height: 38vh; overflow-y: auto; margin: 12px 0; }
.diagrow {
  display: flex; gap: 10px; align-items: baseline;
  padding: 3px 6px; font-size: 11px; border-bottom: 1px solid var(--line);
}
.diagtime { color: var(--dimmer); font-variant-numeric: tabular-nums; flex: 0 0 62px; }
.diagkind { color: var(--warn); flex: 0 0 auto; }
.diagdetail { color: var(--dim); flex: 1; overflow: hidden; text-overflow: ellipsis; }

/* Who can mix what.
   Assigned and not-assigned have to be different at a glance and not by a
   shade of grey — getting it wrong hands somebody a church that is not
   theirs. The tick carries the state; colour only reinforces it. */
.adminrow { flex-wrap: wrap; }
.assignrow {
  flex: 1 0 100%; display: flex; align-items: center; gap: 10px;
  margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line);
}
.assignlabel { font-size: 11px; color: var(--dim); flex: 0 0 auto; }
.assignbox { display: flex; flex-wrap: wrap; gap: 6px; }
.assigntag {
  display: inline-flex; align-items: center; gap: 5px;
  background: transparent; border: 1px dashed var(--line2); color: var(--dimmer);
  border-radius: 6px; padding: 4px 10px; font-size: 11px;
}
.assigntag .tick { font-weight: 700; opacity: .6; }
.assigntag:hover { border-color: var(--accent); color: var(--text); }
.assigntag.on {
  background: var(--accent2); border: 1px solid var(--accent); color: #fff;
}
.assigntag.on .tick { color: var(--good); opacity: 1; }

/* Account actions, on their own line. One of these removes a person. */
.personacts {
  flex: 1 0 100%; display: flex; gap: 6px; margin-top: 8px;
  justify-content: flex-end;
}

/* The install line. Monospace and wrapping, because it is long, and somebody
   is going to read it before they run it — which they should. */
.handout.cmd {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px; letter-spacing: 0; line-height: 1.5;
  word-break: break-all; text-align: left; padding: 10px 12px;
}
#handoutInstall { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 12px; }
#handoutInstall[hidden] { display: none; }
#handoutCopyCmd { width: 100%; margin-top: 8px; }
.sheet button:not([class]):hover,
.drawerbody button:not([class]):hover { border-color: var(--accent); }

@media (max-width: 700px) {
  .widthctl .wlabel { display: none; }
  .widthctl input[type=range] { width: 60px; }
}
@media (max-width: 940px) {
  .topbar { flex-wrap: wrap; gap: 10px; }
  /* flex-basis, not width: the base rule sets flex:1, whose basis of 0% wins
     over any width on a flex item, so "width: 100%" silently did nothing and
     the panel kept sharing the row instead of wrapping below it. */
  .loudness { order: 3; flex: 1 0 100%; min-width: 0; }
  .brand { min-width: 0; flex: 1 1 auto; }
  .topactions { order: 2; }
}
@media (max-width: 760px) {
  .detail { max-height: 52vh; }
}

/* ------------------------------------------------------------- on a phone
 *
 * The desk itself needs no special case any more: channels and master share
 * one scroller at every width, so a phone gets the same layout a laptop does,
 * just with less of it on screen at once. What is left here is the furniture
 * around the desk, which on a 375px screen was eating the space before the
 * first fader. */
@media (max-width: 760px) {
  .strips { padding: 6px; }

  /* The header was taking four hundred pixels before the first fader. */
  .topbar { gap: 8px; padding: 6px 10px; }
  .loudness { padding: 6px 10px; }
  .lufsval { font-size: 22px; }
  .lufsbig { min-width: 0; }
  /* The row of panels ran off the side mid-word. Let it scroll instead of
     being clipped — a button you cannot see is a button you do not have. */
  .topactions {
    overflow-x: auto; scrollbar-width: none; max-width: 100%;
    -webkit-overflow-scrolling: touch;
  }
  .topactions::-webkit-scrollbar { display: none; }
  .topactions button { flex: 0 0 auto; }
  /* Strip width is a laptop luxury; on a phone it is the thing between you
     and the next channel. */
  .widthctl { display: none; }
}


/* ---- group editor in the modal ---- */
.grow {
  background: var(--panel2); border: 1px solid var(--line);
  border-radius: 7px; padding: 10px 12px; margin-bottom: 9px;
}
.grow.empty { border-style: dashed; }
.growhead { display: flex; align-items: center; gap: 8px; margin-bottom: 9px; }
.growhead input.gname {
  flex: 1; background: var(--control); border: 1px solid var(--line2);
  border-radius: 5px; padding: 5px 8px; font-weight: 600;
}
.growhead input.gname:focus { outline: none; border-color: var(--accent); }
.swatches { display: flex; gap: 3px; }
.swatch {
  width: 16px; height: 16px; border-radius: 4px; border: 2px solid transparent;
  padding: 0; cursor: pointer;
}
.swatch.on { border-color: #fff; }
/* The paste menu, dropped from the paste button like the preset picker
   beside it. A list rather than a row of pills: Aziel asked for it twice,
   the second time pointing at "apply preset" as the thing it should feel
   like. It lives at the end of the page and is placed by hand, because the
   panel it belongs to has a sticky header that scrolls sideways. */
.pastemenu {
  position: absolute; z-index: 60; min-width: 184px;
  background: var(--panel); border: 1px solid var(--line2);
  border-radius: 8px; padding: 5px;
  box-shadow: 0 12px 30px rgba(0, 0, 0, .55);
}
.pastemenu[hidden] { display: none !important; }
.pastemenu .pmhead {
  font-size: 10px; color: var(--dim); padding: 5px 10px 7px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 260px;
}
.pastemenu .pmrow {
  display: flex; align-items: center; gap: 7px; width: 100%;
  background: none; border: 0; border-radius: 5px;
  padding: 6px 10px; font-size: 12px; color: var(--text); text-align: left;
}
.pastemenu .pmrow:hover:not(:disabled) { background: var(--panel2); }
.pastemenu .pmrow:disabled { opacity: .35; }
/* The tick keeps its space whether or not it is showing, so the names do
   not shuffle sideways as things are ticked. */
.pastemenu .pmrow .tick {
  width: 11px; flex: 0 0 11px; color: var(--pick); font-weight: 700;
}
.pastemenu .pmrow.pmall .pmname { font-weight: 600; }
.pastemenu .pmsep { height: 1px; background: var(--line); margin: 4px 6px; }
.pastemenu .pmgo {
  width: 100%; margin-top: 2px;
  background: var(--pick); border: 1px solid var(--pick-edge, var(--pick));
  border-radius: 6px; padding: 7px 10px;
  font-size: 12px; font-weight: 600; color: #fff;
}
.pastemenu .pmgo:disabled {
  background: transparent; border-color: var(--line2); color: var(--dim);
  font-weight: 400;
}

.chanpick { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 9px; }
.cpick {
  background: var(--control); border: 1px solid var(--line2); border-radius: 5px;
  padding: 4px 9px; font-size: 11px;
}
.cpick:hover { border-color: var(--accent); }
.cpick.on { background: var(--accent2); border-color: var(--accent); color: #fff; }
.cpick.taken { opacity: .4; }
.growactions { display: flex; gap: 7px; align-items: center; }
.growactions .count { flex: 1; font-size: 11px; color: var(--dim); }


/* ---- effects editor ---- */
.sheet.wide { width: min(660px, 100%); }
.fxrow {
  background: var(--panel2); border: 1px solid var(--line);
  border-radius: 7px; padding: 11px 13px; margin-bottom: 10px;
}
.fxrow.active { border-color: var(--accent2); }
.fxhead { display: flex; align-items: center; gap: 9px; margin-bottom: 10px; }
.fxhead .busname {
  font-weight: 600; font-size: 13px; min-width: 88px;
}
.fxhead select {
  background: var(--control); border: 1px solid var(--line2);
  border-radius: 5px; padding: 5px 9px; font-size: 12px;
}
.fxhead select:focus { outline: none; border-color: var(--accent); }
.fxhead .sendcount { flex: 1; text-align: right; font-size: 11px; color: var(--dim); }
.fxparams {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: 10px;
}
.fxparams:empty { display: none; }
.fxsends {
  display: flex; flex-wrap: wrap; gap: 4px;
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line);
}
.fxsend {
  background: var(--control); border: 1px solid var(--line2); border-radius: 5px;
  padding: 4px 9px; font-size: 11px;
}
.fxsend:hover { border-color: var(--accent); }
.fxsend.on { background: var(--accent2); border-color: var(--accent); color: #fff; }
.fxsend .amt { opacity: .75; margin-left: 4px; font-variant-numeric: tabular-nums; }


/* ---- solo / cue ---- */
.solobanner {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 14px; flex: 0 0 auto;
  background: linear-gradient(90deg, #3a3112, #2a2410);
  border-bottom: 1px solid var(--solo);
  font-size: 12px; color: #f0dfa0;
}
.solobanner[hidden] { display: none; }

/* Watching somebody else's desk. Blue rather than red: nothing is wrong, you
   simply are not the one holding it. */
.watchbanner {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 14px; flex: 0 0 auto;
  background: linear-gradient(90deg, #12283a, #0f1e2a);
  border-bottom: 1px solid var(--accent2);
  font-size: 12px; color: #b8d8f0;
}
.watchbanner[hidden] { display: none; }

/* And the desk itself goes untouchable, rather than accepting moves it is
   going to throw away. The top bar stays live on purpose — listening,
   switching church and reading the connection are all still yours. */
body.watching .console,
body.watching .detail,
body.watching .scenebar { pointer-events: none; }
body.watching .console,
body.watching .detail { opacity: .72; filter: saturate(.7); }

/* A desk that has given up. Louder than every other banner on purpose: it is
   the one state where nothing at all is happening and nothing on the console
   would otherwise say so. One sat unnoticed for nine hours. */
.deafbanner.gaveup {
  background: linear-gradient(90deg, #4a1410, #33100c);
  border-bottom-width: 2px;
  color: #ffd9d2; font-weight: 600;
  animation: gaveuppulse 1.9s ease-in-out infinite;
}
@keyframes gaveuppulse { 50% { background: linear-gradient(90deg, #6b1a14, #3d130e); } }
.deafbanner.gaveup .dotpulse { background: var(--hot); }
.deafbanner.gaveup button {
  background: var(--hot); border-color: var(--hot); color: #2a0b08;
  font-weight: 700;
}

/* Silence on every input. Red rather than amber: a desk that cannot hear the
   board is not a warning, it is off the air. */
.deafbanner {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 14px; flex: 0 0 auto;
  background: linear-gradient(90deg, #45191a, #2f1214);
  border-bottom: 1px solid var(--bad, #e8674f);
  font-size: 12px; color: #f3c9c2;
}
.deafbanner[hidden] { display: none; }
.deafbanner span:nth-child(2) { flex: 1; }
.deafbanner .dotpulse { background: var(--bad, #e8674f); }
.deafbanner button {
  background: var(--bad, #e8674f); border: none; border-radius: 5px;
  padding: 5px 13px; font-size: 11px; font-weight: 600; color: #2a0f0c;
}
.deafbanner button[hidden] { display: none; }

/* The desk not matching the hardware. A note inside the Audio panel rather
   than a bar across the top of the desk: it is only sometimes true, and a
   permanent amber stripe above the faders was the wrong way to say it. */
.fitnote {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: 7px;
  background: #2f2712; border: 1px solid var(--warn, #e8b84f);
  font-size: 11px; line-height: 1.45; color: #f3e4c2;
}
/* The effect window's warning that its mix is at 0%. The same amber note as
   .fitnote above, which carries its own background, so it reads the same in
   every look — --warn is defined once and not repainted per look. No display
   on the class itself: this file has no global [hidden] rule, so a class that
   set display would beat the hidden attribute and the warning could never go
   away. Hence the explicit [hidden] rule, as .modal and .toast have. */
.fxwarn {
  margin: 0 0 12px; padding: 7px 10px; border-radius: 7px;
  background: #2f2712; border: 1px solid var(--warn, #e8b84f);
  font-size: 11px; line-height: 1.45; color: #f3e4c2;
}
.fxwarn[hidden] { display: none; }
.fitnote span { flex: 1; }
.fitnote button {
  background: var(--warn, #e8b84f); border: none; border-radius: 5px;
  padding: 5px 11px; font-size: 11px; font-weight: 600; color: #2a2208;
  cursor: pointer; flex: 0 0 auto; white-space: nowrap;
}
.solobanner span:nth-child(2) { flex: 1; }
.solobanner button {
  background: var(--solo); border: none; border-radius: 5px;
  padding: 5px 13px; font-size: 11px; font-weight: 600; color: #1a1505;
}
.dotpulse {
  width: 9px; height: 9px; border-radius: 50%; background: var(--solo);
  animation: pulse 1.1s ease-in-out infinite; flex: 0 0 auto;
}
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

.cuesection { display: flex; padding-left: 10px; border-left: 1px solid var(--line); }
.strip.iscue { background: #1b1a14; border-color: #4a4530; }
.strip.iscue .colorbar { background: var(--solo); }
.cuemode {
  width: 100%; background: var(--control); border: 1px solid var(--line2);
  border-radius: 4px; font-size: 10px; padding: 3px 4px; color: var(--text);
}
.cuemode:focus { outline: none; border-color: var(--accent); }
.cuemode.danger { border-color: var(--hot); color: #f0a99e; }
.cuewarn {
  font-size: 8px; line-height: 1.25; color: var(--hot); text-align: center;
}
.tinyhint {
  font-size: 10px; color: var(--dimmer); line-height: 1.45; margin: 7px 0 0;
}


/* ---- add / remove channels ---- */
.addstrip {
  flex: 0 0 auto; width: 54px; align-self: stretch; margin-left: 9px;
  background: transparent; border: 1px dashed var(--line2);
  border-radius: var(--radius); color: var(--dim);
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 5px; font-size: 10px; line-height: 1.3;
  text-align: center; padding: 6px 4px;
}
.addstrip:hover { border-color: var(--accent); color: var(--accent); }
.addstrip .plus { font-size: 20px; line-height: 1; }
.addstrip[disabled] { opacity: .35; cursor: default; }
.addstrip[disabled]:hover { border-color: var(--line2); color: var(--dim); }

.removech {
  background: var(--panel2); border: 1px solid var(--line2);
  border-radius: 5px; padding: 3px 9px; font-size: 11px; color: var(--dim);
}
.removech:hover { border-color: var(--hot); color: var(--hot); }


/* ---- fader link ("G") ----
   Sits on the group-badge row rather than alongside MUTE and SOLO: three
   buttons do not fit across a narrow strip, and it belongs with the group
   information anyway. */
.glink {
  font-size: 9px; font-weight: 700; line-height: 1;
  padding: 2px 6px; border-radius: 3px;
  background: var(--accent2); border: 1px solid var(--accent); color: #fff;
  flex: 0 0 auto;
}
/* Defeated reads as "off": the link is not acting on this channel. */
.glink.defeat {
  background: var(--panel2); border-color: var(--line2); color: var(--dimmer);
  text-decoration: line-through;
}
.linkopts { display: flex; gap: 12px; margin: 8px 0 2px; }
.linkopt { display: flex; align-items: center; gap: 5px; font-size: 11px; color: var(--dim); }
.linkopt input { width: 13px; height: 13px; accent-color: var(--accent); }


/* Below about 84px there is no room for the words. */
.strips.tight .sbtn.mute { font-size: 0; }
.strips.tight .sbtn.mute::after { content: "M"; font-size: 10px; }
.strips.tight .sbtn.solo { font-size: 0; }
.strips.tight .sbtn.solo::after { content: "S"; font-size: 10px; }
.strips.tight .sname { font-size: 10px; }
.strips.tight .snum { font-size: 8px; }
/* Shrunk, not hidden. It was going to 8px and disappearing into the colour
   bar behind it; the chip above and one more pixel of size keep it there. */
.strips.tight .chnum { font-size: 9px; top: 3px; left: 3px; padding: 1px 3px; }


/* ---- unlock ---- */
.authmodal { z-index: 200; background: rgba(4, 7, 10, .93); }
.authsheet { width: min(360px, 100%); text-align: center; }
.authlockup {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  margin: 2px auto 16px;
}
.authmark {
  width: 56px; height: 56px; border-radius: 13px;
  display: block; object-fit: contain;
}
/* On its own at the top of a sheet (sign in, an invitation) rather than in
   the lockup, where the flex column already centres it. */
.authsheet > .authmark { margin: 0 auto 10px; }
.authword { width: 168px; height: auto; display: block; opacity: .97; }
/* The same gradients as Home, quieter — this sheet is small and the field in
   it is the thing somebody has to find. */
.authsheet {
  background:
    radial-gradient(420px 260px at 20% -20%, rgba(59,91,245,.16), transparent 64%),
    radial-gradient(420px 260px at 88% 118%, rgba(193,59,232,.14), transparent 62%),
    var(--panel);
}
.authsheet h2 { margin-bottom: 6px; }
.authsheet .hint { margin-bottom: 14px; }
/* Every field in an auth sheet, not just the passphrase one — the sign-in
   modal has two, and tying this to a single id left them unstyled and sitting
   side by side. */
.authsheet input {
  display: block;
  width: 100%; background: var(--panel2); border: 1px solid var(--line2);
  border-radius: 6px; padding: 10px 12px; text-align: center;
  font-size: 15px; letter-spacing: .3px; margin-bottom: 10px;
}
.authsheet input:focus { outline: none; border-color: var(--accent); }
.authsheet button.primary { width: 100%; }
.authsheet .error { margin: 0 0 12px; text-align: left; }


/* ---- listen (monitor audio in the browser) ---- */
.topbtn.listen { display: flex; align-items: center; gap: 7px; }
.topbtn.listen::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%;
  background: var(--dimmer); flex: 0 0 auto;
}
.topbtn.listen.on {
  background: var(--accent2); border-color: var(--accent); color: #fff;
}
.topbtn.listen.on::before {
  background: #fff; animation: pulse 1.4s ease-in-out infinite;
}
.topbtn.listen.connecting::before { background: var(--warn); }


/* ---- projects panel ---- */
.projsection { margin-bottom: 18px; }
.projhead {
  font-size: 10px; text-transform: uppercase; letter-spacing: .6px;
  color: var(--dim); margin-bottom: 8px;
}
.projlist { display: flex; flex-direction: column; gap: 6px; margin-bottom: 9px; }
.projrow {
  display: flex; align-items: center; gap: 10px;
  background: var(--panel2); border: 1px solid var(--line);
  border-radius: 7px; padding: 9px 12px;
}
.projrow.current { border-color: var(--accent); background: var(--row-on); }
.projrow .pname { font-weight: 600; }
.projrow .pmeta { flex: 1; font-size: 11px; color: var(--dim); }
.projrow .badge {
  font-size: 9px; text-transform: uppercase; letter-spacing: .4px;
  padding: 2px 7px; border-radius: 10px;
  background: var(--accent2); color: #fff;
}
.projrow .badge.builtin { background: var(--line2); color: var(--dim); }
.projrow button {
  background: var(--control); border: 1px solid var(--line2); border-radius: 5px;
  padding: 4px 11px; font-size: 11px; color: var(--text);
}
.projrow button:hover { border-color: var(--accent); }
.projrow button.danger:hover { border-color: var(--hot); color: var(--hot); }
/* A service, read on a Monday. A .projrow is a line of controls; this is
   something to read, so it stacks instead. The first line is the only one
   anybody looks at when the answer is "it was fine". */
.projrow.svcrow { flex-direction: column; align-items: stretch; gap: 4px; }
.svcrow .svchead { display: flex; align-items: center; gap: 10px; }
.svcrow .svchead .pmeta { flex: 0 0 auto; }
.svcrow .svcline { font-size: 11px; display: flex; align-items: center; gap: 8px; }
.svcrow .svcline.dim { color: var(--dim); }
.svcrow .svcline.err { color: var(--hot); font-family: ui-monospace, monospace; }

.newproj { display: flex; gap: 8px; }
.newproj input {
  flex: 1; background: var(--panel2); border: 1px solid var(--line2);
  border-radius: 6px; padding: 8px 10px;
}
.newproj input:focus { outline: none; border-color: var(--accent); }
.newproj select {
  background: var(--panel2); border: 1px solid var(--line2);
  border-radius: 6px; padding: 8px 10px; color: var(--text);
}
.newproj button {
  background: var(--panel2); border: 1px solid var(--line2);
  border-radius: 6px; padding: 8px 15px; color: var(--text);
}
.newproj button:hover { border-color: var(--accent); }
.templatenote {
  font-size: 11px; color: var(--dimmer); margin-top: 7px; line-height: 1.5;
}

/* The gain advice under INPUT GAIN, which rewrites itself four times a second
   against the live peak. Its verdicts are not the same length — "about right"
   is one line and "far too quiet, bring the gain up and check what the
   console is sending" is three — so without a reserved height the module
   grew and shrank as somebody spoke, and everything below it, the equaliser
   included, moved up and down with their voice. Aziel, 2026-10-14: "when the
   words are coming out, it keeps pushing my EQ up and down."

   Three lines of room, held whether or not they are used. The cost is a
   little empty space under a short message; the alternative is a desk that
   will not sit still while anybody is talking. */
#trimNote { min-height: calc(3 * 1.5 * 11px); }

/* A chosen device is not necessarily the one making sound. When the engine is
   on its own demo material, or stopped, the picker above is a plan rather
   than a fact, and saying so is what stops an afternoon spent wondering why
   the board's meters do not move. */
.note {
  font-size: 11px; color: var(--warn, #e8b84f); margin-top: 6px; line-height: 1.5;
}


/* ---- venue bar ----
   Which service you are touching has to be unmissable. The active venue is
   filled, bright and outlined; the others are plainly not. Reaching for the
   wrong church's fader is the real hazard when mixing two at once. */
.venuebar {
  display: flex; align-items: stretch; gap: 8px; flex: 0 0 auto;
  padding: 7px 14px; background: var(--well);
  border-bottom: 1px solid var(--line); overflow-x: auto;
}
.venuebar[hidden] { display: none; }
.venue {
  display: flex; align-items: center; gap: 9px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 7px; padding: 6px 12px; min-width: 190px;
  text-align: left; flex: 0 0 auto;
}
.venue:hover { border-color: var(--line2); }
.venue.active {
  background: linear-gradient(180deg, #1e3242, #16212b);
  border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent2) inset;
}
.venue .vdot {
  width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto;
  background: var(--dimmer);
}
.venue .vdot.connected { background: var(--good); }
.venue .vdot.offline   { background: var(--hot); }
.venue .vdot.connecting, .venue .vdot.needs-passphrase { background: var(--warn); }
.venue .vbody { flex: 1; min-width: 0; }
.venue .vname {
  font-size: 12px; font-weight: 600; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.venue.active .vname { color: #fff; }
.venue .vmeta {
  font-size: 10px; color: var(--dim); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.venue .vmeta.bad { color: var(--hot); }
.venue .vmeter {
  width: 46px; height: 6px; border-radius: 3px; overflow: hidden;
  background: var(--well-deep); border: 1px solid var(--line); flex: 0 0 auto;
}
.venue .vfill { height: 100%; width: 0%; background: var(--good); transition: width .12s linear; }
.venue .vfill.warn { background: var(--warn); }
.venue .vfill.hot  { background: var(--hot); }
.venue .vhere {
  font-size: 8px; text-transform: uppercase; letter-spacing: .5px;
  color: var(--accent); font-weight: 700;
}

/* ------------------------------------------------- choosing a church
   An operator arriving at the relay has one thing to do and no idea that
   there is a row at the top to do it in. Manuel signed in on 2026-09-21 and
   saw a black screen. While there is no desk, the choice *is* the screen. */
.venuepicker {
  display: none; flex-direction: column; align-items: center;
  justify-content: center; gap: 4px; flex: 1; min-height: 0;
  padding: 34px 16px; overflow-y: auto;
}
.venuepicker:not([hidden]) { display: flex; }
.venuepicker h2 {
  font-size: 19px; font-weight: 600; margin: 0 0 3px; text-align: center;
}
.venuepicker .pickhint {
  font-size: 12.5px; color: var(--dim); margin: 0 0 22px;
  text-align: center; max-width: 430px; line-height: 1.55;
}
.venuecards {
  display: flex; flex-wrap: wrap; gap: 13px; justify-content: center;
  width: 100%; max-width: 780px;
}
.venuecard {
  display: flex; align-items: center; gap: 14px; text-align: left;
  background: var(--panel); border: 1px solid var(--line2);
  border-radius: 11px; padding: 17px 20px;
  min-width: 270px; flex: 0 1 330px;
  transition: border-color .15s, transform .08s, background .15s;
}
.venuecard:hover { border-color: var(--accent); background: #1b2530; }
.venuecard:active { transform: translateY(1px); }
.venuecard.off { opacity: .68; }
.venuecard.off:hover { border-color: var(--hot); }
.venuecard .cdot {
  width: 13px; height: 13px; border-radius: 50%; flex: 0 0 auto;
  background: var(--dimmer);
}
.venuecard.on  .cdot { background: var(--good); box-shadow: 0 0 9px var(--good); }
.venuecard.off .cdot { background: var(--hot); }
.venuecard .cbody { flex: 1; min-width: 0; }
.venuecard .cname {
  font-size: 15.5px; font-weight: 600; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.venuecard .cmeta { font-size: 11.5px; color: var(--dim); margin-top: 3px; }
.venuecard .cmeta.bad { color: var(--hot); }
.venuecard .cgo {
  font-size: 11px; font-weight: 700; letter-spacing: .4px;
  text-transform: uppercase; color: var(--accent); flex: 0 0 auto;
}
.venuecard.off .cgo { color: var(--dimmer); }
@media (max-width: 560px) {
  .venuepicker { padding: 22px 14px; }
  .venuecard { min-width: 0; flex: 1 1 100%; }
}
.venueadd {
  background: transparent; border: 1px dashed var(--line2); color: var(--dim);
  border-radius: 7px; padding: 6px 14px; font-size: 12px; flex: 0 0 auto;
}
.venueadd:hover { border-color: var(--accent); color: var(--accent); }
.venueform { display: grid; grid-template-columns: 1fr 1fr 1fr auto; gap: 8px; }
.venueform input {
  background: var(--panel2); border: 1px solid var(--line2);
  border-radius: 6px; padding: 8px 10px; min-width: 0;
}
.venueform input:focus { outline: none; border-color: var(--accent); }
@media (max-width: 900px) { .venueform { grid-template-columns: 1fr; } }


/* ---- admin: codes and invite links meant to be read out or copied ---- */
.handout {
  background: var(--panel2); border: 1px solid var(--line2); border-radius: 8px;
  padding: 18px 12px; margin: 10px 0 14px; text-align: center;
  font-size: 30px; font-weight: 700; letter-spacing: 4px;
  font-variant-numeric: tabular-nums; word-break: break-all;
}
.handout.link { font-size: 13px; font-weight: 500; letter-spacing: 0; }
/* Admin rows carry far more than a project row: a name, a role, a set of
   church assignments and four buttons. Let them wrap instead of crushing the
   controls into each other. */
.adminrow { flex-wrap: wrap; row-gap: 8px; }
.adminrow .pmeta { flex: 0 0 auto; }
.adminrow .spacer { flex: 1 1 auto; }
.adminrow select.role {
  flex: 0 0 auto; min-width: 118px;
  background: var(--panel); border: 1px solid var(--line2);
  border-radius: 6px; padding: 5px 8px; font-size: 12px;
}
/* The date a loan ends. Styled to match the selects beside it rather than
   left as the browser's default, which on macOS is a pale box with its own
   font and sits a few pixels lower than everything on the line. */
.adminrow input.loanuntil {
  flex: 0 0 auto;
  background: var(--panel); border: 1px solid var(--line2); color: inherit;
  border-radius: 6px; padding: 4px 8px; font-size: 12px;
  font-family: inherit;
}
.adminrow .assignrow .hint { flex: 1 0 100%; margin-top: 4px; }
/* What a church pays for: the state on one line, the form under it. */
.licencerow { flex-wrap: wrap; }
.licencerow .licbadge { white-space: nowrap; }
.licencerow .licform { flex: 1 0 100%; align-items: center; gap: 8px 14px; }
.licfield { display: inline-flex; align-items: center; gap: 6px; }
.licfield[hidden] { display: none; }
.adminrow input.licextra { width: 56px; }

/* Updating churches, at the top of the admin page's church list. */
.rollout {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 10px; margin-bottom: 8px;
  border: 1px solid var(--line2); border-radius: 6px; background: var(--panel);
}
.rollout.stopped { border-color: var(--hot); }
/* Written to beat `.projrow .badge`, which sets a background of its own and
   sits later in this file — equal specificity loses to whichever comes last. */
.projrow .badge.behind, .badge.behind { background: var(--warn); color: #2a1f05; }
.projrow .badge.ok, .badge.ok { background: var(--good); color: #06240f; }

/* The loudest a strip has been, above its meter.
   Logic and Ableton both put a resettable number here, and it is what makes a
   clipped drum transient findable after the fact rather than a red flicker
   somebody was not looking at. Quiet until it matters, then the same red as
   the meter's own over. */
.peaknum {
  display: block; width: 100%; margin: 0 0 2px;
  font-size: 9px; font-variant-numeric: tabular-nums; text-align: center;
  padding: 1px 0; border-radius: 3px;
  background: transparent; border: 1px solid transparent; color: var(--dimmer);
}
.peaknum:hover { border-color: var(--line2); }
.peaknum.close {
  background: var(--warn); border-color: var(--warn); color: #2a1f05;
  font-weight: 600;
}
.peaknum.over {
  background: var(--hot); border-color: var(--hot); color: #2a0b08;
  font-weight: 600;
}

/* ------------------------------------------- seeing what somebody is doing
   Drawn over the desk, never in the way of it. pointer-events: none is the
   load-bearing line: a pointer drawn over a fader must not be able to take a
   click meant for the fader. */
.viewlayer {
  position: fixed; inset: 0; pointer-events: none; z-index: 40;
  overflow: hidden;
}
.theirptr {
  position: absolute; width: 10px; height: 10px; margin: -5px 0 0 -5px;
  border-radius: 50%; background: var(--accent2);
  box-shadow: 0 0 0 2px rgba(0,0,0,.45);
  transition: left .1s linear, top .1s linear;
}
.theirname {
  position: absolute; left: 12px; top: -2px; white-space: nowrap;
  font-size: 9px; padding: 1px 5px; border-radius: 3px;
  background: var(--accent2); color: #fff;
}
/* Their selection, on my screen, where it already is — the view never moves. */
.strip.theirsel { box-shadow: inset 0 0 0 2px var(--accent2); }
.theirscroll {
  position: absolute; height: 3px; background: var(--accent2);
  opacity: .55; border-radius: 2px;
}
/* Said plainly to the person being watched. They agreed to "see my desk" and
   this is a good deal more than that. */
/* Out of the document flow, always.
   This was a bar above the strips, so it pushed the whole desk down when it
   appeared and let it spring back when it went. Anything that can appear and
   disappear on its own must not be able to move the desk — an operator
   reaching for a fader does not need it to have moved 20 px since they
   looked. Bottom centre: the request panel owns bottom right and messages own
   bottom left, and those must not share a corner. */
.watchedby {
  position: fixed; bottom: 10px; left: 50%; transform: translateX(-50%);
  z-index: 45; pointer-events: none;
  padding: 4px 12px; font-size: 11px; border-radius: 12px;
  color: var(--accent2); background: rgba(16,22,30,.92);
  border: 1px solid var(--accent2);
  box-shadow: 0 2px 10px rgba(0,0,0,.45);
}
.watchedby[hidden] { display: none; }

/* The cue feed's clip light. Dark and unremarkable until it has something to
   say, then it is the loudest thing on the strip — an over on the cue is
   distortion in somebody's headphones a thousand miles away. */
.cueclip {
  display: block; width: 100%; margin: 3px 0 0;
  font-size: 9px; letter-spacing: .06em; text-align: center;
  padding: 2px 0; border-radius: 4px;
  background: transparent; border: 1px solid var(--line2); color: var(--dimmer);
}
.cueclip.lit {
  background: var(--hot); border-color: var(--hot); color: #2a0b08;
  font-weight: 600;
}

.projrow .pending { background: var(--accent); }

/* A select inside one of these forms had no styling at all, so it fell back to
   the browser default: a white box, which with the app's light text on it was
   unreadable. */
.venueform select, .adminrow select {
  background: var(--panel); color: var(--text);
  border: 1px solid var(--line2); border-radius: 6px;
  padding: 7px 9px; font-size: 12px;
}
.venueform select:focus, .adminrow select:focus {
  outline: none; border-color: var(--accent);
}

.assignbox { display: flex; flex-wrap: wrap; gap: 4px; margin: 0 8px; }
.assignbox .sbtn { padding: 3px 8px; font-size: 11px; }

/* Taking a desk off someone. Deliberately not styled as a primary action —
   it interrupts a colleague mid-service and should feel like a decision. */
.venuetake {
  align-self: center; margin-left: -6px; margin-right: 6px;
  font-size: 11px; padding: 5px 10px;
  background: var(--panel2); border: 1px solid var(--line2);
  border-radius: 6px; color: var(--text); cursor: pointer;
}
.venuetake:hover { border-color: var(--accent); }
/* Handing your own desk back is an ordinary thing to do, not a warning. */
/* ------------------------------------------------------------- messages
   A sidebar rather than a window: it can sit open beside the desk through a
   service without covering a fader, and closes to nothing. */
.msgbtn { position: relative; padding: 8px 13px; font-size: 14px; }
.msgcount {
  position: absolute; top: -5px; right: -5px; min-width: 16px; height: 16px;
  padding: 0 4px; border-radius: 8px; background: var(--hot); color: #fff;
  font-size: 10px; font-weight: 700; line-height: 16px; text-align: center;
}
.msgcount[hidden] { display: none; }

.msgpanel {
  position: fixed; top: 0; right: 0; bottom: 0; width: 300px; z-index: 70;
  display: flex; flex-direction: column;
  background: var(--panel); border-left: 1px solid var(--line2);
  box-shadow: -8px 0 28px rgba(0,0,0,.45);
}
.msgpanel[hidden] { display: none; }
.msghead {
  display: flex; align-items: center; gap: 8px;
  padding: 12px 14px; border-bottom: 1px solid var(--line);
}
.msgtitle { font-size: 14px; font-weight: 600; flex: 1; }
.msgx { background: none; border: none; color: var(--dim); font-size: 19px; }
.msgx:hover { color: var(--text); }

.msgpeople { flex: 1; overflow-y: auto; padding: 6px; }
.msgperson {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 9px 10px; border-radius: 7px; text-align: left;
  background: none; border: 1px solid transparent; color: var(--text);
}
.msgperson:hover { background: var(--panel2); border-color: var(--line2); }
.msgdot {
  width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto;
  background: var(--dimmer);
}
.msgdot.on { background: var(--good); box-shadow: 0 0 7px var(--good); }
.msgname { flex: 1; font-size: 13px; }
.msgrole { font-size: 9.5px; color: var(--dimmer); text-transform: uppercase; letter-spacing: .4px; }
.msgunread {
  min-width: 18px; padding: 1px 5px; border-radius: 9px;
  background: var(--hot); color: #fff; font-size: 10px; font-weight: 700;
  text-align: center;
}

/* `display: flex` beats the browser's own [hidden] rule, so both of these
   have to say it. Exactly the trap the strips hit earlier — a panel that is
   hidden and still on screen. */
.msgpeople[hidden], .msgthread[hidden] { display: none; }
.msgthread { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.msgback { align-self: flex-start; background: none; border: none; color: var(--dim); font-size: 11px; padding: 8px 12px; }
.msgback:hover { color: var(--accent); }
.msgwho { padding: 0 14px 8px; font-size: 13px; font-weight: 600; }
.msglog { flex: 1; overflow-y: auto; padding: 4px 12px 10px; display: flex; flex-direction: column; gap: 6px; }
.msgline { max-width: 85%; padding: 7px 10px; border-radius: 9px; font-size: 12.5px; line-height: 1.45; }
.msgline.them { background: var(--panel2); align-self: flex-start; border-bottom-left-radius: 3px; }
.msgline.me { background: #1d3646; align-self: flex-end; border-bottom-right-radius: 3px; }
.msgwhen { display: block; font-size: 9.5px; color: var(--dimmer); margin-top: 3px; }
.msgsend { display: flex; gap: 6px; padding: 10px 12px; border-top: 1px solid var(--line); }
.msgsend input { flex: 1; font-size: 12px; }
.msgsend button { padding: 7px 13px; font-size: 12px; }
.msgempty { padding: 18px 14px; color: var(--dim); font-size: 12px; line-height: 1.6; }

/* A message arriving while you are mixing. Bottom left, so it never shares a
   corner with a watch request and never sits over the master strip. */
.msgbanners {
  position: fixed; left: 14px; bottom: 14px; z-index: 60;
  display: flex; flex-direction: column; gap: 7px; max-width: 300px;
}
.msgbanners[hidden] { display: none; }
.msgbanner {
  background: var(--panel); border: 1px solid var(--line2);
  border-left: 3px solid var(--accent); border-radius: var(--radius);
  padding: 9px 12px; font-size: 12px; line-height: 1.45;
  box-shadow: 0 5px 18px rgba(0,0,0,.45); cursor: pointer;
}
.msgbanner:hover { border-color: var(--accent); }
.msgbanner b { display: block; font-size: 12.5px; margin-bottom: 1px; }
.msgbanner span { color: var(--dim); }

@media (max-width: 560px) {
  .msgpanel { width: 100%; }
}

/* Somebody asking to watch the desk you are mixing.
   Bottom right, out of the way of the strips, and it waits — his choice was
   that a request stays pending until it is answered, so this does not time
   out and does not cover anything you might be reaching for. */
.watchasks {
  position: fixed; right: 14px; bottom: 14px; z-index: 60;
  display: flex; flex-direction: column; gap: 8px; max-width: 320px;
}
.watchasks[hidden] { display: none; }
.watchask {
  background: var(--panel); border: 1px solid var(--accent2);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius); padding: 11px 13px;
  box-shadow: 0 6px 22px rgba(0,0,0,.5);
  font-size: 12px; line-height: 1.5;
}
.watchask b { color: var(--text); }
.watchask .askwho { font-size: 13px; font-weight: 600; margin-bottom: 2px; }
.watchask .askwhy { color: var(--dim); }
.watchask .askrow { display: flex; gap: 7px; margin-top: 9px; }
.watchask .askrow button {
  flex: 1; padding: 6px 0; font-size: 11px;
  background: var(--panel2); border: 1px solid var(--line2);
  border-radius: 5px; color: var(--text);
}
.watchask .askrow button:hover { border-color: var(--dim); }
.watchask .askyes {
  background: var(--accent); border-color: var(--accent);
  color: #07121b; font-weight: 700;
}
.watchask .askyes:hover { background: #63b0ef; border-color: #63b0ef; }

/* Waiting for somebody to answer. Shown where the desk would be. */
.waitingwatch { text-align: center; }
.waitingwatch .dotpulse { margin: 0 auto 10px; display: block; }

.venueask { color: var(--warn); border-color: #6b5a22; }
.venueask:hover:not(:disabled) { color: #fff; border-color: var(--warn); }
.venueask:disabled { opacity: .6; cursor: default; }

/* A moderator reaching into a desk somebody else is running. */
.venuecontrol { color: var(--accent); border-color: var(--accent2); }
.venuecontrol:hover { color: #fff; border-color: var(--accent); }
.venuerelease { color: var(--dim); }
.venuerelease:hover { color: var(--text); border-color: var(--accent); }
.vdot.held, .vdot.watching { background: var(--accent); }

/* A stalled engine is not a notice, it is an emergency: the desk looks alive
   and is processing nothing. */
#audioError.stalled {
  background: #3a1417; border-color: #7a2630; color: #ffd7db;
  font-size: 13px; line-height: 1.45;
}

/* Picking which of a board's inputs are on the desk. A wide console means a
   lot of these, so they wrap into a grid rather than a long list. */
.inputgrid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 6px; margin: 12px 0; max-height: 46vh; overflow-y: auto;
}
.inputcell {
  display: flex; align-items: center; gap: 7px;
  background: var(--panel2); border: 1px solid var(--line);
  border-radius: 6px; padding: 7px 9px; cursor: pointer; text-align: left;
}
.inputcell:hover { border-color: var(--line2); }
.inputcell.on { border-color: var(--accent); background: var(--row-on); }
.inputcell .num {
  font-size: 11px; font-weight: 700; color: var(--dim);
  font-variant-numeric: tabular-nums; min-width: 18px;
}
.inputcell.on .num { color: var(--accent); }
.inputcell .who {
  font-size: 10px; color: var(--dimmer);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.inputcell.on .who { color: var(--text); }

/* The channel colour dot is a control, not a decoration. */
.detail-head .dot { border: 1px solid var(--line2); cursor: pointer; padding: 0; }
.detail-head .dot:hover { border-color: var(--text); }
.detail-head .swatches { margin-right: 4px; }

/* Choosing what a new bus is for. */
.buskinds { display: grid; gap: 7px; margin: 12px 0 4px; }
.buskind {
  display: block; width: 100%; text-align: left;
  background: var(--panel2); border: 1px solid var(--line2);
  border-radius: 7px; padding: 10px 12px; cursor: pointer; color: var(--text);
}
.buskind:hover { border-color: var(--accent); background: var(--row-on); }
.buskind .what { font-weight: 600; font-size: 13px; }
.buskind .why { display: block; font-size: 11px; color: var(--dim); margin-top: 2px; }

/* Taking a bus off the desk. Small and in the corner: it is not something to
   reach for during a service. */
.strip .busx {
  position: absolute; top: 4px; right: 5px;
  background: none; border: none; padding: 0 2px; line-height: 1;
  font-size: 12px; color: var(--dimmer); cursor: pointer;
}
.strip .busx:hover { color: var(--hot); }

/* Adding one, alongside the buses it would join. */
.addbus {
  flex: 0 0 auto; align-self: stretch; min-width: 54px;
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 4px;
  background: transparent; border: 1px dashed var(--line2); border-radius: 8px;
  color: var(--dim); font-size: 10px; cursor: pointer;
}
.addbus:hover { border-color: var(--accent); color: var(--text); }
.addbus .plus { font-size: 17px; line-height: 1; }

/* The insert slot on a bus, the way an aux shows its plugin. Empty reads as an
   invitation; filled reads as a thing that is on. */
/* The channel's chain, one row per effect in the order they run, and a row
   to add the next. It grows only as it is used: a channel with nothing is
   one row, exactly as it has always been.

   The margin moves here from the buttons so that eight of them do not stack
   eight margins; inside, they sit tight together and read as one rack. */
/* The rack: a fixed region whose height does not depend on how long the
   chain is. Every strip reserves the same depth, so the faders below start
   at the same height and the row can be read across — and the depth is
   capped, so one channel with a long chain does not take rows off every
   fader on the desk.

   --ins-rows is one until somebody builds a chain, and never more than
   INSERT_ROWS_SHOWN.

   The row height is pinned rather than left to the font, because this
   arithmetic has to come out exact. Left to itself a row measured 18.5px
   against the 17px once assumed here, so every rack on the desk overflowed
   by a pixel and carried a scrollbar it had no use for. */
.strip .insrack {
  display: flex; flex-direction: column; gap: 2px;
  margin: 3px 0 0; flex: 0 0 auto;
  height: calc(var(--ins-rows, 1) * var(--ins-row)
               + (var(--ins-rows, 1) - 1) * 2px);
}
/* The effects scroll inside the rack. The add row below is a sibling, not a
   child, so no amount of scrolling in here can put it out of reach. */
.strip .insrack .inserts {
  display: flex; flex-direction: column; gap: 2px;
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; scrollbar-width: thin;
}
.strip .insrack .addins { flex: 0 0 var(--ins-row); }
.strip .insrack .insert {
  margin: 0; height: var(--ins-row); padding: 0; flex: 0 0 var(--ins-row);
}

.strip .insert {
  width: 100%; margin: 3px 0 0; padding: 3px 0;
  font-size: 9px; letter-spacing: .4px; text-transform: uppercase;
  border-radius: 4px; cursor: pointer;
  background: transparent; border: 1px dashed var(--line2); color: var(--dimmer);
}
.strip .insert:hover { border-color: var(--accent); color: var(--text); }
.strip .insert.on {
  background: var(--accent2); border: 1px solid var(--accent); color: #fff;
  border-style: solid;
}
/* An effect whose mix is at 0%: still there, still clickable, making no
   sound. Drawn as an outline rather than filled — solid, where an empty slot
   is dashed — so working, silent and empty read as three different things
   from across the desk. On 2026-10-18 a silent reverb looked exactly like a
   working one, and its knobs were turned mid-service for nothing. */
.strip .insert.on.silent { background: transparent; color: var(--accent); }
.buskind.on { border-color: var(--accent); background: var(--row-on); }

/* The note under the insert controls. It carries the one thing an operator
   can get wrong here without noticing — putting the same reverb on six
   channels instead of sending six channels to one — so it is quiet but
   always present rather than a tooltip nobody hovers. */
.mnote {
  font-size: 11px; line-height: 1.45; color: var(--dimmer);
  padding: 8px 2px 0; }
.mnote:empty { display: none; }
#modInsert.active .mhead { color: var(--text); }


/* On Home, the desk and everything that describes it is not on screen.
   Listed rather than hidden one at a time in script: `hidden` is only a
   default and loses to any rule setting display, which is how a flex row
   carried on showing underneath. */
body.athome main.console,
body.athome .viewbar,
body.athome .detail,
body.athome .venuebar,
body.athome .loudness,
body.athome .topactions,
body.athome .deafbanner,
body.athome .watchbanner,
body.athome .solobanner { display: none !important; }

/* ------------------------------------------------------------------ Home
 *
 * The one screen that can afford room. The desk cannot: thirty-two strips,
 * faders and meters at once, and every pixel of padding there is a channel
 * somebody cannot see. Here there are a handful of things, so they get air,
 * and the product looks like something somebody sells rather than a tool.
 */
/* `hidden` is only a default and any display rule beats it — which is how
   Home carried on covering the desk after Live stream was pressed, with
   home.hidden reading true the whole time. Said explicitly, once. */
.home[hidden] { display: none !important; }

.home {
  flex: 1 1 auto; min-height: 0; display: flex; overflow: hidden;
  position: relative;
  /* Four layers rather than two: a cool blue low on the left and a magenta
     high on the right, which is the logo's own run from #3b5bf5 to #c13be8,
     plus a wide purple wash to tie them together. The logo is a gradient, so
     the screen it lands on should be one. */
  background:
    radial-gradient(760px 520px at 10% 112%, rgba(59, 91, 245, .22), transparent 64%),
    radial-gradient(880px 600px at 96% -12%, rgba(193, 59, 232, .20), transparent 62%),
    radial-gradient(1250px 760px at 42% 18%, rgba(138, 69, 240, .17), transparent 66%),
    linear-gradient(160deg, #11131b 0%, var(--bg) 46%, #140f1d 100%);
}
/* A lit edge where Home meets the bar above it. */
.home::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 1px;
  background: linear-gradient(90deg, transparent,
              rgba(138,69,240,.55) 28%, rgba(193,59,232,.45) 62%, transparent);
}

.homemark {
  display: block; width: clamp(190px, 21vw, 268px); height: auto;
  margin: 0 0 22px; opacity: .97;
}

.homeside {
  flex: 0 0 272px; min-width: 0; overflow-y: auto;
  padding: 22px 16px; border-right: 1px solid var(--line);
  background: rgba(0, 0, 0, .18);
}
.homesidehead {
  font-size: 11px; letter-spacing: .6px; text-transform: uppercase;
  color: var(--dimmer); margin: 0 6px 12px;
}
.homelist { display: flex; flex-direction: column; gap: 8px; }

/* A church, and what it is doing — read before committing to opening it. */
.homechurch {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 11px 12px; border-radius: 12px; text-align: left;
  background: var(--panel2); border: 1px solid var(--line); cursor: pointer;
}
.homechurch:hover { border-color: var(--pick); background: var(--panel3); }
.homechurch.here {
  border-color: var(--pick);
  background: linear-gradient(120deg, rgba(59,91,245,.18), rgba(193,59,232,.14));
}
.homechurch .hcd {
  width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto;
  background: var(--dimmer);
}
.homechurch.on .hcd { background: #3ad07a; box-shadow: 0 0 7px #3ad07a88; }
.homechurch.bad .hcd { background: #e0574a; }
.homechurch .hcn { display: block; font-weight: 600; font-size: 13px; }
.homechurch .hcm {
  display: block; font-size: 10px; color: var(--dimmer); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.homemain {
  flex: 1 1 auto; min-width: 0; overflow-y: auto;
  display: flex; flex-direction: column; justify-content: center;
  padding: 40px clamp(24px, 6vw, 72px);
}
.homehero h1[hidden] { display: none; }
.homehero h1 {
  margin: 0; font-size: clamp(26px, 3.4vw, 40px); font-weight: 650;
  letter-spacing: -.5px;
}
.homestate { margin: 8px 0 0; color: var(--dim); font-size: 13px; }

.homeactions {
  display: grid; gap: 14px; margin-top: 34px;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); max-width: 760px;
}
.homecard {
  display: flex; align-items: center; gap: 14px; text-align: left;
  padding: 18px 20px; border-radius: 16px; cursor: pointer;
  background: var(--panel2); border: 1px solid var(--line);
  transition: border-color .12s, transform .12s;
}
.homecard:hover { border-color: var(--pick); transform: translateY(-1px); }
.homecard.go {
  /* The logo's own sweep, blue into magenta, rather than one flat purple. */
  background: linear-gradient(135deg, rgba(59,91,245,.34) 0%,
              rgba(138,69,240,.30) 48%, rgba(193,59,232,.26) 100%), var(--panel2);
  border-color: var(--pick-edge);
  box-shadow: 0 8px 26px rgba(138,69,240,.16);
}
.homecard.go:hover { box-shadow: 0 10px 30px rgba(138,69,240,.26); }
.homecard[disabled] { opacity: .45; cursor: default; transform: none; }
.hcicon {
  width: 40px; height: 40px; border-radius: 12px; flex: 0 0 auto;
  display: grid; place-items: center; font-size: 15px; color: #fff;
  background: linear-gradient(145deg, #3b5bf5, #8a45f0 55%, #c13be8);
}
.hctext { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.hctitle { font-size: 15px; font-weight: 600; }
.hcsub { font-size: 11px; color: var(--dimmer); }

/* The badge is the way back, so it has to look like something you can press. */
.markbtn {
  padding: 0; border: none; background: none; cursor: pointer;
  border-radius: 9px; line-height: 0; flex: 0 0 auto;
}
.markbtn:hover { box-shadow: 0 0 0 2px var(--pick-edge); }

@media (max-width: 760px) {
  .home { flex-direction: column; }
  .homeside { flex: 0 0 auto; border-right: none; border-bottom: 1px solid var(--line); }
  .homemain { padding: 24px 18px; }
}

/* ===========================================================================
   LOOKS
   ===========================================================================
   Paint only. Nothing here moves a control, changes a size, or alters what
   anything does — the same fader is in the same place doing the same job.
   That is the whole rule for this section: if a change would make somebody
   reach to a different spot, it does not belong in a look.

   Inspired by hardware rather than copied from it. Every digital desk since
   the eighties has had metal, lit buttons and a sunken fader slot, and that
   idiom belongs to nobody. What is deliberately avoided is any one maker's
   signature — their button-colour arrangement, their typography, their
   chassis shape. We are building a console, not a photocopy of one.
   ========================================================================= */

/* --------------------------------------------------------------- Console
   Warm graphite and amber: the colour of a lamp over a desk, rather than
   the cold blue of a screen. The purple selection stays, because it comes
   from the logo and it is the one colour on here that is his. */
:root[data-look="console"] {
  --bg:        #16171a;
  --panel:     #212328;
  --panel2:    #292c32;
  --panel3:    #31353c;
  --control:   #191b1f;
  --well:      #0f1012;
  --well-deep: #08090a;
  --line:      #3a3e46;
  --line2:     #4d525b;
  --text:      #f2ede4;
  --dim:       #9d968a;
  --dimmer:    #6d675e;
  --accent:    #d99a3f;
  --accent2:   #a8762c;
  --row-on:    #2e2a22;
  --radius:    5px;
}

/* What makes a screen look like an object: light falling on it from above.
   A panel lit along its top edge and shaded at its foot reads as a thing
   with thickness, and that one cue does more than any amount of texture. */
:root[data-look="console"] .strip {
  background:
    linear-gradient(180deg,
      rgba(255, 255, 255, .055) 0%,
      rgba(255, 255, 255, .012) 14%,
      transparent 42%,
      rgba(0, 0, 0, .16) 100%),
    var(--panel);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .10),   /* the lit top edge */
    inset 0 -1px 0 rgba(0, 0, 0, .55),        /* shade at the foot */
    0 1px 2px rgba(0, 0, 0, .45);             /* and a shadow beneath it */
  border-color: #40444c;
}
:root[data-look="console"] .strip.isbus {
  background:
    linear-gradient(180deg, rgba(255,255,255,.06) 0%, transparent 42%,
                    rgba(0,0,0,.16) 100%),
    var(--panel2);
}
:root[data-look="console"] .strip.selected {
  background:
    linear-gradient(180deg, var(--pick-wash), transparent 62%),
    linear-gradient(180deg, rgba(255,255,255,.055) 0%, transparent 42%,
                    rgba(0,0,0,.16) 100%),
    var(--panel);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.12),
              inset 0 -1px 0 rgba(0,0,0,.55),
              0 0 0 1px var(--pick-edge) inset;
}
:root[data-look="console"] .detail,
:root[data-look="console"] .sheet {
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .07);
}

/* The chassis the strips are bolted to. Darker than the strips, so they
   read as panels sitting on something rather than holes cut out of it. */
:root[data-look="console"] body {
  background:
    radial-gradient(130% 55% at 50% -12%, #25272d 0%, transparent 62%),
    #0e0f11;
}

/* Section labels screen-printed onto the metal rather than typed on glass. */
:root[data-look="console"] .mhead > span:first-child,
:root[data-look="console"] .faderscale .fnum,
:root[data-look="console"] .strip .snum {
  text-shadow: 0 1px 0 rgba(0, 0, 0, .65);
}

/* Meters sunk into the panel. */
:root[data-look="console"] .meter,
:root[data-look="console"] .chanmeter {
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .8);
}

/* Buttons that look lit rather than coloured: dark when off, glowing when
   on. A real desk's buttons are lamps, and a lamp is obvious across a room,
   which is the actual reason this is worth doing at all. */
:root[data-look="console"] .mutebtn.on,
:root[data-look="console"] .solobtn.on,
:root[data-look="console"] .tog.on {
  box-shadow: 0 0 9px -1px currentColor, inset 0 1px 0 rgba(255,255,255,.3);
}

/* A touch more relief on the fader slot, so the cap sits in something. */
:root[data-look="console"] input[type=range].fader::-webkit-slider-runnable-track {
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, .75);
}

/* --------------------------------------------------- choosing a look
   Cards rather than a dropdown. The only way to choose how something looks
   is to see it, so each one carries a little painted desk in its own
   colours. */
.lookpick { display: flex; gap: 9px; flex-wrap: wrap; }
.lookcard {
  display: flex; align-items: center; gap: 11px; text-align: left;
  flex: 1 1 210px; padding: 9px 11px;
  background: var(--panel2); border: 1px solid var(--line2);
  border-radius: var(--radius); color: var(--text);
}
.lookcard:hover { border-color: var(--pick-edge); }
.lookcard.on { border-color: var(--pick); background: var(--pick-wash); }
.lookswatch {
  flex: 0 0 auto; display: flex; gap: 2px; align-items: flex-end;
  padding: 5px 5px 4px; border-radius: 4px; border: 1px solid;
}
.lookswatch i { display: block; width: 5px; border-radius: 1px; }
.lookswatch i:nth-child(1) { height: 15px; }
.lookswatch i:nth-child(2) { height: 21px; }
.lookswatch i:nth-child(3) { height: 11px; }
/* Painted with each look's own values rather than inherited, so the card
   shows that look even while you are standing in the other one. */

/* ----------------------------------------------------------------- Stage
   Flat, cold and nearly black: a desk whose surface is a screen rather than
   a lit sheet of metal. Where Console is a lamp over graphite, this is the
   room gone dark with the display carrying everything — no lit edges, no
   shading, one hairline between things, and each channel's own colour doing
   the organising instead.

   Asked for on 2026-10-14, inspired by a Behringer Wing the way Console was
   inspired by an X32. Same rule as before and it is worth repeating: the
   idiom of a modern desk — near-black, flat tiles, colour-coded channels,
   crisp white type — is the idiom of every touchscreen console made since
   about 2015 and belongs to nobody. What is not taken is any maker's own
   palette, their typography or the arrangement of their controls. The
   accent here is teal because teal is what a screen-era desk reaches for,
   and because it is as far from Console's amber as this file goes.

   His purple selection stays, as it does in every look. It is from the logo
   and it is the one colour on this desk that is his. */
:root[data-look="stage"] {
  --bg:        #07090b;
  --panel:     #11151a;
  --panel2:    #171c23;
  --panel3:    #21272f;
  --control:   #0b0e11;
  --well:      #040507;
  --well-deep: #000103;
  --line:      #222a33;
  --line2:     #38414e;
  --text:      #f6f9fd;
  --dim:       #97a2b1;
  --dimmer:    #606a78;
  --accent:    #1fc0c7;
  --accent2:   #148e95;
  --row-on:    #10272a;
  --radius:    9px;
}

/* Flat on purpose. The default desk is already quiet; this one is quieter
   still, so what little light there is has to come from the colours rather
   than from the surfaces. A panel with no gradient at all reads as a tile on
   a screen, which is the whole idea. */
:root[data-look="stage"] .strip,
:root[data-look="stage"] .strip.isbus {
  background: var(--panel);
  box-shadow: none;
}

/* The one thing turned up rather than down. With the surfaces this flat the
   channel colour is the only way a strip tells itself apart at a glance, so
   it is allowed to be the brightest thing on the desk. Paint, not size — the
   bar is exactly where it was and exactly as tall. */
:root[data-look="stage"] .colorbar { filter: saturate(1.35) brightness(1.12); }

/* A hairline rather than a bevel. Everything here is edge-lit by nothing,
   so the line between two things is the only thing separating them. */
:root[data-look="stage"] .module,
:root[data-look="stage"] .detail {
  background: var(--panel);
  box-shadow: none;
}


/* -------------------------------------------------------------- Daylight
   The only light one. A white chassis with the purple doing the work —
   asked for on 2026-10-14: "a white one ... still should look cool with
   purples."

   Two things had to be unpicked rather than recoloured, because the rest of
   the desk was written on the assumption of a dark room. Both are below and
   both are paint.

   Worth saying out loud: a white screen in a dark building is a lamp
   pointed at the operator. This is for a lit room, a rehearsal, or anybody
   who simply prefers it — the dark looks are still there and still the
   default, and the choice is per person. */
:root[data-look="daylight"] {
  /* Off-white rather than white, and the steps between surfaces kept small.
     Pure #ffffff against a grey page is a glare with a hard edge round it;
     softening both ends is what makes this read as paper rather than as a
     light that has been left on. "The whites, make more subtle", 2026-10-14. */
  --bg:        #e6e8ee;
  --panel:     #f2f4f7;
  --panel2:    #ebeef3;
  --panel3:    #e6e9ef;
  --control:   #e1e4ea;
  --well:      #d9dde5;
  --well-deep: #cbd1db;
  --line:      #dde0e7;
  --line2:     #c7ccd6;
  --text:      #1b1e24;
  --dim:       #5e6676;
  --dimmer:    #868e9d;
  --accent:    #6d4af0;
  --accent2:   #5433d6;
  --row-on:    #ece6ff;
  --radius:    8px;
  /* A recess on paper, not a hole cut in it. Near-black here made the meter
     the heaviest thing on a strip and the whole desk read like a negative —
     "it looks too inverted", 2026-10-14. The lines go dark for the same
     reason the pan dial's did. */
  --meter-bg:   #dee2ea;
  --meter-line: rgba(20, 22, 27, .16);
  /* A grid you can see without being a grid you look at. */
  --eq-grid:    rgba(20, 22, 27, .09);
  --eq-axis:    rgba(20, 22, 27, .22);
  --eq-curve:   #6d4af0;
  --eq-label:   #6b7384;
  /* The accent's own tint rather than navy, so the purple on top of it reads. */
  --pill-on:    #e7e0fb;
}

/* The pan dial's mark is NOT repainted here, and that is deliberate. It is
   drawn on the purple dial, not on the page, so white reads against it in
   every look — it is the logo. Darkening it for this one left channels 1-8
   looking like somebody else's desk while 9 and 10, which are panned and so
   kept the brighter mark, looked right. He spotted it immediately. */

/* The fader cap stays dark, which is what a cap on a white console is
   anyway. Left to itself it fades into --well and takes the white reading
   line with it, and a fader you cannot read the position of is not a fader.
   Pinned rather than derived, so it cannot drift with the palette. */
:root[data-look="daylight"] input[type=range].fader::-webkit-slider-thumb {
  background:
    linear-gradient(180deg, transparent 8px, #ffffff 8px, #ffffff 9px, transparent 9px),
    repeating-linear-gradient(90deg, rgba(255,255,255,.07) 0 1px, transparent 1px 3px),
    linear-gradient(180deg, #8d99ab 0%, #6f7b8a 40%, #202730 43%, #11151b 100%);
  border: 1px solid #9aa5b5;
  border-bottom-color: #0b0e13;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.45),
    inset 0 -1px 0 rgba(0,0,0,.85),
    0 2px 5px rgba(20, 24, 33, .28);
}

/* Shadows on white have to be softer and cooler, or every panel looks like
   it is floating an inch off the page. */
:root[data-look="daylight"] .strip,
:root[data-look="daylight"] .module {
  background: var(--panel);
  box-shadow: 0 1px 2px rgba(22, 26, 36, .045);
}

.lookswatch.stream1 { background: #151a20; border-color: #333d4a; }
.lookswatch.stream1 i { background: #4ea3e8; }
.lookswatch.stream1 i:nth-child(2) { background: #8a45f0; }
.lookswatch.console { background: #212328; border-color: #4d525b; }
.lookswatch.console i { background: #d99a3f; }
.lookswatch.console i:nth-child(2) { background: #8a45f0; }
.lookswatch.daylight { background: #ffffff; border-color: #bcc4d2; }
.lookswatch.daylight i { background: #6d4af0; }
.lookswatch.daylight i:nth-child(2) { background: #8a45f0; }
.lookswatch.stage { background: #11151a; border-color: #38414e; }
.lookswatch.stage i { background: #1fc0c7; }
.lookswatch.stage i:nth-child(2) { background: #8a45f0; }
.looktext { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.lookname { font-weight: 600; font-size: 12px; }
.lookblurb { font-size: 10px; color: var(--dim); line-height: 1.35; }

/* ------------------------------------------------------------- settings
   A drawer, not a modal. It was one sheet holding devices, monitoring,
   routing, diagnostics and the look of the desk, reached by a button
   labelled Settings that opened something called Audio Devices.

   From the right on purpose: the strips run left to right and channel one
   is almost always the one that matters, so covering the right-hand end
   costs least. Leaving the desk visible beside it is the point — you change
   an input and watch the meters react, which is how anybody actually works
   out whether they picked the right one. */
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 40;
  width: min(440px, 100vw);
  display: flex; flex-direction: column;
  background: var(--panel); border-left: 1px solid var(--line2);
  box-shadow: -18px 0 40px rgba(0, 0, 0, .45);
}
.drawer[hidden] { display: none !important; }
.drawerhead {
  display: flex; align-items: center; gap: 10px;
  padding: 13px 16px 11px; border-bottom: 1px solid var(--line);
}
.drawertitle { font-weight: 600; font-size: 15px; flex: 1; }
.drawerclose {
  background: none; border: 0; color: var(--dim);
  font-size: 22px; line-height: 1; padding: 0 4px;
}
.drawerclose:hover { color: var(--text); }
.drawertabs {
  display: flex; gap: 2px; padding: 8px 10px 0;
  border-bottom: 1px solid var(--line);
}
.drawertabs button {
  flex: 1; background: none; border: 0; color: var(--dim);
  padding: 7px 4px 9px; font-size: 11.5px;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.drawertabs button:hover { color: var(--text); }
.drawertabs button[hidden] { display: none; }
.drawertabs button.on { color: var(--text); border-bottom-color: var(--pick); }
/* The one scrolling part. The head and tabs stay put, so you never lose
   which tab you are on halfway down a long list of devices. */
.drawerbody { flex: 1; overflow-y: auto; padding: 14px 16px 22px; }
.drawerbody section[hidden] { display: none !important; }
.drawerbody .field { margin-bottom: 15px; }
.drawerbody .actions { display: flex; flex-wrap: wrap; gap: 7px; }
.drawerbody code {
  background: var(--well); border: 1px solid var(--line);
  border-radius: 4px; padding: 1px 5px; font-size: 10.5px;
}
/* On a phone it takes the screen; there is no room to peek at the desk. */
@media (max-width: 560px) {
  .drawer { width: 100vw; border-left: 0; }
}

/* Listening to the de-esser's band. Marked as loudly as a mute, because it
   is the one control on the detail panel that changes what leaves the desk —
   left on, a service goes out as nothing but hiss. */
.delisten { width: 100%; margin-top: 6px; }
.delisten.on {
  background: var(--warn); border-color: var(--warn); color: #2a1f05;
  font-weight: 600;
}

/* Recording. The one control on the bar that is writing to somebody's disk,
   so it stops being a label and becomes a clock while it runs. */
.topbtn.rec { display: flex; align-items: center; gap: 7px; }
.topbtn.rec .recdot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--line2); flex: 0 0 auto;
}
.topbtn.rec.on { border-color: var(--hot); color: var(--text); }
.topbtn.rec.on .recdot {
  background: var(--hot); box-shadow: 0 0 8px var(--hot);
  animation: recpulse 1.6s ease-in-out infinite;
}
/* Damaged is not a shade of recording — it is its own state, and it says so
   in the colour the desk already uses for "something is wrong". */
.topbtn.rec.bad { border-color: var(--warn); color: var(--warn); }
.topbtn.rec.bad .recdot { background: var(--warn); box-shadow: none; animation: none; }
@keyframes recpulse { 0%, 100% { opacity: 1 } 50% { opacity: .35 } }

/* One of Aziel's own engineers, who never counts against a church's operator
   limit. Marked rather than merely absent from the count, because "why does
   this church show one operator when I can see two" has to be answerable by
   looking. */
/* Written the day before the light look existed, as a navy with pale text on
   it — which on a white desk is a hole rather than a badge. Same treatment as
   an engaged pill, from the same variable, so there is one place to change
   and a fifth look cannot miss one of them. */
.badge.staff { background: var(--pill-on); color: var(--accent); }

/* A licence that has run out. Not "behind" like a church inside its grace —
   the app is locked, which is a different thing to tell somebody. */
.badge.licbadge.locked { background: #5a2330; color: #ffd6de; }

/* The eight insert slots, as a row of numbers above the effect being edited.
   A filled slot is marked so the chain can be read at a glance without
   clicking through all eight — which is the whole reason the row exists
   rather than a list. */
.insslots { display: flex; gap: 3px; margin: 0 0 7px; }
.insslots button {
  flex: 1; padding: 3px 0; font-size: 11px; line-height: 1.1;
  border-radius: 4px; cursor: grab;
}
.insslots button.filled { background: var(--pill-on); color: var(--accent); }
.insslots button.here   { outline: 2px solid var(--accent, #8a6fd0); }
.insslots button.drop   { outline: 2px dashed #8fc04a; }

/* A strip being hovered with an insert held over it. Dashed rather than solid
   so it does not read as the purple selection, which means something else. */
.strip.dropins { outline: 2px dashed #8fc04a; outline-offset: -2px; }
