/* Demo Gate — matte dark, lilac/cream accents. No gradients, no glow. */
:root {
  --bg: #0b0b0d;
  --surface: #131316;
  --surface-2: #1a1a1f;
  --border: #232329;
  --border-2: #3a3a44;   /* hover borders, focus, dashed wells */
  --text: #f2f2f4;
  --text-2: #c6c6cf;     /* body-adjacent: chips, filter rows */
  --text-3: #6a6a74;     /* quieter than muted: fine print, resting states */
  --muted: #8e8e98;
  --dim: #55555e;        /* furniture: the footer */
  --lilac: #cdbcf2;      /* pale accent — UI chrome only */
  --cream: #efe7cf;      /* pale accent — UI chrome only */
  --s-label: #8f6ae8;    /* data series: label */
  --s-demo: #bd8b31;     /* data series: demo */
  --amber: #d79a3a;      /* status: someone else's action on your record */
  --ok: #4ade80;
  --danger: #d96a6a;
  /* The pair every "you are here" fill is built from — white pill on dark,
     near-black pill on light. One attribute flips every active control. */
  --invert-bg: #fff;
  --invert-fg: #0b0b0d;
  --rail-bg: #0d0d10;
  /* The waveform's two states. Not an accent: the played half IS --lilac, and
     the rest is a neutral that has to stay legible against --surface on both
     grounds, which is why it is a token rather than an opacity on the same
     colour (a 30%-alpha lilac goes muddy on the light skin). */
  --wf-rest: #35353f;
  --wf-played: #cdbcf2;
  --scrim: rgba(6, 6, 8, 0.72);
  /* 10px, down from 18 — the same move the platform surface made on
     2026-08-26: the deep radius read as a consumer toy, and both products
     should wear the same corners. Controls sit one step tighter than cards,
     value tags one step tighter than controls. */
  --r: 10px;
  --r-ctl: 8px;
  --r-chip: 6px;
  --rail: 250px;         /* the consumer sidebar; a top bar below 900px */
  /* Outfit, matching nine71.ai and Signal. This file used to name "Inter" and
     never load it, so Foundry rendered in system-UI while the landing page
     rendered in Outfit. The data-series colours above are NOT touched: the
     violet/amber pair is contrast-validated on this surface (see HANDOFF.md). */
  --font: 'Outfit', -apple-system, 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', sans-serif;

  /* Native widgets (selects, the audio transport) follow the surface, not the OS. */
  color-scheme: dark;
}

/* ---------- light ----------
 * One attribute on <html>, set before first paint by the inline snippet in
 * index.html and flipped by js/theme.js. Only tokens change — every rule below
 * draws from them, so a colour that needs a light value it does not get is a
 * bug in the token list, not a case for a scoped override.
 *
 * Accents darken rather than survive: lilac/cream are highlight colours on
 * near-black and would be invisible ink on white. The data-series pair stays
 * literal — #8f6ae8 and #bd8b31 hold on both grounds, and repainting the
 * series would silently change what every existing chart claims. The sign-in
 * screenshot panel (.auth-shot) keeps its own dark ground on both themes: it
 * is a picture of the dark product, not a surface of this page. */
:root[data-theme="light"] {
  --bg: #f2f2f5;
  --surface: #ffffff;
  --surface-2: #ebebef;
  --border: #d8d8e0;
  --border-2: #b9b9c6;
  --text: #17171c;
  --text-2: #3d3d49;
  --text-3: #686873;
  --muted: #5c5c68;
  --dim: #6d6d78;
  --lilac: #6f56bd;
  --wf-rest: #c9c9d4;
  --wf-played: #6f56bd;
  --cream: #8a6d1f;
  --amber: #9a6d17;
  --ok: #2c7a4b;
  --danger: #b04545;
  --invert-bg: #17171a;
  --invert-fg: #ffffff;
  --rail-bg: #ececf0;
  --scrim: rgba(240, 240, 244, 0.75);

  color-scheme: light;
}

/* The wordmark ships as a white SVG (licensing: outlines, not a webfont — see
   the note at .wordmark-img). Pure white inverts to pure black exactly, so the
   light theme gets the black mark without a second asset to keep in step. */
:root[data-theme="light"] .wordmark-img { filter: invert(1); }

/* ---------- the theme toggle ----------
   Quiet by design: furniture, not a feature. The icon shows what pressing it
   GETS you — a sun while dark, a moon while light — swapped on the same
   attribute that swaps the tokens, so button and page cannot disagree. */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex: none;
  border: 1px solid var(--border); border-radius: 50%;
  background: none; color: var(--muted); cursor: pointer;
}
.theme-toggle:hover { color: var(--text); border-color: var(--border-2); }
.theme-toggle:focus-visible { outline: 2px solid var(--lilac); outline-offset: 2px; }
.theme-toggle .tt-moon { display: none; }
:root[data-theme="light"] .theme-toggle .tt-sun { display: none; }
:root[data-theme="light"] .theme-toggle .tt-moon { display: block; }
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 15px;
  -webkit-font-smoothing: antialiased;
  display: flex; flex-direction: column;
}
#shell { display: flex; flex-direction: column; min-height: 100vh; }

/* ---------- gate ---------- */
.gate {
  position: fixed; inset: 0; background: var(--bg); z-index: 50;
  display: flex; align-items: center; justify-content: center;
}
.gate-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  padding: 40px 44px; width: 360px; text-align: center;
}
.gate-card .wordmark { justify-content: center; margin-bottom: 10px; }
.gate-card form { display: flex; gap: 8px; margin-top: 18px; }
.gate-card input { flex: 1; }
.gate-error { color: var(--danger); font-size: 13px; margin-top: 12px; min-height: 16px; }

/* ---------- header ---------- */
header {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 18px 30px;
}
.wordmark { font-size: 19px; display: flex; align-items: baseline; gap: 0; }
.module-label { color: var(--muted); font-size: 12px; font-weight: 500; text-transform: none; letter-spacing: 0; margin-left: 10px; align-self: baseline; }
.pills { display: flex; gap: 4px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-ctl); padding: 4px; }
.pill {
  border: none; background: none; color: var(--muted); font-size: 13.5px; font-weight: 500;
  padding: 8px 20px; border-radius: var(--r-chip); cursor: pointer; font-family: inherit;
}
.pill.active { background: var(--invert-bg); color: var(--invert-fg); }
.header-right { min-width: 110px; text-align: right; }
.status-chip {
  font-size: 12px; color: var(--muted); border: 1px solid var(--border);
  border-radius: var(--r-chip); padding: 5px 12px;
}
.status-chip.ok { color: var(--cream); }

/* ---------- layout ---------- */
/* Fluid gutters (2026-08-26): fixed 24px padding on a fixed cap read as dead
   margin on any real monitor. The gutter now scales with the viewport. */
main {
  flex: 1; width: 100%; max-width: 1360px; margin: 0 auto;
  padding: 10px clamp(16px, 2.6vw, 40px) 48px;
}
.view { display: none; }
.view.active { display: block; }
.cols { display: grid; grid-template-columns: 300px 1fr; gap: 16px; align-items: start; }
.cols.half { grid-template-columns: 1fr 1fr; }
.col-main { display: flex; flex-direction: column; gap: 16px; }
#label-body { display: flex; flex-direction: column; gap: 16px; }
@media (max-width: 900px) { .cols, .cols.half { grid-template-columns: 1fr; } }

.card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  padding: 20px 22px;
}
.card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
h2 { font-size: 15.5px; font-weight: 600; letter-spacing: -0.2px; }
.small-cap { font-size: 12px; }
.muted { color: var(--muted); font-size: 13.5px; line-height: 1.55; }
.empty-card { padding: 34px; text-align: center; }

/* ---------- controls ---------- */
input[type=text], input[type=password], input[type=email] {
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
  border-radius: 10px; padding: 9px 12px; font-size: 14px; font-family: inherit; outline: none;
  width: 100%;
}
input:focus { border-color: var(--border-2); }
.btn {
  font-family: inherit; font-size: 13.5px; font-weight: 500; cursor: pointer;
  border-radius: 10px; padding: 9px 16px; border: 1px solid var(--border);
  background: var(--surface-2); color: var(--text);
}
.btn.primary { background: var(--invert-bg); color: var(--invert-fg); border-color: var(--invert-bg); }
.btn.ghost { background: none; color: var(--muted); }
.btn.ghost:hover { color: var(--danger); border-color: var(--danger); }
.btn.small { padding: 7px 13px; font-size: 12.5px; }
/* An <a class="btn"> is an inline box by default, so its vertical padding
   overflows the line box rather than pushing it — the anchor draws ON TOP of
   the paragraph above it. Every other .btn here is a <button> (inline-block
   already) or a flex item (blockified), which is why this only surfaced when
   the track studio put a link-shaped button under a paragraph.
   text-decoration goes too: a button with an underline is two affordances. */
a.btn { display: inline-block; text-decoration: none; }
/* A disabled .btn.primary is white on white text and looks exactly like an
   enabled one, so the only feedback for pressing it was nothing happening. */
.btn:disabled { opacity: 0.42; cursor: default; }
.inline-form { display: flex; gap: 8px; margin-bottom: 14px; }

/* ---------- label list ---------- */
.label-list { display: flex; flex-direction: column; gap: 4px; }
.label-item {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 10px 12px; border-radius: 12px; cursor: pointer; border: 1px solid transparent;
}
.label-item:hover { background: var(--surface-2); }
.label-item.active { background: var(--surface-2); border-color: var(--border); }
.label-item .n { font-size: 14px; font-weight: 500; }
.label-item .c { font-size: 12px; color: var(--muted); }

/* ---------- tiles ---------- */
.tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin: 14px 0 4px; }
@media (max-width: 700px) { .tiles { grid-template-columns: repeat(2, 1fr); } }
.tile { background: var(--surface-2); border: 1px solid var(--border); border-radius: 14px; padding: 14px 16px; }
.tile-num { font-size: 26px; font-weight: 700; letter-spacing: -0.5px; }
.tile-cap { font-size: 11.5px; color: var(--muted); margin-top: 3px; text-transform: uppercase; letter-spacing: 0.6px; }

/* ---------- chips ----------
   Squared tokens, not capsules (2026-08-26, with the platform restyle): a tag
   naming a value is a small squared label on both products now. Meters and
   progress bars keep their round ends — they are the moving parts. */
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.chip {
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text-2);
  font-size: 12px; padding: 3px 9px; border-radius: var(--r-chip); white-space: nowrap;
}

/* ---------- dropzone ---------- */
.dropzone {
  border: 1.5px dashed var(--border-2); border-radius: var(--r); padding: 28px 18px; text-align: center;
  cursor: pointer; display: flex; flex-direction: column; gap: 5px;
}
.dropzone:hover, .dropzone.drag { border-color: var(--lilac); background: var(--surface-2); }
.dz-big { font-size: 15px; font-weight: 600; }
.dz-small { color: var(--muted); font-size: 12.5px; }
.progress { margin-top: 12px; color: var(--cream); font-size: 13.5px; }
.hidden { display: none !important; }

/* ---------- tracks ---------- */
.track-row { border-top: 1px solid var(--border); }
.track-row:first-child { border-top: none; }
.track-row summary {
  list-style: none; display: flex; align-items: center; gap: 12px; padding: 12px 4px; cursor: pointer;
}
.track-row summary::-webkit-details-marker { display: none; }
.track-row .caret { color: var(--muted); font-size: 10px; transition: transform .15s; width: 12px; }
.track-row[open] .caret { transform: rotate(90deg); }
.track-row .t-name { flex: 1; font-size: 14px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.track-row .t-meta { color: var(--muted); font-size: 12.5px; white-space: nowrap; }
.track-body { padding: 2px 4px 16px 26px; }

/* ---------- test results ---------- */
#test-result { display: flex; flex-direction: column; gap: 16px; margin-top: 16px; }
.hero { display: flex; align-items: center; gap: 26px; }
/* The band replaced a 56px number. It is words now, so it wraps and it cannot be
   set at display size — the old rule was tuned for two digits and turned "Middle
   match" into two lines of near-headline type. */
.hero-band {
  font-size: 27px; font-weight: 800; letter-spacing: -0.4px; line-height: 1.1;
  max-width: 190px;
}
.hero-band.good { color: var(--lilac); }
.hero-band.mid { color: var(--cream); }
.hero-band.bad { color: var(--muted); }
.hero-cap { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.6px; margin-top: 6px; }
.hero-text h2 { font-size: 18px; margin-bottom: 4px; }

/* "See why" — the result's own explainer, folded until asked for. */
.see-why { border-top: 1px solid var(--border); margin-top: 14px; }
.see-why summary {
  list-style: none; cursor: pointer; padding: 11px 0 2px;
  font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.6px;
}
.see-why summary::-webkit-details-marker { display: none; }
.see-why summary::after { content: ' +'; }
.see-why[open] summary::after { content: ' \2212'; }
.see-why summary:hover { color: var(--cream); }
.see-why p { font-size: 13px; color: var(--muted); line-height: 1.55; margin: 8px 0 0; }
.see-why p b { color: var(--cream); font-weight: 600; }
.see-why p:last-child { padding-bottom: 10px; }

.rank-row { border-top: 1px solid var(--border); }
.rank-row:first-child { border-top: none; }
.rank-more-row { padding: 10px 4px 4px; border-top: 1px solid var(--border); }
.rank-row summary { list-style: none; display: flex; align-items: center; gap: 14px; padding: 13px 4px; cursor: pointer; }
.rank-row summary::-webkit-details-marker { display: none; }
.rank-row .r-pos { color: var(--muted); font-size: 12.5px; width: 18px; }
.rank-row .r-name { flex: 0 0 190px; font-size: 14px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rank-row .r-bar { flex: 1; background: var(--surface-2); border-radius: 999px; height: 8px; overflow: hidden; }
.rank-row .r-bar div { background: var(--s-label); height: 100%; border-radius: 999px; }
/* .r-score is gone with the number it held; the band carries the meaning and the
   bar carries the comparison. */
.rank-row .r-verdict {
  flex: 0 0 130px; text-align: right; font-size: 12.5px; font-weight: 600;
  color: var(--muted);
}
.rank-row .r-verdict.good { color: var(--lilac); }
.rank-row .r-verdict.mid { color: var(--cream); }
.rank-body { padding: 6px 4px 18px 36px; }
.rank-body h3, .track-body h3 { font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.7px; color: var(--muted); margin: 14px 0 8px; }

.sim-table { width: 100%; border-collapse: collapse; }
.sim-table td { padding: 5px 8px 5px 0; font-size: 13.5px; vertical-align: middle; }
.sim-table td:first-child { max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sim-table .bar { background: var(--surface-2); border-radius: 999px; height: 6px; min-width: 90px; overflow: hidden; }
.sim-table .bar div { background: var(--s-label); height: 100%; border-radius: 999px; }
.sim-table .pct { color: var(--muted); width: 36px; text-align: right; font-size: 12.5px; }

/* ---------- charts ---------- */
.chart { width: 100%; }
.chart svg { display: block; width: 100%; height: auto; }
.chart text { font-family: inherit; }
.legend { display: flex; gap: 16px; margin-bottom: 8px; }
.legend span { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
.legend i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }

.tooltip {
  position: fixed; z-index: 60; pointer-events: none;
  background: var(--surface-2); border: 1px solid var(--border-2); border-radius: 8px;
  padding: 6px 10px; font-size: 12.5px; color: var(--text);
}

footer { text-align: center; color: var(--dim); font-size: 12px; padding: 16px; }

/* ===========================================================================
   Consumer Foundry (/foundry/)
   The catalogue-management UI above is /foundry/manage/ and shares this file.
   =========================================================================== */

.foundry-consumer main { max-width: 760px; }
.foundry-consumer footer {
  display: flex; justify-content: center; gap: 18px; align-items: baseline;
}
.foundry-consumer footer a { color: var(--dim); text-decoration: none; }
.foundry-consumer footer a:hover { color: var(--muted); }
.home-link { text-decoration: none; color: inherit; }

/* The one thing on the page, so it gets room rather than a card. */
.intro { padding: 34px 4px 26px; }
.intro h1 {
  font-size: 34px; font-weight: 600; letter-spacing: -0.9px; line-height: 1.15;
  margin-bottom: 12px;
}
.lede { color: var(--muted); font-size: 15px; line-height: 1.6; max-width: 60ch; }
@media (max-width: 600px) { .intro h1 { font-size: 27px; } }

.privacy-note { color: var(--text-3); font-size: 12px; margin-top: 12px; }
/* Ruled, like the platform's card heads: the line is what makes a cap read as
   the header of a region instead of a stray small word (2026-08-26). */
.section-cap {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.7px;
  color: var(--muted); margin: 18px 0 8px;
  padding-bottom: 5px; border-bottom: 1px solid var(--border);
}
.vibe { margin-top: 6px; }
.spent-note { margin: 14px 0 12px; }

.label-pick { margin-bottom: 14px; }

/* ---------- the send picker ----------
   Checkboxes in a scrolling well rather than a <select multiple>: on a phone a
   multi-select is a scroll trap that drops every previous choice on a mistap, and
   this is the control that decides who receives someone's unreleased music.

   The well is capped in height and scrolls. Twenty-three labels laid out in full
   would push the drop zone — the thing this screen is for — below the fold. */
.label-send { margin-bottom: 14px; }
.send-picker {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 2px 12px;
  max-height: 208px; overflow-y: auto;
  border: 1px solid var(--border); border-radius: 12px;
  background: var(--surface-2); padding: 10px 12px;
}
.send-opt { padding: 5px 0; font-size: 13.5px; align-items: center; }
.send-opt.on { color: var(--text); }
/* A group caption inside the well. Full-width so the grid restarts under it —
   without the span the caption becomes a grid cell and the first label sits
   beside it as though "Labels" were somebody's name. */
.send-group {
  grid-column: 1 / -1;
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.7px;
  color: var(--muted); padding: 8px 0 3px;
}
.send-group:first-child { padding-top: 2px; }
/* The artist's label association, said quietly beside the name. */
.send-parent { font-style: normal; color: var(--muted); font-size: 11.5px; margin-left: 4px; }
/* A closed inbox stays listed, says so, and is still pickable (migration 019):
   the send waits in line and arrives if they reopen. Muted because the state
   deserves reading before the tick, not because the choice is off the table. */
/* WAITLISTED (NIN-52) — seeded, nobody has claimed the profile. Three states
   have to be told apart at a glance in this well, and dimming alone cannot do
   it: send_only is undimmed by rule, open is undimmed and plain, so the
   waitlisted row carries a mark of its own as well as the dim. A left rule,
   not a filled chip or a colour: the row is still PICKABLE, and anything that
   reads as a warning badge would say "you cannot send here" when the whole
   point of the state is that you can. */
.send-opt.send-closed { color: var(--dim); border-left: 2px solid var(--border); padding-left: 8px; }
.send-wait { color: var(--muted); }
/* The sentence, once, full width so it reads as prose rather than as a
   name in a column. Not .send-group: that caption is uppercase and
   letter-spaced for one-word group names, and a ninety-character sentence set
   that way is unreadable. */
.send-wait-note {
  grid-column: 1 / -1;
  margin: 8px 0 4px; padding-top: 8px;
  border-top: 1px solid var(--border);
  color: var(--muted); font-size: 11.5px; line-height: 1.45;
}
.send-wait-note:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
/* "N more in the directory" — the pointer to everything the default list is
   deliberately not showing. Full width for the same reason the sentence is:
   as a grid cell it reads as somebody's name. */
.send-more { grid-column: 1 / -1; margin: 8px 0 0; }
/* send_only (migration 092) gets NO rule here, deliberately. It is not
   send-closed and must not be dimmed: sending is the one thing that works, so
   the row stays fully legible and only its note says the fit test is gone. The
   greying lives on the disabled <option> in the test dropdown, which the
   browser styles for us. */
/* At the cap the unticked options disable rather than disappear. A label that
   vanished from the list would read as "they stopped taking demos", which is a
   different and much worse claim than "you have picked five". */
.send-opt input[type=checkbox]:disabled { opacity: 0.3; cursor: not-allowed; }
.send-opt:has(input:disabled) { color: var(--dim); cursor: not-allowed; }
#send-picker-note { margin-left: 0; margin-top: 8px; }

/* The confirmation. A rule down the edge, no filled panel: the send already
   happened and cannot be undone, so this is a receipt rather than an alert.

   --s-label, NOT a green. There is no success colour in this file and inventing
   one would be a fourth accent; more to the point the pair already carries
   meaning — --s-label is the catalogue (the rank bars, the sign-in frame, the
   "for labels" panel) and --s-demo is the producer's own record (the .notice in
   My tracks). This card is about labels receiving something, so it takes the
   catalogue accent. */
.sent-card { border-left: 2px solid var(--s-label); }
.sent-card h2 { font-size: 18px; }

.field-label {
  display: block; font-size: 11.5px; text-transform: uppercase;
  letter-spacing: 0.7px; color: var(--muted); margin-bottom: 6px;
}
.set-select {
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
  border-radius: 10px; padding: 9px 12px; font-size: 14px; font-family: inherit;
  outline: none; width: 100%;
}

/* Keyboard focus on the dropzone: it is a div acting as a button, so it needs a
   visible focus ring of its own. */
.dropzone:focus-visible { outline: 2px solid var(--lilac); outline-offset: 2px; }

.foundry-consumer .btn.ghost:hover { color: var(--text); border-color: var(--border-2); }

/* A ranked row that is not expandable: the consumer surface has nothing to show
   underneath, because per-track similarities would mean shipping the catalogue. */
.rank-row.static {
  display: flex; align-items: center; gap: 14px; padding: 13px 4px;
}
.rank-row.static .r-name { flex: 0 0 190px; }
@media (max-width: 620px) {
  .rank-row.static { flex-wrap: wrap; gap: 8px 12px; }
  .rank-row.static .r-name { flex: 1 1 auto; }
  .rank-row.static .r-verdict { flex: 0 0 auto; }
}

.nudge { display: flex; align-items: center; gap: 18px; justify-content: space-between; }
.nudge .muted { margin: 0; }
.nudge .btn { flex: 0 0 auto; }
@media (max-width: 600px) { .nudge { flex-direction: column; align-items: flex-start; } }

.notice {
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--s-demo); border-radius: 12px;
  padding: 13px 16px; margin-bottom: 16px;
  color: var(--muted); font-size: 13px; line-height: 1.55;
}
.notice strong { color: var(--text); font-weight: 600; }
.notice code { font-size: 12px; color: var(--cream); }

/* ---------- sign-in fragments (the account modal moved into #view-profile) ---------- */
.modal-alt { margin-top: 16px; font-size: 13px; color: var(--muted); }
.linkish {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--lilac); font: inherit; font-size: 13px; text-decoration: underline;
}

/* The wordmark is an SVG, not text. Coolvetica's licence permits "logo" and
   "web page (not embedded)" but forbids "web page (embedded)", so it cannot ship
   as an @font-face webfont — outlines are the licensed route. Height in em so the
   existing font-size rules still control its scale; 0.78 matches the optical size
   the live text had, since a glyph's ink is shorter than its em box. */
.wordmark-img { display: block; height: 0.78em; width: auto; }

/* ---------- sign-in: providers, phone, profile ----------
   Provider buttons are full-width and stacked, in the order a person is most
   likely to want them. They read as one group with the form below rather than
   as a second, competing choice. */
.auth-providers { display: flex; flex-direction: column; gap: 8px; margin-top: 18px; }

.btn.provider {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text);
  font-weight: 500;
}
.btn.provider:hover { border-color: var(--border-2); }
.btn.provider svg { flex: none; }

/* Load-bearing. `display: flex` above beats the UA stylesheet's `[hidden] {
   display: none }`, so an unconfigured provider stayed fully visible while the
   DOM property said hidden — a button that reports itself absent and renders
   anyway, which is the worst of both. Measured: provider-google.hidden === true
   with the button plainly on screen. */
.btn.provider[hidden] { display: none; }

/* The wall's two doors ("Sign in and get started buttons", 2026-08-28): equal
   halves, the pressed one wearing primary via openSignup. */
.auth-modes { display: flex; gap: 9px; margin-top: 16px; }
.auth-modes .auth-mode { flex: 1; }

/* A rule with the word set into it, rather than a bare line: the line alone
   reads as a section break and people miss that the form below is an
   alternative to the buttons above. */
.auth-or {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 16px 0 4px;
  color: var(--muted);
  font-size: 12px;
}
.auth-or::before, .auth-or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

#phone-form { display: flex; flex-direction: column; gap: 8px; margin-top: 18px; }
#phone-hint { margin-top: 2px; }

/* The reCAPTCHA host must stay in the DOM but never take layout: Firebase
   renders into it, and `display: none` makes the invisible challenge fail to
   mount rather than making it invisible. */
#recaptcha-host { position: fixed; bottom: 0; left: 0; width: 0; height: 0; overflow: hidden; }

/* The consumer surface has no NAVIGATION <header> — its navigation is the rail
   in the "workspace shell" section at the foot of this file, which handles its
   own collapse to a top bar. The media query that used to live here wrapped
   `.foundry-consumer header` and `#mode-pills`, neither of which exists on that
   page any more; /foundry/manage/ still uses the header and pills above and is
   deliberately untouched by any of the shell rules.

   IT DOES HAVE ONE <header> ELEMENT SINCE 2026-10-02: the profile's own, which
   is a section header and not a navigation bar. The bare `header` rule above is
   a page-chrome rule written when the only header on any of these documents was
   /manage/'s top bar, and an element selector cannot tell the two apart — so it
   laid the profile header out as a flex row with 18px/30px of chrome padding,
   and the name rendered one letter per line inside a 146px column. The profile
   header resets it where it is defined, at the foot of this file, rather than
   that rule being narrowed here: narrowing it would be the larger change, and
   /manage/ is the surface this stylesheet is least able to re-photograph. */

/* ===========================================================================
   The record queue and My tracks (/foundry/)
   =========================================================================== */

/* EVERY control the record form uses is named here, and that is deliberate.
   The rules further up this file select on `input[type=text]` and
   `input[type=password]` — a typed selector is blind to any type it does not
   list, and the phone step already shipped once with the browser's default
   WHITE box sitting inside a dark card because `tel` was missing from the list.
   The record form adds a select, a textarea and a checkbox, none of which any
   existing rule reaches. */
.field-input,
.foundry-consumer textarea,
.foundry-consumer select {
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
  border-radius: 10px; padding: 9px 12px; font-size: 14px; font-family: inherit;
  outline: none; width: 100%; display: block;
}
.foundry-consumer textarea { resize: vertical; min-height: 54px; line-height: 1.5; }
/* appearance:none is what stops macOS painting a native light-grey select into a
   dark card — and it also removes the disclosure arrow, leaving something that
   looks like a text input and does not invite a click. The arrow is drawn back
   as an inline SVG data URI: an external asset here would be one more request
   that can 404 and leave the control looking inert. */
.foundry-consumer select {
  appearance: none; cursor: pointer; padding-right: 34px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%238e8e98' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 12px center;
}
.foundry-consumer .field-input:focus,
.foundry-consumer textarea:focus,
.foundry-consumer select:focus { border-color: var(--border-2); }
/* The dark theme applies to the OPEN list too. Without this the menu that drops
   out of a dark select is white on macOS, which is the same failure as the
   white input box, one interaction later where a screenshot will not catch it. */
.foundry-consumer select option { background: var(--surface-2); color: var(--text); }

.queue-item {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  padding: 18px 22px 20px; margin-top: 16px;
}
#queue { display: flex; flex-direction: column; }
.qi-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding-bottom: 12px; border-bottom: 1px solid var(--border);
}
.qi-file {
  font-size: 14px; font-weight: 600; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.qi-state { font-size: 12px; color: var(--muted); white-space: nowrap; }
.qi-state.ok { color: var(--cream); }
.qi-state.bad { color: var(--danger); }

.qi-form { padding-top: 16px; display: flex; flex-direction: column; gap: 12px; }
.field-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 600px) { .field-pair { grid-template-columns: 1fr; } }

/* A real fieldset, not a div with a heading. The rights questions are one group
   and a screen reader should be told so. */
.rights {
  border: 1px solid var(--border); border-radius: 12px; padding: 14px 16px 16px;
  display: flex; flex-direction: column; gap: 8px;
}
.rights legend {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.7px;
  color: var(--muted); padding: 0 6px;
}
.rights .field-label { margin-top: 6px; margin-bottom: 0; }
.check {
  display: flex; align-items: flex-start; gap: 10px;
  font-size: 14px; line-height: 1.45; cursor: pointer;
}
.check input[type=checkbox] {
  appearance: none; flex: none; width: 17px; height: 17px; margin-top: 1px;
  border: 1px solid var(--border-2); border-radius: 5px; background: var(--surface-2);
  cursor: pointer; position: relative;
}
.check input[type=checkbox]:checked { background: var(--lilac); border-color: var(--lilac); }
.check input[type=checkbox]:checked::after {
  content: ""; position: absolute; left: 5px; top: 1px;
  width: 4px; height: 9px; border: solid var(--invert-fg);
  border-width: 0 2px 2px 0; transform: rotate(45deg);
}
.check input[type=checkbox]:focus-visible { outline: 2px solid var(--lilac); outline-offset: 2px; }
.fine { color: var(--text-3); font-size: 12px; line-height: 1.5; margin: -2px 0 2px 27px; }

/* ---------- first-run onboarding ----------
   A full screen like the sign-in wall, one centred column, three steps of one
   form. EVERY colour here is a token: this is the first screen a new account
   sees and it must hold in both themes before they have found the switch —
   which is also why the screen carries its own .theme-toggle, pinned to the
   corner the way the platform gate pins its own. Selected genre chips reuse
   .pf-chip.on (the --s-label violet with white text), which is contrast-safe
   on both grounds; no new selected state is invented here. */
.onboard {
  position: fixed; inset: 0; z-index: 70; background: var(--bg);
  overflow-y: auto; display: flex; justify-content: center;
}
.onboard .theme-toggle { position: fixed; top: 18px; left: 18px; }
.ob-inner {
  width: 100%; max-width: 620px; padding: 56px 26px 64px;
  display: flex; flex-direction: column; gap: 10px;
}
.ob-inner .wordmark { font-size: 19px; margin-bottom: 26px; }
.ob-count { letter-spacing: 0.06em; text-transform: uppercase; }
.ob-inner h2 { font-size: 26px; font-weight: 600; letter-spacing: -0.6px; }
.ob-step { display: flex; flex-direction: column; gap: 14px; margin-top: 18px; }
.ob-step .pf-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.15rem; }
.ob-step input[type=text], .ob-step input[type=url] {
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
  border-radius: 10px; padding: 11px 13px; font-size: 14px; font-family: inherit;
  outline: none; width: 100%;
}
.ob-step input:focus { border-color: var(--border-2); }
.ob-avatar-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.ob-avatar-row .fine { flex: 1 1 200px; margin: 0; }
.ob-avatar-thumb {
  width: 56px; height: 56px; border-radius: 50%; object-fit: cover; flex: none;
  border: 1px solid var(--border);
}
.ob-genres {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  gap: 8px;
}
.ob-genres .pf-chip { width: 100%; text-align: left; border-radius: 10px; padding: 0.6rem 0.8rem; }

/* Step 0, the entry fork: two large choices in the same voice as the rail's
   pills — a name and a one-line explanation — because it is the same kind of
   decision. The org sub-form reveals underneath the picked side. */
.ob-fork { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.ob-fork-btn {
  display: flex; flex-direction: column; gap: 4px; text-align: left;
  font-family: inherit; cursor: pointer;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-radius: 12px;
  padding: 16px 16px 18px;
}
.ob-fork-btn:hover { border-color: var(--border-2); }
.ob-fork-btn.on { border-color: var(--lilac); }
.ob-fork-name { font-size: 15px; font-weight: 600; }
.ob-fork-sub { font-size: 12px; color: var(--text-3); line-height: 1.4; }
.ob-orgkinds { display: flex; gap: 8px; flex-wrap: wrap; }
@media (max-width: 560px) { .ob-fork { grid-template-columns: 1fr; } }

.ob-actions {
  display: flex; justify-content: space-between; align-items: center;
  margin-top: 26px; padding-top: 16px; border-top: 1px solid var(--border);
}
@media (max-width: 560px) {
  .ob-step .pf-row { grid-template-columns: 1fr; }
  .ob-inner { padding: 40px 18px 48px; }
}

/* A stored face over the initials. The image is a background so the circle's
   size, ring and fallback text all stay exactly what .avatar already is; the
   class only silences the initials while an image covers them. */
.avatar.has-img { background-size: cover; background-position: center; color: transparent; }

/* ---------- teams ----------
   A row per workspace: name, the hat they claimed, and either the door in or
   the claim's state in words. The form under it is one column — three controls
   and a button, nothing to compare. */
.team-row {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 2px; border-top: 1px solid var(--border);
}
.team-row:first-child { border-top: none; }
.team-name { font-size: 14px; font-weight: 500; flex: 1 1 160px; min-width: 0; }
.team-role { color: var(--muted); font-size: 12.5px; white-space: nowrap; }
.team-state { color: var(--muted); font-size: 12.5px; }
/* The workspace-profile editor, under the row it describes (migration 020).
   The producer form's field classes carry the look; this only shapes the well. */
.team-editor {
  display: grid; gap: 12px; max-width: 34rem;
  padding: 12px 14px 14px; margin: 2px 0 8px;
  border: 1px solid var(--border); border-radius: 12px;
  background: var(--surface-2);
}

/* ---------- cover art on the record form ----------
   A row, not a dropzone: the audio drop target is the one big gesture on this
   screen and a second one would compete with it. The <label> wraps the hidden
   file input so the button opens the picker with no JS of its own. */
.f-art-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.f-art-row .fine { flex: 1 1 220px; margin: 0; }
.f-art-pick { display: inline-flex; align-items: center; cursor: pointer; }
.f-art-thumb {
  width: 44px; height: 44px; object-fit: cover; flex: none;
  border-radius: 8px; border: 1px solid var(--border);
}

.qi-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.qi-error { font-size: 12.5px; color: var(--danger); }
/* The same slot, two meanings. "Confirm the rights, then test" is an
   instruction to someone who has not got there yet; in the danger colour it
   read as a rejection of a form they had not touched. */
.qi-error.hint { color: var(--muted); }
.qi-result:not(:empty) { display: flex; flex-direction: column; gap: 16px; margin-top: 16px; }
/* The result is rendered as cards inside a card. Flatten the inner ones so it
   does not read as a box in a box in a box. */
.qi-result .card { background: none; border: none; border-radius: 0; padding: 0; }
.qi-result .card + .card { border-top: 1px solid var(--border); padding-top: 16px; }

/* An anonymous visitor gets no form, so the card is a strip rather than a
   panel — otherwise it is a bordered box with one line in it. */
.queue-item.anon { padding-bottom: 4px; }
.queue-item.anon .qi-head { border-bottom: none; padding-bottom: 4px; }

/* ---------- My tracks ---------- */
#tracks-body { display: flex; flex-direction: column; gap: 16px; }
.record .rec-artist { color: var(--muted); font-weight: 400; font-size: 14px; margin-left: 8px; }
.rec-note { margin-top: 10px; }
.test-block { margin-top: 18px; }
.test-block .section-cap { margin-bottom: 4px; }
/* Muted to the point of being furniture. It is the absence of a claim, and it
   must never read as a status badge. */
/* A download HAPPENED, so it is not muted like the empty state next to it. Amber
   rather than the accent: it is news about someone else's action, not a score. */
.rec-activity.on { color: var(--text); }
.rec-activity.on strong { color: var(--amber); font-weight: 600; }
.rec-activity {
  margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--border);
  color: var(--text-3); font-size: 12px;
}
/* ---------- playback ----------
   The player is the first thing under the title, because a producer opening this
   screen is looking for their track and not for their own rights declaration
   read back to them. */
.rec-audio { margin-top: 14px; display: flex; flex-direction: column; gap: 6px; }
.rec-player { width: 100%; height: 34px; }
.rec-audio-state { color: var(--muted); font-size: 13px; }
.rec-audio-meta { color: var(--text-3); font-size: 11.5px; letter-spacing: 0.04em; }
/* Absence, stated. Muted to the level of the "not recorded yet" line below it —
   a record with no audio is not in an error state and must not be styled as one. */
.rec-audio-none {
  margin-top: 14px; color: var(--text-3); font-size: 12.5px; line-height: 1.5;
}

/* The one inline link on this screen. Left at the browser default it renders
   bright blue and underlined, which is the loudest thing on a page whose whole
   palette is greys — and it is a footnote. */
.privacy-note a { color: var(--muted); text-decoration: underline; text-underline-offset: 2px; }
.privacy-note a:hover { color: var(--text); }

.empty-card .btn { margin-top: 14px; }

.method-list { list-style: none; margin: 18px 0 0; padding: 0; }
.method-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
}
.method-row:last-child { border-bottom: none; }
.method-name { font-size: 14px; }
.method-state { font-size: 12px; color: var(--muted); }
.method-state.on { color: var(--ok); }

.profile-head { display: flex; align-items: center; gap: 14px; margin-bottom: 22px; }
.profile-head h2 { margin-bottom: 2px; }

/* ===========================================================================
   THE WORKSPACE SHELL (/foundry/ only)
   A fixed rail on the left, the work on the right. Everything here is scoped to
   .foundry-consumer: /foundry/manage/ shares this stylesheet and keeps the
   centred header and .pills above.
   =========================================================================== */

.foundry-consumer #shell { flex-direction: row; align-items: stretch; }

/* THE RAIL ONLY STICKS IF #shell IS TALLER THAN IT IS, and it was not.
   `html, body { height: 100% }` at the top of this file caps the body at the
   viewport, so #shell measured 100vh no matter how long the page got: the
   document scrolled, #shell did not grow, and `position: sticky` on .side had
   zero room to travel inside its containing block. The whole rail translated up
   with the page and left its border and background behind it — the empty column
   under the account button.
   Measured before and after on a 3,000px page: .side sat at top -1500 after
   scrolling 1500px, and sits at 0 with this rule, with #shell growing to 3742.
   Scoped to the consumer body because /foundry/manage/ shares this stylesheet
   and lays itself out with a header rather than a rail. */
body.foundry-consumer { height: auto; min-height: 100%; }

/* And the rail scrolls its OWN overflow rather than the page's. Without this a
   short viewport clips the account button off the bottom with no way to reach
   it, which is worse than the bug above. */
.foundry-consumer .side { overflow-y: auto; }

.side {
  flex: 0 0 var(--rail); width: var(--rail);
  display: flex; flex-direction: column;
  border-right: 1px solid var(--border);
  background: var(--rail-bg);
  padding: 22px 16px 18px;
  position: sticky; top: 0; height: 100vh;
}
/* The rail top is this page's top-left, so it carries the theme toggle beside
   the mark — wordmark leading, control trailing. */
.side-top {
  padding: 4px 8px 26px;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.side .wordmark { font-size: 19px; }
.side .module-label { margin-left: 9px; }

.side-nav { display: flex; flex-direction: column; gap: 2px; }
/* The group names in the rail (2026-08-13): the same quiet uppercase the
   platform's filter rail uses for its section heads. aria-hidden in the
   markup because the buttons under each name already say where they go — a
   reader hearing "Inbox, button, Activity" gains nothing from the label. */
.side-sec {
  font-size: 10.5px; color: var(--text-3); text-transform: uppercase;
  letter-spacing: 0.08em; padding: 14px 12px 4px;
}
.side-sec:first-child { padding-top: 2px; }

/* A rule and a weight change, not a filled capsule. The filled white pill this
   replaced was the brightest object on the page, which put the emphasis on the
   navigation instead of on the record being tested. The marker is drawn on a
   pseudo-element so the item does not shift by a border's width when it becomes
   current. */
.nav-item {
  position: relative;
  display: flex; flex-direction: column; gap: 2px;
  text-align: left; width: 100%;
  border: none; background: none; cursor: pointer;
  font-family: inherit; color: var(--muted);
  padding: 10px 12px 11px 15px;
  border-radius: 12px;
}
.nav-item::before {
  content: ""; position: absolute; left: 0; top: 9px; bottom: 9px;
  width: 2px; border-radius: 2px; background: transparent;
}
.nav-item:hover { background: var(--surface); color: var(--text-2); }
.nav-item.current { background: var(--surface); color: var(--text); }
.nav-item.current::before { background: var(--lilac); }
.nav-item:focus-visible { outline: 2px solid var(--lilac); outline-offset: 2px; }
.nav-name { font-size: 14px; font-weight: 500; }
.nav-sub { font-size: 11.5px; color: var(--text-3); line-height: 1.35; }
.nav-item.current .nav-sub { color: var(--muted); }

/* Pinned to the bottom by the spacer this margin creates. */
.side-foot { margin-top: auto; padding-top: 16px; border-top: 1px solid var(--border); }

.account {
  display: flex; align-items: center; gap: 11px; width: 100%;
  border: 1px solid transparent; background: none; cursor: pointer;
  font-family: inherit; text-align: left;
  padding: 9px 10px; border-radius: 12px;
}
.account:hover { background: var(--surface); border-color: var(--border); }
.account:focus-visible { outline: 2px solid var(--lilac); outline-offset: 2px; }
.account-lines { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.account-who {
  font-size: 13px; color: var(--text); font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 155px;
}
.account-act { font-size: 11.5px; color: var(--muted); }

/* Amber, because this is the producer's own account — the same thing the amber
   series means everywhere else in this product. It is a ring rather than a fill
   while signed out, so an empty state is visibly empty. */
.avatar {
  flex: none; width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 12.5px; font-weight: 600; letter-spacing: 0.02em;
  border: 1px solid var(--border-2); color: var(--muted); background: var(--surface-2);
}
.avatar.on { border-color: var(--s-demo); color: #14100a; background: var(--s-demo); }
.avatar.big { width: 44px; height: 44px; font-size: 17px; }

.work { flex: 1; min-width: 0; display: flex; flex-direction: column; }
/* Centred in the work column rather than pinned to the rail. With the rail on
   the left and nothing on the right, a left-aligned 780px column on a wide
   monitor put the drop target — the one thing this page is for — against the
   navigation, with the whole right half of the screen empty. */
.foundry-consumer .work main {
  max-width: 780px; margin: 0 auto;
  /* Read by .home-cta to bleed exactly across this padding, no further — which
     is why the padding is written THROUGH the variable: the two must move
     together now that the gutter scales with the viewport. */
  --main-pad: clamp(18px, 3vw, 40px);
  padding: 4px var(--main-pad) 56px;
}
/* The WIDE views. 780px is right for the test screen — one action, one column
   — and wrong for the screens that are inventories: Home's two-up cards were
   stacking into a half-empty strip and the stat row clipped ("can we make this
   go end to end"). Same move the platform makes for its inbox: the container
   widens for exactly the views that earn it. :not(.hidden) because showView()
   drives visibility with the hidden CLASS, not an active one. */
.foundry-consumer .work main:has(#view-artist-inbox:not(.hidden)),
.foundry-consumer .work main:has(#view-home:not(.hidden)),
.foundry-consumer .work main:has(#view-sent:not(.hidden)),
.foundry-consumer .work main:has(#view-profile:not(.hidden)),
.foundry-consumer .work main:has(#view-tracks:not(.hidden)),
/* Explore is an inventory too, and the most inventory-shaped of them: a feed
   scanned for records, with a filter rail beside it. It read as one stranded
   card in a reading column until it joined this list. */
.foundry-consumer .work main:has(#view-pool:not(.hidden)) {
  /* Wider and fluid (2026-08-26, "margins should not look like this"): the
     inventory views fill a big monitor instead of floating in it. */
  max-width: 1440px;
}
.foundry-consumer .work footer { margin-top: auto; }

/* Below 900px the rail is a top bar. It stays sticky, because the three
   destinations are how you get out of a long list of records on a phone. */
@media (max-width: 900px) {
  .foundry-consumer #shell { flex-direction: column; }
  .side {
    flex: none; width: 100%; height: auto; position: sticky; top: 0; z-index: 30;
    flex-direction: row; flex-wrap: wrap; align-items: center;
    border-right: none; border-bottom: 1px solid var(--border);
    padding: 12px 14px 0;
  }
  .side-top { flex: 1; padding: 0 0 12px; }
  .side-foot { margin-top: 0; padding-top: 0; border-top: none; padding-bottom: 12px; }
  .account { padding: 5px 8px; }
  .account-lines { display: none; }
  .side-nav {
    order: 3; width: 100%; flex-direction: row; gap: 4px;
    overflow-x: auto; scrollbar-width: none;
  }
  .side-nav::-webkit-scrollbar { display: none; }
  /* `width: auto` is load-bearing. The rail's .nav-item is width:100%, and a
     flex row of three 100%-wide items is three full-viewport tabs of which only
     the first is on screen — with no scrollbar drawn to say so. */
  .nav-item {
    flex: none; width: auto; padding: 8px 12px 10px; border-radius: 10px 10px 0 0;
  }
  .nav-item::before { left: 12px; right: 12px; top: auto; bottom: 0; width: auto; height: 2px; }
  .nav-sub { display: none; }
  /* The group names go with the subs: a horizontal tab row has no rows to
     group, and an inline "INBOX" between tabs reads as a broken tab. */
  .side-sec { display: none; }
  .foundry-consumer .work main { padding: 4px 18px 44px; --main-pad: 18px; }
}

/* ===========================================================================
   SIGN IN (/foundry/) — a split screen, not a card on black
   =========================================================================== */

.auth {
  position: fixed; inset: 0; z-index: 70; background: var(--bg);
  /* Narrow, and capped. Left at a bare percentage the form column reached 650px
     on a 1920 monitor and a 372px form sat marooned in the middle of it; the
     540px ceiling keeps the column reading as a column at any width, and the
     420px floor stops it crushing the inputs just above the 900px breakpoint. */
  display: grid; grid-template-columns: minmax(420px, min(34%, 540px)) 1fr;
}
/* The identity sits at the top of the column, the form in the middle of it, the
   small print at the foot — pinned by the two `auto` margins below rather than
   by centring the lot, which floated the wordmark into the middle of the page. */
.auth-panel {
  overflow-y: auto; display: flex; align-items: stretch; justify-content: center;
  padding: 30px 44px 32px; border-right: 1px solid var(--border);
  background: var(--bg);
}
.auth-inner { width: 100%; max-width: 372px; display: flex; flex-direction: column; }

.auth-top {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  margin-bottom: auto; padding-bottom: 48px;
}
.auth-mark { font-size: 19px; }

.auth h2 { font-size: 26px; font-weight: 600; letter-spacing: -0.6px; line-height: 1.2; margin-bottom: 8px; }
.auth .muted { font-size: 14px; }

/* Untyped on purpose. Every typed selector in this file has been wrong at least
   once — `tel` was missing and the phone step rendered the browser's white box
   inside a dark card. This one cannot miss a type that gets added later. */
.auth-inner input:not([type=checkbox]):not([type=radio]) {
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
  border-radius: 10px; padding: 12px 13px; font-size: 14px; font-family: inherit;
  outline: none; width: 100%;
}
.auth-inner input:focus { border-color: var(--border-2); }
.auth-inner form { display: flex; flex-direction: column; gap: 9px; margin-top: 18px; }

/* THE ONE ACCENT. Amber is the producer's own record everywhere else in this
   product, and this button is the producer's own account. Nothing else on this
   screen is coloured — the violet in the frame to the right is the catalogue,
   and the two must not be arguing. */
.auth .btn.primary {
  background: var(--s-demo); border-color: var(--s-demo); color: #140f06;
  padding: 12px 16px; font-size: 14px; font-weight: 600;
}
.auth .btn.primary:hover { background: #cf9a38; border-color: #cf9a38; }
.auth .btn.primary:disabled { opacity: 0.55; cursor: default; }
.auth .btn.primary:focus-visible { outline: 2px solid var(--s-demo); outline-offset: 3px; }
/* Quiet, so the amber above stays the only coloured thing. */
.auth .linkish { color: var(--text); }
.auth .auth-providers { margin-top: 26px; }
/* The contact step (#contact-step in app/index.html): one way to reach this
 * person before the account is any use to them. Lives inside the auth card and
 * borrows its provider buttons, so it needs spacing and nothing else. */
#contact-step { display: flex; flex-direction: column; gap: 12px; }
#contact-step h3 { font-size: 17px; font-weight: 600; margin: 0; }
#contact-step .auth-providers { margin: 0; }
#contact-note:empty { display: none; }
/* .auth-fine is normally the card's footer and claims the slack with
 * `margin-top: auto`. Inside this step it is a caption under the button, so the
 * slack must stay with the card and not be handed to a flex child of a child. */
#contact-step .auth-fine { margin-top: 4px; padding-top: 0; }

.auth-fine { color: var(--text-3); font-size: 12px; line-height: 1.5; margin-top: auto; padding-top: 48px; }
.auth-fine a { color: var(--muted); text-decoration: underline; text-underline-offset: 2px; }
.auth-fine a:hover { color: var(--text); }

/* ---- the evidence panel ---- */
.auth-shot { position: relative; overflow: hidden; background: #08080a; }
/* Wider and taller than the column it sits in, so it bleeds off the right edge
   and off the top and bottom. A screenshot fitted neatly inside its box reads as
   a picture of a product; one that runs off the edges reads as the product,
   continuing past the sign-in. object-fit means it covers at any window shape
   rather than letterboxing at a tall one. */
.auth-shot img {
  position: absolute; inset: 0;
  width: min(158%, 1560px); height: 100%; max-width: none; display: block;
  object-fit: cover; object-position: 8% 44%;
  opacity: 0.5; filter: saturate(0.9);
}
/* Atmosphere, not a surface treatment: it holds the screenshot down so the form
   stays the subject, hides the seam at the left where the panel meets it, and
   makes the caption legible over whatever happens to be underneath. */
.auth-shot-scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(to right, rgba(11,11,13,0.97) 0%, rgba(11,11,13,0.56) 16%, rgba(11,11,13,0.26) 48%, rgba(11,11,13,0.38) 88%, rgba(11,11,13,0.66) 100%),
    linear-gradient(to top, rgba(11,11,13,0.94) 0%, rgba(11,11,13,0.6) 15%, rgba(11,11,13,0) 36%),
    linear-gradient(to bottom, rgba(11,11,13,0.5) 0%, rgba(11,11,13,0) 18%);
}
.auth-shot-cap {
  position: absolute; left: 48px; right: 48px; bottom: 46px;
  display: flex; flex-direction: column; gap: 7px; max-width: 42ch;
}
.cap-title { font-size: 15px; font-weight: 600; letter-spacing: -0.2px; }
.cap-body { font-size: 13px; color: #9a9aa5; line-height: 1.55; }

/* Below 900px the screenshot is GONE, not shrunk. A 2,200px dashboard at phone
   width is texture, and texture is not evidence. */
@media (max-width: 900px) {
  .auth { grid-template-columns: 1fr; }
  .auth-shot { display: none; }
  .auth-panel { border-right: none; padding: 28px 22px 40px; align-items: flex-start; }
  .auth-top { margin-bottom: 34px; }
  .auth h2 { font-size: 23px; }
}

/* ---------- AI origin flag ----------
   Amber, matching the one status colour already in the language. Square corners
   and a left rule rather than a rounded pill: the reset allows border-radius
   only for true circles. Hidden entirely when empty — :empty rather than a
   class, so a cleared node cannot leave a stray box behind. */
.qi-origin:empty { display: none; }
.qi-origin.flag {
  display: block;
  margin: 10px 0 0;
  padding: 10px 12px;
  border-left: 2px solid var(--amber);
  background: rgba(201, 138, 43, 0.07);
}
.qi-origin.flag strong {
  display: block;
  font-family: var(--wide, inherit);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--amber);
}
.qi-origin.flag span {
  display: block;
  margin-top: 4px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-3);
}

/* ---------- the producer's numbers ----------
   The same stat blocks the platform workspaces read (platform/web/css), copied
   rather than imported for the reason stated at the top of that file: separate
   static bundles, no build step, nothing to resolve an @import through. */
.stat-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px; margin: 4px 0 14px;
}
.stat-row.tight { margin-bottom: 10px; }
.stat {
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 12px;
  padding: 12px 14px; display: flex; flex-direction: column; gap: 4px; min-width: 0;
}
.stat-n { font-size: 24px; font-weight: 600; letter-spacing: -0.5px; font-variant-numeric: tabular-nums; }
.stat-n.muted { color: var(--muted); }
.stat-k { color: var(--muted); font-size: 12px; line-height: 1.35; }
@media (max-width: 700px) {
  .stat-row { grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }
  .stat-n { font-size: 20px; }
}

/* ---------- Home ----------
   The signed-in landing. Every colour is a token, because this screen must
   hold in both themes without a scoped override — a hex here is a bug in the
   token list waiting to be found the hard way. */
.home-cols { margin-bottom: 16px; }
#home-stats:not(:empty) { margin-bottom: 16px; }
/* The popover anchors to tiles inside this view. */
#view-home { position: relative; }

/* A belt is FRAMELESS — no card, no border, no fill ("don't put that gray
   border around it"). The heading row keeps the card-head layout; the belt
   itself is the surface. And the belts breathe: twice the rhythm the cards
   keep between themselves, because a moving row needs still air around it. */
.home-belt { margin-top: 36px; }
.home-belt .card-head { margin-bottom: 10px; }
.home-belt > .small-cap { margin-bottom: 10px; }

/* Sends: a row per recipient — name, which promise the send holds, when. */
.hs-row {
  display: flex; align-items: baseline; gap: 12px;
  padding: 9px 2px; border-top: 1px solid var(--border);
}
.hs-row:first-child { border-top: none; }
.hs-name {
  flex: 1 1 auto; min-width: 0; font-size: 14px; font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hs-state { color: var(--muted); font-size: 12px; white-space: nowrap; }
/* Waiting is the cream/status tone, not a danger: nothing is wrong, a door is
   simply closed and the row says so. */
.hs-state.hs-wait { color: var(--cream); }
.hs-when { color: var(--text-3); font-size: 11.5px; white-space: nowrap; }

/* One dot, one meaning: a reply you have not read. It clears by reading, not
   by dismissing — the thread's GET is what marks the rows read server-side. */
.hs-dot {
  flex: none; width: 7px; height: 7px; border-radius: 50%;
  background: var(--lilac); align-self: center;
}

/* ---------- the Sent ledger, one card per track (2026-08-28) ----------
   Cards separated by rules, not boxes — the ledger sits inside a card already,
   and a card-in-a-card reads as chrome. The recipients are chips because a
   track with twelve sends is still ONE fact per recipient, and chips wrap
   where rows would scroll. Every colour is a token; the chip states borrow
   the meanings this file already assigned — --ok for a thing that happened,
   --lilac for the pale accent, --muted for a door still closed. No glow,
   no gradients. */
.sent-track-card { border-top: 1px solid var(--border); padding: 15px 2px; }
.sent-track-card:first-child { border-top: none; padding-top: 4px; }
.stc-head { display: flex; align-items: center; gap: 12px; }
/* The cover is a thumbnail, and a thumbnail has a size (the .home-grid
   lesson). Same inner classes as the shelf face, so mountArt() and the
   no-cover glyph need no second markup. */
.stc-face {
  flex: none; width: 52px; height: 52px; border-radius: 10px; overflow: hidden;
  display: block; background: var(--surface-2); border: 1px solid var(--border);
}
.stc-face .t-face-glyph { font-size: 20px; }
.stc-id { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.stc-title {
  font-size: 14.5px; font-weight: 600; letter-spacing: -0.1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.stc-sub { color: var(--muted); font-size: 12px; }
.stc-open { flex: none; }
.stc-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.sent-chip {
  display: inline-flex; align-items: baseline; gap: 8px; max-width: 100%;
  padding: 5px 12px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface-2); color: var(--text); cursor: pointer;
  font-family: inherit; font-size: 12.5px; text-align: left;
}
.sent-chip:hover { border-color: var(--border-2); }
.sent-chip:focus-visible { outline: 2px solid var(--lilac); outline-offset: 2px; }
.sent-chip.open { border-color: var(--lilac); }
.sc-name {
  font-weight: 500; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sc-state { color: var(--muted); font-size: 11.5px; white-space: nowrap; }
/* The states, in the order they outrank each other. Downloaded and played are
   accents on border AND text — the border alone vanishes at chip size.
   Waiting mutes the whole chip: nothing is wrong, a door is simply closed. */
.sent-chip-downloaded { border-color: var(--ok); }
.sent-chip-downloaded .sc-state { color: var(--ok); }
.sent-chip-played { border-color: var(--lilac); }
.sent-chip-played .sc-state { color: var(--lilac); }
.sent-chip-waiting { color: var(--muted); }
@media (max-width: 600px) {
  /* The head stacks: the button drops under the title line and stays aligned
     with the text column rather than hanging off the right edge. */
  .stc-head { flex-wrap: wrap; }
  .stc-open { margin-left: 64px; }  /* face width + gap */
}

/* The thread (migration 038) — shared look for the artist inbox detail and the
   Sent ledger. Two sides differ by a left rule, not bubbles: correspondence
   about a track, not a chat product. */
.ib-msgs { display: grid; gap: 8px; padding: 12px 2px; border-top: 1px solid var(--border); }
.msg-list { display: grid; gap: 8px; max-width: 620px; }
.msg { display: grid; gap: 2px; padding-left: 10px; border-left: 2px solid var(--border); }
.msg-you { border-left-color: var(--lilac); }
.msg-who { color: var(--text-3); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.04em; }
.msg-body { color: var(--text-2); font-size: 13.5px; line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; }
.msg-empty { margin: 0; }
.msg-compose { display: flex; align-items: flex-start; gap: 10px; flex-wrap: wrap; max-width: 620px; }
.msg-box { resize: vertical; font-family: inherit; }

/* Uploads: small tiles, cover where one exists, an honest note where none. */
.home-grid {
  display: grid; gap: 10px;
  /* FIXED columns, not minmax(…, 1fr). The stretchy version let a coverless
     placeholder balloon to the card's full width — a several-hundred-pixel
     white square with a lone glyph in it, one tile per row (Nikhil's
     screenshot). A cover face is a thumbnail, and a thumbnail has a size. */
  grid-template-columns: repeat(auto-fill, 150px);
  justify-content: start;
}
.hu-tile {
  display: flex; flex-direction: column; gap: 5px; text-align: left;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 12px; padding: 10px; cursor: pointer;
  font-family: inherit; color: var(--text); min-width: 0;
}
.hu-tile:hover { border-color: var(--border-2); }
.hu-tile:focus-visible { outline: 2px solid var(--lilac); outline-offset: 2px; }
.hu-art {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  border-radius: 8px; border: 1px solid var(--border);
}
/* No cover: a quiet glyph on the surface, not a fake one. */
.hu-noart {
  display: grid; place-items: center;
  color: var(--text-3); font-size: 22px; background: var(--surface);
}
.hu-name {
  font-size: 12.5px; font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hu-when { font-size: 11px; color: var(--text-3); }

/* The network: a strip that scrolls inside its own box (the page never
   scrolls sideways). Tiles carry the workspace's own uploaded image
   (migration 023) or fall back to initials — an invented square would be
   decoration pretending to be identity, but an uploaded one IS identity. */
.net-strip {
  display: flex; gap: 10px; overflow-x: auto; padding: 2px 0 6px;
}

/* ---------- the conveyor ----------
   Two copies of one track, translated by exactly one track-width, so the loop
   has no seam. Duration scales with tile count in nothing but feel — 40s over
   up to 24 tiles reads as drift, not a ticker. Hover pauses, because a tile
   you are reading must hold still; reduced-motion turns the belt back into
   the static strip, because a permanently moving surface is a vestibular
   trigger and never worth a design flourish. */
.net-conveyor {
  display: flex; overflow: hidden; padding: 2px 0 6px;
  /* The fade at both edges says "this continues" without a scrollbar. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
}
.net-track {
  display: flex; gap: 10px; flex: none; padding-right: 10px;
  animation: net-belt 40s linear infinite;
}
.net-conveyor:hover .net-track { animation-play-state: paused; }
@keyframes net-belt {
  from { transform: translateX(0); }
  to   { transform: translateX(-100%); }
}
@media (prefers-reduced-motion: reduce) {
  .net-conveyor { overflow-x: auto; mask-image: none; -webkit-mask-image: none; }
  .net-track { animation: none; }
  .net-track[aria-hidden] { display: none; }
}

/* The tile's image face: same footprint as the initials avatar so a strip
   mixing both keeps one rhythm. */
.net-img {
  /* The face is the point of the tile ("the logos are not big enough"). */
  width: 96px; height: 96px; object-fit: cover; flex: none;
  border-radius: 12px; border: 1px solid var(--border); display: block;
  background: var(--surface);
}
/* A cover tile on the "your records" belt is a button; strip the button skin
   without losing the tile's. */
.hc-tile { cursor: pointer; font-family: inherit; color: inherit; text-align: left; }
.hc-tile .net-img { width: 96px; height: 96px; border-radius: 10px; }
.net-tile {
  flex: 0 0 128px; min-width: 0;
  display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
  /* Frameless like the belt it rides — the image carries the tile now, and a
     boxed grey square around every face was the border Nikhil struck. */
  background: none; border: none; border-radius: 12px; padding: 4px;
  font-family: inherit; color: inherit; text-align: left; cursor: pointer;
}
.net-tile:focus-visible { outline: 2px solid var(--lilac); outline-offset: 2px; }
.net-tile:hover .net-img, .net-tile:hover .net-avatar { border-color: var(--border-2); }
.net-avatar { width: 96px; height: 96px; font-size: 28px; border-radius: 12px; }
.net-name {
  font-size: 13px; font-weight: 500; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.net-sub {
  font-size: 11px; color: var(--muted); max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Closed is a stated state, not an absence — dimmed, still on the shelf, the
   same claim the send picker makes about the same door. */
.net-tile.net-closed { opacity: 0.55; }
/* The set tile: the set's own artwork, biggest face on the page ("we need to
   see DJ set photos, not numbers — and these need to be bigger"). It is an
   anchor out to the set's page on nine71charts.com, so it drops the button
   cursor semantics and keeps the link's. */
.ch-set { flex-basis: 150px; text-decoration: none; }
.ch-img {
  width: 140px; height: 140px; object-fit: cover; display: block;
  border-radius: 12px; border: 1px solid var(--border);
  background: var(--surface);
}

/* The provenance line on a for-me tile. Quieter than the event line above it:
   it is bookkeeping about WHY the set is here, not part of the set's identity,
   and it must never read as a badge the set earned. */
.fm-because {
  display: block; font-size: 10.5px; color: var(--text-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ch-set:hover .ch-img { border-color: var(--border-2); }

/* ---------- the banner ----------
   The one photograph on the home screen, and it keeps its own dark ground in
   BOTH themes — like .auth-shot, it is a picture, not a surface, so the theme
   tokens deliberately stop at its border: fixed light text over a fixed dark
   scrim, and a fixed light button, because the invert tokens would paint that
   button dark-on-dark in light mode. */
.home-cta {
  position: relative; overflow: hidden; border-radius: var(--r);
  min-height: 300px; display: flex; align-items: center;
  background: #0b0b0d url('/img/foundry-cta.jpg') center 40% / cover no-repeat;
  /* IN the content column now, aligned with every other card (Nikhil,
     2026-08-28: "needs proper margins"). The full-bleed version read as a
     layout mistake — copy flush against the viewport on wide screens —
     rather than as a photographic moment. Unbordered still: a photograph
     does not need a grey frame to be a photograph. */
  border: none;
  margin: 44px 0 0;
}
.home-cta-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(to right,
    rgba(11, 11, 13, 0.92) 0%, rgba(11, 11, 13, 0.72) 38%,
    rgba(11, 11, 13, 0.28) 70%, rgba(11, 11, 13, 0.45) 100%);
}
.home-cta-copy {
  position: relative; padding: clamp(26px, 4.5vw, 56px); max-width: 44rem;
  display: flex; flex-direction: column; align-items: flex-start; gap: 12px;
}
/* Display scale — "it needs to be much bigger. I'd make that wider." */
.home-cta-copy h2 {
  font-size: clamp(28px, 4vw, 44px); line-height: 1.08;
  letter-spacing: -0.8px; color: #f2f2f4;
}
.home-cta-copy p { font-size: 15px; line-height: 1.55; color: #b9b9c2; max-width: 36rem; }
.home-cta-btn {
  margin-top: 6px; background: #fff; color: #0b0b0d; border-color: #fff;
  font-weight: 600; padding: 10px 18px;
}
.home-cta-btn:hover { background: #efe7cf; border-color: #efe7cf; }
@media (max-width: 600px) {
  .home-cta { min-height: 220px; }
  .home-cta-copy { padding: 24px 20px; }
}

/* ---------- the network popover ----------
   One tiny anchored menu: the workspace's name and its one or two verbs.
   Token surfaces only, so it holds in both themes. */
.net-pop {
  position: absolute; z-index: 40;
  display: flex; flex-direction: column; align-items: flex-start; gap: 8px;
  background: var(--surface); border: 1px solid var(--border-2);
  border-radius: 12px; padding: 12px 14px; max-width: 260px;
}
.net-pop-name { font-size: 13.5px; font-weight: 600; }

/* ---------- My tracks: a shelf of boxes ----------
   Cards in a grid, not rows in a table. A dozen tracks from one session read as
   something you scan across rather than scroll down, and the four facts worth
   comparing between records — name, when, best match, score — sit in the same
   place on every card so the eye can run along them.

   auto-fill with a minmax floor rather than a fixed column count: the rail is
   250px and the work column is capped at 780px, so the number of columns has to
   fall out of the space left rather than be asserted. */
.t-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; margin-bottom: 14px; flex-wrap: wrap;
}
.t-chips { display: flex; gap: 6px; }
.t-chip {
  font-family: inherit; font-size: 12.5px; cursor: pointer;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 12px; border-radius: var(--r-ctl);
  border: 1px solid var(--border); background: var(--surface); color: var(--muted);
}
.t-chip:hover { color: var(--text); border-color: var(--border-2); }
.t-chip.on { color: var(--text); border-color: var(--border-2); background: var(--surface-2); }
.t-chip-n { color: var(--text-3); font-size: 11.5px; }
.t-chip.on .t-chip-n { color: var(--muted); }
.t-chip:focus-visible { outline: 2px solid var(--lilac); outline-offset: 2px; }
.t-search {
  width: 200px; background: var(--surface-2); border: 1px solid var(--border);
  color: var(--text); border-radius: 10px; padding: 8px 12px;
  font-size: 13.5px; font-family: inherit; outline: none;
}
.t-search:focus { border-color: var(--border-2); }

.t-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
}
/* The card follows Home's tile language now ("the my tracks looks bad"):
   surface-2 ground, no resting border — the lilac open-state marker is the one
   border with something to say — and the COVER IS THE FACE, a full-width
   square at the top of the card, the same shape the uploads tiles wear. The
   corner-chip version put a 40px stamp next to the title and left the card
   reading as a settings row. */
.t-card {
  display: flex; flex-direction: column;
  background: var(--surface-2); border: 1px solid transparent;
  border-radius: 14px; padding: 10px 10px 12px;
  position: relative;
}
.t-card:hover { border-color: var(--border-2); }
.t-face {
  display: block; width: 100%; aspect-ratio: 1;
  border-radius: 10px; overflow: hidden; margin-bottom: 8px;
  background: var(--surface); border: 1px solid var(--border);
}
.t-face-img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* No cover: the same quiet glyph the uploads tiles use — never a bare white
   void pretending a cover failed to load. */
.t-face-glyph {
  width: 100%; height: 100%; display: grid; place-items: center;
  color: var(--text-3); font-size: 26px;
}
.t-detail-art {
  width: 120px; height: 120px; object-fit: cover; float: right; margin: 0 0 10px 14px;
  border-radius: 10px; border: 1px solid var(--border);
}
/* The open card is marked with the lilac already used for every "you are here"
   in this file, so the panel below it has a visible owner. */
.t-card.open { border-color: var(--lilac); background: var(--surface-2); }
.t-card-open {
  display: flex; flex-direction: column; gap: 3px; min-width: 0;
  border: none; background: none; padding: 0; cursor: pointer;
  font-family: inherit; color: var(--text); text-align: left;
}
.t-card-open:focus-visible { outline: 2px solid var(--lilac); outline-offset: 3px; }
.t-card-title {
  font-size: 14px; font-weight: 600; letter-spacing: -0.1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.t-card-artist, .t-card-when, .t-card-runs { color: var(--muted); font-size: 12px; }
.t-card-artist { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.t-card-when { font-size: 11.5px; color: var(--text-3); }
/* The score line is the one the eye lands on, so it gets the space and the rule
   above it. Not coloured — a verdict sits beside the bar in the detail, and a
   green number on a card is a claim the score alone does not make. */
.t-card-score-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border);
}
.t-card.open .t-card-score-row { border-top-color: var(--border-2); }
.t-card-best {
  font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.t-card-score { font-size: 19px; font-weight: 700; font-variant-numeric: tabular-nums; }
.t-card-runs { margin-top: 2px; font-size: 11px; }
.t-card-test { align-self: flex-start; margin-top: 11px; }

/* The detail is a full-width panel under the grid rather than an expanding cell.
   A card that grew to hold a player, a rights list and every run would reflow
   every other card on its row and shove the rest of the shelf down the page. */
.t-detail {
  margin-top: 12px; padding: 18px 20px 20px;
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--lilac); border-radius: var(--r);
}
.t-detail-cta {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border);
}
.t-detail-cta .muted { font-size: 12px; max-width: 42ch; }

/* A record sent back to the test screen. No form on it, so the card is mostly
   the two decisions still open: which label, and whether to send. */
.queue-item.stored .qi-stored-note { margin: 8px 0 12px; font-size: 12.5px; }

@media (max-width: 600px) {
  .t-grid { grid-template-columns: 1fr 1fr; }
  .t-search { width: 100%; }
  .t-detail { padding: 16px 15px 18px; }
}
@media (max-width: 380px) { .t-grid { grid-template-columns: 1fr; } }

/* ---------- the stage: mode toggle ----------
   A segmented control, not a pill row. The rail already rejected a filled white
   capsule for being the brightest object on a matte page, and the same argument
   applies here with more force: this sits directly above the drop target, so a
   bright control would out-shout the thing it introduces. The current segment is
   a lifted surface plus a rule in the lilac used for every other "you are here"
   marker in this file (.nav-item::before, the focus ring). */
.stage-modes {
  display: flex; gap: 6px; margin: 0 0 16px;
  border: 1px solid var(--border); border-radius: 14px;
  background: var(--surface); padding: 5px;
}
.stage-mode {
  position: relative; flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
  padding: 9px 14px 10px; border-radius: 10px;
  border: none; background: none; cursor: pointer;
  font-family: inherit; color: var(--muted); text-align: left;
}
.stage-mode:hover { color: var(--text); }
.stage-mode.current { background: var(--surface-2); color: var(--text); }
/* A LEFT rule, the same marker .nav-item uses in the rail — not a bottom rule.
   Along the bottom edge it sat directly under .sm-sub and read as an underline
   of that sentence rather than as a marker for the segment. */
.stage-mode.current::before {
  content: ""; position: absolute; left: 4px; top: 9px; bottom: 9px;
  width: 2px; border-radius: 2px; background: var(--lilac);
}
.stage-mode { padding-left: 16px; }
.stage-mode:focus-visible { outline: 2px solid var(--lilac); outline-offset: 2px; }
.sm-name { font-size: 13.5px; font-weight: 600; letter-spacing: -0.1px; }
.sm-sub { font-size: 11.5px; color: var(--muted); }
.stage-mode.current .sm-sub { color: var(--muted); }

/* The stage is centred; the workspace is not.
   #view-test is the one screen whose whole job is a single action, so the
   heading, the lede and the toggle sit over the drop target rather than beside
   it. "My tracks" keeps its left edge — a table read left-to-right under a
   centred heading looks like two pages stapled together. */
#view-test .intro { text-align: center; }
#view-test .intro .lede { margin-left: auto; margin-right: auto; }

@media (max-width: 600px) {
  .stage-modes { gap: 4px; padding: 4px; }
  .stage-mode { padding: 8px 10px 10px; }
  .sm-sub { display: none; }
  #view-test .intro { text-align: left; }
}

/* ---------- the profile in two columns + the info dots ----------
   One rule for the whole surface (2026-08-13, Nikhil): artists get one short
   line in the open page, and every paragraph lives behind a small round "i".
   The dot is 18px, keyboard-focusable, and the popover reuses .net-pop's
   surface so both themes are already solved. */
#view-profile { position: relative; }
.profile-cols {
  display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: 16px; align-items: start;
}
.profile-cols > div { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
@media (max-width: 980px) { .profile-cols { grid-template-columns: 1fr; } }

.info-dot {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; flex: none; vertical-align: text-bottom;
  border: 1px solid var(--border-2); border-radius: 50%;
  background: none; color: var(--muted); cursor: pointer;
  font-family: inherit; font-size: 11px; font-style: normal; line-height: 1;
  margin-left: 6px;
}
.info-dot:hover { color: var(--text); border-color: var(--text-3); }
.info-dot:focus-visible { outline: 2px solid var(--lilac); outline-offset: 2px; }
/* The popover itself is .net-pop with words instead of verbs. */
.info-pop { max-width: 340px; font-size: 12.5px; line-height: 1.55; color: var(--text-2); }
.pf-links-k { margin-top: 4px; }

/* ---------- Your profile -------------------------------------------------
   A form, not a dashboard. The fields a label reads are few and short, so the
   page is one column of them rather than cards: there is nothing here to
   compare, only things to state. */
.profile-form { display: grid; gap: 1.15rem; max-width: 46rem; margin-top: 1.4rem; }
.pf-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.15rem; }
.pf-row:has(.pf-narrow) { grid-template-columns: 1fr 1fr 6rem; }
.pf-field { display: grid; gap: 0.4rem; }
.pf-k { font-size: 0.82rem; color: var(--muted); letter-spacing: 0.01em; }
/* Spaced away from the label it qualifies, or the two read as one sentence:
   "What you make pick up to three". */
.pf-hint { color: var(--muted); opacity: 0.75; font-weight: 400; margin-left: 0.5rem; }
.profile-form input[type="text"],
.profile-form input[type="url"],
.profile-form textarea {
    width: 100%;
    padding: 0.62rem 0.75rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--text);
    font: inherit;
    font-size: 0.92rem;
}
.profile-form textarea { resize: vertical; line-height: 1.55; }
.profile-form input:focus, .profile-form textarea:focus {
    outline: none; border-color: var(--lilac);
}

/* Chips rather than a multi-select: four options is few enough to show all of
   them, and a producer should see the whole vocabulary they are choosing from —
   it is the same one their record will be compared against. */
.pf-genres { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.pf-chip {
    padding: 0.42rem 0.8rem;
    border-radius: var(--r-ctl);
    border: 1px solid var(--border);
    background: transparent;
    color: var(--text-2);
    font: inherit; font-size: 0.86rem;
    cursor: pointer;
}
.pf-chip:hover { border-color: var(--muted); color: var(--text); }
/* The label series violet, which is what this surface already uses for the
   label half of every chart. A selected chip is a statement about sound, and
   sound is that colour here. */
.pf-chip.on { background: var(--s-label); border-color: var(--s-label); color: #fff; }

.pf-note { color: var(--muted); font-size: 0.82rem; margin: -0.4rem 0 0; }

.pf-actions {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1.2rem; flex-wrap: wrap;
    border-top: 1px solid var(--border); padding-top: 1.15rem;
}
.pf-publish { display: grid; gap: 0.3rem; }
.pf-check { display: flex; align-items: center; gap: 0.55rem; cursor: pointer; font-size: 0.92rem; }
.pf-check input { width: 1rem; height: 1rem; accent-color: var(--s-label); }
/* The state line is the honest half of the checkbox: it says what is true now,
   not what the control does. */
.pf-state { font-size: 0.82rem; color: var(--muted); }
.pf-state.on { color: var(--lilac); }

@media (max-width: 700px) {
    .pf-row, .pf-row:has(.pf-narrow) { grid-template-columns: 1fr; }
}

/* ---------- played out ----------
   Its own card, below the ranking, and visually quieter than it. The ranking is
   what the product measured; this is a name found in a list somebody typed, and
   the styling is not allowed to give it equal weight. */
.played-row {
  display: grid; grid-template-columns: 72px minmax(0, 1fr) auto;
  gap: 12px; align-items: center; padding: 10px 2px;
  border-top: 1px solid var(--border); text-decoration: none; color: inherit;
}
.played-row:first-of-type { border-top: none; }
.played-row:hover { background: var(--surface-2); }
.played-row .pl-cue { font-size: 13px; color: var(--lilac); }
.played-row .pl-what { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.played-row .pl-set {
  font-size: 14px; font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.played-row .pl-track {
  font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.played-row .pl-go { font-size: 12px; }


/* ===========================================================================
   THE ARTIST INBOX (#view-artist-inbox)
   The platform inbox's filter rail and row language, copied into this bundle
   rather than imported — the two surfaces are separate static bundles with no
   build step, the same reason every shared token block in this file is a copy.
   Class names match platform/web/css/style.css deliberately, so a fix learned
   on one surface is findable on the other. The label-only pieces (score slot,
   placing column, roster key) are absent because the markup never renders
   them: between artists nobody is shown what label a track fits.
   =========================================================================== */
.ai-head { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 12px; }
.ai-head .set-select { width: auto; }
.ai-door { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ai-tabs { display: flex; gap: 6px; margin: 4px 0 10px; }

.inbox-layout {
  display: grid; grid-template-columns: 232px minmax(0, 1fr);
  gap: 4px 26px; align-items: start; margin-top: 10px;
}
/* Flows with the page, never its own scroll region — an inner scrollbar hides
   whole groups while looking complete (measured on the platform's first cut). */
.inbox-side {
  border-right: 1px solid var(--border); padding: 2px 18px 16px 0;
  display: flex; flex-direction: column; gap: 16px;
}
.side-head {
  display: flex; align-items: baseline; justify-content: space-between;
  font-size: 11px; color: var(--muted); text-transform: uppercase;
  letter-spacing: 0.08em; padding: 4px 6px 0;
}
.side-clear {
  border: none; background: none; font-family: inherit; cursor: pointer;
  color: var(--lilac); font-size: 11.5px; text-transform: none; letter-spacing: 0;
  padding: 0;
}
.side-clear:hover { color: var(--cream); }
.fgroup { display: flex; flex-direction: column; gap: 1px; }
.fgroup-name {
  font-size: 11px; color: var(--muted); text-transform: uppercase;
  letter-spacing: 0.07em; padding: 0 6px 5px;
}
.frow {
  display: flex; align-items: center; gap: 8px; width: 100%;
  border: none; background: none; color: var(--text-2); font-family: inherit;
  font-size: 13px; padding: 5px 6px; border-radius: 8px; cursor: pointer;
  text-align: left;
}
.frow:hover { background: var(--surface-2); color: var(--text); }
.frow .fbox {
  flex: 0 0 auto; width: 14px; height: 14px; position: relative;
  background: var(--surface-2); border: 1px solid var(--border-2); border-radius: 4.5px;
}
.frow[aria-pressed="true"] { color: var(--text); }
.frow[aria-pressed="true"] .fbox { background: var(--invert-bg); border-color: var(--invert-bg); }
.frow[aria-pressed="true"] .fbox::after {
  content: ""; position: absolute; left: 3px; top: 3.5px; width: 6px; height: 3px;
  border: solid var(--invert-fg); border-width: 0 0 2px 2px; transform: rotate(-45deg);
}
.frow-t { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.frow-n { color: var(--muted); font-size: 11.5px; font-variant-numeric: tabular-nums; }
/* An option nothing currently matches stays on screen and dims — "none of
   those among the ones you asked for" is a filter's most useful sentence. */
.frow.f-empty { opacity: 0.38; }
.frow.f-empty[aria-pressed="true"] { opacity: 1; }
/* "Not measured yet" is a different KIND of answer: dashed, outstanding,
   never a solid claim of absence. */
.frow.f-unknown { color: var(--text-3); }
.frow.f-unknown .fbox { border-style: dashed; }
.frow.f-unknown[aria-pressed="true"] .fbox { background: var(--lilac); border-color: var(--lilac); }
.gdot { display: inline-block; flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%; }

.inbox-top { display: flex; gap: 18px; align-items: flex-start; flex-wrap: wrap; }
.filters-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; flex-wrap: wrap; margin: 12px 0 8px;
}
.filters-right { display: flex; gap: 8px; align-items: center; }
.filters-right .set-select { width: auto; padding: 7px 26px 7px 10px; font-size: 12.5px; }
.inbox-controls { align-items: center; }
.inbox-search { flex: 1 1 260px; min-width: 180px; padding: 8px 12px; font-size: 13px; }

/* ---------- the two panes ----------
   Message-list idiom: rows on the left, the open item on the right. Below
   900px the panes take turns on the has-open class renderAiDetail() sets. */
.ai-split {
  display: grid; grid-template-columns: minmax(250px, 330px) minmax(0, 1fr);
  gap: 0 22px; align-items: start;
}
.ai-pane-list { min-width: 0; border-right: 1px solid var(--border); padding-right: 4px; }
.ai-pane-detail { min-width: 0; padding-top: 4px; }
.ai-detail-empty {
  border: 1px dashed var(--border-2); border-radius: var(--r);
  padding: 44px 22px; text-align: center;
}

.inbox-list { display: flex; flex-direction: column; }
/* One row per send: circle, who/what, and the state at the end — a dot while
   no call has been made, the call itself once one has. */
.ib-item {
  width: 100%; display: grid; grid-template-columns: 34px minmax(0, 1fr) auto;
  gap: 10px; align-items: center; text-align: left;
  background: none; border: none; border-top: 1px solid var(--border);
  color: inherit; font-family: inherit; padding: 11px 8px 11px 6px; cursor: pointer;
}
.ib-item:first-child { border-top: none; }
.ib-item:hover { background: var(--surface-2); }
/* Flat fill plus an inset edge, not an outline — the selected row reads as
   the one the right pane is showing. */
.ib-item.open { background: var(--surface-2); box-shadow: inset 2px 0 0 var(--lilac); }
.ib-item:focus-visible { outline: 2px solid var(--lilac); outline-offset: -2px; }

.ib-ava {
  width: 34px; height: 34px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface-2); border: 1px solid var(--border-2);
  color: var(--text-2); font-size: 12px; font-weight: 600; letter-spacing: 0.02em;
}
.ib-ava-lg { width: 46px; height: 46px; font-size: 15px; }

.ib-item-mid { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ib-item-top {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; min-width: 0;
}
.ib-item-who {
  font-size: 13.5px; font-weight: 500; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ib-item-when { font-size: 11.5px; color: var(--muted); white-space: nowrap; flex: none; }
.ib-item-title {
  font-size: 13px; color: var(--text-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.untitled { color: var(--muted); font-style: normal; }
.ib-item-facts {
  font-size: 11.5px; color: var(--muted); font-variant-numeric: tabular-nums;
  display: flex; align-items: center; gap: 6px;
  overflow: hidden; white-space: nowrap;
}
.ib-item-status {
  font-size: 11px; color: var(--muted); text-transform: lowercase;
  white-space: nowrap; justify-self: end;
}
.ib-item-status.s-shortlisted { color: var(--ok); }
.ib-item-status.s-passed { color: var(--text-3); }
/* The unread mark: a send nobody has triaged yet. Amber while it is only
   waiting behind a private door. */
.ib-dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--lilac);
  justify-self: end; margin-right: 3px;
}
.ib-dot.d-wait { background: var(--amber); }

.sb-ai {
  font-size: 10.5px; color: var(--danger); border: 1px solid var(--danger);
  border-radius: var(--r-chip); padding: 1px 5px; font-weight: 600;
  background: rgba(217, 106, 106, 0.10);
}
.ib-facts .v-ai { color: var(--danger); }

/* ---------- the open item ---------- */
.ai-back { display: none; align-self: flex-start; }
.ib-sender {
  display: flex; gap: 12px; align-items: flex-start;
  padding-bottom: 14px; border-bottom: 1px solid var(--border);
}
.ib-sender-id { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.ib-sender-name { font-size: 16px; font-weight: 600; overflow-wrap: anywhere; }
.ib-sender-name .chip { font-weight: 400; vertical-align: 2px; }
.ib-sender-where { font-size: 12.5px; color: var(--muted); }
.ib-sender-genres { display: flex; gap: 6px; flex-wrap: wrap; }
.ib-sender-bio {
  margin: 2px 0 0; font-size: 13px; color: var(--text-2);
  line-height: 1.5; max-width: 560px;
}
.ib-sender-links { display: flex; gap: 12px; flex-wrap: wrap; }
.pd-link {
  color: var(--lilac); font-size: 12.5px; text-decoration: none;
  border-bottom: 1px solid var(--border-2);
}
.pd-link:hover { color: var(--text); border-bottom-color: currentColor; }

.ib-track-card { display: flex; gap: 14px; align-items: flex-start; }
.ib-track-meta { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.ib-track-title { font-size: 17px; font-weight: 600; overflow-wrap: anywhere; }
.ib-track-line { font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
.ib-rights { font-size: 12.5px; color: var(--text-2); overflow-wrap: anywhere; }
.ib-rights .k { color: var(--muted); }
.ib-track-card .ib-art-img { width: 108px; height: 108px; }

.ib-detail { padding: 0 0 18px; display: flex; flex-direction: column; gap: 14px; }
.ib-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 0 18px; }
.ib-facts > div {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 7px 0; border-top: 1px solid var(--border-soft, var(--border)); min-width: 0;
}
.ib-facts .k { color: var(--muted); font-size: 12.5px; white-space: nowrap; }
.ib-facts .v { font-size: 12.5px; text-align: right; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ib-sound-panel { border-top: 1px solid var(--border); padding-top: 10px; }
.ib-art { margin: 2px 0 4px; }
.ib-art-img {
  display: block; width: 148px; height: 148px; object-fit: cover;
  border-radius: 10px; border: 1px solid var(--border);
}
.ib-audio { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ib-audio .k, .ib-triage .k { color: var(--muted); font-size: 12.5px; }
.ib-player { height: 34px; max-width: 420px; flex: 1 1 260px; }
.ib-triage { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.seg { display: flex; gap: 2px; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-ctl); padding: 3px; }
.seg-btn {
  font-family: inherit; font-size: 12.5px; cursor: pointer; border: none; background: none;
  color: var(--muted); padding: 5px 13px; border-radius: var(--r-chip);
}
.seg-btn.active { background: var(--invert-bg); color: var(--invert-fg); }
.ib-note { flex: 1 1 220px; min-width: 160px; padding: 8px 10px; font-size: 13px; }
.ib-saved { font-size: 12px; color: var(--muted); min-width: 44px; }

.empty-state {
  border: 1px dashed var(--border-2); border-radius: 14px; padding: 22px;
  display: flex; flex-direction: column; gap: 6px;
}
.empty-head { font-size: 14.5px; font-weight: 500; }
.empty-note { padding: 16px 2px; }

@media (max-width: 940px) {
  .inbox-layout { grid-template-columns: 1fr; }
  .inbox-side {
    border-right: none; border-bottom: 1px solid var(--border); padding: 0 0 14px;
  }
}
/* One pane at a time on a phone: the list until a row is opened, then the
   open item with its way back. */
@media (max-width: 900px) {
  .ai-split { grid-template-columns: 1fr; }
  .ai-pane-list { border-right: none; padding-right: 0; }
  .ai-split.has-open .ai-pane-list { display: none; }
  .ai-split:not(.has-open) .ai-pane-detail { display: none; }
  .ai-back { display: inline-flex; }
}

/* ---------- boot splash ----------
   Covers the app until the Firebase session resolves so neither the test
   screen (signed out) nor the app (signed in) flashes before the sign-in wall
   is decided. Token background so it holds in both themes; the wordmark
   inverts via the shared .wordmark-img light rule. app.js adds .booted to fade
   it, then removes the node. */
#boot-splash {
  position: fixed; inset: 0; z-index: 200;
  background: var(--bg);
  display: flex; align-items: center; justify-content: center;
  transition: opacity 220ms ease;
}
#boot-splash .wordmark-img { height: 22px; width: auto; opacity: 0.55; }
body.booted #boot-splash { opacity: 0; pointer-events: none; }

/* ---------- My tracks: the record insights dialog ----------
 * A dialog over the shelf, not a panel under it. Same surface language as
 * .card; the backdrop dims the shelf enough to read the dialog as the one
 * live thing without hiding where you were. */
.t-overlay {
  position: fixed; inset: 0; z-index: 60;
  background: color-mix(in srgb, var(--bg) 74%, transparent);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 6vh 16px 16px; overflow-y: auto;
}
.t-modal {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  width: min(720px, 100%); max-height: 86vh; overflow-y: auto;
  padding: 18px 22px 20px;
}
.t-modal-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px; margin-bottom: 12px;
}
.t-modal-id { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.t-modal-title { font-size: 17px; overflow-wrap: anywhere; }
.t-modal-when { font-size: 12.5px; }
.t-modal-danger {
  margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border);
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
}
.t-del-arm, .t-del-confirm { color: var(--danger); border-color: var(--danger); }

/* ---------- the card's act chooser + the library's sound line ---------- */
.qi-choose { display: flex; gap: 6px; flex-wrap: wrap; margin: 10px 0 2px; }
.qi-act { color: var(--muted); }
.qi-act.current { color: var(--text); border-color: var(--border-2); background: var(--surface-2); }
.t-card-tags { font-size: 12px; color: var(--muted); letter-spacing: 0.2px; }
.rec-sound { margin: 10px 0 2px; }
.rec-tags { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 6px; }
.tag-chip {
  font-size: 12px; color: var(--muted);
  border: 1px solid var(--border); border-radius: var(--r-chip); padding: 2px 9px;
}
.rec-words { font-size: 13.5px; line-height: 1.5; font-style: italic; }

/* ---------- send picker: search + pinned picks ---------- */
.send-search { width: 100%; margin-bottom: 8px; }
.send-picked { display: flex; flex-wrap: wrap; gap: 6px; }
.send-picked:not(:empty) { margin-bottom: 8px; }
.send-picked-chip {
  cursor: pointer; background: var(--surface-2); color: var(--text);
  border-color: var(--border-2);
}
.send-picked-chip:hover { color: var(--danger); border-color: var(--danger); }

/* ---------- the run result panel ---------- */
.run-overlay {
  position: fixed; inset: 0; z-index: 70;
  background: color-mix(in srgb, var(--bg) 55%, transparent);
  display: flex; justify-content: flex-end;
}
.run-sheet {
  width: min(560px, 94vw); height: 100%;
  background: var(--bg); border-left: 1px solid var(--border);
  padding: 18px 20px 28px; overflow-y: auto;
  display: flex; flex-direction: column; gap: 14px;
  animation: run-in 160ms ease-out;
}
@keyframes run-in { from { transform: translateX(24px); opacity: 0.4; } to { transform: none; opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .run-sheet { animation: none; } }
.run-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.run-head h2 { font-size: 16px; overflow-wrap: anywhere; }
#run-body { display: flex; flex-direction: column; gap: 14px; }
.run-strip { display: flex; align-items: center; gap: 10px; padding: 10px 0 2px; }
.run-strip-name { font-weight: 600; }

/* ---------- the track dashboard (2026-08-28) ----------
   One saved track, its facts, and the four things you can do with it. The
   accents are the app's own four — lilac, cream, amber, ok — used as a thin
   left rule and a coloured act name, matte on the same surfaces as everything
   else. No fills, no gradients, no glow; all four already carry light-theme
   values, so nothing new has to be maintained in two palettes. */
.run-sheet.wide { width: min(880px, 96vw); }

.dash-head { flex-direction: row; display: flex; gap: 18px; align-items: flex-start; }
.dash-art {
  width: 96px; height: 96px; border-radius: var(--r); object-fit: cover;
  border: 1px solid var(--border); flex: none;
}
.dash-art-empty { background: var(--surface-2); }
.dash-id { min-width: 0; }
.dash-id h2 { font-size: 20px; overflow-wrap: anywhere; }
.dash-saved { font-size: 12.5px; color: var(--cream); margin: 5px 0 9px; }
.dash-id .vibe { margin-bottom: 8px; }

.dash-acts { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 680px) { .dash-acts { grid-template-columns: 1fr; } }
.dash-act {
  text-align: left; font: inherit; color: var(--text); cursor: pointer;
  background: var(--surface-2); border: 1px solid var(--border);
  border-left-width: 3px; border-radius: var(--r);
  padding: 14px 16px; display: flex; flex-direction: column; gap: 5px;
  transition: background 120ms ease, border-color 120ms ease;
}
.dash-act:hover:not(:disabled) { background: var(--surface); border-color: var(--border-2); }
.dash-act:disabled { opacity: 0.55; cursor: default; }
.dash-act.a-lilac { border-left-color: var(--lilac); }
.dash-act.a-cream { border-left-color: var(--cream); }
.dash-act.a-amber { border-left-color: var(--amber); }
.dash-act.a-ok    { border-left-color: var(--ok); }
.dash-kicker { font-size: 14.5px; font-weight: 600; }
.k-lilac { color: var(--lilac); }
.k-cream { color: var(--cream); }
.k-amber { color: var(--amber); }
.k-ok    { color: var(--ok); }
.dash-desc { font-size: 12.5px; color: var(--muted); line-height: 1.45; }
/* The reason a scored act is held while the bounce is still being measured
 * (track-studio.js easyHtml). Italic and in the waiting colour rather than the
 * muted body colour, so a disabled button reads as "not yet" and not as "broken":
 * :disabled already drops the opacity, and opacity alone cannot say why. */
.dash-held { font-style: italic; color: var(--amber); }
.dash-act[data-studio-held] { cursor: progress; }

.dash-single { margin-top: 14px; display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; }
.dash-single-field { flex: 1; min-width: 220px; }
.dash-foot { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.dash-back { padding-bottom: 2px; }
.dash-saved-note { padding: 10px 2px; }
.dash-saved-note a { color: var(--text-2); }
.dash-note { color: var(--danger); padding-top: 10px; }

/* The inline pitch picker on the track page (2026-08-28): its own picked set,
   the same checkbox manners as the stage picker, capped at five. */
.dash-pitch { margin-top: 16px; border-top: 1px solid var(--border); padding-top: 14px; }
.dash-pitch-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 10px; flex-wrap: wrap; }
.dash-pitch-head strong { font-size: 14.5px; }
.dash-pitch-search { width: 200px; max-width: 100%; }
.dash-pitch-list { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 18px; margin-bottom: 12px; max-height: 240px; overflow-y: auto; }
@media (max-width: 640px) { .dash-pitch-list { grid-template-columns: 1fr; } }
.dash-pitch-opt { display: flex; gap: 8px; align-items: baseline; font-size: 14px; }
.dash-pitch-opt.on span { color: var(--text); }
.dash-pitch-closed { color: var(--dim); font-size: 12px; }
/* The track page's copy of the three states (NIN-52). Same marks as the stage
   picker so one producer reading both screens is not told two things. */
.dash-pitch-closed-row { border-left: 2px solid var(--border); padding-left: 8px; }
.dash-pitch-wait, .dash-pitch-list > .fine {
  grid-column: 1 / -1; margin: 6px 0 2px; color: var(--muted); line-height: 1.45;
}
.dash-pitch-foot { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.dash-pitch-note { color: var(--muted); max-width: 46ch; }
#dash-result:empty { display: none; }


/* The fresh card, simplified: the optional declarations fold away and the
   send-to-top-3 tickbox is gone from it — that decision now lives where the
   result is, on the dashboard and the result sheet. Stored library cards
   keep both. */
.queue-item.fresh .f-send-box { display: none; }
.qi-more { margin-top: 6px; }
.qi-more summary {
  cursor: pointer; list-style: none; font-size: 12.5px; color: var(--muted);
  padding: 6px 0 4px;
}
.qi-more summary::-webkit-details-marker { display: none; }
.qi-more summary::after { content: ' +'; }
.qi-more[open] summary::after { content: ' \2212'; }
.qi-more summary:hover { color: var(--text-2); }

/* The declarations, open on the card below a quiet divider (2026-08-28). */
.qi-declare { border-top: 1px solid var(--border); margin-top: 12px; padding-top: 12px; display: grid; gap: 4px; }
/* The artist field when the profile owns the name: reads as a fact, not a form. */
.f-artist-locked {
  background: transparent; border-color: transparent; padding-left: 0;
  font-weight: 600; color: var(--text); cursor: default;
}
.f-artist-locked:focus { border-color: transparent; }


/* Past plays lay out as a responsive grid, not a single column — a title
 * that appears in eight sets should read as a shelf, not a scroll. Each row
 * keeps its own internal layout; the grid only decides how many sit abreast. */
[data-played] { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 8px 12px; }
[data-played] > .card-head, [data-played] > p { grid-column: 1 / -1; }
[data-played] .played-row { border-top: 1px solid var(--border); border-radius: 10px; }

/* Send picker: room to breathe, captions that hold while you scroll. */
.send-picker { max-height: 320px; }
.send-group { position: sticky; top: -10px; background: var(--surface-2); z-index: 1;
  display: block; padding: 6px 0 4px; }

/* ==========================================================================
   Versions (migration 046) — the bounces of one track, in the library detail.

   A row is a line, not a card. There can be twelve of them inside a panel that
   already holds artwork, a player, rights chips and a test history, and twelve
   cards inside a card is the "settings screen" failure the surface tiers exist
   to prevent (see the three-tier note in the frontend section of CLAUDE.md).
   ========================================================================== */
.v-list { list-style: none; margin: 6px 0 10px; padding: 0; }
.v-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 7px 0; border-top: 1px solid var(--border); font-size: 13.5px;
}
.v-row:last-child { border-bottom: 1px solid var(--border); }
/* The current version is the one a pitch will send, so it is the one the eye
   should land on. A left rule rather than a filled background: a tinted row
   inside a panel reads as selected-by-the-user rather than true-of-the-data. */
.v-row.current { border-left: 2px solid var(--accent, #d99a2b); padding-left: 8px; }
.v-tag { font-weight: 600; min-width: 3.5em; }
.v-badge {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--accent, #d99a2b);
}
.v-state { color: var(--muted); font-size: 12.5px; }
.v-state.ok  { color: var(--muted); }
/* The one place a failure is coloured. Everything else about a version is
   neutral, so a red word here means something. */
.v-state.bad { color: #c0553f; }
.v-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.v-note { color: var(--muted); font-size: 12.5px; font-style: italic; }
/* Actions sit right, and only appear per row — a version with one file and no
   siblings shows no delete, because the API refuses it and offering a button
   that always fails is worse than not offering one. */
.v-acts { margin-left: auto; display: flex; flex-wrap: wrap; gap: 4px; }
.btn.tiny {
  font-size: 11.5px; padding: 3px 7px; border-radius: 8px;
}
.btn.disabled, .btn.disabled:hover { opacity: .5; cursor: default; }

/* The org team panel, opened under its row on Your profile. Indented so it reads
   as belonging to the organization above it rather than as a sibling card — the
   list is short and a second card tier here would make one company look like
   two things. */
.org-team-panel {
  margin: 0 0 12px 14px; padding: 8px 0 4px 12px;
  border-left: 1px solid var(--border);
}
.org-team-panel .team-row { padding: 5px 0; }

/* ---------- the account menu ----------
 * Anchored to the rail's bottom block and opening upward, the way an account
 * switcher pinned to a bottom-left corner does. Personal and organizations are
 * .am-row destinations; .am-item is a plain action. Below 900px the rail is a
 * top bar, so the menu drops DOWN from the avatar instead. */
.side-foot { position: relative; }
.account-menu {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 0;
  min-width: 230px;
  max-width: 290px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 8px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
  z-index: 40;
}
.am-ident {
  color: var(--muted); font-size: 11.5px; padding: 4px 10px 8px; margin: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.am-row {
  display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
  width: 100%; padding: 8px 10px; border: none; border-radius: 9px;
  background: none; color: var(--text); font: inherit; text-align: left;
  cursor: pointer;
}
.am-row:hover:not(:disabled) { background: var(--surface-2); }
.am-row:disabled { cursor: default; opacity: 0.55; }
.am-row .am-name { font-weight: 600; font-size: 13.5px; }
.am-row .am-sub { font-size: 11px; color: var(--muted); }
/* An organization name is free text with nothing to wrap on; it truncates the
 * way .am-ident already does rather than escaping the 290px panel. */
.am-row .am-name,
.am-row .am-sub { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.am-rule { border-top: 1px solid var(--border); margin: 6px 4px; }
.am-item {
  display: block; width: 100%; padding: 7px 10px; border: none; border-radius: 9px;
  background: none; color: var(--text); font: inherit; font-size: 13px;
  text-align: left; cursor: pointer;
}
.am-item:hover { background: var(--surface-2); }
@media (max-width: 900px) {
  .account-menu { bottom: auto; top: calc(100% + 8px); left: auto; right: 0; }
  /* The drop-down must ESCAPE the bar. .side carries overflow-y:auto for the
     desktop rail (a long rail scrolls); on the mobile top bar that same rule
     is a clip box, and the menu was being sliced to its first line — measured
     2026-08-28, a 137px menu showing 11px of itself ("the profile button is
     covered up"). The bar wraps rather than scrolls on mobile, so visible
     costs nothing. */
  .foundry-consumer .side { overflow-y: visible; }
}

/* ---------- where you are: country selects, city autocomplete, ob footer ---------- */
/* The selects wear exactly what the text inputs beside them wear, plus their
   own chevron — a bare <select> takes the OS widget frame and reads as a
   different product from the input one field over. */
.pf-field select.country-select, .ob-step select.country-select {
  appearance: none; -webkit-appearance: none;
  background-color: var(--surface-2); border: 1px solid var(--border);
  color: var(--text); border-radius: 10px; padding: 11px 34px 11px 13px;
  font-size: 14px; font-family: inherit; outline: none; width: 100%;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' fill='none' stroke='%238e8e98' stroke-width='1.6' stroke-linecap='round'/></svg>");
  background-repeat: no-repeat; background-position: right 13px center;
}
.pf-field select.country-select:focus, .ob-step select.country-select:focus {
  border-color: var(--border-2);
}

/* Google's autocomplete dropdown, dressed in the page's own tokens: the stock
   .pac-container is a white card with a Roboto shadow and looks pasted on. */
.pac-container {
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35); font-family: var(--font);
  margin-top: 6px; padding: 4px;
}
.pac-item {
  background: none; border-top: none; color: var(--text-2);
  font-family: inherit; font-size: 13px; padding: 8px 10px; border-radius: 8px;
  cursor: pointer;
}
.pac-item:hover, .pac-item-selected { background: var(--surface-2); }
.pac-item-query { color: var(--text); font-size: 13px; }
.pac-icon { display: none; }
/* Google's terms require attribution; the logo strip stays, dimmed to furniture. */
.pac-logo:after { opacity: 0.5; margin: 4px 10px 2px; }

/* The overlay covers the app footer, so long onboarding steps ended in bare
   background. Same voice as the app footer, inside the scrolling column. */
.ob-footer {
  display: flex; gap: 16px; flex-wrap: wrap; align-items: baseline;
  margin-top: 34px; padding-top: 14px; border-top: 1px solid var(--border);
  color: var(--dim); font-size: 11.5px;
}
.ob-footer a { color: var(--muted); text-decoration: none; }
.ob-footer a:hover { color: var(--text); }

/* The also-an-org fold on the artist flow's last step. */
.ob-also-org { margin-top: 10px; padding-top: 14px; border-top: 1px solid var(--border); }
.ob-also-org .linkish { font-size: 13px; }
#ob-also-org-form { display: flex; flex-direction: column; gap: 12px; margin-top: 12px; }

/* ---------- the send-to-a-DJ dialog ---------- */
.djm {
  position: fixed; inset: 0; z-index: 80; background: rgba(0, 0, 0, 0.55);
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.djm-card {
  position: relative; width: 100%; max-width: 440px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 16px;
  padding: 22px 22px 18px; display: flex; flex-direction: column; gap: 10px;
  max-height: 86vh; overflow-y: auto;
}
.djm-card h2 { font-size: 18px; font-weight: 650; }
.djm-close {
  position: absolute; top: 10px; right: 14px; background: none; border: none;
  color: var(--muted); font-size: 22px; cursor: pointer; line-height: 1;
}
.djm-close:hover { color: var(--text); }
.djm-card input[type=search] {
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text);
  border-radius: 10px; padding: 11px 13px; font: inherit; font-size: 14px;
  outline: none; width: 100%;
}
.djm-card input[type=search]:focus { border-color: var(--border-2); }
.djm-picked { display: flex; gap: 6px; flex-wrap: wrap; }
.djm-picked:empty { display: none; }
.djm-results { display: flex; flex-direction: column; gap: 2px; min-height: 40px; }
.djm-row {
  display: flex; align-items: baseline; gap: 10px; width: 100%;
  background: none; border: none; border-radius: 9px; padding: 8px 10px;
  color: var(--text-2); font: inherit; font-size: 13.5px; text-align: left; cursor: pointer;
}
.djm-row:hover:not(:disabled) { background: var(--surface-2); }
.djm-row:disabled { opacity: 0.45; cursor: default; }
.djm-row.on { color: var(--text); }
.djm-row .djm-name { font-weight: 600; }
.djm-row .djm-sub { font-size: 11.5px; color: var(--muted); }
.djm-row .djm-mark { margin-left: auto; color: var(--good); }
.djm-actions {
  display: flex; justify-content: space-between; margin-top: 6px;
  padding-top: 12px; border-top: 1px solid var(--border);
}

/* The picker well's empty state is a sentence, not a control — full width,
   no hover face, nothing to press. */
.send-empty { grid-column: 1 / -1; padding: 4px 2px; cursor: default; }

/* The producer's own released-match warning in the Library. Danger-toned but
   written as information: matching is an opinion, and the copy says so. */
.rec-release {
  border: 1px solid rgba(217, 106, 106, 0.45); border-radius: 11px;
  padding: 10px 13px; font-size: 13px; line-height: 1.55; color: var(--text-2);
}
.rec-release strong { color: var(--danger); }

/* The rights claim carries its own weight (2026-08-25): the disclaimer is not
   fine print, because "we are not responsible for your claim" read at 11px is
   a promise made quietly. */
.rights-disclaimer {
  margin: 8px 0 12px; padding: 10px 12px;
  border: 1px solid currentColor; border-radius: 8px;
  font-size: 0.95em; line-height: 1.45; opacity: .92;
}

/* The release-check warning above a result: the one thing on the card the
   producer must not scroll past, so it is a bordered block, not a caption. */
.release-warn {
  margin: 0 0 12px; padding: 12px 14px;
  border: 2px solid currentColor; border-radius: 10px;
}
.release-warn p { margin: 6px 0 8px; }
.release-warn .release-note { width: 100%; margin-top: 4px; }
.release-note-row { display: flex; align-items: center; gap: 10px; margin-top: 6px; }

/* Org rows in the account menu are facts, not doors (2026-08-28). */
.am-row-static { cursor: default; }
.am-row-static:hover { background: none; }

/* ===========================================================================
 * THE DOOR — the screen a stranger meets, and the record on it
 *
 * Sits above the shell and below the sign-in modal (#signup is 70), so pressing
 * "Sign in" opens the modal OVER this rather than replacing it: someone who
 * opens the modal by mistake closes it and is back where they were, with the
 * disc still turning.
 * ========================================================================= */
/* LIGHT, ALWAYS, WHATEVER THE APP IS SET TO (Nikhil, 2026-09-05: "only light
   mode on this screen").
   
   Done by redefining the tokens ON `.door` rather than by writing
   `data-theme="light"` onto <html>. Three reasons, and the third is the one
   that would have bitten:
     - the app behind keeps whatever theme the visitor chose, and closing the
       door does not have to put it back;
     - nothing has to be un-done on sign-in, so there is no state to leak;
     - the disc's gradient and its halo were tuned on a light ground and the
       type on the disc is white on both — a global flip would have left the
       rest of the app light too, which is not what was asked for.
   
   The token list is copied from :root[data-theme="light"]. It is a copy on
   purpose: this screen is a poster with a fixed palette, and inheriting a
   theme it is not allowed to be is how it would drift back to two. */
.door {
  /* LIGHTER THAN THE FLAT #f2f2f5 IT WAS (Nikhil, 2026-09-08, with two
     reference plates). The ground now runs from near-white at the top to a
     light grey at the foot, which does two things a flat field could not: the
     disc sits in a soft light rather than on a plate, and the type at the top
     gets the most contrast where it is smallest.
     --bg keeps the MIDPOINT of that gradient, because every component on this
     screen that paints its own background reads the token, and a token that
     matched neither end would leave those panels floating. */
  --bg: #f4f4f6;
  --surface: #ffffff;
  --surface-2: #ebebef;
  --border: #dcdce3;
  --border-2: #bdbdc9;
  --text: #17171c;
  --text-2: #3d3d49;
  --text-3: #686873;
  --muted: #5c5c68;
  --dim: #6d6d78;
  --s-demo: #9a6d17;
  --invert-bg: #17171a;
  --invert-fg: #ffffff;
  color-scheme: light;

  position: fixed; inset: 0; z-index: 65;
  color: var(--text);
  display: flex; flex-direction: column;
  overflow-y: auto;
  isolation: isolate;   /* so the grain's blend mode cannot reach the app behind */

  /* TWO LAYERS: a wide, very soft highlight roughly where the record sits, over
     a top-to-bottom wash. Both are ATMOSPHERE on a full-screen ground — not a
     surface and not a mark, which is the line this stylesheet's header draws.
     The stops are close together on purpose: at more than about six points of
     lightness across the page it stops reading as light and starts reading as
     a graphic, and the grain below is what keeps the whole thing from banding
     on an 8-bit panel. */
  background-color: var(--bg);
  background-image:
    radial-gradient(120% 90% at 30% 22%, #ffffff 0%, rgba(255,255,255,0) 62%),
    linear-gradient(180deg, #fafafb 0%, #f4f4f6 52%, #e9e9ee 100%);
  background-attachment: local;
}
/* The wordmark ships as a white SVG and is inverted for light grounds by a
   [data-theme="light"] rule that cannot see this scope. The door is always a
   light ground, so it always inverts. */
.door .wordmark-img { filter: invert(1); }
.door.hidden { display: none; }

/* THE GRAIN.
 *
 * A flat near-black field at this size reads as an unpainted background rather
 * than a surface — the eye has nothing to fix on between the disc and the type,
 * and gradients on a large dark area band visibly on 8-bit panels. Film grain
 * fixes both: it gives the field a texture and it dithers the disc's halo, so
 * the concentric rings stop stepping.
 *
 * ONE INLINE SVG, NOT AN IMAGE FILE. feTurbulence generates the noise in the
 * renderer, so there is no asset to fetch, nothing to cache-bust, and no
 * request on a screen whose whole job is to be the first thing that paints.
 * `stitchTiles="stitch"` makes the 300px tile seamless when it repeats;
 * without it the seams are visible as a grid at low opacity, which is worse
 * than no grain.
 *
 * SEPARATE OPACITIES PER THEME, not one value with a blend mode. Overlay-blended
 * noise on near-black barely moves and on white blows out; the honest fix is two
 * numbers. Dark carries more because dark is where banding lives.
 *
 * pointer-events: none — it covers the disc, and a decorative layer that eats
 * the click on the only control would be a very quiet bug.
 */
.door::before {
  content: ''; position: absolute; inset: 0; z-index: 0;
  pointer-events: none;
  /* NORMAL BLEND, not soft-light or overlay. Both of those preserve the base
     luminance, which is exactly wrong here: on a #0b0b0d field they move the
     pixel by under one 8-bit step and the grain is invisible — measured, that
     is what a first pass at soft-light 0.4 looked like. Mid-grey noise laid on
     at low alpha lifts the black slightly and speckles it, which is what film
     grain actually is. The cost is that pure black is no longer pure; at 0.09
     the field sits around #1a1a1c, which is this product's own --surface-2 and
     reads as a painted surface rather than an unpainted one. */
  /* MULTIPLY, now that the ground is always light. On the old near-black field
     the only blend that moved a pixel at all was `normal`, which meant laying
     grey ON the black and lifting it. On a #f2f2f5 ground `multiply` darkens
     instead, which is what grain does to paper — and it lets the opacity go up
     to somewhere it is actually visible (Nikhil asked for grainier twice) with
     no risk of washing the page out, because multiply cannot lighten.
     
     baseFrequency 1.1 rather than 0.9: finer grain. At 0.9 on a light ground
     the clumps read as compression artifacts rather than as texture. */
  opacity: 0.5;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.1' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='260' height='260' filter='url(%23n)'/%3E%3C/svg%3E");
  background-repeat: repeat;
}
@media (prefers-reduced-motion: reduce) { /* grain is static; nothing to do */ }

/* Everything on the door sits above the grain. Without this the disc paints
   UNDER it and the halo goes muddy. */
.door > * { position: relative; z-index: 1; }

.door-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 26px clamp(28px, 5vw, 120px); flex: 0 0 auto;
}
.door-top-right { display: flex; align-items: center; gap: 12px; }
/* Bigger, both of them (Nikhil, 2026-09-05). The wordmark is an <img> sized by
   the .wordmark rule's font-size/height pair, so the mark scales with this. */
.door-mark { font-size: 27px; }
.door-mark .module-label { font-size: 15px; margin-left: 13px; }

/* Outlined, not filled. The filled control on this screen is the record, and
   two solid buttons would make the visitor choose between them — which is the
   choice this page exists to avoid asking. */
.door-signin {
  border: 1.5px solid var(--text); background: none; color: var(--text);
  border-radius: 999px; padding: 13px 30px; font-size: 16px; font-weight: 600;
}
.door-signin:hover { background: var(--text); color: var(--bg); }

/* The disc left, the words right — AudioShake's split, and it is the right one
   here for a reason that is not imitation: the control is the thing that moves,
   and on a left-to-right page the eye lands on it before it reads anything.
   Below 900px the copy goes above the disc, because a headline under the fold
   is a headline nobody read. */
/* `.door > .door-main`, and the child combinator is carrying specificity, not
   structure. The door's stage is a <main>, and line 321 sets
   `.foundry-consumer main { max-width: 760px }` — the app's reading column,
   specificity (0,1,1), which outranks a bare `.door-main` (0,1,0). The door is
   a full-bleed screen and inherited that 760px cap: measured, the grid came out
   760px wide inside a 1440px page, both columns fell to 300px, and the headline
   stacked as four single words. */
.door > .door-main {
  flex: 1 1 auto;
  display: grid; align-items: center;
  /* TWO FRACTIONS, and `minmax(0, 1fr)` rather than a bare `1fr`: a bare 1fr
     has a min-content floor, so the copy column could never be narrower than
     the longest unbreakable word and the disc column would give way first.
     `auto` sizing was tried and is wrong here — the grid then shrinks to fit
     its content instead of filling the page, and the copy column collapsed to
     164px (min-content of the headline), which is what four-line "Know / what /
     you / made." was. Measured, not guessed. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  /* NO max-width, and that is the point (Nikhil, 2026-09-05: "spread it out
     over the screen"). Every other surface in this product is a reading
     column — 760px, because prose and inbox rows are read, not looked at. This
     screen is a poster with one control on it, and a 1180px box centred in a
     27" display is a browser window pretending to be a phone.

     So the grid takes the viewport and the padding scales with it: 5vw a side,
     floored at 28px so a laptop keeps a real margin and ceilinged at 120px so
     an ultrawide does not push the two halves into different postcodes.

     `width: 100%` with NO auto margin: an auto margin on the cross axis of a
     flex item cancels stretch and shrinks the item to fit its content, which
     is a different bug that also lived here. */
  gap: clamp(32px, 6vw, 160px);
  padding: 8px clamp(28px, 5vw, 120px) 40px;
  width: 100%;
  /* EXPLICIT `none`, not the absence of a max-width. `.foundry-consumer main`
     sets 760px — the app's reading column — and simply not declaring one here
     leaves that rule the winner. Dropping the declaration is what put this
     screen back in a 760px box after it had already been widened once, and the
     symptom is subtle: the page still looks deliberate, just small. */
  max-width: none;
}

.door-stage { display: flex; flex-direction: column; align-items: center; gap: 20px; }

/* One square box holding two stacked layers: the turning rim, and the disc that
   does not turn. Sized in vmin as well as px so the record is never taller than
   the viewport on a laptop — at 420px fixed it overflowed a 13" screen and the
   drop target went under the fold. */
/* Grows with the screen now that the page does. The vmin term is the one that
   binds on a laptop (it keeps the disc off the fold on a 13" 800px-tall
   viewport); the vw term lets a large display actually use its width; 560px is
   where it stops, because past that the rim type gets big enough to read as a
   headline and starts competing with the one on the right. */
.vinyl-wrap {
  position: relative;
  width: clamp(300px, min(28vw, 62vmin), 520px);
  height: clamp(300px, min(28vw, 62vmin), 520px);
  display: grid; place-items: center;
}

/* THE RIM. The whole <svg> turns; the <textPath> inside it does not move
   relative to the circle, which is what keeps every letter upright against the
   curve instead of tumbling. */
.vinyl-ring {
  position: absolute; inset: 0; width: 100%; height: 100%;
  animation: vinyl-spin 33s linear infinite;
}
.vinyl-text {
  fill: var(--muted);
  font-family: var(--font);
  /* In SVG user units, so it scales with the viewBox and therefore with the
     disc — a fixed px size would shrink to nothing at 560px and crowd the rim
     at 300px. */
  font-size: 14px; font-weight: 600;
  letter-spacing: 3.4px;
  text-transform: uppercase;
}
@keyframes vinyl-spin { to { transform: rotate(360deg); } }

/* THE DISC. The one gradient in this stylesheet, and it is deliberate: the file
   header says "no gradients, no glow", and that rule is about CHROME — panels,
   rails, chips, the furniture a producer looks past. This is the single object
   on the single screen whose whole job is to be pressed, and a flat circle at
   this size reads as a placeholder for an image that failed to load.
   Cyan into violet: the violet end is --s-label, the colour this product
   already uses for a catalogue everywhere else. */
.vinyl {
  position: relative;
  width: 62%; height: 62%; border-radius: 50%;
  display: grid; place-items: center;
  cursor: pointer;
  border: none; padding: 0;
  background:
    radial-gradient(circle at 32% 26%, #57d8f5 0%, #3aa6ee 34%, #6f6ae8 72%, #8f6ae8 100%);
  /* The soft halo AudioShake gets from two concentric translucent rings. One
     spread shadow in the same hue does it with nothing to keep in step. */
  box-shadow:
    0 0 0 14px color-mix(in srgb, #4aa8ef 12%, transparent),
    0 0 0 30px color-mix(in srgb, #4aa8ef 6%, transparent),
    0 18px 60px -18px rgba(60, 140, 235, 0.55);
  transition: transform .18s ease, box-shadow .18s ease;
  outline: none;
}
.vinyl:hover, .vinyl:focus-visible { transform: scale(1.035); }
.vinyl:focus-visible {
  box-shadow:
    0 0 0 4px var(--bg),
    0 0 0 7px var(--text),
    0 18px 60px -18px rgba(60, 140, 235, 0.55);
}
/* Dragging a file over it. Bigger and brighter, so the answer to "will it take
   this" arrives before the mouse button is released. */
.vinyl.drag {
  transform: scale(1.075);
  box-shadow:
    0 0 0 16px color-mix(in srgb, #4aa8ef 22%, transparent),
    0 0 0 36px color-mix(in srgb, #4aa8ef 10%, transparent),
    0 18px 60px -14px rgba(60, 140, 235, 0.7);
}
.vinyl-orb { display: grid; place-items: center; text-align: center; padding: 0 12%; }
/* White on both themes: this sits on the gradient, which does not change with
   the theme, so a token here would make the label vanish in light mode. */
.vinyl-cta {
  color: #fff; font-size: 15px; font-weight: 600; letter-spacing: -0.1px;
  text-shadow: 0 1px 10px rgba(20, 50, 110, 0.35);
}

.door-formats { color: var(--text-3); font-size: 12.5px; text-align: center; margin: 0; }

/* PIXELS, NOT ch. `ch` resolves against THIS element's font-size, which is the
   body's 15px — so `30ch` was about 240px and forced the 64px headline to four
   lines ("Know / what / you / made."), breaking the <br> that was meant to give
   it two. The heading is the widest thing in this column, so the column is
   measured in the units the heading is set in. */
/* A measure, not a box. The headline scales with the viewport and the
   paragraph must not run to 120 characters on a wide screen — that is the one
   thing a full-bleed layout gets wrong. Capped in px because `ch` resolves
   against THIS element's font-size (the body's 15px), not the heading's. */
/* Left aligned at every width (Nikhil, 2026-09-05: "left align that header").
   The narrow-viewport rule below used to centre this column along with the
   disc; only the DISC is centred there now. */
.door-copy { max-width: 620px; text-align: left; }
.door-copy h1 {
  font-size: clamp(38px, 4vw, 78px); font-weight: 700;
  letter-spacing: -2px; line-height: 1.0; margin: 0 0 20px;
}
.door-sub { color: var(--muted); font-size: clamp(15px, 1.15vw, 18px); line-height: 1.6; margin: 0 0 22px; max-width: 54ch; }
.door-cta { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
/* THE PAGE'S ONE FILLED BUTTON besides the disc. Near-black on the light
   ground rather than the amber the app uses for a producer's own record: amber
   beside a cyan-to-violet disc is a third hue on a page that already has two,
   and this button is a door, not a record. */
.door-try {
  background: var(--invert-bg); border-color: var(--invert-bg); color: var(--invert-fg);
  border-radius: 999px; padding: 15px 34px; font-size: 16.5px; font-weight: 600;
}
.door-try:hover { background: #000; border-color: #000; }
.door-free { display: inline-flex; align-items: center; gap: 9px; color: var(--text-2); font-size: 13.5px; margin: 0; }
.door-free-tag {
  background: var(--s-demo); color: #140f06; border-radius: var(--r-chip);
  padding: 3px 9px; font-size: 11.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.6px;
}

.door-fine {
  flex: 0 0 auto; color: var(--text-3); font-size: 11.5px; line-height: 1.6;
  text-align: center; padding: 0 28px 26px; margin: 0;
}
.door-fine a { color: var(--muted); text-decoration: underline; text-underline-offset: 2px; }
.door-fine a:hover { color: var(--text); }

/* THE DISC STOPS. A element turning forever is exactly what this setting is
   for, and the page loses nothing without it: the rim still says what to do,
   because the words were never carried by the motion. */
@media (prefers-reduced-motion: reduce) {
  .vinyl-ring { animation: none; }
  .vinyl, .vinyl:hover, .vinyl:focus-visible { transition: none; transform: none; }
}

@media (max-width: 900px) {
  /* Copy first. A headline below a 420px disc on a phone is a headline that
     needs a scroll before it can be read, and the disc means nothing until the
     sentence beside it has been. */
  .door > .door-main {
    grid-template-columns: 1fr; gap: 30px; padding: 4px 22px 32px;
    justify-items: stretch;
  }
  /* The DISC centres on a narrow screen; the copy does not. Centred type under
     a centred disc was the layout that made "left align that header" a note
     worth writing. */
  .door-stage { order: 2; align-items: center; }
  .door-copy { order: 1; max-width: none; text-align: left; }
  .door-copy h1 { letter-spacing: -1.4px; }
  .door-top { padding: 18px 20px; }
  .door-mark { font-size: 22px; }
  .door-signin { padding: 10px 22px; font-size: 14.5px; }
}

/* ===========================================================================
 * THE TRACK REPORT — the measured description of one bounce
 * ========================================================================= */
.track-report { display: flex; flex-direction: column; gap: 26px; }

.tr-sec h3 {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.7px;
  color: var(--muted); margin: 0 0 10px;
}
.tr-rows { display: flex; flex-direction: column; }

/* Label, number, and the sentence under them. The number is the widest thing
   in the row on purpose: it is what somebody came to read. */
.tr-row {
  display: grid; grid-template-columns: 1fr auto; align-items: baseline;
  gap: 6px 18px; padding: 11px 0; border-top: 1px solid var(--border);
}
.tr-row:first-child { border-top: none; }
.tr-label { color: var(--text-2); font-size: 14px; }
.tr-value {
  font-size: 19px; font-weight: 600; color: var(--text);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.tr-note { grid-column: 1 / -1; color: var(--text-3); font-size: 12px; line-height: 1.5; }
.tr-unit { font-size: 12.5px; font-weight: 500; color: var(--muted); margin-left: 3px; }
.tr-sep { color: var(--text-3); margin: 0 4px; }

/* NOT DETECTED, and it must not look like a number. Same weight as the fine
   print, never tabular, never styled as a value — the whole point of rule 1 is
   that a person can tell at a glance that nobody measured this. */
.tr-none {
  color: var(--text-3); font-size: 13px; font-weight: 400;
  font-style: italic; letter-spacing: 0;
}

/* The four bands. Proportional widths, because they do not sum to 100% — the
   ranges are named the way a listener names them and there are real gaps
   between them (120-150 Hz belongs to neither "kick" nor "body"). */
.tr-bands { display: flex; gap: 4px; align-items: stretch; }
.tr-band {
  flex-basis: 0; min-width: 62px;
  display: flex; flex-direction: column; gap: 4px;
  padding-top: 8px;
}
.tr-band-fill { height: 6px; border-radius: 3px; display: block; }
.tr-band-kick     { background: #3aa6ee; }
.tr-band-body     { background: #5b8ff0; }
.tr-band-presence { background: #7d74ea; }
.tr-band-hats     { background: #a08ae8; }
.tr-band-name { font-size: 12.5px; color: var(--text-2); }
.tr-band-val { font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; }
.tr-band-hz { font-size: 11px; color: var(--text-3); }

/* THE ONE ALLOWED CONSEQUENCE — arithmetic on a published target. Marked out
   from the measurements above it so a screen cannot render one as the other. */
.tr-consequence {
  margin: 14px 0 0; padding: 11px 13px;
  background: var(--surface-2); border-radius: var(--r-ctl);
  color: var(--text-2); font-size: 13px; line-height: 1.55;
}
.tr-consequence strong { color: var(--text); font-weight: 600; }

.tr-fine { color: var(--text-3); font-size: 11.5px; line-height: 1.55; margin: 10px 0 0; }
.tr-provenance {
  color: var(--text-3); font-size: 11.5px; line-height: 1.55;
  margin: 0; padding-top: 16px; border-top: 1px solid var(--border);
}
.tr-empty { padding: 24px 0; }

/* ---------- the report screen ----------
 * Two panels, and they are the two questions the upload raises: where does it
 * fit, and what does it measure. Side by side above 1040px; stacked below,
 * with the fit first — that is what a producer opened the page for. */
/* THE REPORT IS A DASHBOARD, NOT PROSE, so it does not sit in the 760px reading
   column the rest of this surface uses (Nikhil, 2026-09-05: the margins on a
   computer). Two panels of measurements read better side by side and wide than
   stacked and narrow — and unlike an inbox row or a paragraph, nothing here
   gets harder to read as the line gets longer, because nothing here IS a line.

   `#view-report` and not `.report-cols`: the cap is on the <main> ancestor, so
   widening the grid alone changes nothing. Specificity (0,1,1) to match
   `.foundry-consumer main`, and an explicit value rather than an absence —
   dropping the declaration hands the 760px back, which is the trap the door
   already fell into twice. */
.foundry-consumer main:has(> #view-report:not(.hidden)) { max-width: 1320px; }
/* THE STUDIO SPANS THE PAGE (Nikhil, 2026-09-09: "it should span the whole
   page"). Same mechanism as the report above; the analytics grid below is
   `columns`, so it simply gains columns as the width does. */
.foundry-consumer main:has(> #view-studio:not(.hidden)) { max-width: none; padding-inline: clamp(20px, 3vw, 48px); }
/* The record's own words, under its title: what it is, not what it measures. */
.studio-about { margin-top: 10px; }
.studio-about .vibe { font-size: 15.5px; line-height: 1.5; margin: 0 0 8px; max-width: 900px; }
.studio-about .chips { margin-top: 8px; }

/* ---- above the tabs: the acts, the shortlist, the belt ----
   These three are what the screen is FOR, so they are never behind a tab. They
   keep a card each because they are three different objects, unlike the
   measurements below, which are one description of one record. */
.studio-fit, .studio-net {
  margin: 16px 0 0; background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; padding: 18px 22px;
}
.studio-fit h3, .studio-net h3 { margin-top: 0; }
.studio-net { overflow: hidden; }

/* ---- the tabs ----
   Listen, Measurements, Sharing. Why these three and not the acts above them,
   and why a tab is safe here at all, is argued above TABS in track-studio.js. */
.studio-tabs {
  display: flex; gap: 2px; flex-wrap: wrap;
  margin: 26px 0 20px; border-bottom: 1px solid var(--border);
}
.studio-tab {
  appearance: none; background: none; border: none; cursor: pointer;
  font: inherit; font-size: 14px; color: var(--muted);
  padding: 10px 16px; margin-bottom: -1px;
  border-bottom: 2px solid transparent;
}
.studio-tab:hover { color: var(--text); }
.studio-tab[data-on] { color: var(--text); font-weight: 600; border-bottom-color: var(--lilac); }
.studio-panel[hidden] { display: none; }

/* ---- the measurement sheet ----
   ONE SHEET, NOT NINE BOXES — the reasoning is in track-studio.js above the
   measure panel. Every `.tr-sec` in here loses its card and becomes a section
   of one document: a rule above it, a quiet caption, and rows whose numbers
   all land in the same column. Capped in width because a spec sheet is read
   down, not across a 1400px monitor. */
.studio-sheet { max-width: 920px; }
.studio-sheet .tr-sec,
.studio-sheet .track-report .tr-sec {
  margin: 0; padding: 20px 0 4px; background: none; border: none; border-radius: 0;
  border-top: 1px solid var(--border-soft);
}
.studio-sheet > :first-child .tr-sec:first-child,
.studio-sheet > .tr-sec:first-child { border-top: none; padding-top: 4px; }
.studio-sheet .tr-sec h3 {
  margin: 0 0 6px; font-size: 12.5px; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--muted);
}
.studio-sheet .tr-row {
  display: grid; grid-template-columns: 1fr auto; align-items: baseline;
  gap: 2px 20px; padding: 11px 0; border-bottom: 1px solid var(--border-soft);
}
.studio-sheet .tr-row:last-child { border-bottom: none; }
.studio-sheet .tr-label { font-size: 14px; color: var(--text-2); }
.studio-sheet .tr-value { font-size: 20px; font-weight: 600; letter-spacing: -0.02em; text-align: right; }
.studio-sheet .tr-note { grid-column: 1 / -1; font-size: 12.5px; color: var(--muted); margin: 0; }
.studio-sheet .tr-fine { font-size: 12px; max-width: 72ch; }
.studio-sheet .tr-consequence { margin: 10px 0 0; }

/* The glance line above the sheet: three numbers, no card — a card would make
   it a fourth section, and it is a summary OF the sections. */
.studio-summary { display: flex; flex-wrap: wrap; gap: 10px 36px; margin: 0 0 24px; }
.studio-sum { display: flex; flex-direction: column; gap: 2px; }
.studio-sum-k { font-size: 12px; color: var(--muted); }
.studio-sum-v { font-size: 26px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.1; }
.studio-sum-v em { font-style: normal; font-size: 15px; color: var(--muted); margin-left: 6px; }
.studio-summary-fine { margin: 0 0 18px; max-width: 70ch; }

/* ---- sharing: two forms, and a form IS an object, so these keep their box ---- */
.studio-share-cols {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: 16px; align-items: start;
}
.studio-share-cols .tr-sec {
  margin: 0; background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; padding: 18px 22px;
}
.studio-share-cols .tr-sec h3 { margin-top: 0; }

/* THE ANALYTICS HALF: an even grid of cards rather than masonry columns, so
   the eye reads rows; the number is the biggest thing in each row and the
   explanation is fine print under it. */
.studio-cols { columns: unset; display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; align-items: start; }
.studio-cols .tr-sec { break-inside: auto; margin: 0; }
.studio-cols .tr-row { padding: 12px 0; }
.studio-cols .tr-value { font-size: 22px; letter-spacing: -0.01em; }
.studio-cols .tr-label { font-size: 13.5px; }
.studio-cols .tr-note { font-size: 12px; }
.studio-cols .tr-fine { font-size: 11.5px; }
#view-report .intro, #view-report .report-cols { width: 100%; }

/* THE CARD GRID. `display: contents` on #report-body is what makes this work:
   track-report.js emits five <section class="tr-sec"> siblings, and without it
   they would be five children of ONE grid cell — the tall-column layout this
   replaced. With it they are five grid items in their own right, so the browser
   flows them beside the shortlist. The renderer needs no knowledge of the
   layout, which is the point of it emitting sections rather than markup with
   the columns baked in. */
/* MULTI-COLUMN, NOT GRID, and the difference is the holes.
   
   CSS grid aligns rows, so one tall card (The Master, with its four rows and
   two notes) sets the height of its whole row and leaves the short cards beside
   it sitting over dead space — measured at 1600px, a ~350px hole under the
   shortlist and another under Frequency Balance. These five sections have no
   relationship to each other's height and no reading order across a row, which
   is exactly the case multicol is for: cards flow down a column and across, and
   `break-inside: avoid` keeps each one whole. Masonry grid would be the modern
   answer and is not portably available yet.
   
   `columns: 3 320px` is a maximum and a minimum together: at most three
   columns, and never one narrower than 320px — so this is one column on a
   phone, two on a laptop, three on a wide display, from one declaration. */
.report-cols {
  columns: 3 320px;
  column-gap: 18px;
}
.report-measures { display: contents; }

/* Each section is a card in its own right. The shared .card rule paints the
   shortlist; these get the same treatment without needing a wrapper div per
   section in the renderer's output. */
/* DESCENDANT SELECTORS, NOT `>`. `display: contents` changes the BOX tree, not
   the DOM tree — the sections become grid items, but in the document they are
   still grandchildren of #report-body, so `.report-cols > .tr-sec` matches
   nothing. They laid out correctly and painted with no card at all, which is a
   confusing way for this to fail: the layout looks deliberate. */
/* DESCENDANT SELECTORS, NOT `>`. `display: contents` changes the BOX tree, not
   the DOM tree — the sections join the column flow, but in the document they
   are still grandchildren of #report-body, so `.report-cols > .tr-sec` matches
   nothing. They flowed correctly and painted with no card at all, which is a
   confusing way for this to fail: the layout looks deliberate. */
.report-cols .tr-sec,
.report-cols > .card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 18px 20px;
  /* Both are needed and they do different things: `break-inside` stops a card
     being split across a column boundary, and `inline-block` stops the top
     margin of the first child collapsing out through the card's own edge,
     which in multicol shows up as one card sitting a few px higher than its
     neighbours for no visible reason. */
  break-inside: avoid;
  display: inline-block; width: 100%;
  margin-bottom: 18px;
}
/* The provenance line is a footnote under the whole thing rather than a card in
   it, so it leaves the column flow. */
.report-cols .tr-provenance,
.report-cols .tr-empty { column-span: all; border-top: none; padding-top: 4px; }

/* `.track-report`'s own column gap is gone with the grid; it is display:contents
   now and lays nothing out. */
.track-report { display: contents; }

.report-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 4px; }
.report-title { font-size: 24px; font-weight: 600; letter-spacing: -0.5px; margin: 0; }
.report-sub { color: var(--muted); font-size: 13.5px; }

/* The keep-it prompt a free try ends on. Amber, like every other place this
   product means "your own record". */
.report-keep {
  margin-top: 20px; padding: 18px 20px;
  border: 1px solid var(--s-demo); border-radius: var(--r);
  background: color-mix(in srgb, var(--s-demo) 8%, transparent);
}
.report-keep h3 { font-size: 15px; font-weight: 600; margin: 0 0 6px; }
.report-keep p { color: var(--text-2); font-size: 13.5px; line-height: 1.55; margin: 0 0 14px; }
/* On the guest page this is the ONLY call to action on the screen, and at the
   foot of a long report it has to still be there when the reader arrives. It
   grows; it does not move, float or interrupt — a wall over the analysis would
   make the free try a bait, and the door's copy promises the opposite. */
body.guest-report .report-keep { margin-top: 34px; padding: 24px 26px; }
body.guest-report .report-keep h3 { font-size: 19px; margin-bottom: 8px; }
body.guest-report .report-keep p { font-size: 14.5px; max-width: 62ch; }

/* The door's own status line. aria-live, so a screen reader hears the upload
   finish; empty and zero-height at rest, so nothing reserves space on a page
   whose whole composition is one disc. */
.door-state {
  min-height: 1.2em; margin: 0; text-align: center;
  color: var(--text-2); font-size: 13.5px; line-height: 1.5;
  max-width: 42ch;
}
.door-state:empty { min-height: 0; }
.door-state.bad { color: var(--danger); }
.door-pct { font-variant-numeric: tabular-nums; color: var(--text); font-weight: 600; }

/* The ranking on the report screen. .rank-row already carries the row layout;
   this only strips the list furniture and pins the score column. */
.rank-list { list-style: none; margin: 0; padding: 0; }
.rank-list .rank-row { display: grid; grid-template-columns: 18px 1fr auto; align-items: baseline; gap: 10px; }
.rank-list .r-name { font-size: 15px; color: var(--text); }
.rank-list .r-score { font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums; }
/* The floored marker (093): a replayed score that was LIFTED to a label's
   minimum rather than measured. Small and unemphatic — it qualifies the number
   it sits beside, it does not shout over it — but present, because an unmarked
   lifted score is the fabricated number CLAUDE.md rule 1 forbids. */
.rank-list .r-floored { font-size: 11px; font-weight: 500; vertical-align: super;
  margin-left: 1px; color: var(--muted); }

/* ---------- the credential badge ----------
 * A small card under the disc carrying one measured comparison. Modelled on
 * AudioShake's Sony Demixing Challenge badge: a claim somebody else could
 * check, sitting next to the control, rather than an adjective in the copy.
 *
 * White card on the door's light ground so it reads as a certificate pinned to
 * the page rather than as another section of it. */
.door-badge {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 14px 18px;
  max-width: 400px;
  box-shadow: 0 8px 26px -18px rgba(20, 24, 44, 0.5);
}
.db-stat {
  margin: 0; font-size: 15px; line-height: 1.35; color: var(--text-2);
  font-weight: 500;
}
.db-stat strong { font-size: 25px; font-weight: 700; color: var(--text); letter-spacing: -0.5px; }
.db-model { color: var(--text); font-weight: 600; white-space: nowrap; }
.db-task { margin: 3px 0 0; font-size: 13.5px; color: var(--text-2); line-height: 1.4; }
/* The caveat, and it is deliberately the same size as the task line rather than
   shrunk into fine print — it is the sentence that makes the number above it
   defensible, so it may not be styled as something to skip. */
.db-fine { margin: 7px 0 0; font-size: 12.5px; color: var(--text-3); line-height: 1.5; }
.db-meta {
  margin: 9px 0 0; padding-top: 8px; border-top: 1px solid var(--border);
  font-size: 11px; color: var(--text-3); letter-spacing: 0.2px; line-height: 1.5;
}
@media (max-width: 900px) { .door-badge { max-width: none; } }

/* ===========================================================================
 * THE TIMELINE CHART, and THE TRACK STUDIO around it
 *
 * Two rules govern everything below, and both are about the same thing — the
 * charts are drawn in a 1000-unit viewBox with preserveAspectRatio="none", so
 * they stretch to the card:
 *
 *   1. NOTHING WITH A FIXED ASPECT LIVES INSIDE THE SVG. No text, no circles.
 *      Labels, tick marks and the playhead are HTML positioned in percent over
 *      the plot, which is why .tlc-plot is position:relative and its children
 *      are absolute. A resize needs no listener and cannot be one frame late.
 *   2. THE PLOT'S HEIGHT IS SET HERE, NOT IN THE MARKUP. The SVG carries a
 *      viewBox height so its geometry is defined; the pixels come from CSS, so
 *      one number changes the chart on every screen that draws it.
 * ========================================================================= */
.tlc { display: block; }
.tlc-h2 {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.7px;
  color: var(--muted); margin: 26px 0 10px;
}

/* The stack: the waveform sits behind the band ribbon, which is why the ribbon
   is drawn at 0.85 opacity and the wave at 0.55 — two flat fills reading as one
   picture, rather than a chart on top of a decoration. */
.tlc-plot {
  position: relative; display: block; width: 100%;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-ctl); overflow: hidden; cursor: pointer;
}
.tlc-svg { display: block; width: 100%; }
.tlc-wave { height: 62px; }
.tlc-ribbon { height: 104px; }
.tlc-energy {
  height: 78px; margin-top: 8px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-ctl);
}
.tlc-width {
  height: 40px; margin-bottom: 10px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-ctl);
}

/* Section boundaries, over the plot. A hairline and nothing else: the sections
   table underneath carries every number, and a label on each line here would
   be six overlapping words on a phone. */
.tlc-marks { position: absolute; inset: 0; pointer-events: none; }
.tlc-mark {
  position: absolute; top: 0; bottom: 0; width: 1px;
  background: var(--text); opacity: 0.28;
}

/* The playhead. `hidden` until the first timeupdate — a line parked at 0:00
   before anything has played reads as a section boundary at the very start. */
.tlc-head {
  position: absolute; top: 0; bottom: 0; width: 2px; margin-left: -1px;
  background: var(--cream); pointer-events: none;
}
.tlc-head[hidden] { display: none; }

.tlc-axis {
  display: flex; justify-content: space-between;
  margin-top: 6px; font-size: 11px; color: var(--text-3);
  font-variant-numeric: tabular-nums;
}

.tlc-key { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-top: 10px; }
.tlc-key-i { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--text-2); }
/* A true circle, so the radius is a radius. */
.tlc-swatch { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
.tlc-key-hz { color: var(--text-3); font-size: 11px; }

/* ---------- the sections table ----------
 * Six columns on a laptop, and the bar column is the one that collapses first:
 * the times and the level are the numbers somebody reads, the balance bar is
 * the thing they glance at. */
.tlc-sections { display: flex; flex-direction: column; }
.tlc-srow {
  display: grid; align-items: center; gap: 10px;
  grid-template-columns: 22px 92px 44px 72px 1fr 68px;
  padding: 9px 0; border-top: 1px solid var(--border);
  font-size: 13px; font-variant-numeric: tabular-nums;
}
.tlc-srow:first-child { border-top: none; }
.tlc-sn {
  width: 22px; height: 22px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface-2); color: var(--text-2); font-size: 11.5px;
}
.tlc-st { color: var(--text); white-space: nowrap; }
.tlc-sd, .tlc-se { color: var(--text-2); }
.tlc-se { font-weight: 600; color: var(--text); }
.tlc-sb { display: flex; gap: 2px; height: 8px; align-items: stretch; }
.tlc-mini { display: block; border-radius: 2px; min-width: 2px; flex-basis: 0; }
.tlc-stag { text-align: right; }
.tlc-tag {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--s-demo); border: 1px solid var(--border-2);
  border-radius: var(--r-chip); padding: 2px 6px; white-space: nowrap;
}
.tlc-tag.quiet { color: var(--text-3); }
@media (max-width: 620px) {
  .tlc-srow { grid-template-columns: 22px 1fr 72px; }
  .tlc-sd, .tlc-sb { display: none; }
}

/* ---------- the tier panel ----------
 * A dashed edge, not a smoked pane over a fake chart. It has to be legible as
 * "there is more, and here is what it is" and never as "here is your data,
 * obscured" — a producer cannot tell a blurred measurement from a missing one,
 * and "not measured" is a real state on this screen. */
.tlc-pro, .tlc-soon {
  border: 1px dashed var(--border-2); border-radius: var(--r);
  padding: 16px 18px; background: transparent;
  /* THE COLUMN MAY NOT SPLIT IT. .report-cols is a CSS multi-column layout, and
     a fragmented panel put its button on top of its own last paragraph —
     measured in the harness at 1180px. Every sibling card in that grid already
     avoids the break; these two were added without it. */
  break-inside: avoid; -webkit-column-break-inside: avoid;
}
.tlc-pro-what { margin: 0 0 10px; font-size: 13.5px; line-height: 1.55; color: var(--text-2); }
.tlc-pro-list {
  margin: 0 0 12px; padding-left: 18px;
  font-size: 13.5px; line-height: 1.6; color: var(--text-2);
}
.tlc-pro-list li { margin-bottom: 7px; }
.tlc-pro-list strong { color: var(--text); font-weight: 600; }
.tlc-pro .btn { margin-top: 4px; }

/* The snippet window: a control over the plot, not a mark on it. Positioned in
   percent like everything else above the stretched SVG.
   IT MUST NOT LOOK LIKE THE PLAYHEAD. Both were cream, and in the harness shot
   the window's two edges and the playhead's line were three near-identical
   vertical strokes over one waveform — a producer cannot drag what they cannot
   tell apart from where they are. The window is amber and labelled; the
   playhead stays a plain cream line. */
.tlc-window {
  position: absolute; top: 0; bottom: 0;
  background: rgba(215, 154, 58, 0.20);
  border-left: 2px solid var(--amber); border-right: 2px solid var(--amber);
  cursor: grab; touch-action: none;
}
.tlc-window::after {
  content: attr(data-label);
  position: absolute; top: 4px; left: 4px;
  font-size: 10px; letter-spacing: 0.4px; white-space: nowrap;
  color: var(--invert-fg); background: var(--amber);
  padding: 1px 5px; border-radius: var(--r-chip);
  pointer-events: none;
}
.tlc-window:active { cursor: grabbing; }

.studio-pool { border: 1px dashed var(--border-2); border-radius: var(--r); padding: 16px 18px;
             break-inside: avoid; -webkit-column-break-inside: avoid; }
.studio-pool-pick { font-size: 13.5px; color: var(--text-2); margin: 10px 0; }
.studio-pool-at {
  width: 74px; font: inherit; font-size: 13px; padding: 4px 8px;
  border: 1px solid var(--border); border-radius: var(--r-chip);
  background: var(--surface-2); color: var(--text); font-variant-numeric: tabular-nums;
}
.studio-pool-note { display: block; margin: 10px 0 12px; }
.studio-pool-note span { display: block; font-size: 12px; color: var(--text-3); margin-bottom: 5px; }
.studio-pool-note input {
  width: 100%; font: inherit; font-size: 13.5px; padding: 8px 10px;
  border: 1px solid var(--border); border-radius: var(--r-ctl);
  background: var(--surface-2); color: var(--text);
}
.studio-pool-state { margin-left: 10px; font-size: 12.5px; color: var(--text-3); }
.studio-pool-rel { border: 0; margin: 10px 0 12px; padding: 0; }
.studio-pool-rel legend { padding: 0; font-size: 12px; color: var(--text-3); margin-bottom: 6px; }
.studio-pool-rel label {
  display: inline-flex; align-items: center; gap: 6px;
  margin-right: 16px; font-size: 13.5px; color: var(--text-2); cursor: pointer;
}

/* ---------- private links (migration 106) ----------
 *
 * BESIDE THE EXPLORE PANEL AND DELIBERATELY NOT IDENTICAL TO IT. The two are
 * the only ways a record leaves the studio and they are opposites — twenty
 * seconds to everybody, or this bounce to one named person — so a reader
 * glancing at the column has to be able to tell them apart without reading the
 * heading. `.studio-pool` is a DASHED border (a thing you post); this is a SOLID
 * hairline on the panel surface (a thing you hold). Same radius, same padding,
 * same column-break rules, so they still read as siblings.
 *
 * A DEAD LINK STAYS ON THE SCREEN, dimmed rather than removed: it is the record
 * of who opened what, and `revoked_at` exists precisely so that survives the
 * link being turned off (106's header). Opacity rather than a grey palette so
 * the names in it stay legible.
 */
.studio-link { border: 1px solid var(--border); border-radius: var(--r);
             background: var(--surface-2); padding: 16px 18px;
             break-inside: avoid; -webkit-column-break-inside: avoid; }
.studio-link h4 { margin: 18px 0 8px; font-size: 13px; letter-spacing: 0.2px;
                color: var(--text-2); }
.studio-link h4:first-child { margin-top: 0; }

/* The code, shown once. Amber because it is the one thing on this panel that
 * cannot be recovered — the server keeps only sha256 of it — and the colour is
 * the same "pay attention now" the live dot uses elsewhere. */
.studio-link-code { margin: 12px 0 0; padding: 9px 11px; font-size: 13px;
                  border: 1px solid var(--amber); border-radius: var(--r-ctl);
                  color: var(--text); background: var(--surface); }

.studio-link-list { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 10px; }
.studio-link-row { border: 1px solid var(--border); border-radius: var(--r-ctl);
                 background: var(--surface); padding: 11px 12px; }
.studio-link-row.is-dead { opacity: 0.55; }
.studio-link-top { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.studio-link-door { font-size: 12px; font-weight: 600; color: var(--text); }
.studio-link-note { font-size: 12.5px; color: var(--text-2); }
.studio-link-state { margin-left: auto; font-size: 11.5px; color: var(--text-3);
                   font-variant-numeric: tabular-nums; }

/* The address, readable AND copyable. The field is `readonly` rather than
 * disabled so it can still be selected and read aloud — a disabled input is
 * skipped by some screen readers, and this string is the whole point of the
 * panel. */
.studio-link-url { display: flex; gap: 6px; margin: 8px 0 6px; }
.studio-link-field { flex: 1 1 auto; min-width: 0; font: inherit; font-size: 12.5px;
                   padding: 6px 8px; border: 1px solid var(--border);
                   border-radius: var(--r-chip); background: var(--surface-2);
                   color: var(--text); }
.studio-link-say { display: inline-block; margin-left: 8px; font-size: 12.5px;
                 color: var(--text-3); }

/* The three doors. Stacked, each with its sentence under the label, because the
 * sentence IS the choice — a row of three bare radio labels would make an
 * artist pick "anyone signed in" without learning what it means. */
.studio-link-doors, .studio-link-serve { border: 0; margin: 0 0 12px; padding: 0; }
.studio-link-doors legend, .studio-link-serve legend {
  padding: 0; font-size: 12px; color: var(--text-3); margin-bottom: 6px; }
.studio-link-door-opt { display: grid; grid-template-columns: auto 1fr;
                      gap: 2px 8px; align-items: start; padding: 7px 0; }
.studio-link-door-opt input { grid-row: span 2; margin-top: 3px; }
.studio-link-door-name { font-size: 13.5px; font-weight: 600; color: var(--text); }
.studio-link-door-opt .tr-fine { margin: 0; grid-column: 2; }

.studio-link-who textarea { width: 100%; font: inherit; font-size: 13.5px;
                          padding: 8px 10px; border: 1px solid var(--border);
                          border-radius: var(--r-ctl); background: var(--surface-2);
                          color: var(--text); resize: vertical; }

.studio-link-serve label { display: block; font-size: 13.5px; color: var(--text-2);
                         padding: 4px 0; }
.studio-link-bounds { display: flex; gap: 14px; flex-wrap: wrap; margin: 0 0 14px;
                    font-size: 13px; color: var(--text-2); }

/* Who opened it. A three-column grid so the names line up and the dates line up
 * — a list where "played 3 times" sits at a different x on every row is a list
 * nobody scans. Tabular figures for the same reason. */
.studio-open-list { list-style: none; margin: 0 0 10px; padding: 0; display: grid; gap: 2px; }
.studio-open-row { display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
                 gap: 10px; align-items: baseline; padding: 6px 0;
                 border-bottom: 1px solid var(--border-2); }
.studio-open-row:last-child { border-bottom: 0; }
.studio-open-who { font-size: 13.5px; color: var(--text); overflow-wrap: anywhere; }
.studio-open-what { font-size: 12.5px; color: var(--text-2); }
.studio-open-when { font-size: 12px; color: var(--text-3); font-variant-numeric: tabular-nums; }

@media (max-width: 520px) {
  /* The date drops under the name rather than squeezing the name to nothing:
     the name is what the artist came to read. */
  .studio-open-row { grid-template-columns: minmax(0, 1fr) auto; }
  .studio-open-when { grid-column: 1 / -1; }
}

/* ---------- the studio ---------- */
/* THE HEAD IS TWO ROWS, NOT ONE (2026-10-13). It was a single flex row holding
   four different kinds of thing — the way out, which bounce, which record, and
   what it sounds like — all at one weight. Now: navigation above a rule, the
   record below it. */
.studio-head { margin-bottom: 20px; }
.studio-head-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 16px;
  padding-bottom: 12px; border-bottom: 1px solid var(--border-soft);
}
.studio-head-id { display: flex; align-items: flex-start; gap: 16px; }
.studio-id { display: flex; flex-direction: column; gap: 4px; margin-right: auto; }
.studio-id .report-title { margin: 0; }

/* One version per tab. The current one is filled with the same invert pair
   every other "you are here" control on this surface uses, so a producer does
   not have to learn a second active state. */
.studio-rail { display: flex; gap: 6px; flex-wrap: wrap; }
.studio-v {
  border: 1px solid var(--border); background: var(--surface);
  color: var(--text-2); border-radius: var(--r-ctl);
  padding: 6px 12px; font: inherit; font-size: 13px; cursor: pointer;
}
.studio-v:hover { border-color: var(--border-2); color: var(--text); }
.studio-v.current { background: var(--invert-bg); color: var(--invert-fg); border-color: var(--invert-bg); }

.studio-stage {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 18px; margin-bottom: 20px;
}
.studio-transport { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
/* A true circle. */
.studio-play {
  width: 40px; height: 40px; border-radius: 50%; flex: none;
  border: 1px solid var(--border-2); background: var(--surface-2);
  color: var(--text); font-size: 13px; cursor: pointer; line-height: 1;
}
.studio-play:hover { background: var(--invert-bg); color: var(--invert-fg); border-color: var(--invert-bg); }
.studio-time { font-variant-numeric: tabular-nums; color: var(--text-2); font-size: 13.5px; }
.studio-state { color: var(--text-3); font-size: 12.5px; }

/* The stage while a version is being measured. Same footprint as the empty
   plot so the columns below do not jump when the chart replaces it. */
.studio-measuring {
  min-height: 160px; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 8px; text-align: center;
  border: 1px dashed var(--border); border-radius: 10px; padding: 24px 16px;
}
.studio-measuring-text { font-size: 15px; font-weight: 500; color: var(--text); }
.studio-elapsed { font-variant-numeric: tabular-nums; color: var(--text-3); margin-left: 6px; }
.studio-spin {
  width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid var(--border); border-top-color: var(--text);
  animation: studio-spin 0.9s linear infinite;
}
@keyframes studio-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .studio-spin { animation: none; } }

.studio-cols { margin-top: 4px; }
.studio-cons { margin: 8px 0 0; padding-left: 18px; font-size: 13px; line-height: 1.7; color: var(--text-2); }

/* The two card actions, side by side under the face. */
.t-card-acts { display: flex; gap: 6px; }
.t-card-acts .btn { flex: 1; }
.t-modal-studio { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 4px; }

/* The report screen's plot sits full width above the card grid — it is a
   picture of the whole record and the grid beside it is 300px columns. */
.report-plot { margin: 0 0 22px; }

/* ---------- the pool ----------
 *
 * A BROWSE SURFACE, not a reading column. `.foundry-consumer .work main` caps
 * the reading screens at 780px and widens for the inventories through a
 * :has(#view-… :not(.hidden)) list — #view-pool is in that list, which is why
 * nothing here fights its own parent's max-width from the inside.
 *
 * The row is deliberately unlike .t-card: a library card is a record you own
 * and opens a studio, a pool row is twenty seconds you can hear, and the thing
 * that has to read at a glance is the sound — cover, waveform, tempo — not the
 * filename.
 */
.pool-cols { display: grid; grid-template-columns: 212px minmax(0, 1fr); gap: 34px; align-items: start; }
.pool-main { min-width: 0; }
.pool-count { font-size: 12.5px; color: var(--text-3); margin: 0 0 12px; }

/* ---- the two rails ---- */
.rails { display: flex; flex-direction: column; gap: 26px; margin-bottom: 30px; }
.rail-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 11px; flex-wrap: wrap; }
.rail-head h2 { margin: 0; font-size: 17px; font-weight: 600; letter-spacing: -0.015em; }
.strip {
  display: grid; grid-auto-flow: column; grid-auto-columns: 214px;
  gap: 12px; overflow-x: auto; padding-bottom: 6px; scroll-snap-type: x proximity;
}
.strip::-webkit-scrollbar { height: 7px; }
.strip::-webkit-scrollbar-thumb { background: var(--border-2); border-radius: 4px; }
.tile {
  scroll-snap-align: start;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 11px;
  display: flex; flex-direction: column; gap: 9px; text-align: left;
}
.tile:hover { border-color: var(--border-2); }
.tile.on { border-color: var(--lilac); }
.tile-t { font-size: 13.5px; font-weight: 600; letter-spacing: -0.01em; line-height: 1.3; }
.tile-a { font-size: 12px; color: var(--muted); }
.tile-because {
  font-size: 11.5px; color: var(--text-3); line-height: 1.45;
  border-top: 1px solid var(--border); padding-top: 7px; margin: auto 0 0;
}
.tile-because em { font-style: normal; color: var(--lilac); }
/* An empty rail SAYS it is empty rather than disappearing. A section that
   vanishes when it has nothing reads as a bug the first time and as missing
   the second; a sentence reads as a state. */
.rail-empty {
  background: var(--surface); border: 1px dashed var(--border-2);
  border-radius: var(--r); padding: 20px; display: flex; gap: 13px; align-items: flex-start;
}
.rail-empty svg { flex: none; margin-top: 2px; opacity: .5; }
.rail-empty p { margin: 0; font-size: 13px; color: var(--muted); line-height: 1.55; max-width: 62ch; }
.rail-empty strong { color: var(--text-2); font-weight: 600; }

/* ---- the control bar ---- */
.pool-bar {
  position: sticky; top: 0; z-index: 20; background: var(--bg);
  border-bottom: 1px solid var(--border); padding-block: 12px; margin-bottom: 20px;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.pool-search-wrap {
  flex: 1 1 240px; min-width: 0; display: flex; align-items: center; gap: 8px;
  background: var(--field-bg, var(--surface)); border: 1px solid var(--border);
  border-radius: var(--r-ctl); padding: 0 11px;
}
.pool-search-wrap:focus-within { border-color: var(--border-2); }
.pool-search-wrap svg { flex: none; opacity: .55; }
.pool-search-wrap input {
  flex: 1; min-width: 0; border: 0; background: none; outline: none;
  color: var(--text); font: inherit; font-size: 14px; padding: 9px 0;
}
.segment { display: flex; background: var(--surface-2); border-radius: var(--r-ctl); padding: 2px; gap: 2px; }
.segment button {
  border: 0; background: none; border-radius: 6px; padding: 6px 12px;
  font-size: 13px; font-weight: 500; color: var(--text-3); cursor: pointer;
}
.segment button[aria-pressed="true"] { background: var(--invert-bg); color: var(--invert-fg); }

/* ---- the filter rail ---- */
.pool-filters { position: sticky; top: 80px; display: flex; flex-direction: column; gap: 22px; }
.fgroup { display: flex; flex-direction: column; gap: 9px; }
.fgroup > h2 {
  margin: 0; font-size: 10.5px; font-weight: 600; letter-spacing: .09em;
  text-transform: uppercase; color: var(--text-3);
}
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  border: 1px solid var(--border); background: var(--surface);
  border-radius: var(--r-chip); padding: 4px 9px;
  font-size: 12.5px; color: var(--text-2); white-space: nowrap; cursor: pointer;
}
.chip:hover { border-color: var(--border-2); }
.chip[aria-pressed="true"] { background: var(--invert-bg); color: var(--invert-fg); border-color: var(--invert-bg); }
.chip .n { font-variant-numeric: tabular-nums; opacity: .5; margin-left: 5px; font-size: 11px; }
.chip[aria-pressed="true"] .n { opacity: .6; }
.pool-reset {
  align-self: start; background: none; border: 0; padding: 0; cursor: pointer;
  font-size: 12px; color: var(--lilac); text-decoration: underline; text-underline-offset: 3px;
}
.range { padding: 2px 2px 0; }
.range-read { font-family: var(--mono-font, ui-monospace, Menlo, monospace); font-size: 12px; color: var(--text-2); font-variant-numeric: tabular-nums; margin-bottom: 9px; }
.range-track { position: relative; height: 22px; }
.range-track .groove { position: absolute; inset: 9px 0 auto; height: 4px; background: var(--surface-2); border-radius: 2px; }
.range-track .fill { position: absolute; top: 9px; height: 4px; background: var(--lilac); border-radius: 2px; }
.range-track input[type="range"] {
  position: absolute; inset: 0; width: 100%; margin: 0;
  -webkit-appearance: none; appearance: none; background: none; pointer-events: none;
}
.range-track input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; pointer-events: auto; width: 14px; height: 14px;
  border-radius: 50%; background: var(--surface); border: 2px solid var(--lilac); cursor: grab;
}
.range-track input[type="range"]::-moz-range-thumb {
  pointer-events: auto; width: 14px; height: 14px;
  border-radius: 50%; background: var(--surface); border: 2px solid var(--lilac); cursor: grab;
}

/* ---- the row ---- */
.pool-feed { display: flex; flex-direction: column; gap: 10px; }
.pool-row {
  display: grid; grid-template-columns: 76px minmax(0, 1fr); gap: 16px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 14px 16px;
}
.pool-row:hover { border-color: var(--border-2); }
.pool-row.on { border-color: var(--lilac); }

/* The sleeve. Generated from the entry id until a real cover rides in pool/ —
   decorative by construction, so it is never mistaken for artwork somebody
   chose. A canvas rather than a gradient so the SAME id always draws the same
   sleeve across a repaint and a reload. */
.pool-art { position: relative; aspect-ratio: 1; border-radius: var(--r-ctl); overflow: hidden; }
.pool-art canvas, .pool-art img { display: block; width: 100%; height: 100%; object-fit: cover; }
.pool-play {
  position: absolute; inset: 0; display: grid; place-items: center;
  border: 0; background: rgba(11, 11, 13, .34); color: #fff; cursor: pointer;
  opacity: 0; transition: opacity 120ms ease;
}
.pool-row:hover .pool-play, .pool-row.on .pool-play,
.tile:hover .pool-play, .tile.on .pool-play,
.pool-play:focus-visible { opacity: 1; }
.pool-play svg { filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .5)); }

.pool-meat { display: flex; flex-direction: column; gap: 9px; min-width: 0; }
.pool-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.pool-title { font-size: 15px; font-weight: 600; letter-spacing: -0.012em; margin: 0; overflow-wrap: anywhere; }
.pool-artist { font-size: 13px; color: var(--muted); }
.pool-artist a { color: var(--lilac); text-decoration: none; }
.pool-artist a:hover { text-decoration: underline; text-underline-offset: 2px; }
/* Unreleased is the DEFAULT expectation on this surface, so it is quiet; a
   record that is already out is the exception worth catching an eye. Neither
   is the accent colour — they are states, not emphasis. */
.pool-flag {
  font-size: 11px; font-weight: 600; letter-spacing: .02em;
  color: var(--text-3); border: 1px solid var(--border);
  border-radius: var(--r-chip); padding: 2px 7px; white-space: nowrap;
}
.pool-flag.out { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 45%, transparent); }
.pool-ago { margin-left: auto; font-size: 11.5px; color: var(--text-3); white-space: nowrap; }

/* THE WAVEFORM, AND THE BAR THAT STANDS IN FOR IT.
   A resting row has no peaks to draw — they are not on the row yet — so it
   gets an honest progress track, not a decorative squiggle. The row being
   PLAYED has its samples decoded in this tab, so that one draws its real
   waveform. Nothing here ever invents a shape for audio it has not read. */
.pool-wave { width: 100%; height: 46px; display: block; cursor: pointer; }
.pool-wave rect { shape-rendering: crispEdges; }
.pool-track {
  position: relative; height: 10px; border-radius: 5px;
  background: var(--surface-2); border: 1px solid var(--border);
  cursor: pointer; margin: 18px 0;
}
.pool-track .played { position: absolute; inset: 0 auto 0 0; background: var(--lilac); border-radius: 3px; }

.pool-facts { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.pool-stat {
  font-family: var(--mono-font, ui-monospace, Menlo, monospace); font-size: 11px;
  font-variant-numeric: tabular-nums; color: var(--text-2);
  background: var(--surface-2); border-radius: var(--r-chip); padding: 3px 7px; white-space: nowrap;
}
.pool-tag {
  font-size: 11.5px; color: var(--text-2); border: 1px solid var(--border);
  border-radius: var(--r-chip); background: none; padding: 2px 8px; cursor: pointer;
}
.pool-tag:hover { border-color: var(--border-2); color: var(--text); }
.pool-len {
  margin-left: auto; font-family: var(--mono-font, ui-monospace, Menlo, monospace);
  font-size: 11px; color: var(--text-3); font-variant-numeric: tabular-nums;
}
.pool-note {
  margin: 0; font-size: 13px; line-height: 1.55; color: var(--text-2);
  border-left: 2px solid var(--border); padding-left: 10px;
}
.pool-more { margin-top: 16px; text-align: center; }

/* ---- the dock ---- */
.pool-dock {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  background: var(--surface); border-top: 1px solid var(--border);
  box-shadow: 0 -4px 20px rgba(0, 0, 0, .10);
}
.pool-dock.hidden { display: none; }
.pool-dock-in {
  max-width: 1140px; margin: 0 auto; padding: 10px 20px;
  display: flex; align-items: center; gap: 14px;
}
.pool-dock .pool-art { width: 44px; flex: none; }
.pool-dock-id { min-width: 0; width: 160px; flex: none; }
.pool-dock-t { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pool-dock-a { font-size: 11.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pool-dock-wave { flex: 1; min-width: 0; }
.pool-dock-wave .pool-wave { height: 30px; }
.pool-dock-wave .pool-track { margin: 12px 0; }
.pool-transport { display: flex; align-items: center; gap: 4px; }
.tbtn {
  border: 0; background: none; width: 34px; height: 34px; cursor: pointer;
  display: grid; place-items: center; border-radius: 50%; color: var(--text-2);
}
.tbtn:hover { background: var(--surface-2); color: var(--text); }
.tbtn.big { background: var(--invert-bg); color: var(--invert-fg); width: 38px; height: 38px; }
.pool-clock {
  font-family: var(--mono-font, ui-monospace, Menlo, monospace); font-size: 11px;
  color: var(--text-3); font-variant-numeric: tabular-nums; white-space: nowrap;
}

@media (max-width: 900px) {
  .pool-cols { grid-template-columns: minmax(0, 1fr); gap: 20px; }
  .pool-filters { position: static; flex-direction: row; gap: 18px; overflow-x: auto; padding-bottom: 6px; }
  .fgroup { flex: none; }
  /* The two-handle range collapses to a pair of touching thumbs if it is
     allowed to shrink-to-fit in the horizontal rail — a control nobody can
     aim at. It keeps a usable track and the row scrolls instead. */
  .fgroup:has(.range) { min-width: 172px; }
  .pool-reset { align-self: center; }
}
@media (max-width: 620px) {
  .pool-row { grid-template-columns: 56px minmax(0, 1fr); gap: 12px; padding: 12px 13px; }
  .pool-dock-id { width: 104px; }
  .pool-clock, .pool-transport .tbtn:not(.big) { display: none; }
  .strip { grid-auto-columns: 168px; }
}

/* ---------- the signed-out report ----------
 *
 * A stranger who dropped a track at the door lands here, and the rail behind it
 * offers five destinations an anonymous session cannot open. So it goes, and
 * the report becomes the whole page: the wordmark, the measurements, and the
 * ask for an account at the foot — AFTER the analysis, which is the entire
 * bargain of the free try.
 *
 * body-level rather than scoped to the view, because the thing being hidden
 * (.side) is a sibling of the view's container and cannot be reached from it. */
body.guest-report .side { display: none; }
body.guest-report .work { max-width: none; }
/* The report's own column stays measured — a full-bleed page of cards at 2000px
   is not more readable, it is just wider. */
body.guest-report main { max-width: 1180px; margin: 0 auto; padding-inline: clamp(20px, 4vw, 40px); }

.report-guest-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 22px 0 26px; margin-bottom: 6px;
  border-bottom: 1px solid var(--border);
}
.report-guest-top[hidden] { display: none; }
.report-guest-top .wordmark { font-size: 22px; }
.rgt-right { display: flex; gap: 8px; }

/* ======================================================================
   SPACING AUDIT (Nikhil, 2026-09-09: "some are way too close, they are not
   all spaced properly, all throughout foundry text is touching buttons").
   One rule set at the end of the sheet so it wins the cascade. Every known
   button row gets a real gap and breathing room above it; any button that
   sits next to text or another button OUTSIDE those rows gets a margin, and
   the rows that already lay out with `gap` reset that margin so nothing
   doubles up. Buttons never touch text again by accident. */
.qi-choose { gap: 8px 10px; margin: 14px 0 12px; }
.qi-actions { gap: 10px 12px; margin-top: 12px; }
.qi-actions .qi-error, .qi-actions .fine { margin-left: 2px; }
.t-card-acts { gap: 10px; }
.dash-foot { gap: 14px 16px; }
.dash-single, .dash-pitch { margin-top: 14px; }
.dash-single-field + .btn, #dash-single-go { margin-top: 12px; }
.release-note-row { gap: 12px; margin-top: 10px; }
.release-warn .field-input { margin-top: 12px; }
.rank-more-row { padding-top: 12px; }
.studio-head-top { gap: 14px 18px; }
.card-head { gap: 14px; }
.tr-sec p + .btn, .tr-sec .btn + p, .card p + .btn, .card .btn + p { margin-top: 10px; }
.v-row .btn + .btn, .tr-sec .btn + .btn, .card .btn + .btn, .modal .btn + .btn, .sheet .btn + .btn { margin-left: 8px; }
.btn + span, .btn + .fine, .btn + .muted, .btn + .small-cap, .btn + label,
span + .btn, .fine + .btn, .muted + .btn, label + .btn, input + .btn, select + .btn { margin-left: 10px; }
/* Rows that already lay out with gap: no doubling. */
.qi-actions > *, .qi-choose > *, .t-card-acts > *, .dash-foot > *, .release-note-row > *,
.card-head > *, .studio-head-top > *, .rank-more-row > *, .dash-acts > *, .studio-acts > *,
.ob-actions > *, .pf-actions > *, .studio-transport > *, .net-pop > * { margin-left: 0; }
.ob-actions, .pf-actions { gap: 10px 12px; }
.net-pop .btn + .btn { margin-left: 0; margin-top: 6px; }

/* ======================================================================
   DESIGN PASS (2026-09-09, Nikhil: "looks like a basic wireframe-ass UI").
   One block at the end of the sheet so it wins the cascade; nothing above
   is rewritten. Light is the producer's theme and is what this was drawn
   on; every value is a token so dark still holds. The rules the owner set:
   no glow, no gradients outside data, no icon chips, text-forward cards,
   the same treatment on every surface, and the studio's numbers stay
   measurements. Order: tokens, type scale, surfaces, controls, the rail,
   then screen by screen.
   ====================================================================== */

/* ---- tokens: three new ones, and a lighter hairline on light ---- */
:root {
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.28);
  --shadow-pop: 0 16px 40px rgba(0, 0, 0, 0.5), 0 2px 6px rgba(0, 0, 0, 0.3);
  --field-bg: var(--surface-2);
  --border-soft: #1d1d22;
}
:root[data-theme="light"] {
  --bg: #f5f5f7;
  --surface: #ffffff;
  --surface-2: #f1f1f4;
  --border: #e4e4ea;
  --border-2: #c9c9d3;
  --border-soft: #ececf0;
  --rail-bg: #f5f5f7;
  --shadow-card: 0 1px 2px rgba(16, 16, 24, 0.04), 0 2px 8px rgba(16, 16, 24, 0.04);
  --shadow-pop: 0 16px 40px rgba(16, 16, 24, 0.12), 0 2px 6px rgba(16, 16, 24, 0.06);
  --field-bg: #ffffff;
}

/* ---- type scale ----
   Display 28 / title 20 / head 15 / body 14 / small 13 / fine 12 / cap 11.
   Caps are one voice everywhere: 11px, semibold, 0.08em, the quiet grey. */
.foundry-consumer { line-height: 1.45; -webkit-font-smoothing: antialiased; }
.foundry-consumer .intro { padding: 30px 0 22px; }
.foundry-consumer .intro h1 {
  font-size: 28px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.15;
  margin-bottom: 8px;
}
.foundry-consumer .lede { font-size: 15px; line-height: 1.55; color: var(--muted); }
.foundry-consumer .card-head h2 { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; line-height: 1.3; }
.foundry-consumer .card-head { margin-bottom: 14px; align-items: center; }
.foundry-consumer .section-cap,
.foundry-consumer .tr-sec h3,
.foundry-consumer .tlc-h2,
.foundry-consumer .side-sec,
.foundry-consumer .field-label,
.foundry-consumer .rights legend,
.foundry-consumer .send-group,
.foundry-consumer .rank-body h3,
.foundry-consumer .track-body h3,
.foundry-consumer .hero-cap,
.foundry-consumer .side-head,
.foundry-consumer .fgroup-name,
.foundry-consumer .ob-count {
  font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-3);
}
.foundry-consumer .section-cap { margin: 22px 0 10px; padding-bottom: 8px; border-bottom: 1px solid var(--border-soft); }
.foundry-consumer .tr-sec h3 { margin: 0 0 12px; }
.foundry-consumer .field-label { margin-bottom: 6px; }
.foundry-consumer .muted { font-size: 13.5px; line-height: 1.55; }
.foundry-consumer .small-cap { font-size: 13px; }
.foundry-consumer .fine,
.foundry-consumer .tr-fine,
.foundry-consumer .tr-provenance,
.foundry-consumer .privacy-note,
.foundry-consumer .auth-fine,
.foundry-consumer .door-fine,
.foundry-consumer .pf-note,
.foundry-consumer .qi-stored-note { font-size: 12px; line-height: 1.5; color: var(--text-3); }
.foundry-consumer .pf-k { font-size: 13px; font-weight: 500; color: var(--text-2); }
.foundry-consumer .pf-hint { font-weight: 400; color: var(--text-3); opacity: 1; }
.foundry-consumer footer { font-size: 12px; padding: 22px 16px; }

/* ---- surfaces ----
   Page slightly off-white, cards white, one radius, a hairline and a soft
   shadow. Everything that is a card gets exactly this; nothing else does. */
.foundry-consumer .card,
.foundry-consumer .queue-item,
.foundry-consumer .studio-share-cols .tr-sec,
.foundry-consumer .report-cols .tr-sec,
.foundry-consumer .report-cols > .card,
.foundry-consumer .studio-stage,
.foundry-consumer .pool-card,
.foundry-consumer .t-detail,
.foundry-consumer .stat,
.foundry-consumer .t-card,
.foundry-consumer .hu-tile,
.foundry-consumer .ob-fork-btn,
.foundry-consumer .door-badge,
.foundry-consumer .sent-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--shadow-card);
}
.foundry-consumer .card { padding: 20px 22px 22px; }
.foundry-consumer .queue-item { padding: 20px 22px 22px; }
.foundry-consumer .studio-share-cols .tr-sec,
.foundry-consumer .report-cols .tr-sec,
.foundry-consumer .report-cols > .card { padding: 20px 22px; }
.foundry-consumer .qi-result .card { background: none; border: none; box-shadow: none; border-radius: 0; padding: 0; }
.foundry-consumer .qi-result .card + .card { border-top: 1px solid var(--border-soft); padding-top: 16px; }
.foundry-consumer .t-detail { border-left: 1px solid var(--border); }
.foundry-consumer .sent-card { border-left: 1px solid var(--border); }
.foundry-consumer .stat { padding: 14px 16px 15px; gap: 5px; }
.foundry-consumer .stat-n { font-size: 24px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.1; }
.foundry-consumer .stat-k { font-size: 12.5px; color: var(--muted); }
.foundry-consumer .stat-row { gap: 12px; margin: 6px 0 18px; }
.foundry-consumer .home-cols { gap: 16px; }

/* Pop-overs and sheets: white, lifted, one radius. */
.foundry-consumer .account-menu,
.foundry-consumer .net-pop,
.foundry-consumer .t-modal,
.foundry-consumer .djm-card,
.foundry-consumer .pac-container,
.foundry-consumer .tooltip {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--shadow-pop);
}
.foundry-consumer .run-sheet {
  background: var(--bg); border-left: 1px solid var(--border);
  box-shadow: var(--shadow-pop); padding: 22px 24px 32px; gap: 16px;
}
.foundry-consumer .run-head h2 { font-size: 17px; }
.foundry-consumer .run-overlay { background: color-mix(in srgb, var(--text) 18%, transparent); }
.foundry-consumer .t-overlay { background: color-mix(in srgb, var(--text) 24%, transparent); backdrop-filter: none; -webkit-backdrop-filter: none; }

/* Rules inside cards: the softer hairline, so the card's own edge stays the
   strongest line on it. */
.foundry-consumer .tr-row,
.foundry-consumer .rank-row,
.foundry-consumer .hs-row,
.foundry-consumer .team-row,
.foundry-consumer .method-row,
.foundry-consumer .played-row,
.foundry-consumer .sent-track-card,
.foundry-consumer .tlc-srow,
.foundry-consumer .ib-item,
.foundry-consumer .ib-facts > div,
.foundry-consumer .v-row,
.foundry-consumer .track-row { border-color: var(--border-soft); }
.foundry-consumer .qi-head { border-bottom-color: var(--border-soft); padding-bottom: 14px; }
.foundry-consumer .see-why,
.foundry-consumer .t-card-score-row,
.foundry-consumer .t-detail-cta,
.foundry-consumer .rec-activity,
.foundry-consumer .qi-declare,
.foundry-consumer .dash-pitch,
.foundry-consumer .pf-actions,
.foundry-consumer .ob-actions,
.foundry-consumer .djm-actions,
.foundry-consumer .t-modal-danger,
.foundry-consumer .tr-provenance,
.foundry-consumer .side-foot,
.foundry-consumer .ib-sender,
.foundry-consumer .ib-sound-panel { border-color: var(--border-soft); }

/* Empty states and wells: a tinted field with a solid hairline. Dashed boxes
   are what a wireframe draws, and the drop target is the one exception —
   a dashed edge is the convention for "drop here" and it keeps it. */
.foundry-consumer .empty-state,
.foundry-consumer .ai-detail-empty,
.foundry-consumer .studio-measuring,
.foundry-consumer .tlc-pro,
.foundry-consumer .tlc-soon,
.foundry-consumer .studio-pool {
  border: 1px solid var(--border); border-radius: var(--r);
  background: color-mix(in srgb, var(--surface-2) 60%, var(--surface));
}
.foundry-consumer .empty-card { padding: 40px 24px; }
.foundry-consumer .empty-head { font-size: 14.5px; font-weight: 600; }
.foundry-consumer .dropzone {
  border: 1.5px dashed var(--border-2); border-radius: var(--r);
  background: color-mix(in srgb, var(--surface-2) 50%, var(--surface));
  padding: 34px 18px; gap: 6px;
}
.foundry-consumer .dropzone:hover,
.foundry-consumer .dropzone.drag { border-color: var(--text); background: var(--surface-2); }
.foundry-consumer .dz-big { font-size: 15px; font-weight: 600; }
.foundry-consumer .dz-small { font-size: 13px; color: var(--muted); }
.foundry-consumer .send-picker,
.foundry-consumer .team-editor,
.foundry-consumer .rights { border: 1px solid var(--border); border-radius: var(--r); background: transparent; }
.foundry-consumer .rights { padding: 16px 18px 18px; gap: 10px; }
.foundry-consumer .send-picker { background: var(--field-bg); }
.foundry-consumer .send-group { background: var(--field-bg); }
.foundry-consumer .rights-disclaimer {
  border: 1px solid var(--border); border-radius: var(--r-ctl);
  background: var(--surface-2); color: var(--text-2); font-size: 13px; opacity: 1;
}
.foundry-consumer .release-warn { border: 1px solid var(--danger); border-radius: var(--r-ctl); }
.foundry-consumer .notice { border-radius: var(--r); box-shadow: var(--shadow-card); }
.foundry-consumer .tr-consequence { border-radius: var(--r-ctl); background: var(--surface-2); }

/* ---- controls ----
   Buttons: 36px, or 32px small; radius --r-ctl; primary is the inverted pair
   (near-black on light), secondary white with a hairline, ghost borderless.
   min-height rather than height so the door's big pills keep their size. */
.foundry-consumer .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 36px; padding: 0 14px; line-height: 1.2;
  font-size: 13.5px; font-weight: 500; letter-spacing: -0.005em;
  border: 1px solid var(--border); border-radius: var(--r-ctl);
  background: var(--surface); color: var(--text);
  box-shadow: var(--shadow-card);
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
  white-space: nowrap; text-decoration: none; vertical-align: middle;
}
.foundry-consumer .btn:hover:not(:disabled) { border-color: var(--border-2); background: var(--surface); }
.foundry-consumer .btn.primary { background: var(--invert-bg); color: var(--invert-fg); border-color: var(--invert-bg); box-shadow: none; }
.foundry-consumer .btn.primary:hover:not(:disabled) { background: color-mix(in srgb, var(--invert-bg) 86%, var(--invert-fg)); border-color: transparent; }
.foundry-consumer .btn.ghost { background: transparent; border-color: transparent; color: var(--muted); box-shadow: none; }
.foundry-consumer .btn.ghost:hover:not(:disabled) { background: var(--surface-2); border-color: transparent; color: var(--text); }
.foundry-consumer .btn.small { min-height: 32px; padding: 0 12px; font-size: 13px; }
.foundry-consumer .btn.tiny { min-height: 26px; padding: 0 8px; font-size: 11.5px; border-radius: 6px; }
.foundry-consumer .btn:disabled { opacity: 0.45; box-shadow: none; }
.foundry-consumer .btn:focus-visible,
.foundry-consumer .dash-act:focus-visible,
.foundry-consumer .stage-mode:focus-visible,
.foundry-consumer .nav-item:focus-visible,
.foundry-consumer .account:focus-visible,
.foundry-consumer .t-chip:focus-visible,
.foundry-consumer .pf-chip:focus-visible,
.foundry-consumer .studio-v:focus-visible,
.foundry-consumer .qi-act:focus-visible,
.foundry-consumer .net-tile:focus-visible,
.foundry-consumer .hu-tile:focus-visible,
.foundry-consumer .pool-play:focus-visible,
.foundry-consumer .studio-play:focus-visible,
.foundry-consumer .theme-toggle:focus-visible,
.foundry-consumer .info-dot:focus-visible { outline: 2px solid var(--lilac); outline-offset: 2px; }
.foundry-consumer .t-del-arm,
.foundry-consumer .t-del-confirm { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 50%, var(--border)); }
.foundry-consumer .btn.provider { min-height: 40px; box-shadow: var(--shadow-card); }
.foundry-consumer .home-cta-btn { min-height: 40px; padding: 0 18px; box-shadow: none; }
.foundry-consumer .door-signin,
.foundry-consumer .door-try { box-shadow: none; }
.foundry-consumer .door-signin { border: 1.5px solid var(--text); background: none; color: var(--text); border-radius: 999px; padding: 13px 30px; min-height: 0; }
.foundry-consumer .door-signin:hover { background: var(--text); color: var(--bg); border-color: var(--text); }
.foundry-consumer .door-try { border-radius: 999px; padding: 15px 34px; min-height: 0; }
/* Inline forms (access code, inbox name, change email): the button never
   wraps into two lines beside its input. */
.foundry-consumer .inline-form { gap: 10px; align-items: stretch; }
.foundry-consumer .inline-form .btn { flex: none; white-space: nowrap; }
.foundry-consumer .qi-actions .btn,
.foundry-consumer .dash-foot .btn,
.foundry-consumer .pf-actions .btn { flex: none; }

/* Fields: white on light, hairline, 36px, a two-tone focus ring that is a
   ring and not a glow. One rule reaches every input the app draws. */
.foundry-consumer input[type=text],
.foundry-consumer input[type=password],
.foundry-consumer input[type=email],
.foundry-consumer input[type=tel],
.foundry-consumer input[type=url],
.foundry-consumer input[type=search],
.foundry-consumer input[type=number],
.foundry-consumer .field-input,
.foundry-consumer textarea,
.foundry-consumer select,
.foundry-consumer .set-select,
.foundry-consumer .t-search,
.foundry-consumer .inbox-search,
.foundry-consumer .ob-step input[type=text],
.foundry-consumer .ob-step input[type=url],
.foundry-consumer .profile-form input[type="text"],
.foundry-consumer .profile-form input[type="url"],
.foundry-consumer .profile-form textarea,
.foundry-consumer .pf-field select.country-select,
.foundry-consumer .ob-step select.country-select,
.foundry-consumer .djm-card input[type=search],
.foundry-consumer .studio-pool-note input,
.foundry-consumer .auth-inner input:not([type=checkbox]):not([type=radio]) {
  background: var(--field-bg); border: 1px solid var(--border); color: var(--text);
  border-radius: var(--r-ctl); min-height: 36px; padding: 7px 12px;
  font-family: inherit; font-size: 14px; line-height: 1.4; outline: none;
  box-shadow: none;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.foundry-consumer input::placeholder,
.foundry-consumer textarea::placeholder { color: var(--text-3); opacity: 1; }
.foundry-consumer input[type=text]:focus,
.foundry-consumer input[type=password]:focus,
.foundry-consumer input[type=email]:focus,
.foundry-consumer input[type=tel]:focus,
.foundry-consumer input[type=url]:focus,
.foundry-consumer input[type=search]:focus,
.foundry-consumer input[type=number]:focus,
.foundry-consumer .field-input:focus,
.foundry-consumer textarea:focus,
.foundry-consumer select:focus,
.foundry-consumer .profile-form input:focus,
.foundry-consumer .profile-form textarea:focus,
.foundry-consumer .auth-inner input:focus {
  border-color: var(--text);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--text) 10%, transparent);
}
.foundry-consumer textarea { min-height: 84px; resize: vertical; }
.foundry-consumer select,
.foundry-consumer .set-select,
.foundry-consumer .pf-field select.country-select,
.foundry-consumer .ob-step select.country-select { padding-right: 34px; cursor: pointer; }
/* `background:` above is the shorthand, which drops the chevron the select
   rules further up paint as a background-image; put it back. */
.foundry-consumer select,
.foundry-consumer .set-select,
.foundry-consumer .pf-field select.country-select,
.foundry-consumer .ob-step select.country-select {
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%238e8e98' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat; background-position: right 12px center;
}
.foundry-consumer .f-artist-locked,
.foundry-consumer .f-artist-locked:focus { background: transparent; border-color: transparent; box-shadow: none; padding-left: 0; }
.foundry-consumer .studio-pool-at { min-height: 30px; padding: 3px 8px; border-radius: 6px; background: var(--field-bg); }
.foundry-consumer .filters-right .set-select { min-height: 32px; padding: 5px 28px 5px 10px; }
.foundry-consumer .t-search { min-height: 34px; }

/* Checkboxes: one drawing for all of them — 16px, 4px corners, filled with
   the inverted pair when on. (.frow draws its own box and keeps it.) */
.foundry-consumer input[type=checkbox] {
  appearance: none; -webkit-appearance: none;
  width: 16px; height: 16px; flex: none; margin: 0;
  border: 1.5px solid var(--border-2); border-radius: 4px;
  background: var(--field-bg); cursor: pointer; position: relative;
  vertical-align: -3px; accent-color: var(--invert-bg);
}
.foundry-consumer input[type=checkbox]:hover { border-color: var(--text-3); }
.foundry-consumer input[type=checkbox]:checked { background: var(--invert-bg); border-color: var(--invert-bg); }
.foundry-consumer input[type=checkbox]:checked::after {
  content: ""; position: absolute; left: 4.5px; top: 1px;
  width: 4px; height: 8px; border: solid var(--invert-fg);
  border-width: 0 2px 2px 0; transform: rotate(45deg);
}
.foundry-consumer input[type=checkbox]:focus-visible { outline: 2px solid var(--lilac); outline-offset: 2px; }
.foundry-consumer input[type=checkbox]:disabled { opacity: 0.35; cursor: not-allowed; }
.foundry-consumer .check input[type=checkbox] { margin-top: 2px; }
.foundry-consumer .check { font-size: 14px; gap: 10px; }
.foundry-consumer .pf-check { font-size: 14px; gap: 10px; }
.foundry-consumer .pf-check input { width: 16px; height: 16px; }

/* Chips: small, squared, quiet. A value tag is not a button. */
.foundry-consumer .chip,
.foundry-consumer .tag-chip,
.foundry-consumer .t-card-tags .chip {
  display: inline-flex; align-items: center; min-height: 24px;
  padding: 0 9px; border-radius: var(--r-chip);
  background: var(--surface-2); border: 1px solid transparent;
  color: var(--text-2); font-size: 12px; font-weight: 500; line-height: 1;
}
.foundry-consumer .chips { gap: 6px; }
.foundry-consumer .status-chip { border-radius: var(--r-chip); }
/* Choice chips (genres, filters) are buttons and look like small secondaries;
   the picked one is the inverted pair, the same "you are here" every control
   uses. */
.foundry-consumer .pf-chip,
.foundry-consumer .t-chip,
.foundry-consumer .qi-act,
.foundry-consumer .studio-v,
.foundry-consumer .sent-chip,
.foundry-consumer .ob-orgkinds .pf-chip {
  display: inline-flex; align-items: center; gap: 7px; min-height: 32px;
  padding: 0 12px; border-radius: var(--r-ctl);
  background: var(--surface); border: 1px solid var(--border); color: var(--text-2);
  font-family: inherit; font-size: 13px; font-weight: 500; line-height: 1.2;
  box-shadow: var(--shadow-card); cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}
.foundry-consumer .pf-chip:hover,
.foundry-consumer .t-chip:hover,
.foundry-consumer .qi-act:hover,
.foundry-consumer .studio-v:hover,
.foundry-consumer .sent-chip:hover { border-color: var(--border-2); color: var(--text); }
.foundry-consumer .pf-chip.on,
.foundry-consumer .t-chip.on,
.foundry-consumer .qi-act.current,
.foundry-consumer .studio-v.current {
  background: var(--invert-bg); border-color: var(--invert-bg); color: var(--invert-fg); box-shadow: none;
}
.foundry-consumer .t-chip.on .t-chip-n { color: color-mix(in srgb, var(--invert-fg) 70%, transparent); }
.foundry-consumer .t-chip-n { font-size: 11.5px; }
.foundry-consumer .ob-genres .pf-chip { min-height: 40px; border-radius: var(--r-ctl); padding: 0 14px; }
.foundry-consumer .sent-chip { border-radius: 999px; min-height: 30px; }
.foundry-consumer .sent-chip.open { border-color: var(--text); }
.foundry-consumer .send-picked-chip { background: var(--surface-2); border-color: transparent; }

/* Segmented controls: a tinted track, a white thumb with the card shadow. */
.foundry-consumer .stage-modes,
.foundry-consumer .seg,
.foundry-consumer .pills {
  background: var(--surface-2); border: 1px solid transparent;
  border-radius: var(--r); padding: 4px; gap: 4px; box-shadow: none;
}
.foundry-consumer .stage-mode { padding: 9px 14px 10px; border-radius: var(--r-ctl); color: var(--muted); }
.foundry-consumer .stage-mode::before { display: none; }
.foundry-consumer .stage-mode:hover { color: var(--text); }
.foundry-consumer .stage-mode.current { background: var(--surface); color: var(--text); box-shadow: var(--shadow-card); }
.foundry-consumer .sm-name { font-size: 13.5px; font-weight: 600; }
.foundry-consumer .sm-sub { font-size: 11.5px; color: var(--text-3); }
.foundry-consumer .seg-btn { min-height: 28px; padding: 0 12px; border-radius: 6px; font-weight: 500; }
.foundry-consumer .seg-btn.active { background: var(--surface); color: var(--text); box-shadow: var(--shadow-card); }

/* Round transport buttons: hairline, and the inverted pair when live. */
.foundry-consumer .studio-play,
.foundry-consumer .pool-play {
  width: 38px; height: 38px; border: 1px solid var(--border);
  background: var(--surface); box-shadow: var(--shadow-card);
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.foundry-consumer .theme-toggle { width: 30px; height: 30px; border-color: var(--border); background: var(--surface); box-shadow: var(--shadow-card); }
.foundry-consumer .info-dot { border-color: var(--border-2); }

/* ---- the rail ----
   Same ground as the page, a hairline on its edge; the current item is a
   white card, not a filled bar. The lilac marker goes with the underline it
   was drawn for: a lifted white item already says "here". */
.foundry-consumer .side { padding: 20px 14px 16px; background: var(--rail-bg); border-right: 1px solid var(--border); }
.foundry-consumer .side-top { padding: 2px 8px 22px; }
.foundry-consumer .side-nav { gap: 2px; }
.foundry-consumer .nav-item {
  padding: 9px 12px 10px; border-radius: var(--r-ctl); gap: 1px;
  border: 1px solid transparent; color: var(--muted);
  transition: background 120ms ease, color 120ms ease;
}
.foundry-consumer .nav-item::before { display: none; }
.foundry-consumer .nav-item:hover { background: color-mix(in srgb, var(--text) 5%, transparent); color: var(--text); }
.foundry-consumer .nav-item.current {
  background: var(--surface); color: var(--text);
  border-color: var(--border); box-shadow: var(--shadow-card);
}
.foundry-consumer .nav-name { font-size: 14px; font-weight: 500; letter-spacing: -0.005em; }
.foundry-consumer .nav-sub { font-size: 11.5px; color: var(--text-3); line-height: 1.35; }
.foundry-consumer .nav-item.current .nav-sub { color: var(--muted); }
.foundry-consumer .side-sec { padding: 16px 12px 6px; }
.foundry-consumer .side-foot { padding-top: 14px; }
.foundry-consumer .account {
  padding: 8px 10px; border-radius: var(--r-ctl); gap: 10px;
  border: 1px solid transparent;
}
.foundry-consumer .account:hover { background: var(--surface); border-color: var(--border); box-shadow: var(--shadow-card); }
.foundry-consumer .account-who { font-size: 13px; font-weight: 500; }
.foundry-consumer .account-act { font-size: 11.5px; color: var(--text-3); }
.foundry-consumer .avatar { width: 32px; height: 32px; font-size: 12px; border-color: var(--border); }
.foundry-consumer .avatar.big { width: 44px; height: 44px; font-size: 17px; }
.foundry-consumer .avatar.on { border-color: var(--s-demo); }
.foundry-consumer .net-avatar { width: 96px; height: 96px; font-size: 28px; }
.foundry-consumer .account-menu { padding: 6px; }
.foundry-consumer .am-row, .foundry-consumer .am-item { border-radius: 6px; }
.foundry-consumer .am-ident { font-size: 11.5px; padding: 6px 10px 8px; }
@media (max-width: 900px) {
  .foundry-consumer .side { padding: 10px 12px 0; }
  .foundry-consumer .nav-item { border-radius: var(--r-ctl) var(--r-ctl) 0 0; border-color: transparent; }
  .foundry-consumer .nav-item.current { box-shadow: none; border-bottom-color: var(--surface); }
}

/* ---- the queue card ---- */
.foundry-consumer .qi-file { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
.foundry-consumer .qi-state { font-size: 12px; font-weight: 500; }
.foundry-consumer .qi-form { padding-top: 18px; gap: 14px; }
.foundry-consumer .field-pair { gap: 14px; }
.foundry-consumer .qi-error { font-size: 12.5px; }
.foundry-consumer .qi-more summary { font-size: 12.5px; font-weight: 500; }
.foundry-consumer .qi-origin.flag { border-radius: 0 var(--r-chip) var(--r-chip) 0; }
.foundry-consumer .f-art-thumb { border-radius: var(--r-ctl); }

/* ---- the library ---- */
.foundry-consumer .t-bar { margin-bottom: 16px; }
.foundry-consumer .t-chips { gap: 8px; }
.foundry-consumer .t-grid { gap: 14px; }
.foundry-consumer .t-card { padding: 10px 10px 12px; transition: border-color 120ms ease, box-shadow 120ms ease; }
.foundry-consumer .t-card:hover { border-color: var(--border-2); box-shadow: var(--shadow-pop); }
.foundry-consumer .t-card.open { border-color: var(--text); background: var(--surface); }
.foundry-consumer .t-face { border-radius: var(--r-ctl); background: var(--surface-2); border-color: var(--border-soft); margin-bottom: 10px; }
.foundry-consumer .t-card-title { font-size: 14px; font-weight: 600; letter-spacing: -0.01em; }
.foundry-consumer .t-card-artist { font-size: 12.5px; color: var(--muted); }
.foundry-consumer .t-card-when { font-size: 11.5px; color: var(--text-3); }
.foundry-consumer .t-card-score-row { border-top-color: var(--border-soft); margin-top: 12px; padding-top: 10px; }
.foundry-consumer .t-card.open .t-card-score-row { border-top-color: var(--border-soft); }
.foundry-consumer .t-card-score { font-size: 18px; font-weight: 600; letter-spacing: -0.02em; }
.foundry-consumer .t-card-acts { margin-top: 12px; }
.foundry-consumer .t-card-acts { align-items: stretch; }
.foundry-consumer .t-card-acts .btn { min-height: 34px; padding: 0 10px; font-size: 12.5px; margin-top: 0; align-self: auto; }
.foundry-consumer .t-detail { padding: 20px 22px 22px; }
.foundry-consumer .t-detail-art { border-radius: var(--r-ctl); }

/* ---- home ---- */
.foundry-consumer .home-grid { gap: 12px; }
.foundry-consumer .hu-tile { padding: 8px 8px 10px; transition: border-color 120ms ease, box-shadow 120ms ease; }
.foundry-consumer .hu-tile:hover { border-color: var(--border-2); box-shadow: var(--shadow-pop); }
.foundry-consumer .hu-art { border-radius: 6px; border-color: var(--border-soft); }
.foundry-consumer .hu-noart { background: var(--surface-2); }
.foundry-consumer .hu-name { font-size: 12.5px; font-weight: 500; }
.foundry-consumer .hu-when { font-size: 11px; }
.foundry-consumer .home-belt { margin-top: 32px; }
.foundry-consumer .home-belt > .small-cap { color: var(--muted); }
.foundry-consumer .hs-name { font-size: 14px; }
.foundry-consumer .hs-state { font-size: 12px; }
.foundry-consumer .home-cta { margin-top: 36px; border-radius: var(--r); box-shadow: var(--shadow-card); }
.foundry-consumer .home-cta-copy h2 { font-weight: 600; letter-spacing: -0.02em; }

/* ---- the conveyor and the network tiles ---- */
.foundry-consumer .net-img,
.foundry-consumer .net-avatar,
.foundry-consumer .hc-tile .net-img,
.foundry-consumer .ch-img {
  border-radius: var(--r-ctl); border: 1px solid var(--border-soft);
  box-shadow: var(--shadow-card); background: var(--surface-2);
}
.foundry-consumer .net-tile { gap: 7px; padding: 4px; border-radius: var(--r-ctl); }
.foundry-consumer .net-tile:hover .net-img,
.foundry-consumer .net-tile:hover .net-avatar,
.foundry-consumer .ch-set:hover .ch-img { border-color: var(--border-2); box-shadow: var(--shadow-pop); }
.foundry-consumer .net-name { font-size: 13px; font-weight: 500; letter-spacing: -0.005em; }
.foundry-consumer .net-sub { font-size: 11.5px; color: var(--text-3); }
.foundry-consumer .net-pop { padding: 12px 14px; }
.foundry-consumer .net-pop-name { font-size: 13.5px; font-weight: 600; }

/* ---- the track studio ----
   Measurements stay measurements: the value is set larger and in tabular
   figures; the label is quiet; nothing gets a colour that means a verdict. */
.foundry-consumer .studio-head { margin-bottom: 20px; }
.foundry-consumer .studio-id .report-title,
.foundry-consumer .report-title { font-size: 22px; font-weight: 600; letter-spacing: -0.02em; }
.foundry-consumer .report-sub { font-size: 13.5px; color: var(--muted); }
.foundry-consumer .studio-rail { gap: 8px; }
.foundry-consumer .studio-sheet .tr-sec { background: none; border: none; }
.foundry-consumer .studio-cols { gap: 16px; }
.foundry-consumer .studio-about .vibe { font-size: 15px; line-height: 1.55; color: var(--text); }
.foundry-consumer .studio-acts { gap: 12px; margin-top: 18px; }
.foundry-consumer .studio-stage { padding: 20px 22px 22px; }
.foundry-consumer .studio-transport { margin-bottom: 16px; }
.foundry-consumer .studio-time { font-size: 13.5px; font-weight: 500; }
.foundry-consumer .tr-row { padding: 12px 0; gap: 4px 18px; }
.foundry-consumer .tr-label { font-size: 13.5px; color: var(--text-2); }
.foundry-consumer .tr-value,
.foundry-consumer .studio-cols .tr-value { font-size: 21px; font-weight: 600; letter-spacing: -0.02em; }
.foundry-consumer .tr-unit { font-size: 12px; font-weight: 500; color: var(--text-3); margin-left: 4px; }
.foundry-consumer .tr-note,
.foundry-consumer .studio-cols .tr-note { font-size: 12px; color: var(--text-3); line-height: 1.5; }
.foundry-consumer .tr-band-name { font-size: 12.5px; }
.foundry-consumer .tr-band-val { font-size: 15px; font-weight: 600; }
.foundry-consumer .tr-band-fill { height: 5px; border-radius: 3px; }
.foundry-consumer .tlc-plot,
.foundry-consumer .tlc-energy,
.foundry-consumer .tlc-width { border-radius: var(--r-ctl); border-color: var(--border-soft); background: var(--surface-2); }
.foundry-consumer .tlc-h2 { margin: 22px 0 10px; }
.foundry-consumer .tlc-axis { font-size: 11px; }
.foundry-consumer .tlc-key-i { font-size: 12.5px; }
.foundry-consumer .tlc-sn { background: var(--surface-2); font-size: 11px; font-weight: 500; }
.foundry-consumer .rank-list .rank-row { padding: 10px 0; }
.foundry-consumer .rank-list .r-name { font-size: 14.5px; font-weight: 500; }
.foundry-consumer .rank-list .r-score { font-size: 16px; font-weight: 600; }
.foundry-consumer .rank-row .r-pos { font-size: 12px; font-variant-numeric: tabular-nums; }
.foundry-consumer .rank-row .r-bar { height: 6px; background: var(--surface-2); }
.foundry-consumer .studio-cons { font-size: 13px; }
.foundry-consumer .studio-pool { padding: 18px 20px; }
.foundry-consumer .studio-pool-pick { font-size: 13.5px; }
.foundry-consumer .studio-pool-note span { font-size: 12px; }
.foundry-consumer .tlc-pro { padding: 18px 20px; }
.foundry-consumer .tlc-pro-what, .foundry-consumer .tlc-pro-list { font-size: 13.5px; }
.foundry-consumer .studio-measuring { min-height: 180px; }
.foundry-consumer .studio-measuring-text { font-size: 14.5px; }
.foundry-consumer .report-plot { margin-bottom: 20px; }

/* Act cards: text-forward, the accent as a thin rule at the top edge of the
   card rather than a coloured bar down its side. */
.foundry-consumer .dash-act,
.foundry-consumer .studio-acts .dash-act {
  background: var(--surface); border: 1px solid var(--border); border-left-width: 1px;
  border-radius: var(--r); padding: 15px 16px 16px; gap: 5px;
  box-shadow: var(--shadow-card); position: relative; overflow: hidden;
  transition: border-color 120ms ease, box-shadow 120ms ease; transform: none;
}
.foundry-consumer .dash-act::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 3px;
  background: var(--border-2);
}
.foundry-consumer .dash-act.a-lilac::before { background: var(--lilac); }
.foundry-consumer .dash-act.a-cream::before { background: var(--cream); }
.foundry-consumer .dash-act.a-amber::before { background: var(--amber); }
.foundry-consumer .dash-act.a-ok::before { background: var(--ok); }
.foundry-consumer .dash-act:hover:not(:disabled),
.foundry-consumer .studio-acts .dash-act:hover:not(:disabled) { background: var(--surface); border-color: var(--border-2); box-shadow: var(--shadow-pop); transform: none; }
.foundry-consumer .dash-act:disabled { opacity: 0.5; }
.foundry-consumer .dash-kicker,
.foundry-consumer .studio-acts .dash-kicker { font-size: 14.5px; font-weight: 600; letter-spacing: -0.01em; color: var(--text); }
.foundry-consumer .dash-desc,
.foundry-consumer .studio-acts .dash-desc { font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.foundry-consumer .dash-acts { gap: 12px; }

/* ---- the dashboard slide-over ---- */
.foundry-consumer .dash-head { gap: 18px; }
.foundry-consumer .dash-art { border-radius: var(--r-ctl); border-color: var(--border-soft); box-shadow: var(--shadow-card); }
.foundry-consumer .dash-art-empty { background: var(--surface-2); }
.foundry-consumer .dash-id h2 { font-size: 19px; font-weight: 600; letter-spacing: -0.02em; }
.foundry-consumer .dash-saved { font-size: 12.5px; font-weight: 500; }
.foundry-consumer .dash-pitch-head strong { font-size: 14px; font-weight: 600; }
.foundry-consumer .dash-pitch-opt { font-size: 13.5px; align-items: center; }
.foundry-consumer .dash-pitch-closed { font-size: 12px; }
.foundry-consumer .dash-note { font-size: 13px; }
.foundry-consumer .dash-saved-note { font-size: 13px; }

/* ---- the account page ---- */
.foundry-consumer .profile-cols { gap: 16px; }
.foundry-consumer .profile-form { margin-top: 0; gap: 18px; }
.foundry-consumer .pf-row { gap: 14px; }
.foundry-consumer .pf-field { gap: 6px; }
.foundry-consumer .pf-genres { gap: 8px; }
.foundry-consumer .pf-actions { padding-top: 18px; }
.foundry-consumer .pf-state { font-size: 12.5px; color: var(--text-3); }
.foundry-consumer .profile-head { gap: 14px; margin-bottom: 18px; }
.foundry-consumer .profile-head h2 { font-size: 16px; }
.foundry-consumer .method-list { margin-top: 12px; }
.foundry-consumer .method-row { padding: 11px 0; }
.foundry-consumer .method-name { font-size: 14px; font-weight: 500; }
.foundry-consumer .method-state { font-size: 12.5px; font-weight: 500; }
.foundry-consumer .team-row { padding: 11px 2px; }
.foundry-consumer .team-name { font-size: 14px; font-weight: 500; }
.foundry-consumer .team-role, .foundry-consumer .team-state { font-size: 12.5px; }
.foundry-consumer .team-editor { padding: 14px 16px 16px; }
.foundry-consumer .linkish { color: var(--text); text-decoration-color: var(--border-2); text-underline-offset: 3px; }
.foundry-consumer .linkish:hover { text-decoration-color: currentColor; }

/* ---- onboarding ---- */
.foundry-consumer .ob-inner h2 { font-size: 24px; font-weight: 600; letter-spacing: -0.02em; }
.foundry-consumer .ob-step { gap: 16px; }
.foundry-consumer .ob-fork-btn { padding: 16px 16px 18px; transition: border-color 120ms ease, box-shadow 120ms ease; }
.foundry-consumer .ob-fork-btn:hover { border-color: var(--border-2); box-shadow: var(--shadow-pop); }
.foundry-consumer .ob-fork-btn.on { border-color: var(--text); }
.foundry-consumer .ob-fork-name { font-size: 15px; font-weight: 600; }
.foundry-consumer .ob-fork-sub { font-size: 12.5px; }
.foundry-consumer .ob-avatar-thumb { border-color: var(--border); box-shadow: var(--shadow-card); }
.foundry-consumer .ob-footer { font-size: 12px; }

/* ---- sign in ---- */
.foundry-consumer .auth h2 { font-size: 24px; font-weight: 600; letter-spacing: -0.02em; }
.foundry-consumer .auth .btn.primary {
  background: var(--invert-bg); border-color: var(--invert-bg); color: var(--invert-fg);
  min-height: 42px; font-size: 14px; font-weight: 600;
}
.foundry-consumer .auth .btn.primary:hover:not(:disabled) { background: color-mix(in srgb, var(--invert-bg) 86%, var(--invert-fg)); border-color: transparent; }
.foundry-consumer .auth .btn.primary:focus-visible { outline-color: var(--lilac); }
.foundry-consumer .auth-inner input:not([type=checkbox]):not([type=radio]) { min-height: 42px; }
.foundry-consumer .auth-or { font-size: 12px; }

/* ---- the pool ---- */
.foundry-consumer .pool-grid { gap: 14px; }
.foundry-consumer .pool-card { padding: 14px 16px; gap: 8px 12px; transition: border-color 120ms ease, box-shadow 120ms ease; }
.foundry-consumer .pool-card:hover { border-color: var(--border-2); box-shadow: var(--shadow-pop); }
.foundry-consumer .pool-title { font-size: 14.5px; font-weight: 600; letter-spacing: -0.01em; }
.foundry-consumer .pool-artist { font-size: 12.5px; }
.foundry-consumer .pool-len { font-size: 11px; }
.foundry-consumer .pool-note { font-size: 13px; }
.foundry-consumer .pool-more { margin-top: 20px; }

/* ---- the inbox ---- */
.foundry-consumer .inbox-side { gap: 18px; }
.foundry-consumer .frow { font-size: 13px; border-radius: 6px; padding: 6px 6px; }
.foundry-consumer .frow .fbox { border-radius: 4px; background: var(--field-bg); }
.foundry-consumer .ib-item { padding: 12px 10px 12px 8px; border-radius: 0; }
.foundry-consumer .ib-item.open { box-shadow: inset 2px 0 0 var(--text); }
.foundry-consumer .ib-item-who { font-size: 13.5px; font-weight: 600; }
.foundry-consumer .ib-item-title { font-size: 13px; }
.foundry-consumer .ib-ava { border-color: var(--border); }
.foundry-consumer .ib-sender-name { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }
.foundry-consumer .ib-track-title { font-size: 17px; font-weight: 600; letter-spacing: -0.01em; }
.foundry-consumer .ib-art-img { border-radius: var(--r-ctl); border-color: var(--border-soft); box-shadow: var(--shadow-card); }
.foundry-consumer .pd-link { color: var(--text); border-bottom-color: var(--border-2); }

/* ---- the sent ledger ---- */
.foundry-consumer .stc-face { border-radius: var(--r-ctl); border-color: var(--border-soft); }
.foundry-consumer .stc-title { font-size: 14.5px; font-weight: 600; letter-spacing: -0.01em; }
.foundry-consumer .stc-sub { font-size: 12.5px; }
.foundry-consumer .msg-body { font-size: 13.5px; }

/* ---- the door: only the badge card and the fine print, the poster stays ---- */
.foundry-consumer .door-badge { padding: 16px 18px; }
.foundry-consumer .door-fine { font-size: 12px; }

/* ---------- updates about your tracks (094) ---------- */
#notify-card { display: flex; flex-direction: column; gap: 10px; }
#notify-card .check { font-size: 14px; }
#notify-card .fine { margin-left: 0; }
#notify-card .fine .linkish { margin-left: 6px; }
#notify-card .gate-error:empty { display: none; }

/* the onboarding way-out line (2026-09-22) */
.ob-who { margin-bottom: 4px; }
.ob-who .linkish { margin-left: 4px; }

/* ======================================================================
   YOU — the profile header, the tabs, the read view   (2026-10-02)
   ======================================================================
   Replaces `.profile-cols`, which was two columns of three equal `.card`s.
   app/index.html's section header carries the argument; the short version is
   that nothing on that screen said which box was the thing the screen is about,
   and the one thing it IS about had no picture of its result anywhere on it.

   THE HEADER IS BUILT TO THE SAME MEASUREMENTS AS foundry/a/artist-page.css —
   the 104px band, the 112px circle overlapping it by 44, the name at display
   weight, chips under. That is on purpose and it is the only reason the header
   is worth having: this screen is a PREVIEW of the public page, so the two must
   not be two designs. The sheets stay separate because the two documents share
   no tokens (one is on landing.css, one on this), but the numbers are the same
   numbers and should be changed together.
   ====================================================================== */

/* ---------- the header ---------- */
/* `display: block` and `padding: 0` UNDO THE BARE `header` RULE near the top of
   this file, which is /foundry/manage/'s navigation bar and matches any <header>
   on any of these documents. This is the first one on the consumer surface, and
   without these two declarations it is a flex row with 30px of side chrome: the
   band collapses to zero width, the three grid areas never apply, and the artist
   name wraps one letter per line. A <div> would have dodged it and is the wrong
   fix — this IS the header of its section, and the next <header> somebody adds
   would hit the same rule with no comment anywhere to explain it. */
.me-head {
  display: block;
  padding: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  margin-bottom: 20px;
}
.me-head-band {
  height: 104px;
  position: relative;
  background:
    radial-gradient(120% 180% at 12% 0%, rgba(143, 106, 232, 0.18), transparent 62%),
    linear-gradient(180deg, var(--surface-2), var(--surface));
  border-bottom: 1px solid var(--border-soft);
}
/* THE BANNER SITS ON THE BAND, IT DOES NOT REPLACE IT (migration 098). The
   band's own colour stays underneath and the image is painted over it, so a
   profile with no banner and a profile whose banner failed to fetch are the
   same picture. The alternative — hiding the band when a banner exists — leaves
   a blank 104px strip the moment Storage is slow, which reads as a broken image
   rather than as a design. foundry/a/artist-page.css carries the same note for
   the public page; the two sheets share no tokens and the NUMBERS here (104px
   band, 112px circle, -60px overlap) must be changed together.
   `object-fit: cover` and no cropper: the band is 4:1 and the middle band of a
   wide image is where a banner's subject already is. */
.me-head-banner {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: none;
}
.me-head-band.has-banner .me-head-banner { display: block; }

/* The picker's preview, on the Account tab beside the photo's. 4:1 so what is
   shown is the shape the band will crop to, and the hint is inside the frame so
   an empty one says what it wants rather than sitting blank. */
.pf-banner-prev {
  position: relative;
  width: 232px; max-width: 100%;
  aspect-ratio: 4 / 1;
  flex: none;
  border: 1px solid var(--border);
  border-radius: var(--r);
  overflow: hidden;
  background: var(--surface-2);
  display: grid; place-items: center;
}
.pf-banner-prev-img { width: 100%; height: 100%; object-fit: cover; display: none; }
.pf-banner-prev.has-img .pf-banner-prev-img { display: block; }
.pf-banner-prev.has-img .pf-banner-prev-hint { display: none; }
.pf-banner-prev-hint {
  position: absolute;
  font-size: 11px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-3);
}
.me-head-body {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "face ident acts";
  gap: 0 20px;
  /* START, NOT END. With `end` the circle's negative margin bought nothing: it
     only shrank the face's contribution to the row height, and align-end then
     put it back on the row's baseline — so the avatar sat entirely BELOW the
     band instead of breaking it. Aligning the row to the top and pulling the
     face up is what makes the overlap real, and it also puts the name beside
     the circle rather than above it. */
  align-items: start;
  padding: 16px 24px 20px;
}
/* ONLY THE CIRCLE OVERLAPS THE BAND. The first version pulled the whole body up
   with a negative margin, which is the obvious reading of "the avatar overlaps"
   and is wrong: the body is as tall as its tallest child, which is the name,
   address, place and chips stacked — so the text rode up into the band with it
   and the artist name sat on top of the gradient. The negative margin belongs on
   the one element that is meant to break the line. */
.me-face { align-self: start; margin-top: -60px; }

.me-face {
  grid-area: face;
  position: relative;
  width: 112px; height: 112px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--surface-2);
  border: 3px solid var(--surface);
  display: grid; place-items: center;
}
.me-face-img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The initials sit UNDER the photo and are revealed by removing it, rather than
   added when it fails — so there is no frame of blank circle while a slow
   image resolves, and the fallback never flashes over a photo that arrives. */
.me-face-txt {
  position: absolute; inset: 0;
  display: none; place-items: center;
  font-size: 38px; font-weight: 600; letter-spacing: -0.02em;
  color: var(--muted);
}
.me-face-fell .me-face-img { display: none; }
.me-face-fell .me-face-txt { display: grid; }

.me-ident { grid-area: ident; min-width: 0; padding-bottom: 2px; }
.me-name {
  font-size: 30px; font-weight: 600; letter-spacing: -0.025em; line-height: 1.12;
  margin: 0 0 3px; color: var(--text);
  overflow-wrap: anywhere;    /* an artist name has no guaranteed spaces */
}
.me-at { font-size: 13.5px; color: var(--text-2); margin: 0 0 7px; font-variant-numeric: tabular-nums; }
/* No address yet: quieter, and it reads as a prompt rather than as a value. */
.me-at.is-none { color: var(--text-3); font-style: italic; }
.me-where { font-size: 14px; color: var(--text-2); margin: 0 0 9px; }

.me-chips { display: flex; flex-wrap: wrap; gap: 6px; list-style: none; margin: 0; padding: 0; }
.me-chip {
  font-size: 12px; line-height: 1;
  padding: 6px 10px;
  border: 1px solid var(--border-2);
  border-radius: 999px;
  color: var(--text-2);
}
.me-chips:empty { display: none; }

.me-acts {
  grid-area: acts;
  align-self: start;
  display: flex; gap: 8px; align-items: center;
  /* flex-end so a wrap drops the extra control under the others on the right,
     rather than stranding it mid-row. */
  flex-wrap: wrap; justify-content: flex-end;
}
.me-acts .btn { white-space: nowrap; }

/* The publish line spans the card's own width under the body, because it is a
   statement about everything above it rather than about any one part. */
.me-pub {
  margin: 0;
  padding: 11px 24px;
  border-top: 1px solid var(--border-soft);
  background: var(--surface-2);
  font-size: 12.5px;
  color: var(--text-3);
}
.me-pub.is-on { color: var(--text-2); }

/* ---------- the tabs ---------- */
.me-tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 20px;
}
.me-tab {
  appearance: none;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  padding: 10px 14px;
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  color: var(--muted);
  cursor: pointer;
  transition: color .15s, border-color .15s;
}
.me-tab:hover { color: var(--text-2); }
.me-tab.current { color: var(--text); border-bottom-color: var(--text); }
.me-tab:focus-visible { outline: 2px solid var(--s-label); outline-offset: -2px; border-radius: 4px; }

/* THE `hidden` ATTRIBUTE HAS TO WIN HERE, AND BY DEFAULT IT DOES NOT.
   `[hidden]`'s `display: none` comes from the user-agent stylesheet, so ANY
   author rule setting a display beats it — and the panes and the read view are
   both grids. Without this the three tabs render stacked on top of each other,
   which is exactly what the screen looked like before this rule existed: the
   JavaScript was setting `.hidden = true` correctly and the stylesheet was
   quietly ignoring it.
   Scoped to the section rather than global, and `!important` because the whole
   point is to outrank a display set by any rule in this file. */
#view-profile [hidden] { display: none !important; }

.me-pane { display: grid; gap: 16px; }
/* The read view's cards stack; the pane grid handles the gaps so an empty card
   that has been hidden leaves no double gap behind it. */
#me-read { display: grid; gap: 16px; }

/* ---------- the read view ---------- */
.me-card-h {
  font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-3);
  margin: 0 0 12px;
}
.me-bio { font-size: 14.5px; line-height: 1.6; color: var(--text-2); margin: 0; white-space: pre-wrap; }

.me-addr {
  font-size: 16px;
  margin: 0 0 8px;
  color: var(--text);
  word-break: break-all;
}
.me-addr-pre { color: var(--text-3); }

.me-links { list-style: none; margin: 0; padding: 0; display: grid; }
.me-link {
  display: flex; align-items: baseline; gap: 12px;
  padding: 10px 2px;
  text-decoration: none;
  border-bottom: 1px solid var(--border-soft);
}
.me-links li:last-child .me-link { border-bottom: 0; }
.me-link-kind { font-size: 14px; font-weight: 500; color: var(--text); min-width: 96px; }
.me-link-tail {
  font-size: 14px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: color .15s;
}
.me-link:hover .me-link-tail { color: var(--text); }

/* ---------- the handle field ----------
   One control that reads as one string: the fixed half is part of the box
   rather than a label above it, so what is on screen is the address, and the
   editable part is visibly the only part that is a decision. */
.pf-handle {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--border-2);
  border-radius: var(--r-ctl);
  background: var(--field-bg);
  overflow: hidden;
}
.pf-handle:focus-within { border-color: var(--s-label); }
.pf-handle-pre {
  display: flex; align-items: center;
  padding: 0 2px 0 11px;
  font-size: 14px;
  color: var(--text-3);
  white-space: nowrap;
  user-select: none;
}
/* `.foundry-consumer` SCOPED, because the rule it has to beat is — the design
   pass gives every `input[type=text]` a border, a background and a radius under
   that class, and an unscoped `.pf-handle input` loses to it on specificity.
   Unscoped, the prefix and the field drew a seam down the middle of what is
   meant to read as one address. */
.foundry-consumer .pf-handle input,
.pf-handle input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  padding: 10px 11px 10px 0;
  font: inherit;
  font-size: 14px;
  color: var(--text);
}
.foundry-consumer .pf-handle input:focus,
.pf-handle input:focus { outline: none; border-color: transparent; box-shadow: none; }
.pf-handle-field .pf-note { margin-top: 6px; min-height: 1.1em; }
.pf-note.is-ok  { color: var(--ok); }
.pf-note.is-bad { color: var(--danger); }

.pf-buttons { display: flex; gap: 8px; align-items: center; }

/* ---------- narrow ----------
   The three-column header stacks and centres. Left-aligned at 400px the two
   buttons sit under a 112px circle with a column of air beside them. */
@media (max-width: 760px) {
  .me-head-body {
    grid-template-columns: 1fr;
    grid-template-areas: "face" "ident" "acts";
    justify-items: center;
    text-align: center;
    gap: 12px 0;
    padding: 0 18px 18px;
  }
  .me-name { font-size: 25px; }
  .pf-banner-prev { width: 100%; }
  .me-chips { justify-content: center; }
  .me-acts { width: 100%; }
  .me-acts .btn { flex: 1 1 0; text-align: center; }
  .me-pub { padding: 10px 18px; text-align: center; }
  /* The kind no longer holds a column next to a tail with nowhere to go. */
  .me-link { flex-direction: column; gap: 2px; }
  .me-link-kind { min-width: 0; }
  /* The tabs scroll rather than wrap: three items wrapping to two rows puts a
     1px underline across the middle of the screen. */
  .me-tabs { overflow-x: auto; scrollbar-width: none; }
  .me-tabs::-webkit-scrollbar { display: none; }
  .me-tab { flex: none; }
}
