/* Thermalsock Labs — shared chrome.
 *
 * There were nine separate copies of the .topbar rules across the apps, and
 * they had already drifted apart; two more apps (Signal Path, Transient Lab)
 * used entirely different header components. The switcher was the only truly
 * shared element on the page, which drew attention to the inconsistency
 * around it rather than hiding it.
 *
 * This is the single definition. Load it BEFORE the app's own stylesheet so
 * an app can still override a specific rule where it genuinely needs to —
 * but the default should be to use these as-is.
 *
 * ---- 2026 retheme ----
 * The palette moved from Field Notes (cream paper, rust ink) to the console
 * theme (deep navy, cyan). The variable NAMES were kept even though several
 * are now misleading — --paper is dark and --rust is cyan. Renaming them
 * would have meant touching every var() reference in fourteen pages for no
 * visual gain, and a half-finished rename is worse than an odd name. The
 * semantics are unchanged: --paper is the page, --ink is text on it, --rust
 * is the accent.
 */

@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap');

:root {
  /* Console palette — the canonical values. */
  --paper: #0B0D12;                        /* the page */
  --paper-deep: #0F1118;                   /* panels and cards */
  --void: #05060A;                         /* deepest surface */
  --grid-line: rgba(138, 160, 184, 0.07);
  --ink: #E7F4FF;
  --ink-soft: #8AA0B8;
  --ink-faint: #5E6A7A;
  --rust: #00C8FF;                         /* the accent */
  --rust-hi: #6FE0FF;
  --hairline: rgba(138, 160, 184, 0.16);
  --danger: #FF5A6E;

  --serif-display: 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --serif-body: 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  --app-accent: #00C8FF;

  color-scheme: dark;
}

[data-app="bench"] { --app-accent: #00C8FF; }

/* The apps used to carry individual identity colours — teal for Loom and
   KeyStep, purple for Modulus, rust for the rest. The console theme has one
   accent, so those overrides are gone rather than left set to the same value
   twelve times. If per-app hues come back they belong here. */

/* ---------- The header ----------
   Three regions: brand lockup, app switcher, then a right-hand slot the app
   fills with its own controls. Apps add controls to .topbar-right; they
   don't restyle the bar. */

.ts-topbar,
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  row-gap: 10px;
  gap: 16px;
  padding: 13px 22px;
  border-bottom: 1px solid var(--hairline);
  background: linear-gradient(180deg, rgba(138, 160, 184, 0.05), transparent);
}

.brand-lockup {
  display: flex;
  align-items: center;
  gap: 11px;
  text-decoration: none;
  flex: none;
}
.brand-mark { flex: none; display: block; }
.brand-lockup-text { display: flex; flex-direction: column; line-height: 1.2; }

/* App name prominent and coloured, studio name grey underneath — the
   pattern Transient Lab's hub strip already used and everyone else now
   matches. */
.brand-appname {
  font-family: var(--serif-display);
  font-weight: 600;
  font-size: 15px;
  letter-spacing: 0.01em;
  color: var(--app-accent);
}
.brand-sub {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.05em;
  color: var(--ink-faint);
}
.brand-lockup:hover .brand-appname { filter: brightness(1.12); }

.topbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-left: auto;
}

/* ---------- Status pill ---------- */

/* ==========================================================================
   Buttons
   ==========================================================================
   Everything on this site used to be a pill: the primary action, the undo
   button, a navigation link and a live device readout all rendered as the
   same rounded capsule at the same size. Nothing looked more important than
   anything else, and a status display looked like something you could press.

   Two changes fix most of it. First, a hierarchy — primary, secondary,
   quiet — so weight tells you what a control is for. Second, square corners:
   a 999px capsule is a consumer-web convention, and this site is meant to
   look like equipment. Hardware has rectangular buttons.

     .btn            secondary — outlined, the default
     .btn.primary    the one action the panel exists for
     .btn.quiet      utilities: undo, clear, toggles
     .readout-pill   NOT a button. A live value. No border, not pressable.
   ========================================================================== */

.btn,
button.pill,
a.pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--hairline);
  border-radius: 3px;
  padding: 7px 13px;
  white-space: nowrap;
  cursor: pointer;
  text-decoration: none;
  transition: border-color .14s, color .14s, background .14s;
}
.btn:hover,
button.pill:hover,
a.pill:hover { color: var(--ink); border-color: var(--rust); }

.btn.primary {
  background: var(--rust);
  border-color: var(--rust);
  color: var(--paper);
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  padding: 8px 17px;
}
.btn.primary:hover { filter: brightness(1.12); color: var(--paper); }

/* Utilities should recede. No border at all until you go near them. */
.btn.quiet {
  border-color: transparent;
  color: var(--ink-faint);
  padding: 7px 9px;
}
.btn.quiet:hover { color: var(--ink); background: rgba(138, 160, 184, 0.07); border-color: transparent; }

.btn[aria-pressed="true"]:not(.primary) {
  color: var(--rust);
  border-color: var(--rust);
  background: rgba(0, 200, 255, 0.10);
}
.btn:disabled { opacity: .4; cursor: default; }
.btn:disabled:hover { color: var(--ink-soft); border-color: var(--hairline); background: transparent; }

/* A live value wearing a border looks pressable, and people press it. This
   is deliberately not a button: no border, no background, no pointer. */
.pill,
.readout-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.05em;
  color: var(--ink-faint);
  background: transparent;
  border: 0;
  padding: 6px 4px;
  white-space: nowrap;
}
.status-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--ink-faint);
  flex: none;
}
.status-dot.live { background: var(--app-accent); }

/* ---------- Shared session banner ----------
   Shown when an app reuses the input device chosen in another app, so the
   continuity is visible rather than mysterious. */

.ts-session-note {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-soft);
  background: rgba(0, 200, 255, 0.08);
  border: 1px solid rgba(0, 200, 255, 0.28);
  border-radius: 8px;
  padding: 9px 12px;
  margin: 0 0 14px 0;
}
.ts-session-note strong { color: var(--ink); font-weight: 600; }
.ts-session-note button {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--hairline);
  border-radius: 3px;
  padding: 4px 10px;
  cursor: pointer;
  margin-left: auto;
}
.ts-session-note button:hover { color: var(--ink); border-color: var(--app-accent); }

/* ---------- Cross-app links ----------
   "See this in the Oscilloscope" style links between tools that are about
   the same gear. This is what makes the set read as one studio rather than
   eleven tabs that share a stylesheet. */

.ts-related {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--ink-faint);
  padding: 10px 0 0 0;
}
.ts-related-label {
  letter-spacing: 0.09em;
  text-transform: uppercase;
  font-size: 9.5px;
}
.ts-related a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--ink-soft);
  text-decoration: none;
  background: rgba(138, 160, 184, 0.06);
  border: 1px solid var(--hairline);
  border-radius: 3px;
  padding: 5px 11px;
  transition: color .2s, border-color .2s;
}
.ts-related a:hover { color: var(--app-accent); border-color: var(--app-accent); }
.ts-related a::after {
  content: '\2192';
  opacity: 0.5;
}

@media (max-width: 760px) {
  .ts-topbar, .topbar { padding: 11px 16px; gap: 10px; }
  .brand-appname { font-size: 14px; }
  .topbar-right { gap: 8px; }
}

/* ==========================================================================
   Control panel components
   ==========================================================================
   Lifted from the Oscilloscope, which had the most carefully worked-out
   panel on the site, and promoted here because seven app stylesheets had
   already grown their own near-copies. Those copies had drifted: different
   paddings, different heading sizes, some with icon chips and some without.
   This is now the definition; an app should use these classes rather than
   redeclaring them.

   The vocabulary is small on purpose:

     .card            a bordered panel holding one group of controls
     .card-title      mono uppercase heading with an icon chip
     .chip            the 18px tinted square the icon sits in
     .row             one label + one control, label left, control right
     .readout         a definition list of live values, right-aligned
     .field           a stacked label-above-control, for wider inputs

   The chip hues carry meaning across the site — amber for oscillators and
   sources, violet for modulation, teal for envelopes and time, pink for
   randomness, blue for routing, cyan for output. Keep that mapping rather
   than picking a colour per app.
   ========================================================================== */

.panel-stack {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.card {
  background: var(--paper-deep);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  padding: 16px;
}

/* Only h3 is claimed inside a card. Several apps use h2 there as a readable
   content title — "Tune Up", "Choose what to test" — and blanket-styling it as
   an 11px mono micro-label produced a hybrid: the app's own font-size won but
   the shared text-transform did not, giving a 17px uppercase serif nobody
   asked for. Control-group labels are h3 or .card-title. */
.card-title,
.card > h3,
.panel-group h2,
.panel-group h3 {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-soft);
  margin: 0 0 12px;
}

.chip {
  width: 18px;
  height: 18px;
  border-radius: 6px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.chip svg { width: 11px; height: 11px; }
.chip.amber  { background: rgba(255, 180, 84, 0.15);  color: #FFB454; }
.chip.violet { background: rgba(169, 140, 255, 0.15); color: #A98CFF; }
.chip.teal   { background: rgba(63, 217, 196, 0.15);  color: #3FD9C4; }
.chip.pink   { background: rgba(255, 123, 176, 0.15); color: #FF7BB0; }
.chip.blue   { background: rgba(111, 168, 255, 0.15); color: #6FA8FF; }
.chip.cyan   { background: rgba(0, 200, 255, 0.15);   color: var(--rust); }

.row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
}
.row:last-child { margin-bottom: 0; }
.row label { font-size: 12px; color: var(--ink); }
.row select { width: auto; flex: 1; }
.row input[type="range"] { flex: 1; accent-color: var(--rust); }
.row input[type="checkbox"] { accent-color: var(--rust); }

/* A stacked field, for controls too wide to sit beside their label. */
.field { display: block; margin-bottom: 14px; }
.field:last-child { margin-bottom: 0; }
/* Apps use both <span> and <label> for the caption above a stacked control,
   so both are covered rather than forcing a markup change per app. */
.field > span,
.field > label {
  display: block;
  font-family: var(--mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
  margin-bottom: 6px;
}
.field select, .field input[type="text"], .field input[type="number"] { width: 100%; }

/* A caption carrying its own current value on the right — "DENSITY   85%".
   Three apps grew this shape separately; it belongs with .field. */
.field > label.with-value,
.field > span.with-value {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
.field .out {
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink-soft);
}

/* Live values. The label is dim and set in the body face; the number is mono
   and accented, so a column of them reads as data rather than as prose. */
.readout dl {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 6px 10px;
}
.readout dt { font-size: 12px; color: var(--ink-soft); }
.readout dd {
  margin: 0;
  font-family: var(--mono);
  font-size: 13px;
  color: var(--rust);
  text-align: right;
}

/* The same thing as a single line, where a <dl> would be overkill. */
.readout-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-top: 7px;
}
.readout-line b {
  font-weight: 400;
  font-size: 12px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--rust);
}

/* Form controls, so every app's select and slider match. */
.card select,
.card input[type="text"],
.card input[type="number"],
.card input[type="search"] {
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 6px;
  padding: 8px 10px;
  font-family: var(--serif-body);
  font-size: 13px;
  outline: none;
}
.card select:focus,
.card input:focus { border-color: var(--rust); }
.card input[type="range"] { accent-color: var(--rust); }

/* Sliders are styled rather than left native because the native track is a
   different height in every browser, and a column of them reads as ragged. */
input[type="range"].tsl-range {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 4px; border-radius: 2px;
  background: var(--hairline); outline: none;
}
input[type="range"].tsl-range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 13px; height: 13px; border-radius: 50%;
  background: var(--rust); cursor: pointer; border: 2px solid var(--paper);
}
input[type="range"].tsl-range::-moz-range-thumb {
  width: 13px; height: 13px; border-radius: 50%; border: 2px solid var(--paper);
  background: var(--rust); cursor: pointer;
}

/* A slider with its value beside it. Three columns — caption, track, reading —
   so a stack of them lines up on both edges instead of the numbers wandering
   with the length of each label. */
.slider-row {
  display: grid;
  grid-template-columns: 64px 1fr 46px;
  align-items: center;
  gap: 8px;
  margin: 7px 0;
}
.slider-row > label {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--ink-faint);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.slider-row > .val {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--rust);
  text-align: right;
}

/* A segmented button group — the pattern several apps grew independently. */
.seg { display: flex; gap: 4px; flex-wrap: wrap; }
.seg button {
  flex: 1; min-width: 60px;
  background: var(--paper); border: 1px solid var(--hairline);
  color: var(--ink-soft); border-radius: 3px; padding: 7px 6px;
  font-family: var(--mono); font-size: 10.5px; cursor: pointer;
}
.seg button:hover { color: var(--ink); border-color: var(--rust); }
.seg button.active {
  background: var(--rust); border-color: var(--rust);
  color: var(--paper); font-weight: 600;
}
.seg button.active { border-radius: 3px; }

.card .hint {
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-faint);
  margin: 8px 0 0;
}
