/* ===========================================================================
   Design Token Foundation (2026-08-14) -- single source of truth for every
   colour, radius, shadow, and type-scale value used anywhere on the site.
   Real palette direction, reasoned not just executed: moved deliberately
   away from the current AI-product-default look (warm-cream + muted-earthy-
   accent, the exact cliché this brief named) -- a cooler, stonier warm-
   neutral base (not yellow-cream, not stark white), ink/navy for text and
   structure (calm/administrative, matching Archie's actual "no rush" tone
   rather than cold near-black), and a single forest-green accent (chosen
   over teal, which has become nearly as saturated a default in AI-product
   branding as terracotta). The coding/build dashboard's own dark terminal
   zone is a deliberately separate token block below, not derived from this
   one -- it's supposed to feel like a genuinely different, focused space.
   =========================================================================== */
:root {
  color-scheme: light dark;
  /* Real palette shift (2026-08-28, founder's own call): the stonier
     greige base above was a deliberate earlier reaction against the
     warm-cream AI-product cliche -- but the founder has since asked to
     adopt the actual warm cream from the app-lock artwork site-wide, so
     this now leans back into it deliberately (paired with the same ink/
     navy text + forest-green accent, not a wholesale palette reset). */
  --bg: #f7e9c8;
  --panel-bg: #fbf3de;
  --surface-2: #fffaf0;
  --text: #1c2430;
  --muted: #6b7280;
  --accent: #2f6b4f;
  --accent-hover: #275a42;
  --on-accent: #ffffff;
  --border: #e6d6ac;
  --topbar-bg: #f1e0b4;
  --topbar-text: #1c2430;
  --topbar-muted: #6b7280;
  --topbar-border: #d0ccc0;
  --danger: #b3453c;
  --danger-bg: #f6e3e1;
  --success: #2f7d52;
  --success-bg: #e3efe7;
  --warning: #b3792f;
  --card-accent-brief: #8a7454;
  --card-accent-appstore: #5e7ba0;
  --feed-post-bg: #f3f7f3;

  /* Type scale -- every font-size on the site should reference one of
     these, never a raw rem/px value chosen ad hoc per component. */
  --font-family: -apple-system, "Segoe UI", system-ui, sans-serif;
  --font-family-mono: ui-monospace, monospace;
  /* Real freehand wordmark (2026-08-28, founder's own ask) -- matches the
     hand-lettered "Ask Archie" logotype baked into the app-lock background
     artwork. An earlier Sora serif experiment on chat headings was
     reverted -- default system sans reads less formal, closer to the lock
     screen's own tone. */
  --font-family-hand: "Chewy", "Segoe Script", cursive;
  --font-size-xs: 0.75rem;
  --font-size-sm: 0.85rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.15rem;
  --font-size-xl: 1.4rem;
  --font-size-2xl: 1.8rem;

  /* Spacing scale -- multiples of a single 4px unit. */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;

  /* Real, named 3-column layout widths (2026-08-27) -- locked in here
     rather than picked ad hoc per page, so any future 3-column page
     (Chat is the first; more will follow) reuses the same real
     proportions instead of guessing new ones each time. A "narrow"
     collapsed-utility column (Models: a settings-style summary, not meant
     to be read at length) and a "wide" browsing column (Previous chats:
     real titles need room to actually be legible) are the two real shapes
     this site needs -- everything else is the flexible main content pane. */
  --layout-col-outer: 260px;

  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-pill: 999px;

  --shadow-sm: 0 2px 8px rgba(20, 24, 20, 0.1);
  --shadow-md: 0 8px 30px rgba(20, 24, 20, 0.2);

  /* Chat Window Interface brief: a real, shared transition token, closing
     the zero-transitions gap found in the Design Self-Review -- applied
     consistently rather than per-element, starting with the Archie panel
     (the highest-traffic surface) and the base button rule everything else
     inherits from. Deliberately soft and unhurried (matches "the interface
     itself shouldn't visually imply urgency"), not a snappy UI-kit default. */
  --transition-soft: 220ms ease;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14181c;
    --panel-bg: #1c2126;
    --surface-2: #262c33;
    --text: #e7e5df;
    --muted: #9aa0a6;
    --accent: #3e8862;
    --accent-hover: #4a9970;
    --border: #2b3138;
    --topbar-bg: #191d22;
    --topbar-text: #e7e5df;
    --topbar-muted: #9aa0a6;
    --topbar-border: #2b3138;
    --danger: #d97066;
    --danger-bg: #3a2321;
    --success: #4a9d72;
    --success-bg: #1e3327;
    --feed-post-bg: #1c2420;
    --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.35);
    --shadow-md: 0 8px 30px rgba(0, 0, 0, 0.5);
  }
}

/* Explicit user choice (Settings) always wins over the OS default above. */
:root[data-theme="light"] {
  --bg: #f7e9c8;
  --panel-bg: #fbf3de;
  --surface-2: #fffaf0;
  --text: #1c2430;
  --muted: #6b7280;
  --accent: #2f6b4f;
  --accent-hover: #275a42;
  --border: #e6d6ac;
  --topbar-bg: #f1e0b4;
  --topbar-text: #1c2430;
  --topbar-muted: #6b7280;
  --topbar-border: #d0ccc0;
  --danger: #b3453c;
  --danger-bg: #f6e3e1;
  --success: #2f7d52;
  --success-bg: #e3efe7;
  --warning: #b3792f;
  --card-accent-brief: #8a7454;
  --card-accent-appstore: #5e7ba0;
  --feed-post-bg: #f3f7f3;
  --shadow-sm: 0 2px 8px rgba(20, 24, 20, 0.1);
  --shadow-md: 0 8px 30px rgba(20, 24, 20, 0.2);
}
:root[data-theme="dark"] {
  --bg: #14181c;
  --panel-bg: #1c2126;
  --surface-2: #262c33;
  --text: #e7e5df;
  --muted: #9aa0a6;
  --accent: #3e8862;
  --accent-hover: #4a9970;
  --border: #2b3138;
  --topbar-bg: #191d22;
  --topbar-text: #e7e5df;
  --topbar-muted: #9aa0a6;
  --topbar-border: #2b3138;
  --danger: #d97066;
  --danger-bg: #3a2321;
  --success: #4a9d72;
  --success-bg: #1e3327;
  --feed-post-bg: #1c2420;
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.35);
  --shadow-md: 0 8px 30px rgba(0, 0, 0, 0.5);
}

* { box-sizing: border-box; }

/* @theme-component: body */
body {
  margin: 0;
  font-family: var(--font-family);
  background: var(--bg);
  color: var(--text);
  /* Real fix (2026-09-15, founder-reported: "page is breaking right hand
     side" on mobile). The site's own existing convention is that
     individual widgets scroll horizontally within themselves
     (overflow-x: auto, scoped -- see e.g. .synth-seq-steps,
     .video-editor-panel) and the page itself never needs to. That was
     never actually enforced at the page level, so any one overflowing
     element anywhere (this session's real example: a hidden tooltip
     popover with a fixed width wider than a narrow viewport) could
     widen the whole page past the viewport, which mobile browsers then
     render by zooming the entire page out to fit -- squeezed content,
     dead space on the side. This is the real safety net: clips any such
     overflow at the page edge instead of letting it resize everything.
     No page here relies on intentional whole-page horizontal scroll. */
  overflow-x: hidden;
}

/* Real fix (2026-08-27, caught in the design review): browsers give
   <textarea>/<input>/<select>/<button> their own UA-default font (usually
   monospace, not the page's real body font) unless explicitly told to
   inherit -- every plain-text chat input and form field on the site was
   silently rendering in Courier-style monospace as a result, nothing to
   do with any real font choice. One real, site-wide fix, not scattered
   per-element overrides. */
input, textarea, select, button { font-family: inherit; }

/* Real fix (2026-09-23, founder-reported: "dark mode does not include
   totally in these boxes... they are where the user needs to focus and
   not be visually seeking too much" -- then, confirmed correct, extended
   to the main Chat page: "the same tension needs resolving") -- plain
   text inputs/selects had NO real styling of their own anywhere on the
   site (only the font-family rule above), relying entirely on the
   browser's own native rendering of `color-scheme: light dark` on :root
   -- which several real spots weren't honestly picking up, showing stark
   white against an otherwise dark page instead. Extended one flagged
   area at a time (the founder's own explicit process), sharing one real
   recipe rather than a hand-copied rule per spot. Calm and quiet by
   design: a plain themed box, a real but subtle focus ring (never a
   bright glow) -- the field should be easy to find without competing for
   attention while filled in. */
#registrant-details-form input,
#chat-kb-search-input,
#archie-chat-send-all-select {
  background: var(--panel-bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.55rem 0.75rem;
  font-size: 0.95rem;
  box-sizing: border-box;
  transition: border-color var(--transition-soft), box-shadow var(--transition-soft);
}
#registrant-details-form input { width: 100%; margin-bottom: 0.5rem; }
#registrant-details-form input::placeholder,
#chat-kb-search-input::placeholder { color: var(--muted); }
#registrant-details-form input:focus,
#chat-kb-search-input:focus,
#archie-chat-send-all-select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
}

.hidden { display: none !important; }
/* @theme-component: .muted */
.muted { color: var(--muted); }

/* Real, site-wide link colour -- replaces the browser's default blue/purple
   (unvisited/visited) on every plain <a> with the same accent green used
   throughout the rest of the UI, so a link never looks like a stray,
   unstyled element regardless of whether it's been clicked before. */
/* @theme-component: a */
a { color: var(--accent); }
/* @theme-component: a:visited */
a:visited { color: var(--accent); }
/* @theme-component: a:hover */
a:hover { color: var(--text); }

.screen { min-height: 100vh; }
:root:fullscreen { background: var(--bg); overflow-y: auto; }

/* @theme-component: .auth-box */
.auth-box {
  max-width: 360px;
  margin: 10vh auto;
  padding: 2rem;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
/* @theme-component: .auth-box input */
.auth-box input {
  display: block;
  width: 100%;
  margin-bottom: 0.75rem;
  padding: 0.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg);
  color: var(--text);
}
.auth-actions { display: flex; gap: 0.5rem; }
/* @theme-component: .error */
.error { color: var(--danger); min-height: 1.2em; }

/* @theme-component: .google-signin-btn */
.google-signin-btn {
  display: block;
  width: 100%;
  text-align: center;
  margin: 0.75rem 0;
  padding: 0.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg);
  color: var(--text);
  font-weight: 600;
  text-decoration: none;
}
/* @theme-component: .google-signin-btn:visited */
.google-signin-btn:visited { color: var(--text); }
/* @theme-component: .google-signin-btn:hover */
.google-signin-btn:hover { border-color: var(--accent); color: var(--accent); }

.home-hero {
  text-align: center;
  max-width: 640px;
  margin: 4rem auto 3rem;
  padding: 0 1.5rem;
}
.home-hero h1 { font-family: var(--font-family-hand); font-size: 3.4rem; font-weight: 400; line-height: 1.1; margin-bottom: 0.5rem; color: var(--text); }
/* Real "closed for now" pill (2026-08-28) -- was plain grey lede text,
   now a soft badge so the pre-launch state reads as a deliberate moment
   rather than an error message. */
.home-closed-pill {
  display: inline-block;
  color: var(--muted);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.35rem 1rem;
  font-size: 0.9rem;
  margin: 0 0 1.25rem;
}
/* Real hero badge row (2026-08-28) -- carries the paint-splash icon
   language from the app-lock artwork onto the logged-out landing page
   itself, populated via renderHomeHeroBadges() in app.js. */
.home-hero-badges { display: flex; justify-content: center; flex-wrap: wrap; gap: 0.85rem; margin: 0 0 1.75rem; }
.home-hero-badges .home-card-badge { width: 52px; height: 52px; margin-bottom: 0; }
.home-hero-badge.home-card-badge { width: 44px; height: 44px; margin: 0 auto 0.75rem; }
/* @theme-component: .home-nav */
.home-nav { display: flex; gap: 1.5rem; margin: 1rem 0 1.5rem; flex-wrap: wrap; }
.home-nav a {
  color: var(--accent);
  font-weight: 600;
  text-decoration: none;
  padding-bottom: 0.15rem;
  border-bottom: 2px solid transparent;
}
.home-nav a:hover, .home-nav a:focus-visible { border-bottom-color: var(--accent); }
/* @theme-component: .home-tagline */
.home-tagline { font-size: 1.2rem; color: var(--accent); margin: 0 0 1rem; }
/* @theme-component: .home-lede */
.home-lede { color: var(--muted); line-height: 1.6; margin-bottom: 1.5rem; }
/* @theme-component: .home-cta-link */
.home-cta-link {
  display: inline-block;
  color: var(--accent);
  text-decoration: none;
  font-weight: 600;
  border-bottom: 1px solid var(--accent);
  padding-bottom: 2px;
}

.home-section {
  max-width: 900px;
  margin: 0 auto 3rem;
  padding: 0 1.5rem;
}
.home-section h2 { text-align: center; margin-bottom: 1.5rem; }
.home-illustrative-note { text-align: center; margin-top: -1rem; margin-bottom: 1.5rem; font-size: 0.85rem; }

/* Real bug found live (2026-08-30, founder-led device test): auto-fit sizes
   columns by how many cards fit per row, not by how many rows worth of
   cards there actually are -- with an odd count (e.g. 7 marketing cards)
   or mixed-length content, a card can get stranded alone on its own row
   with nothing to stretch-match its height against, reading as real
   misalignment while scrolling. Deterministic breakpoints (1/2/3 columns)
   fix this the same way on every real device/window size. */
.home-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}
@media (min-width: 560px) {
  .home-cards { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 860px) {
  .home-cards { grid-template-columns: repeat(3, 1fr); }
}
/* @theme-component: .home-card */
.home-card {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.25rem;
}
/* Real paint-splash badges (2026-08-28) -- same icon-on-a-splash-blob
   language as the app-lock background image, carried onto every home card.
   Irregular per-corner border-radius approximates a torn/splashed blob
   without hand-authored SVG path data. */
.home-card-badge {
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  border-radius: 62% 38% 55% 45% / 45% 55% 40% 60%;
  margin-bottom: 0.75rem;
  box-shadow: var(--shadow-sm, 0 1px 3px rgba(0,0,0,0.15));
  transform: rotate(var(--badge-rotate, 0deg));
  transition: transform 0.2s ease-out, box-shadow 0.2s ease-out;
}
/* Real fix (2026-09-01, founder-reported: "the white is invisible can only
   see the blue background") -- real root cause found: the App Store badge
   is the one badge that's a real <a> link (the other five are plain divs),
   and a:visited { color: var(--accent) } genuinely outranks the class rule
   above in specificity once a browser has actually visited /app-store --
   invisible to any JS color check, since browsers deliberately hide the
   true :visited-applied color from getComputedStyle for privacy. Named
   explicitly here so it wins regardless of visited history. */
a.home-card-badge:link,
a.home-card-badge:visited {
  color: #fff;
}
/* Real "nudge out" hover (2026-08-28, founder's own ask) -- a live
   affordance ahead of these badges actually linking anywhere, so they
   already read as interactive. cursor:pointer set here too, deliberately,
   for the same reason. */
.home-card-badge:hover {
  transform: rotate(var(--badge-rotate, 0deg)) scale(1.15) translateY(-3px);
  box-shadow: var(--shadow-md, 0 6px 16px rgba(0,0,0,0.2));
  cursor: pointer;
}
@media (prefers-reduced-motion: reduce) {
  .home-card-badge { transition: none; }
  .home-card-badge:hover { transform: rotate(var(--badge-rotate, 0deg)); }
}
.home-card h3 { margin: 0 0 0.5rem; }
/* @theme-component: .home-card p */
.home-card p { color: var(--muted); line-height: 1.5; margin: 0; }
/* @theme-component: .home-price */
.home-price { color: var(--text) !important; font-weight: 600; margin-bottom: 0.5rem !important; }
.home-card-more-btn { display: block; margin-top: 0.5rem; }

/* Full Pricing Restructure: calm, neutral card backgrounds (matching
   .home-card above) with ONE deliberate accent reserved for the primary
   action and the recommended badge -- never scattered across the page. */
.pricing-card { position: relative; display: flex; flex-direction: column; }
.pricing-card p { flex-grow: 1; }
/* Real bug found live (2026-08-30, founder-led device test, screenshot
   showed real misalignment): .pricing-cards inherited .home-cards' own
   auto-fit grid, which sizes columns by how many short cards fit per row --
   at some real widths that let 3 short cards (Free/£10 bundle/£5 hosting)
   share a row while the genuinely longer 4th card (All-Access bundle,
   real feature list) got stranded alone on the next row, towering with
   nothing beside it to match height against. auto-fit only guarantees
   same-row cards match height (CSS Grid's own default stretch) -- it says
   nothing about which cards land in the same row, and that's what broke
   here. A real, deterministic 2-column grid (1 column below 600px) fixes
   this the same way on every real device/window size, not just the one
   this was caught on. */
/* Compound selector deliberately, not .pricing-cards alone (2026-08-31,
   founder-flagged cascade risk): the element carries both classes
   (class="home-cards pricing-cards"), so a single-class override only won
   because this rule happened to sit later in the file than .home-cards --
   identical specificity, pure source-order dependency. .home-cards.pricing-cards
   has strictly higher specificity than .home-cards alone, so this wins
   regardless of file order -- reordering styles.css can no longer silently
   break this. */
.home-cards.pricing-cards { grid-template-columns: 1fr; }
@media (min-width: 600px) {
  .home-cards.pricing-cards { grid-template-columns: repeat(2, 1fr); }
}
/* @theme-component: .synth-compose-row */
.synth-compose-row { display: flex; gap: 0.5rem; margin-top: 0.5rem; }
.synth-compose-row input { flex: 1; }
.synth-controls-row { display: flex; align-items: center; gap: 0.75rem; margin: 0.5rem 0; flex-wrap: wrap; }
/* Real style quick-pick chips (2026-09-05) -- a discovery aid only, same
   real pill shape as .tab, not a gate on the free-text box underneath.
   Real fix (2026-09-05, founder-reported: "i cant see the chip titles when
   im in dark mode"): the Synth page's whole panel (#synth-screen
   .community-block) is a deliberately fixed dark "rack" background,
   independent of the site's own light/dark theme toggle -- var(--text)
   here tracks the OUTER page's theme, so in real light mode it resolves
   dark/near-black, invisible against this panel's own always-dark
   background behind a transparent chip. Every other real label/span in
   this panel already gets its own fixed light color (#d7e3d7) for exactly
   this reason -- chips get the same real panel-specific colors instead of
   the page-theme tokens. */
.synth-style-chips { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-bottom: 0.5rem; }
/* Real grouping (2026-09-10, founder: "we have tone generators and
   effects so they will need to be grouped and a slight colour on the
   chip") -- reuses the exact same real category colours already driving
   the compiled plugin's own knob colours (CATEGORY_HEX in vstCodeGen.ts)
   for a coherent, meaningful distinction rather than an arbitrary one. */
/* Real category colour system (2026-09-14, Founder APPLY) -- the heading
   itself uses the full-strength category accent (--cat, set per group by
   vstBuilderRenderBlockLibrary in app.js from VST_BUILDER_CATEGORY_ACCENTS),
   the same real colour family the generated instrument's own module boxes
   use (VST_BLOCK_CATEGORY_COLORS in vstCodeGen.ts) -- reused, not a second,
   separately-maintained palette. Falls back to the real --muted token if
   --cat is ever unset for some reason, never an invented placeholder. */
.vst-builder-chip-group-label { margin: 0.5rem 0 0.3rem; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--cat, var(--muted)); }
.synth-style-chip-effect { background: #b98cff; color: #1c1f1c; }
.synth-style-chip-effect:hover { filter: brightness(0.94); }
/* @theme-component: .synth-style-chip */
/* Real fix (2026-09-05): #synth-screen .community-block button {color:
   var(--text)} (ID + class + element, specificity 1,1,1) otherwise beats a
   plain #synth-screen .synth-style-chip rule (1,1,0) regardless of source
   order -- the extra "button" here matches that same specificity shape so
   this real fix actually wins. */
#synth-screen .community-block button.synth-style-chip {
  background: transparent;
  color: #d7e3d7;
  border: 1px solid #3d423d;
  border-radius: 20px;
  padding: 0.3rem 0.75rem;
  font-size: 0.8rem;
  cursor: pointer;
}
/* @theme-component: .synth-style-chip:hover */
#synth-screen .community-block button.synth-style-chip:hover { border-color: #7fd992; color: #7fd992; }
.synth-vary-toggle-label { display: flex; align-items: center; gap: 0.4rem; cursor: pointer; }
/* Real full mixer (2026-09-05) -- one real channel strip per unit,
   side-by-side, same real card language as .synth-rack-module. */
.synth-mixer-strips { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-top: 0.5rem; }
.synth-mixer-strip {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.6rem 0.75rem;
  background: var(--surface);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  min-width: 6.5rem;
}
.synth-mixer-strip label { display: flex; flex-direction: column; align-items: center; gap: 0.2rem; font-size: 0.75rem; }
.synth-mixer-strip input[type="range"] { writing-mode: vertical-lr; direction: rtl; height: 5rem; }
.synth-mixer-jump-btn {
  border: 1px solid var(--border);
  background: var(--panel-bg, transparent);
  border-radius: var(--radius-sm);
  padding: 0.1rem 0.5rem;
  font-size: 0.7rem;
  cursor: pointer;
}
#synth-screen .synth-mixer-strip { background: #232823; border-color: #3d423d; }
/* Real fix (2026-09-05, same real bug/specificity fix as .synth-style-chip
   above): a plain button gets var(--text) from #synth-screen .community-
   block button (specificity 1,1,1), invisible against this always-dark
   panel in the site's own light theme -- matched specificity so this wins. */
/* Real fix (2026-09-07, founder-reported: "cant see the text on this loop
   when in day mode, its ok in darkmode") -- the color fix above assumed a
   real dark background, but this button's own background comes from
   var(--panel-bg, transparent) -- a site-wide, theme-dependent token that
   resolves light in day mode. Light text (#d7e3d7) on that light
   background is genuinely invisible; only dark mode's own dark --panel-bg
   happened to pair correctly with it. Same real fixed-dark "rack" tone
   .synth-mixer-strip already uses, applied here too, so this button's
   contrast holds regardless of the site's own light/dark theme. */
#synth-screen .community-block button.synth-mixer-jump-btn { color: #d7e3d7; background: #2f342f; border-color: #3d423d; }

/* ==========================================================================
   ASK ARCHIE SYNTH VISUAL PHASE 1 (2026-09-25) -- bounded live prototype,
   Master + Mixer + Voice A ONLY (per the founder-approved Synth full-page
   visual architecture investigation, same date). Every rule below is scoped
   under #synth-screen and/or a new "synth-chassis"/"synth-mixer-chassis"/
   "synth-master-chassis"/"synth-voice-a-chassis" class added ONLY to these
   three sections' own markup -- Voice B/C/D, Drum 808/909, Sequencer, Rack,
   Cross-patch, Song Builder, Keyboard and Sample Studio carry none of these
   classes and are therefore untouched by any rule in this block.

   Values are modelled on the proven VST harness panel/group/label/LED/LCD
   recipe (src/worker/lib/vstHarness.ts's .ahSection/.ahGroup/.ahLabel/.ahLed,
   src/worker/lib/vstKnob.ts's .acKnobValue) -- copied under new, Synth-owned
   class names rather than loaded live from /api/vst-harness-ui.js, since
   that script also carries VST-specific identity-bar/native-WebView-bridge
   code (src/worker/lib/vstChassis.ts) this browser-only page has no use for
   and no way to safely invoke. Using new names (not the literal ah-prefixed / ac-prefixed
   classes) also means this can never collide even if that script is later
   loaded on this same page for some other reason.

   Accent colour is deliberately NOT the harness's red (#e04848) or Sample
   Studio's orange/brown (--vs-*) -- it reuses the warm gold already present
   on this exact page (the existing .synth-303-knob thumb gradient), so the
   "same studio family, still looks like Synth" identity comes from the dark
   chassis material, while the accent colour stays genuinely Synth's own. */

#synth-screen .synth-chassis {
  border: 1px solid #05060a;
  border-radius: 10px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.03), transparent 70px), #1c1f1c;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.05);
  padding: 0.9rem 1rem;
  color: #f2f2f0;
}
/* Real fix, same task -- Mixer and Master's wrapper carries BOTH
   "community-block" and "synth-chassis" (Voice A's own new wrapper carries
   only "synth-chassis", since it never had a .community-block of its own).
   #synth-screen .community-block (styles.css ~1453) is an existing rule of
   EQUAL specificity that happens to be declared LATER in this file, so it
   was silently winning the cascade tie and overriding this chassis
   background/border back to the standard Synth panel look wherever both
   classes coexist. This compound selector is unambiguously more specific,
   so it wins regardless of source order. */
#synth-screen .community-block.synth-chassis {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.03), transparent 70px), #1c1f1c;
  border-color: #05060a;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
#synth-screen .synth-chassis .muted { color: #9aa0a6; }
#synth-screen .synth-chassis h3 {
  color: #f2f2f0;
  font-size: 0.95rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  border-bottom: 1px solid #3a3f3a;
  padding-bottom: 0.5rem;
  margin: 0 0 0.7rem;
}
/* Same equal-specificity/later-in-file issue as the wrapper background
   above, this time against #synth-screen .community-block h3 -- compound
   selector again guarantees this wins for Mixer/Master's own heading. */
#synth-screen .community-block.synth-chassis h3 {
  color: #f2f2f0;
  border-bottom: 1px solid #3a3f3a;
  padding-bottom: 0.5rem;
  margin: 0 0 0.7rem;
}
#synth-screen .community-block.synth-chassis h3::before { background: #d9a856; box-shadow: 0 0 4px 1px rgba(217, 168, 86, 0.7); }

/* Recessed functional grouping (Voice A: Oscillator/Filter/Mixer/Effects/
   Pattern & Steps/Automation; Master: its one "Output Stage" group). Purely
   a wrapper -- every control inside keeps its existing id/class/behaviour. */
#synth-screen .synth-chassis-group {
  border: 1px solid #3a3f3a;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.22);
  padding: 0.6rem 0.75rem;
  margin: 0.7rem 0;
}
#synth-screen .synth-chassis-group:first-of-type { margin-top: 0; }
#synth-screen .synth-chassis-group-title {
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 700;
  color: #d9a856;
  margin-bottom: 0.5rem;
}

/* LCD-style read-only value readout -- Master Trim only, this phase. */
#synth-screen .synth-hw-value {
  font-family: ui-monospace, Consolas, monospace;
  font-size: 0.75rem;
  font-weight: 700;
  color: #f2f2f0;
  background: #0e100e;
  border: 1px solid #2c302c;
  border-radius: 4px;
  padding: 0.15rem 0.5rem;
  margin-left: 0.5rem;
  display: inline-block;
  min-width: 2.6rem;
  text-align: center;
  vertical-align: middle;
}

/* Voice A's own 303 knobs -- same &lt;input type="range"&gt;, same id, same
   range, same event listener; only a richer thumb gradient/size so it reads
   as a real hardware knob rather than a plain slider handle. */
#synth-screen .synth-chassis .synth-303-knob::-webkit-slider-thumb {
  width: 1.3rem; height: 1.3rem;
  background: radial-gradient(circle at 35% 30%, #f2e6c2, #d9a856 55%, #8a6a28);
  border: 1px solid #4a3d18;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
#synth-screen .synth-chassis .synth-303-knob::-moz-range-thumb {
  width: 1.3rem; height: 1.3rem;
  background: radial-gradient(circle at 35% 30%, #f2e6c2, #d9a856 55%, #8a6a28);
  border: 1px solid #4a3d18;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
}

/* Voice A's own Mute/Solo (inside its own full mixer group) get the same
   small LED-dot treatment as the Mixer strips below, at full instrument
   scale -- scoped under .synth-chassis so Voice B/C/D's own identical
   buttons are completely unaffected. */
#synth-screen .synth-chassis .synth-mute-btn,
#synth-screen .synth-chassis .synth-solo-btn { position: relative; padding-left: 1.15rem; }
#synth-screen .synth-chassis .synth-mute-btn::before,
#synth-screen .synth-chassis .synth-solo-btn::before {
  content: ""; position: absolute; left: 0.35rem; top: 50%; transform: translateY(-50%);
  width: 6px; height: 6px; border-radius: 50%; background: #3a1515;
}
#synth-screen .synth-chassis .synth-mute-btn.active::before { background: #ff4d4d; box-shadow: 0 0 6px #ff4d4d; }
#synth-screen .synth-chassis .synth-solo-btn.active::before { background: #ffb020; box-shadow: 0 0 6px #ffb020; }

/* Master -- the Limiter box and the scope/meter read as one bordered
   "output stage" bezel (the .synth-chassis-group wrapper added in
   index.html), with the Limiter itself styled as a small module inside it.
   No change to limiter/audio behaviour -- purely the container/backdrop. */
#synth-screen .synth-master-chassis .synth-rack-module {
  background: rgba(0, 0, 0, 0.18);
  border: 1px solid #3a3f3a;
}
#synth-screen .synth-master-chassis .synth-rack-module-header strong {
  color: #d9a856;
  text-transform: uppercase;
  font-size: 0.7rem;
  letter-spacing: 0.06em;
}
#synth-screen .synth-master-chassis .synth-scope {
  background: #05100a;
  border: 1px solid #2c302c;
  box-shadow: inset 0 0 12px rgba(0, 0, 0, 0.6);
}
#synth-screen .synth-master-chassis .synth-meter {
  border: 1px solid #2c302c;
  background: #0e100e;
}

/* Mixer -- deliberately reduced-scale "overview/control-surface" restyle.
   Height is unchanged (no new rows, no larger strips) -- this only adds
   depth/typography/LED state to the exact same compact strips that already
   exist, so it reads as a summary, not a fourth full instrument module. */
#synth-mixer-strips .synth-mixer-strip {
  background: linear-gradient(180deg, #262b26, #1c1f1c);
  border: 1px solid #05060a;
  border-radius: 8px;
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
#synth-mixer-strips .synth-mixer-strip strong {
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #d9a856;
}
#synth-mixer-strips .synth-mute-btn,
#synth-mixer-strips .synth-solo-btn { position: relative; padding-left: 1.1rem; }
#synth-mixer-strips .synth-mute-btn::before,
#synth-mixer-strips .synth-solo-btn::before {
  content: ""; position: absolute; left: 0.3rem; top: 50%; transform: translateY(-50%);
  width: 6px; height: 6px; border-radius: 50%; background: #3a1515;
}
#synth-mixer-strips .synth-mute-btn.active::before { background: #ff4d4d; box-shadow: 0 0 6px #ff4d4d; }
#synth-mixer-strips .synth-solo-btn.active::before { background: #ffb020; box-shadow: 0 0 6px #ffb020; }
/* Clear Open/detail affordance -- same real jump-to-section behaviour
   (app.js), just given the family's gold accent instead of the site's
   generic green, so it visually reads as "this strip's own doorway out". */
#synth-mixer-strips .synth-mixer-jump-btn {
  border-color: #d9a856;
  color: #d9a856;
}
#synth-mixer-strips .synth-mixer-jump-btn:hover { background: rgba(217, 168, 86, 0.16); }
/* ========================================================================== */

/* Real quick-links row (2026-09-05) -- same real pill shape as .tab/
   .synth-style-chip, same real dark-panel contrast fix applied up front
   this time (the exact bug the chips/jump buttons above caught earlier). */
.synth-quick-links { display: flex; gap: 0.4rem; flex-wrap: wrap; }
/* Real fix (2026-09-05, founder-directed: "make those links persistant so
   when you scroll to and item they hover out at the top") -- sticks just
   below the real .topbar (itself sticky at top: 3.2rem, ~3.2rem tall), so
   this stays reachable while scrolling a long page without ever covering
   the topbar's own Save/Load/Export controls above it. */
.synth-quick-links-block {
  position: sticky;
  top: 6.4rem;
  z-index: 39;
}
/* Real fix (2026-09-05, founder-reported: a real mouse click on the Play
   Song/Loop Sequence toggle wasn't registering) -- root-caused live: the
   sticky topbar + sticky quick-links bar together occlude a real, sizeable
   band at the top of the viewport (confirmed ~246px in the founder's own
   viewport), and any scrollIntoView-style scroll that lands a target
   section right at the viewport top puts it BEHIND that sticky band --
   genuinely unclickable there, not just visually covered. scroll-margin-top
   tells the browser's own real scroll-into-view behavior (scrollIntoView,
   anchor jumps, Tab-focus scrolling) to stop far enough down that every
   real section actually clears the sticky stack, on every unit's own
   section, not just the one this was first caught on. */
/* Real fix, corrected: scroll-margin-top only affects the SPECIFIC element
   a scroll actually targets, not its parent -- every real jump/quick-link
   in this app scrolls to a specific heading or inner element (an <h3>, a
   grid div, a mixer div), never the outer .community-block itself, so the
   first version of this fix (scoped to .community-block) had no real
   effect. Applied broadly to every real element in #synth-screen instead
   of hand-listing every current and future jump target's own id. */
#synth-screen * { scroll-margin-top: 16rem; }
#synth-screen .community-block button.synth-quick-link-btn {
  background: transparent;
  color: #d7e3d7;
  border: 1px solid #3d423d;
  border-radius: 20px;
  padding: 0.3rem 0.75rem;
  font-size: 0.8rem;
  cursor: pointer;
}
#synth-screen .community-block button.synth-quick-link-btn:hover { border-color: #7fd992; color: #7fd992; }
#synth-screen .community-block button.synth-quick-link-btn.active { border-color: #7fd992; background: #2f4a34; color: #7fd992; font-weight: 600; }
/* Real "Enable effects" toggle (2026-09-05, founder-directed: "there should
   be an enable efftcs button on their respected track on the mixer", then
   "a button doing the same here on the right of the effcrts big button
   saying eneable effcts") -- same real dark-panel-safe pill styling as the
   quick-link buttons above, on/off states clearly distinct (red-tinted off,
   green-tinted on) since this is a real hard bypass, not a subtle setting. */
#synth-screen .community-block button.synth-voice-fx-toggle-btn {
  background: #3a2323; color: #e0a0a0; border: 1px solid #5a3232;
  border-radius: 20px; padding: 0.3rem 0.75rem; font-size: 0.8rem; cursor: pointer;
}
#synth-screen .community-block button.synth-voice-fx-toggle-btn.active { background: #2f4a34; color: #7fd992; border-color: #7fd992; font-weight: 600; }
#synth-screen .community-block button.synth-voice-fx-toggle-btn-panel { margin-left: auto; }
/* Real "Snap record" (2026-09-05, founder-directed: "record its midi to a
   sequencer track... punch in punch out") -- a real, obvious recording
   indicator (red) while active, same real convention hardware/DAW record
   buttons use. */
#synth-screen .community-block button#synth-seq-record-btn.active { background: #b3261e; color: #fff; }
/* Real compact transport (2026-09-05, founder-directed: "create one of
   these on the quick link row to the right of the title") -- small,
   secondary next to the "Quick links" heading text, not competing with it. */
.synth-transport-mini { display: inline-flex; gap: 0.3rem; margin-left: 0.75rem; text-transform: none; letter-spacing: normal; font-weight: normal; }
#synth-screen .community-block .synth-transport-mini button {
  background: #232823; color: #d7e3d7; border: 1px solid #3d423d; border-radius: var(--radius-sm);
  padding: 0.2rem 0.45rem; font-size: 0.75rem; cursor: pointer;
}
#synth-screen .community-block .synth-transport-mini button:hover { border-color: #7fd992; color: #7fd992; }
#synth-screen .community-block .synth-transport-mini button#synth-transport-mini-play-btn.active { background: #2f4a34; color: #7fd992; border-color: #7fd992; }
#synth-screen .community-block .synth-transport-mini button#synth-transport-mini-record-btn.active { background: #b3261e; color: #fff; border-color: #b3261e; }
/* Real Song-position strip (2026-09-05) -- same real dark-panel-safe
   button contrast as every other new Synth control this session. Real fix
   (2026-09-05, founder-directed: "the song chain button should be the 1-4
   parts") -- each step is now a real small group (index + one button per
   real unit: Voice A/B, Drum 808/909), not one single button. */
/* Real fix (2026-09-05, founder-reported: "get rid of ths scroll bar and
   wrap two banks at a time") -- the horizontally-scrolling filmstrip
   (added for "can we have it scrolling through these") turned out to be
   the wrong real fix for that ask -- a real 2-column grid wraps every step
   onto its own row pair instead, no scrollbar at all. The currently-playing
   step still auto-scrolls the PAGE to keep it in view (see
   renderSynthSongStrip) since there's now real vertical growth instead of
   horizontal. */
.synth-song-strip { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; margin-bottom: 0.6rem; }
.synth-song-strip-controls { grid-column: 1 / -1; }
.synth-song-strip-block {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.25rem;
  padding: 0.25rem 0.4rem;
  border: 1px solid #3d423d;
  border-radius: var(--radius-sm);
  background: #1c1f1c;
  /* Real fix (2026-09-05, founder-directed: "click on these scene card and
     start play from them... seek") -- the card itself is now clickable. */
  cursor: pointer;
}
.synth-song-strip-block:hover { border-color: #7fd992; }
.synth-song-strip-block.active { border-color: #7fd992; }
.synth-song-strip-index { color: #9aa39a; font-size: 0.75rem; font-weight: 600; padding-right: 0.15rem; }
/* Real "start of a pattern block" indicator (2026-09-07, founder-directed:
   "we dont have any indication on the time line for the start of any
   pattern") -- a small real bar-count badge, always the block's own first
   child so it reads as sitting at the top/start of the card regardless of
   how the rest wraps. */
.synth-song-strip-start-bar {
  flex-basis: 100%;
  order: -1;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: #7fd992;
  border-radius: 3px;
  padding: 0.1rem 0.35rem;
  align-self: flex-start;
}
/* Real fix (2026-09-08, founder-directed: "the colour of these bar
   locators, they are not legible, maybe dark text will do") -- the same
   recurring dark-panel specificity bug as the sample-controls fix earlier
   this file: the plain `.synth-song-strip-start-bar` class (0,1,0) loses
   to the broader `#synth-screen .community-block span:not(.muted)` rule
   (1,2,1) regardless of source order, so this badge's own intended dark
   text (#1c2420) never actually applied -- it silently rendered in that
   rule's light grey-green instead, unreadable on the light green/white
   badge background. Same real `#synth-screen .community-block` prefix
   fix, scoped to just this real element. */
#synth-screen .community-block .synth-song-strip-block .synth-song-strip-start-bar { color: #1c2420; }
.synth-song-strip-block.active .synth-song-strip-start-bar { background: #fff; }
/* Real fix (2026-09-05, founder-reported: "we have not got the loop ammount
   or the add new bank on this section, should have all the song mode
   options including the drop out list") -- Add step / Loop controls and
   real per-unit <select> dropdowns, right in the Sequencer's own strip. */
.synth-song-strip-controls { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.4rem; flex-shrink: 0; }
#synth-screen .community-block button.synth-song-strip-add-btn {
  padding: 0.25rem 0.5rem;
  border: 1px solid #3d423d;
  border-radius: var(--radius-sm);
  background: #232823;
  color: #7fd992;
  font-size: 0.75rem;
  cursor: pointer;
}
#synth-screen .community-block button.synth-song-strip-add-btn:hover { border-color: #7fd992; }
.synth-song-strip-loop-label { display: flex; align-items: center; gap: 0.3rem; color: #d7e3d7; font-size: 0.75rem; }
#synth-screen .community-block .synth-song-strip select.synth-song-strip-select {
  padding: 0.2rem 0.3rem;
  border: 1px solid #3d423d;
  border-radius: var(--radius-sm);
  background: #232823;
  color: #d7e3d7;
  font-size: 0.7rem;
  cursor: pointer;
  max-width: 8.5rem;
}
#synth-screen .community-block .synth-song-strip select.synth-song-strip-select:hover { border-color: #7fd992; }
/* Real fix (2026-09-05, founder-reported: "the loop numbers cant be seen in
   day mode") -- this input has the exact same specificity (1,2,1) as the
   later, generic `#synth-screen .community-block input[type="number"] {
   color: var(--text) }` rule (line ~1047) that colors every plain number
   input in this panel to match the OUTER page theme -- in the site's light
   mode that resolves near-black, invisible against this panel's own
   always-dark background. Same recurring dark-panel contrast bug as the
   style chips earlier this session; fixed the same way, by qualifying the
   selector so it wins on specificity instead of source order. */
#synth-screen .community-block .synth-song-strip input.synth-song-strip-bars {
  width: 2.6rem;
  padding: 0.2rem 0.3rem;
  border: 1px solid #3d423d;
  border-radius: var(--radius-sm);
  background: #232823;
  color: #d7e3d7;
  font-size: 0.7rem;
}
/* Real tempo automation (2026-09-05, founder-directed: "tempo automation")
   -- same real styling as the Bars field just above, slightly wider for a
   3-digit BPM value plus its own placeholder text. */
#synth-screen .community-block .synth-song-strip input.synth-song-strip-bpm,
#synth-screen .community-block .synth-song-entry input.synth-song-strip-bpm {
  width: 3.4rem;
  padding: 0.2rem 0.3rem;
  border: 1px solid #3d423d;
  border-radius: var(--radius-sm);
  background: #232823;
  color: #d7e3d7;
  font-size: 0.7rem;
}
#synth-screen .community-block button.synth-song-strip-remove-btn {
  padding: 0.15rem 0.35rem;
  border: 1px solid #3d423d;
  border-radius: var(--radius-sm);
  background: #232823;
  color: #d7e3d7;
  font-size: 0.7rem;
  cursor: pointer;
}
#synth-screen .community-block button.synth-song-strip-remove-btn:hover { border-color: #e08a8a; color: #e08a8a; }
/* Real unified keyboard piano-roll (2026-09-05) -- a real vertical piano
   keyboard sidebar (sticky while the grid scrolls horizontally) + a real
   horizontal step grid, scrollable since the full real pitch range (5
   octaves) is much taller than one screen. */
.synth-piano-roll-wrap {
  max-height: 24rem;
  overflow: auto;
  border: 1px solid #3d423d;
  border-radius: var(--radius-sm);
  margin-top: 0.5rem;
}
.synth-piano-roll { display: flex; flex-direction: column; width: max-content; }
.synth-piano-roll-row { display: flex; }
.synth-piano-roll-key {
  width: 3.4rem;
  flex: 0 0 3.4rem;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 0.4rem;
  font-size: 0.65rem;
  cursor: pointer;
  border-bottom: 1px solid #3d423d;
  position: sticky;
  left: 0;
  z-index: 2;
}
.synth-piano-roll-key.white { background: #e9e9e9; color: #1c2430; }
.synth-piano-roll-key.black { background: #232823; color: #d7e3d7; }
.synth-piano-roll-key:hover { outline: 2px solid #7fd992; outline-offset: -2px; }
.synth-piano-roll-cell {
  width: 1.7rem;
  height: 1.35rem;
  flex: 0 0 1.7rem;
  border-right: 1px solid #2a2f2a;
  border-bottom: 1px solid #2a2f2a;
  background: #1c1f1c;
  cursor: pointer;
}
.synth-piano-roll-cell.beat-start { border-left: 2px solid #3d423d; }
.synth-piano-roll-cell.active { background: #7fd992; }
.synth-piano-roll-cell:hover:not(.active) { background: #2a2f2a; }
/* Real right-click context menu (2026-09-05) -- a real fixed-position
   dropdown of stacked buttons, same real dark-panel-safe contrast as
   every other new Synth control this session. */
.synth-piano-roll-context-menu {
  position: fixed;
  z-index: 500;
  display: flex;
  flex-direction: column;
  background: #232823;
  border: 1px solid #3d423d;
  border-radius: var(--radius-sm);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
  overflow: hidden;
  min-width: 9rem;
}
.synth-piano-roll-context-menu button {
  background: transparent;
  color: #d7e3d7;
  border: none;
  border-bottom: 1px solid #3d423d;
  padding: 0.45rem 0.75rem;
  font-size: 0.8rem;
  text-align: left;
  cursor: pointer;
}
.synth-piano-roll-context-menu button:last-child { border-bottom: none; }
.synth-piano-roll-context-menu button:hover { background: #2f4a34; color: #7fd992; }
.synth-piano-roll-context-menu button:disabled { opacity: 0.35; cursor: default; }
.synth-piano-roll-context-menu button:disabled:hover { background: transparent; color: #d7e3d7; }
.synth-keyboard { display: flex; gap: 0.25rem; margin-top: 0.75rem; flex-wrap: wrap; }
.synth-key {
  padding: 1rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  font-size: 0.8rem;
  min-width: 2.4rem;
}
.synth-key:active { background: var(--accent); color: #fff; }
.synth-key-sharp { background: var(--panel-bg, #333); color: #eee; }
.synth-key-active { background: var(--accent); color: #fff; }
.synth-rack { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.5rem; }
.synth-rack-module {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.6rem 0.75rem;
  background: var(--surface);
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.synth-rack-module-header { display: flex; align-items: center; gap: 0.5rem; }
.synth-rack-module-header strong { flex: 1; }
.synth-rack-module-header button {
  border: 1px solid var(--border);
  background: var(--panel-bg, transparent);
  border-radius: var(--radius-sm);
  padding: 0.1rem 0.5rem;
  cursor: pointer;
}
.synth-rack-module-header button:disabled { opacity: 0.3; cursor: default; }
.synth-rack-module label { display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; }
/* Real fix (2026-08-31, founder: "fix the track 16 from wrappin round and
   have it a fixed width") -- was wrapping onto a second row whenever the
   16 real steps didn't quite fit; now a real fixed-width, non-wrapping row
   with its own horizontal scroll so step 16 always stays on the same real
   line as step 1, never pushed underneath. */
/* Real fix (2026-08-31, founder: "again this is not what I asked for...
   undo your last steps") -- same real root cause as the pattern block's
   own leftover margin: this margin-top was for when this sat under other
   content, before it became the first (only) child of its own bordered
   box. Removed so it sits flush against the border, matching the pattern
   block on the other side exactly. */
.synth-seq-steps { display: flex; gap: 0.3rem; flex-wrap: nowrap; overflow-x: auto; }
/* Real fix (2026-09-05, founder-reported, marked with a real screenshot
   circling the gap: "close up this space in the rear div for the synth
   steps") -- .synth-unit-box is a plain block div, so it stretched to fill
   the whole real flex-basis .synth-unit-layout-right gives it, leaving a
   real empty band to the right whenever the 16 step cards (fixed width)
   didn't need the full available width. Shrink-wrapping this specific box
   to its own content (not touched globally -- the drum grid's own
   .synth-unit-box variant genuinely wants to fill the row for its wider
   real content) makes the border hug the actual step cards instead. */
.synth-seq-steps-box { display: inline-flex; max-width: 100%; }
.synth-seq-step {
  display: flex; flex-direction: column; align-items: center; gap: 0.2rem;
  border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 0.4rem 0.3rem;
  background: var(--surface); font-size: 0.7rem; width: 3.4rem; flex-shrink: 0;
}
.synth-seq-step.synth-seq-step-current { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent) inset; }
/* Real 4/4 beat markers (2026-09-01, founder-requested: "on the drum
   sequencers and the synths can you add on bar 1,5,9,13 a slight green all
   the way down these columns to signify the 4/4 beat position, only very
   light green", then several rounds of real colour feedback, then: "on the
   synths you can go transparent or darker they dont look right" -- a real
   solid fill read wrong here specifically because most of a synth step
   card's own background is hidden behind its child selects/buttons (unlike
   a drum step button, which IS the whole visible surface) -- a real
   translucent tint back instead, tuned to a real visible-but-not-too-light
   opacity this time, not the original 0.05 that was "too light." */
#synth-seq-steps > .synth-seq-step:nth-child(4n+1),
#synth-seq-steps-b > .synth-seq-step:nth-child(4n+1) {
  background: rgba(111, 191, 115, 0.3);
}
.synth-seq-step select { width: 100%; font-size: 0.7rem; }
.synth-seq-step button { width: 100%; font-size: 0.65rem; padding: 0.15rem; border-radius: 4px; border: 1px solid var(--border); background: var(--panel-bg, transparent); cursor: pointer; }
.synth-seq-step button.active { background: var(--accent); color: #fff; }
.synth-seq-step .synth-seq-step-on { width: 100%; height: 1.4rem; font-size: 0.8rem; }
.synth-seq-step .synth-seq-step-accent.active { background: #d9822b; }
.synth-seq-step .synth-seq-step-slide.active { background: #3b7fd9; }
/* Real fix (2026-08-31, founder-requested): a real visual divider between
   Voice A/Voice B/Drum-808/Drum-909 inside the shared Sequencer block, now
   that each unit's own intro paragraph was removed. */
.synth-unit-divider { border: none; border-top: 1px solid var(--border); margin: 1.4rem 0; }

/* Real automation-lane upgrade (2026-08-31) -- replaces the old single
   in-card cutoff badge with real dedicated lane rows, shared by both synth
   voices and both drum units. */
.synth-automation-lanes { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.6rem; }
.synth-automation-lanes button.active { background: #d9482b; color: #fff; }
/* Real fix (2026-09-05, founder-directed: "if we can reduce the size of the
   automation by adding something like that in the image, also be ablt to
   deit with a pencil") -- replaces the old one-full-row-per-lane badge
   stack (which only grew taller as automation coverage expanded this
   session) with one real shared compact graph: a small clickable legend
   (also the real pencil-tool lane picker) plus a single SVG area holding
   one coloured line per lane. */
.synth-automation-legend { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.4rem; }
#synth-screen .community-block button.synth-automation-legend-chip {
  display: flex; align-items: center; gap: 0.35rem;
  background: #232823; color: #9aa39a; border: 1px solid #3d423d; border-radius: 20px;
  padding: 0.2rem 0.55rem 0.2rem 0.35rem; font-size: 0.7rem; cursor: pointer;
}
#synth-screen .community-block button.synth-automation-legend-chip::before {
  content: ""; display: inline-block; width: 0.55rem; height: 0.55rem; border-radius: 50%;
  background: var(--lane-color, #7fd992); flex-shrink: 0;
}
#synth-screen .community-block button.synth-automation-legend-chip.active { color: #fff; border-color: var(--lane-color, #7fd992); background: #2a2f2a; }
.synth-automation-graph-wrap {
  margin-top: 0.4rem; height: 4.5rem; border: 1px solid #3d423d; border-radius: var(--radius-sm);
  background: rgba(255,255,255,0.03); overflow: hidden;
}
.synth-automation-graph { width: 100%; height: 100%; display: block; cursor: crosshair; }
.synth-automation-graph polyline { stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; opacity: 0.9; }

/* Real native drum machine module (2026-08-31) -- Stage 2 of the ReBirth-inspired dual-303 build. */
.synth-drum-row { display: flex; align-items: center; gap: 0.3rem; margin-top: 0.4rem; }
/* Real fix (2026-08-31, founder: "again this is not what I asked for...
   undo your last steps") -- same real root cause found on the drum units:
   the FIRST row's own margin-top (needed for spacing between the other 10
   rows) was still pushing it down from the bordered box's top, since it's
   now the first child inside that box. Zeroed for the first row only --
   every other row keeps its real spacing. */
.synth-drum-row:first-child { margin-top: 0; }
.synth-drum-row-label { width: 3.2rem; font-size: 0.7rem; font-weight: 600; flex-shrink: 0; }
.synth-drum-step {
  width: 1.8rem; height: 1.8rem; border-radius: 4px; border: 1px solid var(--border);
  background: var(--panel-bg, transparent); color: var(--text); font-size: 0.6rem; cursor: pointer;
}
/* Real 4/4 beat markers (2026-09-01) -- same real real steps 1/5/9/13, all
   the way down every real instrument row, not just one. Each row's 16 real
   step buttons are its only direct <button> children, so :nth-of-type(button)
   lands correctly on steps 1/5/9/13 regardless of the mixer/sample controls
   earlier in the same row. */
/* Real fix: drum step buttons carry their own dark text directly on a real
   opaque panel-colour background (unlike the synth step cards above, whose
   text lives in child buttons, not the card itself) -- a low-alpha overlay
   here barely tinted the dark page behind it, making the number unreadable.
   A real solid, pale green-off-white replaces the panel colour outright,
   keeping the same dark text legible. Real fix (founder: "make them
   slightly darker please, we went to far light") -- one shade darker,
   #dcefd6, matching the synth step cards' own rule above exactly. */
.synth-drum-row > button.synth-drum-step:nth-of-type(4n+1):not(.active) {
  background: #dcefd6;
}
.synth-drum-step.active { background: var(--accent); color: #fff; }
.synth-drum-step.accent { box-shadow: 0 0 0 2px #d9822b inset; }
.synth-drum-step.synth-seq-step-current { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
.synth-drum-step.accent.synth-seq-step-current { box-shadow: 0 0 0 2px var(--accent), 0 0 0 4px #d9822b; }

/* Real per-instrument mixer strip (2026-08-31) -- Volume/Pan/PCF/Distortion per drum voice. */
.synth-drum-mixer { display: flex; align-items: center; gap: 0.25rem; flex-shrink: 0; }
.synth-drum-mixer label { display: flex; flex-direction: column; align-items: center; font-size: 0.55rem; gap: 0.05rem; }
.synth-drum-mixer input[type="range"] { width: 3rem; height: 0.8rem; }
.synth-drum-mixer select { font-size: 0.55rem; max-width: 6rem; }
.synth-drum-dist-btn {
  font-size: 0.55rem; padding: 0.1rem 0.3rem; border-radius: 4px; border: 1px solid var(--border);
  background: var(--panel-bg, transparent); color: var(--text); cursor: pointer;
}
.synth-drum-dist-btn.active { background: #d9482b; color: #fff; }

/* Real per-voice WAV sample override + kit export/import (2026-08-31). */
.synth-drum-sample-controls { display: flex; align-items: center; gap: 0.25rem; flex-shrink: 0; margin-right: 0.4rem; }
/* Real fix (2026-09-05) -- same recurring dark-panel specificity bug as the
   Song-strip/style-chip fixes this session: the plain class selectors below
   (0 id, 2 classes) lose to `#synth-screen .community-block button { color:
   var(--text) }` (1 id) regardless of source order, so this row's own text
   went near-invisible in the site's light theme. Fixed the same way -- an
   `#synth-screen .community-block` prefix so these win on specificity. */
#synth-screen .community-block .synth-drum-sample-controls .sample-btn,
#synth-screen .community-block .synth-drum-sample-controls .sample-reset-btn {
  font-size: 0.55rem; padding: 0.1rem 0.3rem; border-radius: 4px; border: 1px solid #3d423d;
  background: #232823; color: #d7e3d7; cursor: pointer; white-space: nowrap;
}
/* Real fix (2026-09-08, founder-directed: "this exposes something we
   chased and got into confusion with earlier... lets have it just
   highlight the load sample area with a colour... add the highlight to
   all of the tracks including the 909, so this doesnt happen at all") --
   replaces the old variable-width .sample-name text label (any real
   filename pushed that one row's whole step grid out of column alignment
   with every other row's). A fixed-width colour highlight on the button
   itself can never do that, regardless of filename length -- the real name
   is still there as a hover tooltip (title), not inline layout content. */
#synth-screen .community-block .synth-drum-sample-controls .sample-btn.sample-loaded {
  background: #2f6b4f; border-color: #3e8862; color: #fff;
}
.synth-drum-kit-io { display: flex; flex: 1 1 100%; align-items: center; gap: 0.6rem; margin-top: 0.6rem; }
/* Real fix (2026-09-05, founder-reported: "I dont want this to appear here
   when a bank is selected") -- this status line's text (e.g. `Loaded
   "Long".`) had no width constraint at all, so a long message could sprawl
   sideways into the pattern-bank's own slot buttons if the narrow column's
   flex-wrap ever kicked in. Constrained to the same real 11rem the picker
   select already uses, with a genuine wrap instead of overflow -- the full
   real message is still available via its own title attribute (hover). */
#synth-drum-808-kit-io-status, #synth-drum-909-kit-io-status { max-width: 11rem; overflow-wrap: break-word; }
/* Real fix (2026-09-01, founder-reported: "bring bothe export kits from
   both drum machine to the left under the pattern box") -- now lives in the
   narrow 11rem pattern-bank column, where the old horizontal row wouldn't
   fit two buttons plus a status line. */
.synth-unit-layout-left .synth-drum-kit-io { flex-direction: column; align-items: flex-start; gap: 0.3rem; }
/* Real multi-voice kit picker (2026-09-05) -- sits in the same narrow
   left-hand kit-io column, above Export/Import kit. `flex: 1 1 100%` is
   applied from the start this time (a single-voice version of this control
   was previously found sitting inline with the pattern-bank's own
   slot-number buttons at narrow widths, since `.synth-unit-layout-left`
   switches to `flex-direction: row; flex-wrap: wrap` below 900px -- see
   that rule above -- fixed there by forcing every full-width child onto
   its own row regardless of the parent's current flex-direction). Hidden
   via JS (synthDrumRenderKitPicker) entirely when no kit is registered for
   a given unit. */
.synth-drum-kit-bank-picker { flex: 1 1 100%; margin-top: 0.4rem; }
.synth-unit-layout-left .synth-drum-kit-bank-picker { width: 100%; }
#synth-screen .community-block select#synth-drum-808-kit-picker-select,
#synth-screen .community-block select#synth-drum-909-kit-picker-select {
  font-size: 0.7rem; padding: 0.2rem 0.3rem; border-radius: var(--radius-sm); border: 1px solid #3d423d;
  background: #232823; color: #d7e3d7; cursor: pointer; width: 100%; max-width: 11rem;
}

/* Real 8-bank x 4-pattern memory (2026-08-31) -- one per real unit (Voice A, Voice B, Drum-808, Drum-909).
   Real fix (2026-08-31, founder: "again this is not what I asked for...
   undo your last steps") -- found the actual root cause instead of
   reverting blind: this real margin-top was left over from before the
   bordered-box restructure, when this was the first child under a heading
   with natural spacing above it. Now it's the first (only) child of a
   zero-padding box on all four real units, so this stray margin alone was
   creating the exact gap the padding removal didn't touch. */
.synth-pattern-bank { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
/* Real fix (2026-08-31, founder: "insert an inner border like the ones on
   the right" -- confirmed via a quick clarifying question: the whole
   pattern block gets ONE real inner card border, nested inside the outer
   .synth-unit-box, matching the exact same border-radius/padding/
   background the real step cards on the right already use, so both sides
   carry the same real spacing/alignment weight. Founder: "just on the
   synths... change its colour to the back colour background so its
   invisible" -- scoped to Voice A/B only (the drum units already read
   correctly, left untouched). Real bug found along the way: `var(--surface)`
   is genuinely undefined anywhere in this stylesheet (`--surface-2` is the
   real one) -- using it as the border-color made the whole shorthand
   invalid, silently dropping the border to `none` entirely rather than
   just being the wrong colour. Using `transparent` instead: it's a real,
   always-valid colour that's genuinely invisible against anything behind
   it, which is what "invisible" actually needs, not a guess at matching a
   specific background. */
#synth-seq-a-pattern-bank, #synth-seq-b-pattern-bank {
  border: 1px solid transparent; border-radius: var(--radius-sm);
  padding: 0.4rem 0.3rem;
}
.synth-pattern-bank-row { display: flex; gap: 0.2rem; }
.synth-pattern-bank-btn {
  width: 1.6rem; height: 1.6rem; border-radius: 4px; border: 1px solid var(--border);
  background: var(--panel-bg, transparent); color: var(--text); font-size: 0.6rem; cursor: pointer;
}
.synth-pattern-bank-btn.active { background: var(--accent); color: #fff; }
.synth-pattern-bank-btn.has-pattern:not(.active) { box-shadow: 0 0 0 2px #d9822b inset; }
/* Real bar-quantized pending switch (2026-09-05) -- a real, visible "this
   takes over at the next bar" indicator, distinct from .active (what's
   playing now) and .has-pattern (what's saved). A gentle real pulse so
   it reads as "about to happen," not just a static color. */
.synth-pattern-bank-btn.pending { outline: 2px dashed #7fd992; outline-offset: 1px; animation: synth-pattern-bank-pending-pulse 1s ease-in-out infinite; }
@keyframes synth-pattern-bank-pending-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
@media (prefers-reduced-motion: reduce) {
  .synth-pattern-bank-btn.pending { animation: none; }
}
.synth-pattern-bank-label { font-size: 0.65rem; color: var(--muted); font-weight: 600; }
/* Real Mute/Solo (2026-09-01) -- same compact toggle-button convention as
   the pattern-bank slot buttons above, red for Mute and yellow for Solo
   (a real, standard mixing-console colour convention), so both read at a
   glance against the ReBirth Machine's own dark rack panels. */
.synth-mute-solo { display: inline-flex; gap: 0.3rem; margin-left: 0.5rem; }
.synth-mute-btn, .synth-solo-btn {
  padding: 0.15rem 0.5rem; border-radius: 4px; border: 1px solid var(--border);
  background: var(--panel-bg, transparent); color: var(--text); font-size: 0.65rem; cursor: pointer;
}
.synth-mute-btn.active { background: #c0392b; border-color: #c0392b; color: #fff; }
.synth-solo-btn.active { background: #d9a91b; border-color: #d9a91b; color: #1c2430; }
.synth-pattern-bank-steps { display: flex; align-items: center; gap: 0.3rem; font-size: 0.65rem; color: var(--muted); }
.synth-pattern-bank-steps input { width: 3rem; }

/* Real per-pattern step length (2026-08-31) -- steps beyond the active
   length stay real and editable, just visually dimmed. */
.synth-seq-step-inactive, .synth-drum-step.synth-seq-step-inactive { opacity: 0.35; }

/* Real Song mode (2026-08-31) -- chains each unit's own pattern-bank slots into an ordered sequence. */
.synth-song-entries { display: flex; flex-direction: column; gap: 0.35rem; margin-bottom: 0.6rem; }
.synth-song-entry { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; padding: 0.3rem; border: 1px solid var(--border); border-radius: var(--radius-sm); cursor: pointer; }
#synth-screen .community-block .synth-song-entry:hover { border-color: #7fd992; }
.synth-song-entry.active { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.synth-song-entry-index { font-weight: 600; width: 1.4rem; text-align: center; flex-shrink: 0; }
/* Real "start of a pattern block" indicator (2026-09-07) -- same real
   cumulative-bar badge as the compact strip's own .synth-song-strip-start-bar,
   inline here since this row is already a real horizontal layout. */
.synth-song-entry-start-bar {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: #7fd992;
  border-radius: 3px;
  padding: 0.1rem 0.35rem;
  flex-shrink: 0;
}
/* Real fix (2026-09-08, founder-directed: "the colour of these bar
   locators, they are not legible, maybe dark text will do") -- same real
   specificity bug/fix as .synth-song-strip-start-bar above. First attempt
   (`#synth-screen .community-block .synth-song-entry-start-bar`, specificity
   1-2-0) still LOST to the culprit `#synth-screen .community-block
   span:not(.muted)` (1-2-1) -- tied at the class tier, the culprit's own
   `span` type selector broke the tie in its favour. Included the real
   `.synth-song-entry` ancestor as a genuine third class-level qualifier
   (1-3-0) to win outright, not just tie on source order. */
#synth-screen .community-block .synth-song-entry .synth-song-entry-start-bar { color: #1c2420; }
.synth-song-entry.active .synth-song-entry-start-bar { background: #fff; }
.synth-song-entry select { font-size: 0.75rem; }
.synth-song-entry input[type="number"] { width: 3.2rem; }

/* Real pattern-editing tools (2026-08-31) -- Transpose/Shift/Copy/Paste/Randomize/Alter, per unit. */
.synth-pattern-tools { display: flex; flex-wrap: wrap; gap: 0.3rem; margin: 0.3rem 0 0.6rem; }
.synth-pattern-tool-btn {
  font-size: 0.65rem; padding: 0.15rem 0.4rem; border-radius: 4px; border: 1px solid var(--border);
  background: var(--panel-bg, transparent); color: var(--text); cursor: pointer;
}
.synth-pattern-tool-btn:disabled { opacity: 0.35; cursor: default; }

/* Real independent per-voice 303-style controls (2026-08-31, founder:
   "bring them into the unit... look like the 303 style Roland with the
   buttons that correspond to the cutoff rez envelope etc"). Compact,
   round-thumbed sliders standing in for real rotary knobs -- functional
   knob-style controls, not a claim to a full drag-to-rotate widget. */
.synth-303-controls { display: flex; gap: 1rem; margin: 0.5rem 0; padding: 0.5rem 0.75rem; background: rgba(0,0,0,0.15); border-radius: var(--radius-sm); flex-wrap: wrap; }
.synth-303-knob-wrap { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.05em; }
.synth-303-knob { width: 4.5rem; }
.synth-303-knob::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 1rem; height: 1rem; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #e8dfc4, #b89b52 60%, #7a6428);
  border: 1px solid #4a3d18;
  box-shadow: 0 1px 2px rgba(0,0,0,0.5);
  cursor: pointer;
}
.synth-303-knob::-moz-range-thumb {
  width: 1rem; height: 1rem; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #e8dfc4, #b89b52 60%, #7a6428);
  border: 1px solid #4a3d18;
  cursor: pointer;
}

/* Real shared per-unit submix FX (2026-08-31) -- Distortion/PCF/Delay/Compression, one block per unit. */
.synth-unit-fx { display: flex; flex-wrap: wrap; gap: 0.75rem; margin: 0.5rem 0; }
.synth-unit-fx-group {
  display: flex; flex-direction: column; gap: 0.3rem;
  padding: 0.5rem 0.75rem; border: 1px solid #3d423d; border-radius: var(--radius-sm);
  background: rgba(0,0,0,0.15); min-width: 10rem;
}
.synth-unit-fx-group strong { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; }
.synth-unit-fx-group label { display: flex; align-items: center; justify-content: space-between; gap: 0.4rem; font-size: 0.7rem; }
.synth-unit-fx-group input[type="range"] { width: 6rem; }

/* Real ReBirth-style layout (2026-08-31, founder shared a real ReBirth
   screenshot: "move the synth units over to the right and stack the
   pattern select buttons on the left the same as rebirth has them") -- a
   real two-column split per unit: pattern bank + pattern tools stacked in
   a narrow left column, everything else (oscillator/knobs/mixer/steps) in
   the wider right column, matching real ReBirth's own real layout. */
.synth-unit-layout { display: flex; gap: 1.1rem; align-items: flex-start; margin: 0.5rem 0; }
/* Real fix (2026-08-31, founder: "can we have a border around the pattern
   block and align them to the sequencer rows" -- then, correctly: "align
   the borders to each other at the top") -- a matching real bordered box
   on BOTH sides (pattern block AND the step sequencer/drum grid), not a
   border on one side only with a padding guess to line up its content
   against the other's unboxed edge. Both boxes share the exact same
   border/padding via .synth-unit-box, so with align-items:flex-start on
   the shared flex row, their top borders align exactly.
   Real fix (2026-08-31, founder: "put the padding back... theres no
   padding there also please put it back") -- the real misalignment was
   never the box's own padding, it was stray margin-top left on
   .synth-pattern-bank/.synth-seq-steps/.synth-drum-row (fixed separately,
   see those rules below) -- now that the real cause is gone, real padding
   is safe to restore on both sides equally without reintroducing the
   original offset, since both boxes get the exact same inset. */
.synth-unit-box {
  border: 1px solid #3d423d; border-radius: var(--radius-sm);
  background: rgba(0,0,0,0.15); padding: 0.6rem;
}
.synth-unit-layout-left { flex: 0 0 11rem; display: flex; flex-direction: column; gap: 0.5rem; }
.synth-unit-layout-left .synth-pattern-bank { flex-direction: column; align-items: flex-start; }
.synth-unit-layout-right { flex: 1 1 auto; min-width: 0; }
/* Real fix (2026-08-31, founder shared a real ReBirth screenshot: "place
   the transpose buttons along the bottom of the synth so everything lines
   up as in the rebirth picture") -- real ReBirth puts these under the
   keyboard/steps, spanning the unit's own width, not stacked with the
   pattern selector on the left. A real horizontal toolbar row, deliberately
   OUTSIDE the bordered box (below it), matching the pattern block's own
   tools staying separate. */
.synth-pattern-tools-bottom { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.6rem; }
/* Real fix (2026-09-05, founder-reported, with a screenshot marking the
   real misalignment with a red line: "pull the drum matrix to the left a
   few pixels to align with out edge of the pcf") -- this box's own real
   `.synth-unit-box` padding+border (0.6rem + 1px) insets its content from
   the outer edge, while the PCF/Distortion/Delay/Compression cards below
   it (`.synth-unit-fx`) have no such wrapping box and sit flush at the
   section's own left edge -- a real ~10.6px gap between the two, not a
   vague "a few pixels" guess. Cancelled with a matching negative margin so
   the grid's own content genuinely lines up with the PCF row beneath it. */
.synth-drum-grid-scroll { overflow-x: auto; margin-left: calc(-0.6rem - 1px); }
/* Real fix (2026-08-31, founder: "the pattern blocks need to come down to
   match horizontally the synth sequencer, not at the top" -- superseded
   the earlier margin-top offset hack. Oscillator/303-controls/Mixer moved
   above the two-column row entirely (see index.html), so the pattern bank
   and the real step sequencer now start at the same line with no CSS
   offset needed at all. */
@media (max-width: 900px) {
  .synth-unit-layout { flex-direction: column; }
  .synth-unit-layout-left { flex: 0 0 auto; flex-direction: row; flex-wrap: wrap; }
}

/* Real Volume/Pan mixer, made dramatically larger for each synth voice
   (2026-08-31, founder: "the level and pan need to be increased in size
   dramatically for each synth unit") -- only one instance per voice (not
   22 like the drum strips), so there's real room to make it prominent. */
/* Real fix (2026-09-01, founder-reported: "compression is missing from the
   synths also please dd to the same bar", then: "can we reduce the length
   of the slider so the five of them fit on the bar") -- narrower sliders
   and gap so all 5 (Volume/Pan/Send/Comp Ratio/Comp Threshold) genuinely
   fit on one real row again; flex-wrap stays as a safety net for a
   genuinely narrow viewport, not the normal case anymore. */
.synth-voice-mixer { display: flex; flex-wrap: wrap; gap: 1rem; align-items: flex-end; margin: 0.75rem 0 1rem; padding: 0.75rem 1rem; background: rgba(0,0,0,0.15); border: 1px solid #3d423d; border-radius: var(--radius-sm); }
.synth-voice-mixer label { display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.85rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; }
.synth-voice-mixer input[type="range"] { width: 9rem; height: 1.6rem; -webkit-appearance: none; appearance: none; background: transparent; }
.synth-voice-mixer input[type="range"]::-webkit-slider-runnable-track { height: 0.55rem; border-radius: 999px; background: linear-gradient(90deg, var(--accent), #3d423d); }
.synth-voice-mixer input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 1.6rem; height: 1.6rem; border-radius: 50%; margin-top: -0.5rem;
  background: radial-gradient(circle at 35% 30%, #e8dfc4, #b89b52 60%, #7a6428);
  border: 2px solid #4a3d18;
  box-shadow: 0 2px 4px rgba(0,0,0,0.5);
  cursor: pointer;
}
.synth-voice-mixer input[type="range"]::-moz-range-track { height: 0.55rem; border-radius: 999px; background: linear-gradient(90deg, var(--accent), #3d423d); }
.synth-voice-mixer input[type="range"]::-moz-range-thumb {
  width: 1.6rem; height: 1.6rem; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #e8dfc4, #b89b52 60%, #7a6428);
  border: 2px solid #4a3d18;
  cursor: pointer;
}

/* Real fix (2026-08-31, founder: "fix the track 16 from wrappin round and
   have it a fixed width you might have to make the page wider") -- the
   16-step row was wrapping onto a second line at real, normal viewport
   widths since the two-column pattern-bank layout narrowed its available
   space. A real fixed-width, non-wrapping row with its own horizontal
   scroll (never the page itself) plus a wider Synth-only container so it
   fits without scrolling at typical desktop widths. */
#synth-screen .project-list-main-wide { max-width: 1600px; }

/* Real rack-style UI (2026-08-31) -- real ReBirth's own dark brushed-metal
   rack aesthetic (Wikipedia-confirmed), applied to Synth's actual real
   modules (unit sections + effects rack), not a cosmetic-only pass over
   unrelated screens -- every selector here is scoped under #synth-screen. */
#synth-screen .community-block {
  background: linear-gradient(180deg, #2b2f2b 0%, #1c1f1c 100%);
  border: 1px solid #3d423d;
  border-radius: var(--radius-sm);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.05), 0 2px 6px rgba(0,0,0,0.35);
  padding: 1rem 1.1rem;
  margin-bottom: 0.9rem;
  /* Real fix (2026-08-31, found while widening the container): the dark
     panel only ever recolored h3/.muted -- every other real text element
     (row labels, mixer labels, song/pattern-tool button text, select
     options) kept the site's default near-black text colour and was
     genuinely unreadable against this dark background. One light default
     for everything in here, with more specific rules (buttons, inputs)
     still overriding it where they need their own real contrast. */
  color: #d7e3d7;
}
#synth-screen .community-block label,
#synth-screen .community-block span:not(.muted) {
  color: #d7e3d7;
}
#synth-screen .community-block button,
#synth-screen .community-block select,
#synth-screen .community-block input[type="text"],
#synth-screen .community-block input[type="number"] {
  color: var(--text);
}
#synth-screen .community-block h3 {
  color: #d7e3d7;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.85rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
#synth-screen .community-block h3::before {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: #7fd992;
  box-shadow: 0 0 4px 1px rgba(127,217,146,0.7);
  flex-shrink: 0;
}
/* Real fix (2026-09-05, founder-directed: "can we use this slightly
   lighter grey here please", pointing at the Mixer section's own muted
   description text vs the dimmer "Play or upload a reference sound" text
   above it) -- both already shared this one real rule, so the fix is
   genuinely lightening it, not just matching two already-equal values. */
#synth-screen .community-block .muted { color: #b4bdb4; }
/* ASK ARCHIE STUDIO -- SYNTH DOM CONTAINMENT CORRECTION (2026-09-26).
   Rack, Cross-patch, Song Builder, Keyboard, Keyboard filter & effects,
   Presets, Controller and the Music Lab teaser card were, until this pass,
   genuine body-level siblings of #synth-screen (a real stray closing tag --
   see index.html) -- so none of the dark ".community-block" chassis rules
   above ever reached them; they've always rendered as plain, unstyled
   cards. Now correctly nested, they WOULD start picking up that styling as
   a pure side effect of the containment fix -- but this pass is
   containment only, no visual redesign (Founder's own instruction), so
   .synth-pending-visual-pass restores their exact current plain look.

   Update, same day, Founder-directed ("follow on with the remaining in the
   same style"): Rack/Cross-patch/Song Builder/Keyboard/Keyboard filter &
   effects/Presets/Controller have now had that real visual pass -- see the
   "SYNTH GLOBAL CONTROLS HARDWARE PASS" block below -- and no longer carry
   this class in index.html. Music Lab is the one deliberate holdout: it's
   a teaser link to a separate, still-experimental page, not an instrument
   panel, so it was never in scope for the hardware conversion and keeps
   its plain look via this rule on purpose. */
#synth-screen .community-block.synth-pending-visual-pass {
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
  margin-bottom: 1.5rem;
  color: unset;
}
#synth-screen .community-block.synth-pending-visual-pass label,
#synth-screen .community-block.synth-pending-visual-pass span:not(.muted) {
  color: unset;
}
#synth-screen .community-block.synth-pending-visual-pass h3 {
  color: unset;
  text-transform: none;
  letter-spacing: normal;
  font-size: unset;
  display: block;
  gap: unset;
}
#synth-screen .community-block.synth-pending-visual-pass h3::before { content: none; }
#synth-screen .community-block.synth-pending-visual-pass .muted { color: var(--muted); }

/* ==========================================================================
   ASK ARCHIE STUDIO -- SYNTH GLOBAL CONTROLS HARDWARE PASS (2026-09-26,
   Founder-directed: "follow on with the remaining in the same style").
   Rack, Cross-patch, Song Builder, Keyboard, Keyboard filter & effects,
   Presets and Controller each already got the real silver panel (via the
   shared .synth-hw-instrument class) and recessed sub-panel (.synth-hw-group)
   automatically, same as every other chassis in the Synth -- no new CSS
   needed for that part. What's still missing, and genuinely new here, is
   restyling the plain native <select>/<input type="text">/<input
   type="range"> these simpler sections actually contain (they were never
   dense per-row lists like the Drum units, so they keep plain reskinned
   native controls rather than the compact-slider-plus-LCD-readout pattern
   or full ArchieKnobs -- a deliberately lower-touch pass matching these
   sections' lower functional risk). Scoped to "synth-global-hw-chassis"
   specifically (not the bare .synth-hw-instrument all chassis share) so
   Voice/Drum/Mixer/Sequencer's own already-tuned controls are never
   touched by this block. */
#synth-screen .synth-global-hw-chassis select,
#synth-screen .synth-global-hw-chassis input[type="text"],
#synth-screen .synth-global-hw-chassis input[type="number"] {
  width: auto;
  font-size: 0.85rem;
  padding: 0.35rem 0.5rem;
  border-radius: 5px;
  border: 1px solid #83868a;
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  color: #1c1d1e;
  margin-bottom: 0;
}
#synth-screen .synth-global-hw-chassis .synth-compose-row input[type="text"] { flex: 1; width: 100%; }
/* Song Builder's own per-entry Bars/BPM steppers -- kept genuinely small
   (real numeric steppers, not full-width text fields). */
#synth-screen .synth-global-hw-chassis .synth-song-entry input[type="number"] { width: 3.2rem; padding: 0.25rem 0.35rem; }
#synth-screen .synth-global-hw-chassis input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  width: 9rem;
  height: 1.1rem;
  vertical-align: middle;
}
#synth-screen .synth-global-hw-chassis input[type="range"]::-webkit-slider-runnable-track {
  height: 0.35rem;
  border-radius: 999px;
  background: linear-gradient(90deg, #1e9a4a, #83868a);
}
#synth-screen .synth-global-hw-chassis input[type="range"]::-moz-range-track {
  height: 0.35rem;
  border-radius: 999px;
  background: linear-gradient(90deg, #1e9a4a, #83868a);
}
#synth-screen .synth-global-hw-chassis input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 0.95rem;
  height: 0.95rem;
  margin-top: -0.3rem;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #f4f5f6, #9a9da0 60%, #45484b);
  border: 1px solid #101112;
  cursor: pointer;
}
#synth-screen .synth-global-hw-chassis input[type="range"]::-moz-range-thumb {
  width: 0.95rem;
  height: 0.95rem;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #f4f5f6, #9a9da0 60%, #45484b);
  border: 1px solid #101112;
  cursor: pointer;
}
/* Plain buttons (Export/Import kit is a drum-only pattern; here it's
   Save/Load, Learn, Capture/Play back, Record/Undo) -- same real gunmetal
   button already used for the Drum units' kit-io controls, just scoped
   here instead of duplicated as a new one-off look. */
#synth-screen .synth-global-hw-chassis button:not(.link-btn):not(.synth-quick-link-btn) {
  font-size: 0.8rem;
  padding: 0.35rem 0.65rem;
  border-radius: 5px;
  border: 1px solid #101112;
  background: linear-gradient(180deg, #45484b, #26282a);
  color: #e3e5e7;
  cursor: pointer;
}
#synth-screen .synth-global-hw-chassis button:not(.link-btn):not(.synth-quick-link-btn):hover { border-color: #7fd992; }
#synth-screen .synth-global-hw-chassis button:not(.link-btn):not(.synth-quick-link-btn):disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
/* Real Voice A/B "Snap record to" toggle -- same segmented-switch pattern
   already used for the Sequencer's own Play/Loop and Quantized/Instant
   toggles (#synth-hw-sequencer-chassis above), ID-scoped there so the
   shared .synth-quick-link-btn class used by the global Quick Links nav
   bar is never touched by either override. */
#synth-screen .synth-global-hw-chassis #synth-seq-record-unit-toggle .synth-quick-link-btn {
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  border: 1px solid #83868a;
  border-radius: 5px;
  color: #1c1d1e;
  font-size: 0.8125rem;
  font-weight: 700;
  padding: 0.35rem 0.7rem;
  cursor: pointer;
}
#synth-screen .synth-global-hw-chassis #synth-seq-record-unit-toggle .synth-quick-link-btn.active {
  background: linear-gradient(180deg, #c14535, #8f281e);
  border-color: #5a1712;
  color: #fff;
  box-shadow: 0 0 8px rgba(193, 69, 53, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.25);
}
/* ==========================================================================
   ASK ARCHIE STUDIO -- VOICE A HARDWARE CONVERSION (2026-09-25). Founder-
   accepted target: a silver/grey analogue-hardware instrument identity,
   built from the SAME real Ask Archie control DNA the VST harness already
   established (metallic knob, red pointer/tick accents, green operational
   illumination) -- not a Roland/TB-303 replica (no reproduced branding,
   product names, or pixel-for-pixel layout anywhere below).

   Scoped entirely under the new "synth-hw-instrument"/"synth-hw-group"
   classes, which exist ONLY on Voice A's own wrapper (renamed from the
   Phase 1 "synth-chassis"/"synth-chassis-group" specifically so this can
   never share a selector with Mixer's/Master's still-unchanged Phase 1
   dark/gold styling). Placed AFTER the #synth-screen .community-block block
   above on purpose: Voice A's content is still a descendant of that same
   shared .community-block, so these rules must win real same-specificity
   cascade ties for label/h3/muted text colour, not just add new properties
   -- the exact class of bug the earlier VST build-card and Mixer/Master
   chassis work both found and fixed the same way. */

#synth-screen .synth-hw-instrument {
  background: linear-gradient(180deg, #eef0f1 0%, #cfd1d3 45%, #b6b8bb 100%);
  border: 1px solid #6f7275;
  border-radius: 10px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.65);
  padding: 0.9rem 1rem;
  color: #1c1d1e;
}
#synth-screen .synth-hw-instrument label,
#synth-screen .synth-hw-instrument span:not(.muted) { color: #1c1d1e; }
#synth-screen .synth-hw-instrument .muted { color: #45484b; }
#synth-screen .synth-hw-instrument h3 {
  color: #1c1d1e;
  font-size: 0.95rem;
  letter-spacing: 0.08em;
  font-weight: 800;
  border-bottom: 2px solid #7a7d80;
  padding-bottom: 0.5rem;
  margin: 0 0 0.7rem;
}

/* Recessed module panel -- Oscillator/Filter/Mixer/Effects/Pattern & Steps/
   Automation each get one, a real, visible module division (the reference
   hardware's own separate labelled sections), still unmistakably the same
   silver instrument, not "unrelated dark cards on a silver webpage". */
#synth-screen .synth-hw-group {
  position: relative;
  background: linear-gradient(180deg, #c9cbce, #aeb0b3);
  border: 1px solid #83868a;
  border-radius: 8px;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.25);
  padding: 0.7rem 0.85rem;
  margin: 0.7rem 0;
}
#synth-screen .synth-hw-group:first-of-type { margin-top: 0; }
#synth-screen .synth-hw-group-title {
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-weight: 800;
  color: #1c1d1e;
  margin-bottom: 0.5rem;
  border-bottom: 1px solid rgba(0, 0, 0, 0.18);
  padding-bottom: 0.3rem;
}

/* Real hardware knob (ArchieKnob, the exact same shared component the
   generated-VST WebView and the VST Builder website already use, loaded
   unmodified via the existing vstBuilderEnsureHarnessUi()) plus a real,
   genuinely keyboard/screen-reader-accessible native range input bound to
   the exact same commit function and the exact same authoritative
   parameter value -- ArchieKnob is pointer-drag-only today, so this
   fallback is the only keyboard path to these parameters, and automation
   playback (synthUpdateFaderDisplay) finds it purely by its
   [data-auto-key] attribute, never by visibility. The knob's own value
   readout (.acKnobValue, from the shared script's own injected CSS) and
   its label (inside .acKnobItem) both assume a dark background by
   default -- both overridden here for real contrast against this silver
   panel. */
#synth-screen .synth-hw-knob-wrap { position: relative; display: inline-flex; flex-direction: column; align-items: center; gap: 0.3rem; margin: 0.3rem 0.5rem 0.3rem 0; vertical-align: top; }
#synth-screen .synth-hw-instrument .acKnobItem { color: #1c1d1e; }
/* Real fix, found by inspecting the actual live computed style rather than
   assuming: the earlier "span:not(.muted)" dark-text rule above (needed for
   the plain acKnobItem label) also matches this same span and, because
   :not() carries its argument's own specificity, was actually MORE specific
   than a bare ".acKnobValue" selector -- so it silently won, rendering this
   readout in dark-on-dark. Fixed with real, deliberately higher specificity
   rather than editing the shared span rule, so this stays robust regardless
   of source order. */
#synth-screen .synth-hw-instrument .acKnobItem .acKnobValue {
  background: #14171a;
  color: #7fe3a3;
  border: 1px solid #05060a;
  border-radius: 3px;
  padding: 0.1rem 0.4rem;
  display: inline-block;
}
/* ASK ARCHIE STUDIO -- SYNTH HARDWARE READOUT GEOMETRY STANDARDISATION
   (2026-09-26, Founder-directed). Real cause found via a live before/after
   bounding-rect test, not guessed: this readout had no fixed width, height
   or line-height -- it sized itself to its own current text ("1" vs
   "-3987 Hz"), and once a value's formatted text grew wide enough to
   exceed its knob's own fixed-width column, it wrapped to a second line,
   which pushed the knob dial itself upward inside its (separately) fixed-
   height item -- the exact vertical shift the Founder's screenshots
   proved. A real physical-LCD-window treatment: fixed box regardless of
   content, centred text, tabular numerals, never wraps. Two shared size
   classes (data-readout-size, set once per knob in
   vstBuilderCreateHardwareKnobItem from that knob's own real suffix), not
   one width per parameter -- "Hz"/"ms" can legitimately reach 4 digits
   plus a sign ("-4000 Hz"/"1000 ms"); every other real suffix in this app
   ("", "%", "dB") never exceeds 3 digits plus a sign. Widths measured
   against the actual live monospace readout font, with real headroom. */
#synth-screen .synth-hw-instrument .acKnobItem .acKnobValue {
  box-sizing: border-box;
  height: 1.3rem;
  line-height: 1.1rem;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  font-variant-numeric: tabular-nums;
}
#synth-screen .synth-hw-instrument .acKnobItem .acKnobValue[data-readout-size="compact"] {
  min-width: 2.8rem;
}
#synth-screen .synth-hw-instrument .acKnobItem .acKnobValue[data-readout-size="wide"] {
  min-width: 4rem;
}
/* CONTROL DUPLICATION CORRECTION (2026-09-25, Founder-reported): the knob
   and this fallback both visibly rendering made every converted parameter
   show two controls for one value ("ONE PARAMETER = ONE PRIMARY VISIBLE
   CONTROL"). The fallback itself is NOT removed -- doing so would delete
   the only keyboard-accessible path to these parameters and the only
   [data-auto-key] anchor automation playback looks up -- it is visually
   hidden using the standard accessible "hidden but focusable" pattern:
   invisible and out of layout flow (so .synth-hw-knob-wrap naturally
   collapses to just the knob's own height -- real compaction, not faked
   with removed margins) until it actually receives keyboard focus, at
   which point it reappears so a keyboard-only user can see and operate
   it. Binding, state, DSP, automation and ids are all completely
   untouched -- this is a pure CSS visibility change. */
#synth-screen .synth-hw-knob-fallback {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
#synth-screen .synth-hw-knob-fallback:focus {
  position: static;
  width: 72px;
  height: 1rem;
  margin: 0.15rem 0 0;
  padding: initial;
  overflow: visible;
  clip: auto;
  white-space: normal;
}
/* OSCILLATOR -- BUTTONS ONLY (2026-09-25, Founder-reported): the native
   &lt;select&gt; is the sole authoritative oscillator state (every existing
   .value read/session-save/session-load/live-oscillator-type listener
   reads it -- the segmented buttons only ever set its value and dispatch a
   real "change" event), so it cannot be removed -- only its NORMAL visible
   presentation is removed, using the exact same hidden-but-focusable
   pattern already accepted for the knob fallbacks. :focus-within (not
   :focus) because the actual focusable element is the &lt;select&gt; inside
   a &lt;label&gt; inside this row, not the row itself. */
#synth-screen .synth-hw-native-fallback-row {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
#synth-screen .synth-hw-native-fallback-row:focus-within {
  position: static;
  width: auto;
  height: auto;
  padding: initial;
  margin-top: 0.35rem;
  overflow: visible;
  clip: auto;
  white-space: normal;
  opacity: 0.75;
  font-size: 0.78rem;
}

/* Physical selector switch (Oscillator waveform) -- red-illuminated when
   engaged, matching the shared harness's own red accent language. Plain
   &lt;button&gt; elements: fully keyboard-reachable (Tab + Enter/Space) on
   their own, unlike the pointer-only knob above. */
/* DENSITY PASS (2026-09-25, Founder-marked screenshots): 4 waveform
   buttons as a genuine compact 2x2 hardware bank instead of a wrapping
   row -- this is what lets the Oscillator column itself shrink (see
   .synth-hw-group-osc below), which is what releases width back to
   Filter/Mixer. */
#synth-screen .synth-hw-segmented { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.4rem; margin-bottom: 0.4rem; }
#synth-screen .synth-hw-seg-btn {
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  border: 1px solid #83868a;
  border-radius: 5px;
  color: #1c1d1e;
  font-size: 0.8125rem;
  font-weight: 700;
  padding: 0.4rem 0.85rem;
  min-height: 40px;
  cursor: pointer;
}
#synth-screen .synth-hw-seg-btn:hover { border-color: #45484b; }
#synth-screen .synth-hw-seg-btn:focus-visible { outline: 2px solid #1c1d1e; outline-offset: 1px; }
#synth-screen .synth-hw-seg-btn.on {
  background: linear-gradient(180deg, #c14535, #8f281e);
  border-color: #5a1712;
  color: #fff;
  box-shadow: 0 0 8px rgba(193, 69, 53, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.25);
}

/* Illuminated hardware buttons -- Mute/Solo/FX On, the EXISTING shared
   classes every voice/drum already renders (synthRenderMuteSoloButtons /
   renderSynthVoiceMixer, untouched) -- scoped here so ONLY Voice A's own
   instances get this look; Voice B/C/D's identical buttons elsewhere on the
   page are completely unaffected. Distinct colour per state (red=mute,
   amber=solo, green=FX on -- the shared "operational illumination"
   language) AND a real background/border shift on .active, so state is
   never colour-only. */
#synth-screen .synth-hw-instrument .synth-mute-btn,
#synth-screen .synth-hw-instrument .synth-solo-btn,
#synth-screen .synth-hw-instrument .synth-voice-fx-toggle-btn {
  position: relative;
  background: linear-gradient(180deg, #45484b, #26282a);
  border: 1px solid #101112;
  border-radius: 5px;
  color: #e3e5e7;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 0.35rem 0.7rem 0.35rem 1.35rem;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 2px rgba(0, 0, 0, 0.4);
}
#synth-screen .synth-hw-instrument .synth-mute-btn:focus-visible,
#synth-screen .synth-hw-instrument .synth-solo-btn:focus-visible,
#synth-screen .synth-hw-instrument .synth-voice-fx-toggle-btn:focus-visible { outline: 2px solid #1c1d1e; outline-offset: 2px; }
#synth-screen .synth-hw-instrument .synth-mute-btn::before,
#synth-screen .synth-hw-instrument .synth-solo-btn::before,
#synth-screen .synth-hw-instrument .synth-voice-fx-toggle-btn::before {
  content: "";
  position: absolute;
  left: 0.45rem;
  top: 50%;
  transform: translateY(-50%);
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #3a1515;
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.6);
}
#synth-screen .synth-hw-instrument .synth-mute-btn.active,
#synth-screen .synth-hw-instrument .synth-solo-btn.active,
#synth-screen .synth-hw-instrument .synth-voice-fx-toggle-btn.active {
  background: linear-gradient(180deg, #55585b, #34363a);
  border-color: #000;
}
#synth-screen .synth-hw-instrument .synth-mute-btn.active::before { background: #ff4d4d; box-shadow: 0 0 6px #ff4d4d; }
#synth-screen .synth-hw-instrument .synth-solo-btn.active::before { background: #ffb020; box-shadow: 0 0 6px #ffb020; }
#synth-screen .synth-hw-instrument .synth-voice-fx-toggle-btn.active::before { background: #3dff7a; box-shadow: 0 0 6px #3dff7a; }

/* Effects modules -- same silver-consistent card material; parameters keep
   their exact existing native range inputs and DSP-tied behaviour, just
   restyled (no ArchieKnob conversion here this phase -- several of these
   panels rebuild themselves conditionally, e.g. Delay's Synced-to-tempo
   toggle; kept as a lower-risk, disclosed lighter-touch pass, per Task 7's
   own "do not force every parameter into a knob" allowance). */
#synth-screen .synth-hw-instrument .synth-rack-module {
  background: linear-gradient(180deg, #d7d9db, #c2c4c7);
  border: 1px solid #83868a;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
  color: #1c1d1e;
}
#synth-screen .synth-hw-instrument .synth-rack-module-header strong {
  color: #1c1d1e;
  text-transform: uppercase;
  font-size: 0.75rem;
  letter-spacing: 0.06em;
}
#synth-screen .synth-hw-instrument .synth-unit-fx input[type="range"]::-webkit-slider-thumb {
  background: radial-gradient(circle at 35% 30%, #f4f5f6, #9a9da0 60%, #45484b);
  border: 1px solid #101112;
}
#synth-screen .synth-hw-instrument .synth-unit-fx input[type="range"]::-moz-range-thumb {
  background: radial-gradient(circle at 35% 30%, #f4f5f6, #9a9da0 60%, #45484b);
  border: 1px solid #101112;
}

/* Pattern & Steps -- the existing pattern-bank/step-grid/pattern-tools DOM
   and JS are completely untouched; this only restyles the same real
   buttons/cards with the shared hardware look (illuminated green step-on,
   red current-step outline, amber accent, blue slide -- state is never
   colour-only, every one of these also has its own persistent text label:
   step number, "Acc", "Slide"). */
#synth-screen .synth-hw-instrument .synth-pattern-bank-btn {
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  border: 1px solid #83868a;
  color: #1c1d1e;
  border-radius: 4px;
  /* Legibility floor (2026-09-25): the shared 1.6rem/0.6rem (25.6px/9.6px)
     base is below both the button-text and click-target floors -- raised
     here, scoped to Voice A only, Voice B keeps the original shared size. */
  width: 2.15rem;
  height: 2.15rem;
  font-size: 0.75rem;
}
#synth-screen .synth-hw-instrument .synth-pattern-bank-btn.active {
  background: linear-gradient(180deg, #c14535, #8f281e);
  border-color: #5a1712;
  color: #fff;
}
#synth-screen .synth-hw-instrument .synth-pattern-bank-label { color: #1c1d1e; }
#synth-screen .synth-hw-instrument .synth-seq-step {
  background: #c2c4c7;
  border: 1px solid #83868a;
  border-radius: 4px;
  padding: 0.2rem;
}
#synth-screen .synth-hw-instrument .synth-seq-step-current { outline: 2px solid #c14535; outline-offset: 1px; }
#synth-screen .synth-hw-instrument .synth-seq-step-on {
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  border: 1px solid #83868a;
  color: #1c1d1e;
}
#synth-screen .synth-hw-instrument .synth-seq-step-on.active {
  background: linear-gradient(180deg, #4fe38a, #1e9a4a);
  border-color: #0a4a20;
  color: #06210f;
  box-shadow: 0 0 6px rgba(79, 227, 138, 0.6);
}
/* Real bug found while measuring the Height Compression pass (2026-09-25),
   not assumed: this hardware reskin only ever restyled Accent/Slide's
   ACTIVE state -- their inactive state fell back to the base
   ".synth-seq-step button" rule's background: var(--panel-bg, transparent),
   which reads as near-invisible text against Voice A's light silver panel.
   Given the same readable inactive look already proven on .synth-seq-step-on
   (light gradient, dark text), used here too. Active states (.active below)
   keep their existing distinct orange/slide-blue -- untouched, and win on
   specificity since they carry an extra class. */
#synth-screen .synth-hw-instrument .synth-seq-step-accent,
#synth-screen .synth-hw-instrument .synth-seq-step-slide {
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  border: 1px solid #83868a;
  color: #1c1d1e;
}
#synth-screen .synth-hw-instrument .synth-seq-step-accent.active { background: #ffb020; border-color: #8a5a10; color: #2a1a00; }
#synth-screen .synth-hw-instrument .synth-seq-step-slide.active { background: #4a90d9; border-color: #1a4a7a; color: #fff; }
/* Legibility floor (2026-09-25): the shared per-step card (0.65-0.8rem,
   ~10.4-12.8px) is below the button-text floor for On/Acc/Slide -- raised
   here, scoped to Voice A only; a slightly wider step card gives the
   larger text room without wrapping. Voice B's steps are unaffected. Task
   9 explicitly accepts the sequencer needing its own horizontal scroll
   over shrinking this text back down. */
#synth-screen .synth-hw-instrument .synth-seq-step { width: 3.7rem; font-size: 0.75rem; }
#synth-screen .synth-hw-instrument .synth-seq-step select { font-size: 0.75rem; }
#synth-screen .synth-hw-instrument .synth-seq-step button { font-size: 0.8125rem; }
#synth-screen .synth-hw-instrument .synth-seq-step .synth-seq-step-on { font-size: 0.85rem; }
#synth-screen .synth-hw-instrument .synth-pattern-tools button,
#synth-screen .synth-hw-instrument .synth-pattern-bank-steps input {
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  border: 1px solid #83868a;
  color: #1c1d1e;
  border-radius: 4px;
}

/* Automation -- explicitly kept as an editor/instrumentation surface
   (Task 9): the SVG lane plots and their own record/enable/reset controls
   are completely untouched, only the surrounding panel material changes so
   it still reads as part of the same physical instrument. */
#synth-screen .synth-hw-instrument .synth-automation-lanes {
  background: rgba(255, 255, 255, 0.35);
  border: 1px solid #83868a;
  border-radius: 6px;
  /* Height Compression pass (2026-09-25): trimmed from 0.5rem -- this inner
     panel already sits inside the Automation group's own padded/bordered
     box, so its own padding only needed a light trim, not removal (unlike
     .synth-voice-mixer this one is a real, visually distinct inset "lane
     display" look worth keeping). Record/Enable/Reset/graph untouched.
     Second, deeper trim after live re-measurement showed Voice A still
     ~54px taller than the viewport. */
  padding: 0.25rem 0.45rem;
}

/* ASK ARCHIE STUDIO -- VOICE A FRONT-PANEL RESET (2026-09-25). Implements
   the Founder-accepted design-specification report, with two Founder locks
   applied before implementation: (1) Pattern Tools is one compact
   horizontal strip BELOW the sequencer, never a third side column; (2) the
   knob-size hierarchy is re-tuned DOWN from the previous 88/66/50px to
   76/58/50px -- 88px was a maximum, not a target, and ergonomic hierarchy
   comes from grouping/placement/labelling, not oversized dials. This
   replaces the whole prior "Ergonomic Consolidation" pass below, which the
   Founder rejected as controls-rearranged-into-available-space rather than
   a designed instrument. */

/* Knob-size hierarchy (Founder lock #2): scoped under .synth-hw-instrument
   so ONLY Voice A's own knobs are affected -- the shared ArchieKnob
   component (public/api/vst-harness-ui.js) still renders at its own
   original 88/66/50px everywhere else (VST Builder, generated plugins).
   Higher-specificity selectors here (3 classes) safely beat the shared
   component's own injected "<style id=archieKnobCss>" rules (2 classes)
   regardless of injection order.
   PRIMARY (Cutoff, Volume): 76px, within the Founder's accepted 72-78px.
   STANDARD (Resonance): 58px, within the accepted 56-62px.
   UTILITY (Env Mod/Decay/Accent, every Effects knob): left at the shared
   component's own 50px -- already inside the accepted 48-50px range, so no
   override needed there.
   Also raises the shared component's own label/value text (previously a
   flat 9px/11px for EVERY size, never re-examined against a legibility
   floor) to the Founder-locked floor: control labels >= 12px, live
   readouts >= 12px. This is a REAL, previously-unaddressed contributor to
   "text is too small" -- it was true even on the earlier-accepted Filter
   knobs, just less visible at 88px with fewer neighbours. */
#synth-screen .synth-hw-instrument .acKnob.lg { width: 76px; height: 76px; }
#synth-screen .synth-hw-instrument .acKnobItem.lg { width: 88px; }
#synth-screen .synth-hw-instrument .acKnob.md { width: 58px; height: 58px; }
#synth-screen .synth-hw-instrument .acKnobItem.md { width: 68px; }
#synth-screen .synth-hw-instrument .acKnobItem { font-size: 12px; }
#synth-screen .synth-hw-instrument .acKnobItem .acKnobValue { font-size: 13px; }

/* Section/group titles and every hardware button raised to the Founder's
   locked legibility floor (labels >= 12px, button text >= 13px) -- these
   pre-date this pass (0.68rem/0.72rem = ~11-11.5px) and were never
   reconciled against the floor until now. */
#synth-screen .synth-hw-group-title { font-size: 0.75rem; }
#synth-screen .synth-hw-seg-btn { font-size: 0.8125rem; }
#synth-screen .synth-hw-instrument .synth-mute-btn,
#synth-screen .synth-hw-instrument .synth-solo-btn,
#synth-screen .synth-hw-instrument .synth-voice-fx-toggle-btn,
#synth-screen .synth-hw-instrument .synth-hw-fx-sync-btn {
  font-size: 0.8125rem;
  min-height: 40px;
}
#synth-screen .synth-hw-instrument .synth-pattern-bank-label { font-size: 0.75rem; }
#synth-screen .synth-hw-instrument .synth-pattern-tools button {
  font-size: 0.8125rem;
  padding: 0.5rem 0.7rem;
  min-height: 36px;
}

/* Band 1 -- Oscillator | Filter | Mixer/Output side by side. DENSITY PASS
   (2026-09-25, Founder-authorised): the earlier ~3:7:6-of-16 allocation is
   explicitly reopened here -- Oscillator now holds only a compact 2x2
   button bank (4 normal controls, no dropdown), so it no longer needs
   anywhere near its old share. Width released to Oscillator's neighbours,
   using the Founder's own suggested starting ratios (~2.25 : 7.25 : 6.5 of
   16). align-items:stretch is KEPT (still one coherent hardware band with
   a shared outer height) -- the actual height fix is making Mixer/Output's
   own content shorter (2-column mixer-controls grid, below), since Mixer
   was the tallest driver forcing the other two to stretch. Real min-widths
   (not min-width:0) so flex-wrap still triggers naturally rather than
   squeezing controls below their own content size. */
#synth-screen .synth-hw-row { display: flex; flex-wrap: wrap; gap: 0.7rem; align-items: stretch; margin: 0.7rem 0; }
#synth-screen .synth-hw-row > .synth-hw-group { margin: 0; flex: 1 1 240px; }
#synth-screen .synth-hw-row .synth-hw-group-osc { flex: 2.25 1 170px; min-width: 160px; }
#synth-screen .synth-hw-row .synth-hw-group-filter { flex: 7.25 1 420px; min-width: 400px; }
#synth-screen .synth-hw-row .synth-hw-group-mixer { flex: 6.5 1 420px; min-width: 400px; }

/* MIXER/OUTPUT SPLIT (2026-09-25, Founder-locked): two real functional
   areas inside the existing Mixer/Output group -- LEFT "Output" (Volume +
   the one master FX On control) and RIGHT "Mixer controls" (Pan/Send/Comp
   Ratio/Comp Threshold, Mute/Solo at the bottom), separated by a visible
   hardware-style divider. Built in app.js's synthRenderVoiceMixer only
   when `hardware` is true (Voice A) -- Voice B/C/D keep appending straight
   into the flat container exactly as before, so .synth-hw-mixer-output/
   -controls/-divider simply do not exist there. Pan/Send/Comp Ratio/Comp
   Threshold remain their existing native range inputs -- not converted to
   knobs in this pass. */
/* Real fix found while measuring this pass, not assumed: the shared base
   rule (.synth-voice-mixer, used by every voice) sets flex-wrap:wrap --
   harmless for the old flat list of many small controls, but the new
   output/divider/controls trio is wider than that, so it was wrapping
   controlsArea onto a second line and roughly DOUBLING the group's height
   (measured 375px for a component whose own content only needs ~175px).
   nowrap here is scoped to Voice A only; Voice B/C/D's un-split mixer
   keeps wrapping exactly as before. */
#synth-screen .synth-hw-instrument .synth-voice-mixer { align-items: stretch; flex-wrap: nowrap; }
#synth-screen .synth-hw-instrument .synth-hw-mixer-controls input[type="range"] { width: 6.5rem; }
#synth-screen .synth-hw-instrument .synth-hw-mixer-output {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  flex: 0 0 auto;
  padding-right: 0.9rem;
}
#synth-screen .synth-hw-instrument .synth-hw-mixer-divider {
  width: 1px;
  align-self: stretch;
  background: #83868a;
  box-shadow: 1px 0 0 rgba(255, 255, 255, 0.4);
}
/* ASK ARCHIE STUDIO -- VOICE A HEIGHT COMPRESSION PASS (2026-09-25).
   Task 1 measured Band 1's shared 277px height as Mixer's own genuine
   natural need (confirmed live, not a stretch artifact -- Oscillator/Filter
   are shorter and get stretched to match it). The single largest real lever
   found: .synth-voice-mixer's own base rule (shared by every voice) still
   carries a standalone-panel margin+padding+background+border left over
   from before it was nested inside this already-bordered/padded hardware
   group -- a redundant box inside a box, worth ~52px here alone. Scoped to
   originally the new #synth-voice-a-instrument id (not the shared
   .synth-hw-instrument class) because .synth-hw-group/.synth-hw-instrument
   are also reused, unchanged, by the Mixer/Sequencer/Composer chassis -- a
   class-scoped geometry change here would have shrunk their own frozen,
   already-accepted layouts too.
   ASK ARCHIE STUDIO -- VOICE B/C/D HARDWARE PROPAGATION (2026-09-26):
   promoted to the shared ".synth-hw-voice" marker class (added to all four
   voice wrappers alongside their existing .synth-hw-instrument) so this one
   real rule now reaches Voice A/B/C/D instead of four copies -- still never
   Mixer/Sequencer/Composer, which don't carry .synth-hw-voice. Combined
   with .synth-hw-instrument in the selector (not .synth-hw-voice alone) so
   specificity still safely beats the shared, unscoped
   "#synth-screen .synth-hw-instrument" rules rather than merely tying and
   depending on source order. */
#synth-screen .synth-hw-instrument.synth-hw-voice .synth-voice-mixer {
  margin: 0;
  padding: 0;
  background: transparent;
  border: none;
  box-shadow: none;
}
/* Same pass, same reasoning: each hardware group's own padding/margin/title
   gap compounds across Oscillator/Filter/Mixer/Effects/Pattern & Steps/
   Automation. Trimmed, not eliminated -- knob diameters, text sizes and
   click targets are untouched; only the panel's own dead space shrinks.
   #synth-screen .synth-hw-row > .synth-hw-group already zeroes margin for
   the three Band-1 groups (higher specificity, unaffected by this), so the
   margin half of this rule only reaches the vertically-stacked Effects/
   Pattern & Steps/Automation groups. */
#synth-screen .synth-hw-instrument.synth-hw-voice .synth-hw-group { padding: 0.35rem 0.7rem; margin: 0.35rem 0; }
#synth-screen .synth-hw-instrument.synth-hw-voice .synth-hw-group-title { margin-bottom: 0.2rem; padding-bottom: 0.15rem; }
/* .synth-automation-lanes/-legend/-graph-wrap's own margins are base rules
   shared with both drum units (Task 10) -- trimmed here, scoped to hardware
   voices only, gap/graph height and every control untouched. */
#synth-screen .synth-hw-instrument.synth-hw-voice .synth-automation-lanes { gap: 0.25rem; margin-top: 0.3rem; }
#synth-screen .synth-hw-instrument.synth-hw-voice .synth-automation-legend { margin-top: 0.25rem; }
#synth-screen .synth-hw-instrument.synth-hw-voice .synth-automation-graph-wrap { margin-top: 0.25rem; }
/* ASK ARCHIE STUDIO -- SYNTH QUICK LINKS VERTICAL SPACE RECOVERY (2026-09-25).
   Real cause found, not guessed: main.project-list-main's own base rule
   (.project-list-main{margin:2rem auto;...}, shared by many other screens --
   #video-editor-screen already has its own scoped override of this exact
   same rule, same pattern followed here) puts a full 32px margin-top between
   the sticky session toolbar and Quick Links. Scoped to #synth-screen only. */
#synth-screen main.project-list-main { margin-top: 0.5rem; }
/* Quick Links' own padding/heading margin come from the SHARED
   "#synth-screen .community-block" rule (padding:1rem 1.1rem;
   margin-bottom:0.9rem) -- also used by the Composer and Mixer chassis
   (both carry .community-block too), so it can't be edited directly without
   a global spacing change. Scoped to the unique #synth-quick-links-block id
   instead; left/right padding kept exactly as-is, only top/bottom trimmed.
   The h3 heading's own 13.6px top+bottom margin is an unset browser default
   (no existing rule to collide with) -- given an explicit, smaller value. */
#synth-quick-links-block { padding-top: 0.5rem; padding-bottom: 0.5rem; }
#synth-quick-links-block h3 { margin: 0.35rem 0; }
/* ASK ARCHIE STUDIO -- SYNTH QUICK LINKS DESTINATION ALIGNMENT (2026-09-25).
   --synth-scroll-offset is set on #synth-screen by
   synthUpdateQuickLinksScrollOffset() (app.js) from the real, current
   combined height of #header-state-bar + .topbar + #synth-quick-links-block
   -- the three stacked position:sticky bars that were covering each
   destination's own top after scrollIntoView({block:"start"}). One shared
   rule, every existing Quick Links jump target listed once; no per-
   destination margin, no destination id renamed. */
#synth-mixer-heading,
#synth-sequencer-heading,
#synth-voice-a-heading,
#synth-voice-b-heading,
#synth-voice-c-heading,
#synth-voice-d-heading,
#synth-drum-808-heading,
#synth-drum-909-heading,
#synth-song-heading,
#synth-vocals-heading,
#synth-keyboard-heading,
#synth-presets-heading,
#synth-controller-heading,
#vocal-studio-screen {
  scroll-margin-top: var(--synth-scroll-offset, 0px);
}
/* The outer chassis's own padding (0.9rem 1rem, shared base rule) and its
   heading's own margin-bottom (0.7rem, browser default) were the last real,
   still-untouched levers -- found on Voice A's final re-measurement pass,
   ~15px still short of fitting. Scoped to .synth-hw-voice since
   .synth-hw-instrument's base padding is shared with Mixer/Sequencer/
   Composer chassis. Heading text size itself (the one large legible-
   hardware-font heading) is untouched.
   ASK ARCHIE STUDIO -- VOICE B/C/D HARDWARE PROPAGATION (2026-09-26):
   promoted from #synth-voice-a-instrument to .synth-hw-voice; the heading
   rule targets "> h3" (every voice's own first child heading) instead of
   Voice A's specific id, so one rule now reaches all four. */
#synth-screen .synth-hw-instrument.synth-hw-voice { padding: 0.55rem 1rem 0.4rem; }
#synth-screen .synth-hw-instrument.synth-hw-voice > h3 { margin-bottom: 0.4rem; }
/* Task 4: the new steps+tools column wrapper (Pattern Tools now sits
   directly beneath the 16-step grid instead of on its own full-width line
   below the whole bank+steps row). Plain flex column; every voice's own
   #synth-seq-*-pattern-tools/#synth-seq-*-steps keep their own existing
   classes/ids/styling. */
#synth-screen .synth-hw-instrument.synth-hw-voice .synth-hw-seq-col-steps-and-tools {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-width: 0;
}
/* ASK ARCHIE STUDIO -- VOICE A FINAL MICRO-COMPACTION (2026-09-25).
   Real cause found, not guessed: Reso is the "md" knob size (58px dial),
   Env Mod/Decay/Accent are "sm" (50px) -- the size hierarchy itself is
   correct and untouched, but it meant Reso's label+value sat 8px lower
   than the other three's, since each knob's text starts right after its
   own knob with no shared floor. Giving these four a common height (Reso's
   own natural 101px) and bottom-anchoring their content means every knob's
   own dial keeps its exact real diameter, only the empty space above
   shorter knobs changes -- so all four value readouts land on one real
   shared baseline. Cutoff (lg, the primary control) is untouched.
   ASK ARCHIE STUDIO -- VOICE B/C/D HARDWARE PROPAGATION (2026-09-26):
   promoted from the Voice-A-specific #synth-seq-a-303-controls id to the
   shared .synth-303-controls class (every voice's own filter-knob
   container uses this class regardless of its own container id), so the
   same fix reaches Voice B/C/D's Reso/Env Mod/Decay/Accent too. */
.synth-303-controls .acKnobItem.md,
.synth-303-controls .acKnobItem.sm {
  height: 101px;
  justify-content: flex-end;
}
/* Real cause found, not guessed: EQ's "High (dB)" is the one label across
   all five Effects modules whose text (60.7px needed) narrowly exceeds its
   knob item's own width (60px), so it alone wrapped to two lines -- 15px
   taller than Mix/Low (dB)/Mid (dB) and every other module's own knobs,
   which is what dropped its readout lower than the rest. A real nowrap,
   not a one-off negative margin on High specifically -- every other Voice A
   knob label already fits on one line at this width, confirmed measured.
   ASK ARCHIE STUDIO -- VOICE B/C/D HARDWARE PROPAGATION (2026-09-26):
   promoted to .synth-hw-voice so Voice B/C/D's own knob labels get the
   same protection. */
#synth-screen .synth-hw-instrument.synth-hw-voice .acKnobItem > span:not(.acKnobValue) {
  white-space: nowrap;
}
/* DENSITY PASS (2026-09-25, Founder-authorised): this was a 5-row vertical
   stack (Pan/Send/Comp Ratio/Comp Threshold/Mute+Solo), the single tallest
   content in Band 1 and the actual reason align-items:stretch forced
   Oscillator and Filter to match its height. Now a real 2-column grid
   (Pan|Send, Comp Ratio|Comp Threshold, Mute+Solo spanning both columns)
   using the extra width released from the compacted Oscillator -- same
   controls, same ids, same behaviour, just denser. */
#synth-screen .synth-hw-instrument .synth-hw-mixer-controls {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.3rem 0.9rem;
  align-content: start;
  flex: 1 1 auto;
  padding-left: 0.9rem;
}
#synth-screen .synth-hw-instrument .synth-hw-mixer-controls label { margin: 0; }
#synth-screen .synth-hw-instrument .synth-hw-mixer-controls .synth-mute-solo {
  grid-column: 1 / -1;
  justify-content: center;
  margin-left: 0;
  margin-top: 0.3rem;
}

/* Band 2 -- Effects bank, allocated by REAL control count (Founder-locked
   geometry), not five equal generic cards: Chorus 2 / Reverb 2 /
   Distortion 3 / Delay 4 / EQ 4 of 15 logical columns (the effects-enable
   toggle no longer exists in this bank -- removed as a genuine duplicate
   of Mixer/Output's own FX On, see synthRenderVoiceMixer/
   renderSynthVoiceFxPanel).
   .synth-rack-module-<key> is a plain class added in app.js's
   renderSynthVoiceFxPanel with zero behaviour change.
   DENSITY PASS (2026-09-25, Founder-marked screenshots): CSS Grid's own
   DEFAULT align-items is "stretch" -- with no override, every module's
   card was being stretched to match the row's tallest sibling (whichever
   module's knobs wrapped to 2 lines within its own narrow column), then
   align-items:flex-end on the card bottom-aligned its own short content
   inside that stretched box -- together this produced exactly the large
   blank UPPER field the Founder circled, on every module shorter than the
   tallest one. align-items:start here is the actual fix: each card is now
   only as tall as its own real content. */
#synth-screen .synth-hw-fx-bank .synth-unit-fx {
  display: grid;
  grid-template-columns: repeat(15, 1fr);
  align-items: start;
  gap: 0.6rem;
}
#synth-screen .synth-hw-fx-bank .synth-rack-module {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-start;
  align-content: flex-start;
  gap: 0.3rem 0.5rem;
  margin: 0;
  min-width: 0;
  /* Height Compression pass (2026-09-25): trims the base .synth-rack-module
     padding (0.6rem 0.75rem) down slightly -- .synth-hw-fx-bank is unique to
     Voice A's Effects rack, so this can't reach Master's own rack modules
     (those are scoped separately, .synth-master-chassis .synth-rack-module).
     Knobs/readouts/buttons are unchanged, only the card's own margin.
     Second, deeper trim after live re-measurement showed Voice A still
     ~54px taller than the viewport -- still card padding only. */
  padding: 0.35rem 0.5rem;
}
#synth-screen .synth-hw-fx-bank .synth-rack-module-header { flex: 1 1 100%; }
#synth-screen .synth-hw-fx-bank .synth-rack-module-chorus { grid-column: span 2; }
#synth-screen .synth-hw-fx-bank .synth-rack-module-reverb { grid-column: span 2; }
#synth-screen .synth-hw-fx-bank .synth-rack-module-distortion { grid-column: span 3; }
#synth-screen .synth-hw-fx-bank .synth-rack-module-delay { grid-column: span 4; }
#synth-screen .synth-hw-fx-bank .synth-rack-module-eq { grid-column: span 4; }

/* Delay's own Synced-to-tempo illuminated toggle and Steps &lt;select&gt;
   (no knob equivalent for a discrete named-division list). */
#synth-screen .synth-hw-instrument .synth-hw-fx-sync-btn {
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  border: 1px solid #83868a;
  border-radius: 5px;
  color: #1c1d1e;
  font-weight: 700;
  padding: 0.35rem 0.7rem;
  cursor: pointer;
  align-self: flex-end;
}
#synth-screen .synth-hw-instrument .synth-hw-fx-sync-btn:hover { border-color: #45484b; }
#synth-screen .synth-hw-instrument .synth-hw-fx-sync-btn:focus-visible { outline: 2px solid #1c1d1e; outline-offset: 1px; }
#synth-screen .synth-hw-instrument .synth-hw-fx-sync-btn.active {
  background: linear-gradient(180deg, #c14535, #8f281e);
  border-color: #5a1712;
  color: #fff;
  box-shadow: 0 0 8px rgba(193, 69, 53, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.25);
}
#synth-screen .synth-hw-instrument .synth-hw-select {
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  border: 1px solid #83868a;
  border-radius: 5px;
  color: #1c1d1e;
  font-size: 0.8125rem;
  font-weight: 600;
  padding: 0.3rem 0.5rem;
}

/* Band 3 -- Founder lock #1: Pattern Bank stays immediately LEFT of the
   16-step sequencer (top strip); Pattern Tools is now ONE COMPACT
   HORIZONTAL STRIP directly BELOW the sequencer, spanning the full unit
   width -- never a third side column, never consuming sequencer width.
   #synth-seq-a-pattern-bank / #synth-seq-steps / #synth-seq-a-pattern-tools
   keep their exact ids -- pure presentation, no sequencing-behaviour change. */
#synth-screen .synth-hw-instrument .synth-hw-seq-top { flex-wrap: wrap; }
#synth-screen .synth-hw-instrument .synth-hw-seq-col-bank { flex: 0 0 auto; }
#synth-screen .synth-hw-instrument .synth-hw-seq-col-steps { flex: 1 1 auto; min-width: 0; }
#synth-screen .synth-hw-instrument .synth-hw-seq-tools-strip { margin-top: 0.6rem; }
#synth-screen .synth-hw-instrument .synth-hw-seq-tools-strip .synth-pattern-tools-bottom { margin-top: 0; }

/* Responsive fallback: the top row is not forced to a fixed breakpoint --
   real min-widths above already give a genuine three-wide -> two+one ->
   stacked progression at exactly the width real content needs it. Band 3
   still gets a hard stack below 900px (matching this file's own existing
   .synth-unit-layout breakpoint); the step sequencer keeps its own
   existing horizontal scroll (.synth-seq-steps, unchanged) rather than
   crushing 16 steps to fit. */
@media (max-width: 900px) {
  #synth-screen .synth-hw-instrument .synth-hw-seq-top { flex-direction: column; }
}
/* ========================================================================== */

/* ==========================================================================
   ASK ARCHIE STUDIO -- MIXER/MASTER HARDWARE CONSOLIDATION (2026-09-25,
   Founder-approved). The Mixer's wrapper class was changed in index.html
   from the old Phase 1 dark/gold "synth-chassis" scaffolding to the SAME
   "synth-hw-instrument" class Voice A uses -- every generic rule already
   written for Voice A (panel background, h3, labels, .synth-hw-knob-wrap/
   .acKnobItem knob styling, .synth-mute-btn/.synth-solo-btn LED buttons)
   now applies to the Mixer for free, which is exactly why Mute/Solo and
   the new Pan knobs need zero additional styling below -- this section
   only covers what's genuinely NEW: the console layout, the vertical
   fader look, the Master strip, and the segmented LED meter. Scoped
   entirely under .synth-mixer-chassis/.synth-hw-master-strip so Voice
   B/C/D, drums, Rack and every other section are completely unaffected. */

/* Console layout -- #synth-mixer-strips (rebuilt by renderSynthMixer(),
   real per-unit strips) and #synth-master-strip (a STATIC sibling holding
   the real relocated Master controls, never touched by that rebuild) sit
   side by side as one continuous hardware row. */
/* Real fix (2026-09-25, found while correcting Master's Record baseline):
   align-items:stretch here created a feedback loop -- nudging Master's own
   content height also stretched every channel strip (a CSS Grid) to match
   Master's new tallest height, which redistributed the extra space into
   the channels' own auto-sized grid rows and silently preserved the same
   relative offset. flex-start lets each strip size to its own real content
   -- both strip types are independently engineered (fixed row heights on
   channels, a matching measured budget on Master) to reach the same total
   height on their own, so top/bottom boundaries still align without
   fighting a forced stretch. */
#synth-screen .synth-mixer-chassis .synth-mixer-console { display: flex; align-items: flex-start; gap: 0.6rem; }
#synth-screen .synth-mixer-chassis .synth-mixer-strips { flex: 1 1 auto; gap: 0.5rem; margin-top: 0; }

/* Channel strip -- overrides the older, unscoped .synth-mixer-strip rules
   (padding: 0.6rem 0.75rem on a plain --surface card) with the same
   recessed-panel language as Voice A's .synth-hw-group, and tightens
   padding since a channel strip's own controls (not a card's worth of
   web-form content) should determine its size. */
/* MIXER CHANNEL-STRIP ALIGNMENT (2026-09-25, Founder-reported): Voice A-D
   have a real Pan control, Drum 808/909/Vocals do not, so their Mute/Solo
   rows landed at different Y positions when this was a plain flex column
   (fewer real children = a shorter stack). Fixed with real CSS Grid fixed
   layout zones (per the Founder's own instruction: "prefer CSS grid/fixed
   layout zones rather than arbitrary per-channel margins") -- 4 explicit
   rows (heading/Open, fader, Pan-or-reserved-empty-space, Mute/Solo), each
   child assigned an explicit grid-row so a channel missing Pan (or missing
   the Open button, e.g. Vocals) still reserves that row's real measured
   height (43px heading row, 93px Pan row -- both measured live from Voice
   A before writing this) rather than collapsing it. No fake Pan control is
   added anywhere -- rows 3 stays a genuinely empty grid track for Drum
   808/909/Vocals. */
#synth-screen .synth-mixer-chassis .synth-mixer-strip {
  display: grid;
  grid-template-rows: minmax(43px, auto) auto minmax(93px, auto) auto;
  justify-items: center;
  background: linear-gradient(180deg, #c9cbce, #aeb0b3);
  border: 1px solid #83868a;
  border-radius: 8px;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.25);
  padding: 0.5rem 0.5rem 0.6rem;
  gap: 0.4rem;
  min-width: 5.5rem;
}
#synth-screen .synth-mixer-chassis .synth-mixer-strip-header {
  grid-row: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
}
#synth-screen .synth-mixer-chassis .synth-mixer-vol-wrap { grid-row: 2; }
#synth-screen .synth-mixer-chassis .synth-mixer-pan-wrap { grid-row: 3; }
#synth-screen .synth-mixer-chassis .synth-mute-solo { grid-row: 4; align-self: end; }
#synth-screen .synth-mixer-chassis .synth-mixer-strip-title {
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 800;
  color: #1c1d1e;
}
#synth-screen .synth-mixer-chassis .synth-mixer-jump-btn {
  font-size: 0.68rem;
  padding: 0.15rem 0.5rem;
  background: linear-gradient(180deg, #45484b, #26282a);
  color: #e3e5e7;
  border: 1px solid #101112;
  border-radius: 4px;
}

/* MIXER FADER CORRECTION (2026-09-25, Founder-reported: the native bright-
   blue fader thumb was still showing in Chrome/Edge). Real root cause,
   found by inspecting computed style rather than assuming: -webkit-
   appearance:none was only ever set on the ::-webkit-slider-thumb pseudo,
   never on the &lt;input&gt; itself -- without stripping the native
   appearance at the input level FIRST, Chrome keeps painting its own
   default thumb regardless of the pseudo-element rule. Fixed below.
   Structure also changed: the caption ("VOL") is now a plain sibling
   &lt;span&gt; (not a wrapping &lt;label&gt;) so the real dB scale can sit
   beside the fader at exactly the fader's own track height, with an
   aria-label on the input itself preserving accessibility. The input's
   own id/type/min/max/value/listener are completely unchanged. */
#synth-screen .synth-mixer-chassis .synth-mixer-vol-wrap { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; }
#synth-screen .synth-mixer-chassis .synth-mixer-vol-caption {
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: #1c1d1e;
}
#synth-screen .synth-mixer-chassis .synth-mixer-vol-fader-row { display: flex; align-items: stretch; gap: 0.3rem; height: 5.5rem; }
#synth-screen .synth-mixer-chassis input.synth-mixer-vol {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  width: 1.3rem;
  height: 5.5rem;
  background: transparent;
  margin: 0;
}
#synth-screen .synth-mixer-chassis input.synth-mixer-vol::-webkit-slider-runnable-track {
  width: 5px;
  background: linear-gradient(90deg, #05060a, #1c1f1c, #05060a);
  border-radius: 3px;
  box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.8);
}
#synth-screen .synth-mixer-chassis input.synth-mixer-vol::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 24px;
  height: 14px;
  margin-left: -9.5px;
  border-radius: 2px;
  background: linear-gradient(180deg, #eceeef, #9a9da0 45%, #6d7073);
  border: 1px solid #101112;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.4);
  position: relative;
}
/* Thin dark index/centre line across the cap (real DOM constraint: a
   pseudo-element's own ::before/::after cannot be layered onto ANOTHER
   pseudo-element like ::-webkit-slider-thumb, so this is drawn as part of
   the thumb's own background instead of an extra pseudo layer). */
#synth-screen .synth-mixer-chassis input.synth-mixer-vol::-webkit-slider-thumb {
  background-image: linear-gradient(180deg, transparent 45%, #2a1414 45%, #2a1414 55%, transparent 55%), linear-gradient(180deg, #eceeef, #9a9da0 45%, #6d7073);
}
#synth-screen .synth-mixer-chassis input.synth-mixer-vol::-moz-range-track {
  width: 5px;
  background: linear-gradient(90deg, #05060a, #1c1f1c, #05060a);
  border-radius: 3px;
}
#synth-screen .synth-mixer-chassis input.synth-mixer-vol::-moz-range-thumb {
  -moz-appearance: none;
  width: 24px;
  height: 14px;
  border-radius: 2px;
  background-image: linear-gradient(180deg, transparent 45%, #2a1414 45%, #2a1414 55%, transparent 55%), linear-gradient(180deg, #eceeef, #9a9da0 45%, #6d7073);
  border: 1px solid #101112;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

/* Real dB scale -- positions derived from the actual gain mapping (gain =
   raw fader value / 100, the one real formula every Volume fader here
   uses), NOT a fabricated generic mixer scale. See app.js's own addSlider
   for the exact math and why +6dB/-48dB are omitted. bottom:X% is computed
   per-mark in JS against each unit's own real max (120 for Voice A-D, 150
   for Drum 808/909/Vocals), so this single CSS block serves both scales
   correctly without needing two variants. */
#synth-screen .synth-mixer-chassis .synth-mixer-db-scale {
  position: relative;
  width: 1.1rem;
  height: 100%;
}
#synth-screen .synth-mixer-chassis .synth-mixer-db-mark {
  position: absolute;
  left: 0;
  transform: translateY(50%);
  font-size: 0.6rem;
  font-weight: 600;
  color: #45484b;
  line-height: 1;
  white-space: nowrap;
}
#synth-screen .synth-mixer-chassis .synth-mixer-db-mark::before {
  content: "";
  position: absolute;
  left: -0.3rem;
  top: 50%;
  width: 0.25rem;
  height: 1px;
  background: #83868a;
}
#synth-screen .synth-mixer-chassis .synth-mixer-db-mark-unity {
  color: #1c1d1e;
  font-weight: 800;
}
#synth-screen .synth-mixer-chassis .synth-mixer-db-mark-unity::before { background: #c14535; height: 2px; }

/* Pan knob -- reuses .synth-hw-knob-wrap/.acKnobItem verbatim (free, via
   the .synth-hw-instrument class on the Mixer's own wrapper); only extra
   spacing tweak so a "secondary"-sized (50px) knob sits comfortably in a
   narrow channel strip. The hidden-but-focusable fallback pattern is
   untouched -- .synth-hw-knob-fallback's own hidden/:focus rules already
   apply here too, so the generic vertical-fader rule above (scoped to
   .synth-mixer-vol specifically, NOT .synth-hw-knob-fallback) cannot ever
   reach it. */
#synth-screen .synth-mixer-chassis .synth-mixer-pan-wrap { margin: 0; }

/* MASTER STRIP HARDWARE LAYOUT (2026-09-25, Founder-approved): a real,
   static hardware output section, laid out top-to-bottom as Heading ->
   Scope -> Trim fader + vertical meter -> Trim LCD readout -> Limiter ->
   Record, with the vertical budget (title/scope/fader-row/gaps) engineered
   to put Record's own row at the exact same Y baseline as the other
   channels' Mute/Solo row (measured live: 271px from each strip's own top
   to that row -- see the final report for the exact numbers and how this
   was verified). */
#synth-screen .synth-mixer-chassis .synth-hw-master-strip {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  flex: 0 0 auto;
  background: linear-gradient(180deg, #c9cbce, #aeb0b3);
  border: 1px solid #83868a;
  border-radius: 8px;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.25);
  padding: 0.5rem 0.7rem 0.6rem;
}
/* MASTER STRIP GEOMETRY CORRECTION (2026-09-25, Founder-reported: the
   first pass compressed Trim/meter into a narrow central stack that read
   as "small fader + black bar"). Master is deliberately wider than an
   ordinary channel (measured channel width ~167px; this targets ~1.6x)
   specifically to hold a real two-column output section -- Trim fader
   assembly on the left, output meter (with its own outside scale) on the
   right -- rather than squeezing both into one channel's own width. */
#synth-screen .synth-mixer-chassis .synth-hw-master-strip-wide { min-width: 250px; }
#synth-screen .synth-mixer-chassis .synth-hw-master-strip-title {
  font-size: 0.8125rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  font-weight: 800;
  color: #1c1d1e;
}

/* Scope -- same real &lt;canvas id="synth-scope"&gt;, same synthMeterLoop()
   draw call/analyser. Made genuinely SHALLOWER this pass (was 64px,
   overstated as a "dominant" element) -- a movement/reference display,
   not the strip's main control, per the Founder's explicit target of
   ~35-45px. Spans the full new (wider) Master width. */
#synth-screen .synth-mixer-chassis .synth-hw-scope-shallow {
  width: 100%;
  max-width: 100%;
  height: 40px;
  background: #0e100e;
  border: 1px solid #05060a;
  border-radius: 4px;
}

/* Real two-column output section -- LEFT: Master Trim fader assembly
   (fader + scale + its own LCD readout, genuinely one visual unit, not
   left floating). RIGHT: output meter with its scale on the OUTSIDE edge.
   Both columns share the same 110px working height as their common
   reference, so the fader's travel and the meter's fill genuinely line up
   -- and so the two columns' own dB scales (identical real values) read
   at the same physical height for the same dB, confirming one truthful
   mapping presented twice, not two different ones. */
#synth-screen .synth-mixer-chassis .synth-hw-master-output-section {
  display: flex;
  gap: 0.8rem;
  align-items: flex-start;
  justify-content: center;
  width: 100%;
}
#synth-screen .synth-mixer-chassis .synth-hw-master-fader-col { display: flex; flex-direction: column; align-items: center; gap: 0.3rem; }
#synth-screen .synth-mixer-chassis .synth-hw-master-fader-row-lg { display: flex; align-items: flex-end; gap: 0.6rem; height: 110px; }
#synth-screen .synth-mixer-chassis .synth-hw-master-trim-value-lcd { font-size: 0.8125rem; margin: 0; }

/* Master fader -- same real input.synth-mixer-vol cap/track recipe
   (identical id/min/max/value/listener, only larger here), sized to the
   Founder's own "same physical size as the channel faders, or slightly
   larger" instruction: 110px travel (channel faders are 88px) and a
   modestly larger cap. */
/* Real bug found live (Founder-reported: "the fader has no functionality"):
   the vertical orientation for every channel fader comes from
   ".synth-mixer-strip input[type=range] { writing-mode: vertical-lr;
   direction: rtl }" -- scoped to an ancestor class (.synth-mixer-strip)
   that the Master strip (.synth-hw-master-strip) never had, since Master
   sits outside the channel-strip grid entirely. The Trim input LOOKED
   vertical (explicit width/height override) but was still functioning as
   a horizontal slider in a tall narrow box -- only a few px of horizontal
   drag actually moved it, reading as "broken" to anyone dragging
   vertically as the visual clearly invites. Same real fix, applied here. */
#synth-screen .synth-mixer-chassis input.synth-hw-master-trim-fader-lg {
  writing-mode: vertical-lr;
  direction: rtl;
  width: 1.5rem;
  height: 110px;
}
#synth-screen .synth-mixer-chassis input.synth-hw-master-trim-fader-lg::-webkit-slider-thumb {
  width: 27px;
  height: 16px;
  margin-left: -10.5px;
}
#synth-screen .synth-mixer-chassis input.synth-hw-master-trim-fader-lg::-moz-range-thumb { width: 27px; height: 16px; }

/* Real vertical output meter -- synthMeterLoop() drives #synth-meter-bar's
   own .style.WIDTH, unchanged (confirmed by inspection, not assumed). The
   existing horizontal track is rotated -90deg as a real geometry
   transform: authored at its natural 110x26px horizontal size, centred
   and rotated inside a 26x110px vertical wrapper -- the same working
   height as the fader beside it -- so the same width-driven fill
   genuinely rises bottom-to-top. */
#synth-screen .synth-mixer-chassis .synth-hw-master-meter-col { display: flex; flex-direction: row; align-items: flex-end; gap: 0.35rem; }
#synth-screen .synth-mixer-chassis .synth-hw-master-meter-vertical-lg {
  width: 26px;
  height: 110px;
  position: relative;
  overflow: hidden;
}
#synth-screen .synth-mixer-chassis .synth-hw-master-meter-vertical-lg .synth-hw-meter-led-lg {
  position: absolute;
  width: 110px;
  height: 26px;
  top: 50%;
  left: 50%;
  margin: 0;
  transform: translate(-50%, -50%) rotate(-90deg);
}
/* Segmented LED meter -- genuinely segmented at rest, not a plain black
   rectangle: the TRACK itself carries a dim, always-visible repeating
   segment pattern (the "off" LEDs), and the real width-driven bar carries
   the SAME segment geometry in bright green->amber->red, so active
   segments read as lit LEDs against visibly-present dark ones underneath
   -- one truthful mono level, no fabricated stereo, no fabricated
   movement (0% width at silence lights nothing). */
#synth-screen .synth-mixer-chassis .synth-hw-meter-led-lg {
  width: 110px;
  height: 26px;
  background: repeating-linear-gradient(90deg, #16241a 0px, #16241a 8px, #05060a 8px, #05060a 10px);
  border: 1px solid #05060a;
  border-radius: 3px;
  display: flex;
  align-items: center;
  overflow: hidden;
  padding: 2px;
}
#synth-screen .synth-mixer-chassis .synth-hw-meter-led-lg .synth-meter-bar {
  height: 100%;
  width: 0%;
  background: repeating-linear-gradient(
    90deg,
    transparent 0px, transparent 8px,
    rgba(0,0,0,0.6) 8px, rgba(0,0,0,0.6) 10px
  ), linear-gradient(90deg, #4fe38a 0%, #4fe38a 55%, #ffb020 78%, #c14535 100%);
  background-size: 110px 100%;
  background-repeat: no-repeat;
}
/* Outside-edge scale for the meter -- same real dB marks as the fader's
   own scale (identical percentages, same 110px reference height), placed
   to the meter's own right rather than crushed between it and the fader. */
#synth-screen .synth-mixer-chassis .synth-mixer-db-scale-outside { height: 110px; }
#synth-screen .synth-mixer-chassis .synth-mixer-db-scale-outside .synth-mixer-db-mark { left: 0.3rem; }
#synth-screen .synth-mixer-chassis .synth-mixer-db-scale-outside .synth-mixer-db-mark::before { left: -0.3rem; }

/* Limiter -- upgraded to the same real illuminated-switch recipe as Mute/
   Solo/FX (LED dot + active-state colour), using :has() to style off the
   real existing #synth-limiter-enabled checkbox state -- no new state, no
   behaviour change, purely presentational. */
#synth-screen .synth-mixer-chassis .synth-hw-limiter-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: linear-gradient(180deg, #45484b, #26282a);
  border: 1px solid #101112;
  border-radius: 5px;
  color: #e3e5e7;
  font-size: 0.8125rem;
  font-weight: 700;
  padding: 0.3rem 0.6rem 0.3rem 1.35rem;
  cursor: pointer;
}
#synth-screen .synth-mixer-chassis .synth-hw-limiter-toggle::before {
  content: "";
  position: absolute;
  left: 0.45rem;
  top: 50%;
  transform: translateY(-50%);
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #3a1515;
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.6);
}
#synth-screen .synth-mixer-chassis .synth-hw-limiter-toggle:has(input:checked)::before {
  background: #3dff7a;
  box-shadow: 0 0 6px #3dff7a;
}
#synth-screen .synth-mixer-chassis .synth-hw-limiter-toggle input[type="checkbox"] { margin: 0; position: absolute; opacity: 0; width: 1px; height: 1px; }
#synth-screen .synth-mixer-chassis .synth-hw-master-record-row button {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: linear-gradient(180deg, #45484b, #26282a);
  border: 1px solid #101112;
  border-radius: 5px;
  color: #e3e5e7;
  font-size: 0.8125rem;
  font-weight: 700;
  padding: 0.3rem 0.6rem;
  cursor: pointer;
}
#synth-screen .synth-mixer-chassis .synth-hw-master-record-row { display: flex; gap: 0.4rem; }
/* Real Record state (no class toggle in app.js -- it hides/shows
   #synth-record-btn/#synth-stop-btn directly): #synth-stop-btn only ever
   becomes visible while actually recording, so styling IT red is the
   real, honest "recording" indicator, not a guessed .active class. */
#synth-screen .synth-mixer-chassis #synth-stop-btn {
  background: linear-gradient(180deg, #c14535, #8f281e);
  border-color: #5a1712;
}
#synth-screen .synth-mixer-chassis .synth-hw-record-status { font-size: 0.75rem; color: #45484b; margin: 0.2rem 0 0; text-align: center; }

/* Responsive -- do not crush strips below their real content; wrap the
   whole console to a second line (Master strip included) rather than
   shrinking any one strip. */
@media (max-width: 900px) {
  #synth-screen .synth-mixer-chassis .synth-mixer-console { flex-wrap: wrap; }
  #synth-screen .synth-mixer-chassis .synth-hw-master-strip { flex: 1 1 100%; }
}
/* ========================================================================== */

/* ==========================================================================
   ASK ARCHIE STUDIO -- ARCHIE COMPOSER HARDWARE REBUILD (2026-09-25,
   Founder-approved). PRESENTATION ONLY -- see index.html's own comment
   above this section's markup for the full Deep Build / control-contract
   audit. Reuses .synth-hw-instrument (added to the chassis in index.html)
   for the base silver panel/h3/label treatment for free, same as the
   Mixer; everything below is genuinely new structure (topplate, genre
   row, module grid, tempo strip, response display). Root cause of the old
   huge empty area (found before writing any of this): .synth-compose-
   split's own align-items:stretch forced the right-hand response column
   to match the much taller left column's height -- this rebuild removes
   that two-column split entirely for the Composer (VST Builder's own use
   of that shared class elsewhere is completely untouched). */
#synth-screen .archie-composer-chassis { padding: 0.65rem 0.75rem; }

/* Top plate -- green LED + title + secondary wording + the existing real
   help button, one compact row instead of an h3 alone. */
#synth-screen .archie-composer-chassis .archie-composer-topplate {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  border-bottom: 2px solid #7a7d80;
  padding-bottom: 0.45rem;
  margin-bottom: 0.5rem;
}
#synth-screen .archie-composer-chassis .archie-composer-led {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #3dff7a;
  box-shadow: 0 0 5px #3dff7a;
  flex-shrink: 0;
}
#synth-screen .archie-composer-chassis .archie-composer-title {
  font-size: 0.95rem;
  letter-spacing: 0.08em;
  font-weight: 800;
  color: #1c1d1e;
  text-transform: uppercase;
  margin: 0;
  border: none;
  padding: 0;
}
/* TYPOGRAPHY CORRECTION (2026-09-25, Founder-reported: the hardware/
   display treatment -- small size + heavy letter-spacing + uppercase --
   was becoming genuinely hard to read, not just "compact"). The site's
   own base sans-serif (var(--font-family), inherited by default -- no new
   font was ever introduced here) is kept for all of this; only the
   display heading (.archie-composer-title, "ARCHIE COMPOSER" itself, big
   enough to stay legible) keeps the full hardware letter-spacing/
   uppercase treatment. Everything below is raised to real floors: labels
   >=12-13px, buttons >=13px, body text >=12-13px with real line-height,
   letter-spacing cut back sharply wherever it was the actual cause of the
   "condensed" look. */
#synth-screen .archie-composer-chassis .archie-composer-subtitle {
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: #45484b;
  flex: 1 1 auto;
}
#synth-screen .archie-composer-chassis .archie-composer-help-btn {
  font-size: 0.8125rem;
  padding: 0.25rem 0.6rem;
  background: linear-gradient(180deg, #45484b, #26282a);
  color: #e3e5e7;
  border: 1px solid #101112;
  border-radius: 4px;
  cursor: pointer;
  flex-shrink: 0;
}

/* STYLE / GENRE -- one compact hardware selector row, target ~40-45px.
   Overrides the shared .synth-style-chip's own dark-panel colours (light
   text on transparent -- invisible against this silver background),
   scoped to this chassis only; every other real user of .synth-style-chip
   on the page (VST Builder's own chips) is completely unaffected. */
#synth-screen .archie-composer-chassis .archie-composer-genre-panel {
  display: flex;
  gap: 0.35rem;
  flex-wrap: wrap;
  margin: 0 0 0.5rem;
}
#synth-screen .archie-composer-chassis.community-block button.synth-style-chip {
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  color: #1c1d1e;
  border: 1px solid #83868a;
  border-radius: 5px;
  font-size: 0.8125rem;
  font-weight: 700;
  padding: 0.35rem 0.7rem;
  min-height: 34px;
}
#synth-screen .archie-composer-chassis.community-block button.synth-style-chip:hover {
  border-color: #3dff7a;
  color: #1c1d1e;
}

/* Generic hardware module -- recessed panel, same recipe as Voice A's own
   .synth-hw-group, but tighter (controls determine size, not a web-card
   padding budget). */
#synth-screen .archie-composer-chassis .archie-composer-module {
  background: linear-gradient(180deg, #c9cbce, #aeb0b3);
  border: 1px solid #83868a;
  border-radius: 8px;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.25);
  padding: 0.5rem 0.6rem;
  min-width: 0;
}
#synth-screen .archie-composer-chassis .archie-composer-module-title {
  font-size: 0.8125rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  font-weight: 800;
  color: #1c1d1e;
  margin-bottom: 0.4rem;
}
#synth-screen .archie-composer-chassis .archie-composer-row { display: flex; gap: 0.5rem; align-items: center; }
#synth-screen .archie-composer-chassis .archie-composer-row-wrap { flex-wrap: wrap; row-gap: 0.35rem; }
#synth-screen .archie-composer-chassis .archie-composer-row input[type="text"],
#synth-screen .archie-composer-chassis .archie-composer-row input[type="number"] {
  font-size: 0.85rem;
}
#synth-screen .archie-composer-chassis .archie-composer-module .muted { font-size: 0.8125rem; line-height: 1.4; margin: 0.35rem 0 0; }
#synth-screen .archie-composer-chassis .archie-composer-module p:first-of-type { margin-top: 0; }
#synth-screen .archie-composer-chassis .archie-composer-module audio { width: 100%; height: 28px; margin-top: 0.3rem; }

/* PRIMARY COMPOSER CONTROL -- the visual focus, per the brief. Larger real
   input + a strong physical Ask Archie action button beside it, same
   #synth-compose-input/#synth-compose-btn, same submit behaviour. */
#synth-screen .archie-composer-chassis .archie-composer-main-row {
  display: flex;
  gap: 0.6rem;
  margin-bottom: 0.5rem;
  align-items: stretch;
}
#synth-screen .archie-composer-chassis .archie-composer-prompt-module { flex: 2.2 1 380px; }
#synth-screen .archie-composer-chassis .archie-composer-behaviour-module { flex: 1 1 240px; display: flex; flex-direction: column; }
#synth-screen .archie-composer-chassis .archie-composer-prompt-module input[type="text"] {
  font-size: 0.9rem;
  padding: 0.55rem 0.7rem;
  border: 1px solid #83868a;
  border-radius: 5px;
  background: #f4f5f6;
  color: #1c1d1e;
}
#synth-screen .archie-composer-chassis .archie-composer-primary-btn {
  background: linear-gradient(180deg, #4fe38a, #1e9a4a);
  color: #06210f;
  border: 1px solid #0a4a20;
  border-radius: 5px;
  font-weight: 800;
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  padding: 0.5rem 1rem;
  white-space: nowrap;
  cursor: pointer;
}
#synth-screen .archie-composer-chassis .archie-composer-toggle-label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.8125rem;
  color: #1c1d1e;
  margin: 0.3rem 0;
  cursor: pointer;
}

/* REFERENCE AUDIO | MIDI IMPORT | VOCALS -- three modules across on
   desktop, target ~100-120px row height where real content allows (Vocals
   genuinely holds more controls, so it may run taller -- not forced to
   match). */
#synth-screen .archie-composer-chassis .archie-composer-io-row {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  margin-bottom: 0.5rem;
}
#synth-screen .archie-composer-chassis .archie-composer-io-row > .archie-composer-module { flex: 1 1 0; }
#synth-screen .archie-composer-chassis .archie-composer-inline-heading { margin: 0 0 0.4rem; }

/* SONG TEMPO -- a shallow strip, target ~50-60px. Presentation heading
   only; #synth-vocals-source-bpm/#synth-vocals-warp-toggle are completely
   unchanged (see index.html's own comment on this exact point). */
#synth-screen .archie-composer-chassis .archie-composer-tempo-strip {
  display: flex;
  align-items: center;
  gap: 1rem;
  background: linear-gradient(180deg, #c9cbce, #aeb0b3);
  border: 1px solid #83868a;
  border-radius: 8px;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.25);
  padding: 0.4rem 0.7rem;
  margin-bottom: 0.5rem;
}
#synth-screen .archie-composer-chassis .archie-composer-tempo-strip .archie-composer-module-title { margin: 0; }
#synth-screen .archie-composer-chassis .archie-composer-tempo-label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.8125rem;
  color: #1c1d1e;
}
#synth-screen .archie-composer-chassis .archie-composer-tempo-label input {
  font-family: ui-monospace, Consolas, monospace;
  font-size: 0.8125rem;
  font-weight: 700;
  background: #0e100e;
  color: #7fe3a3;
  border: 1px solid #05060a;
  border-radius: 4px;
  padding: 0.2rem 0.4rem;
}

/* ARCHIE'S RESPONSE -- the core structural fix. Shallow empty state
   (~90-120px via min-height, real content-driven otherwise), grows for a
   real response up to a sensible cap, then scrolls internally rather than
   ever making the whole instrument enormous again. */
#synth-screen .archie-composer-chassis .archie-composer-response {
  background: #0e100e;
  border: 1px solid #05060a;
  border-radius: 8px;
  padding: 0.6rem 0.7rem;
  min-height: 64px;
  max-height: 260px;
  overflow-y: auto;
}
#synth-screen .archie-composer-chassis .archie-composer-response .archie-composer-module-title { color: #7fe3a3; }
#synth-screen .archie-composer-chassis .archie-composer-response .muted { color: #d7e3d7; font-size: 0.85rem; line-height: 1.4; }
#synth-screen .archie-composer-chassis .archie-composer-response #synth-compose-save-btn {
  margin-top: 0.5rem;
  background: linear-gradient(180deg, #45484b, #26282a);
  color: #e3e5e7;
  border: 1px solid #101112;
  border-radius: 4px;
  padding: 0.35rem 0.7rem;
  font-size: 0.8125rem;
  cursor: pointer;
}

/* Responsive -- 3 columns -> 2 -> 1, per the brief; no shrinking controls,
   no page-level horizontal overflow (real min-widths, natural wrap). */
@media (max-width: 1000px) {
  #synth-screen .archie-composer-chassis .archie-composer-io-row { flex-wrap: wrap; }
  #synth-screen .archie-composer-chassis .archie-composer-io-row > .archie-composer-module { flex: 1 1 45%; }
}
@media (max-width: 700px) {
  #synth-screen .archie-composer-chassis .archie-composer-main-row { flex-direction: column; }
  #synth-screen .archie-composer-chassis .archie-composer-io-row > .archie-composer-module { flex: 1 1 100%; }
}
/* ========================================================================== */

/* ==========================================================================
   ASK ARCHIE STUDIO -- SEQUENCER HARDWARE FACEPLATE PASS (2026-09-25,
   Founder-approved). PRESENTATION ONLY -- see index.html's own comment
   above this section's markup for the full inspection this was based on.
   Reuses .synth-hw-instrument (added to the chassis in index.html) for the
   base silver panel treatment for free, same as Composer/Mixer/Voice A.
   Every new rule below is scoped to this panel's own specific ids
   (#synth-play-mode-toggle, #synth-bank-switch-mode-toggle,
   #synth-piano-roll-unit-switcher, #synth-song-strip, #synth-seq-play-btn,
   #synth-seq-bpm) rather than the bare .synth-quick-link-btn/.muted/
   .synth-song-strip classes those elements also carry -- those classes are
   real, separately-used site-wide (the global Quick Links nav bar,
   Composer's own help button, etc.), so nothing outside this panel is
   touched. */
#synth-screen .synth-hw-sequencer-chassis .synth-hw-sequencer-topplate {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  border-bottom: 2px solid #7a7d80;
  padding-bottom: 0.45rem;
  margin-bottom: 0.5rem;
}
#synth-screen .synth-hw-sequencer-chassis .synth-hw-sequencer-led {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #3dff7a;
  box-shadow: 0 0 5px #3dff7a;
  flex-shrink: 0;
}
#synth-screen .synth-hw-sequencer-chassis .synth-hw-sequencer-title {
  font-size: 0.95rem;
  letter-spacing: 0.08em;
  font-weight: 800;
  color: #1c1d1e;
  text-transform: uppercase;
  margin: 0;
  border: none;
  padding: 0;
  flex: 1 1 auto;
}

/* Help disclosure -- the long descriptive paragraph, previously
   permanently occupying panel space, now sits behind a real, contract-free
   &lt;details&gt; (no new id/state -- pure HTML) styled to match Composer's
   own help button; opens as a real dropdown panel, closes again, nothing
   server-side involved. */
#synth-screen .synth-hw-sequencer-chassis .synth-hw-help-details { position: relative; flex-shrink: 0; }
#synth-screen .synth-hw-sequencer-chassis .synth-hw-help-details summary {
  font-size: 0.8125rem;
  padding: 0.25rem 0.6rem;
  background: linear-gradient(180deg, #45484b, #26282a);
  color: #e3e5e7;
  border: 1px solid #101112;
  border-radius: 4px;
  cursor: pointer;
  list-style: none;
}
#synth-screen .synth-hw-sequencer-chassis .synth-hw-help-details summary::-webkit-details-marker { display: none; }
#synth-screen .synth-hw-sequencer-chassis .synth-hw-help-details[open] summary { border-color: #3dff7a; }
#synth-screen .synth-hw-sequencer-chassis .synth-hw-help-details p.muted {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 5;
  width: 20rem;
  max-width: 70vw;
  margin-top: 0.3rem;
  padding: 0.6rem 0.7rem;
  font-size: 0.8125rem;
  line-height: 1.4;
  color: #1c1d1e;
  background: linear-gradient(180deg, #eceeef, #d7d9db);
  border: 1px solid #83868a;
  border-radius: 6px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
}

/* Transport/timing toolbar -- same real #synth-seq-play-btn/#synth-seq-bpm
   and the two existing .synth-quick-link-btn mode-toggle groups, one
   coherent hardware row. */
#synth-screen .synth-hw-sequencer-chassis .synth-hw-seq-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.7rem;
  margin-bottom: 0.5rem;
}
#synth-screen .synth-hw-sequencer-chassis .synth-hw-seq-play-btn {
  background: linear-gradient(180deg, #4fe38a, #1e9a4a);
  color: #06210f;
  border: 1px solid #0a4a20;
  border-radius: 5px;
  font-weight: 800;
  font-size: 0.8125rem;
  padding: 0.4rem 0.8rem;
  cursor: pointer;
}
#synth-screen .synth-hw-sequencer-chassis .synth-hw-seq-bpm-label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.8125rem;
  font-weight: 700;
  color: #1c1d1e;
}
#synth-screen .synth-hw-sequencer-chassis #synth-seq-bpm { accent-color: #c14535; width: 7rem; }
#synth-screen .synth-hw-sequencer-chassis #synth-seq-bpm-value { font-size: 0.8125rem; }

/* Illuminated segmented switches -- Play Song/Loop Sequence and Quantized/
   Instant, ID-scoped so the shared .synth-quick-link-btn class used by the
   global Quick Links nav bar (and Composer's own help button) is never
   touched. Same real "active" class the existing click handlers already
   toggle -- no new state. */
#synth-screen .synth-hw-sequencer-chassis #synth-play-mode-toggle,
#synth-screen .synth-hw-sequencer-chassis #synth-bank-switch-mode-toggle,
#synth-screen .synth-hw-sequencer-chassis #synth-piano-roll-unit-switcher {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
#synth-screen .synth-hw-sequencer-chassis #synth-play-mode-toggle .synth-quick-link-btn,
#synth-screen .synth-hw-sequencer-chassis #synth-bank-switch-mode-toggle .synth-quick-link-btn,
#synth-screen .synth-hw-sequencer-chassis #synth-piano-roll-unit-switcher .synth-quick-link-btn {
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  border: 1px solid #83868a;
  border-radius: 5px;
  color: #1c1d1e;
  font-size: 0.8125rem;
  font-weight: 700;
  padding: 0.4rem 0.75rem;
  min-height: 36px;
  cursor: pointer;
}
#synth-screen .synth-hw-sequencer-chassis #synth-play-mode-toggle .synth-quick-link-btn.active,
#synth-screen .synth-hw-sequencer-chassis #synth-bank-switch-mode-toggle .synth-quick-link-btn.active,
#synth-screen .synth-hw-sequencer-chassis #synth-piano-roll-unit-switcher .synth-quick-link-btn.active {
  background: linear-gradient(180deg, #c14535, #8f281e);
  border-color: #5a1712;
  color: #fff;
  box-shadow: 0 0 8px rgba(193, 69, 53, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.25);
}

/* Song strip -- same real renderSynthSongStrip() output/visibility logic
   (hidden unless a real Song exists), restyled as a recessed sub-panel
   consistent with the rest of the chassis, not moved or reinterpreted. */
#synth-screen .synth-hw-sequencer-chassis #synth-song-strip.synth-hw-seq-song-strip {
  background: rgba(0, 0, 0, 0.12);
  border: 1px solid #83868a;
  border-radius: 6px;
  padding: 0.5rem 0.6rem;
  margin-bottom: 0.5rem;
}
/* Real fix, found live: .synth-mixer-jump-btn (the real Vocals link
   rendered inside this strip by renderSynthSongStrip()) assumes a dark
   panel background (light text) -- this strip's own silver-family
   background made that light-on-light and effectively invisible. Scoped
   here only, so the Mixer's own real use of the same class is untouched. */
#synth-screen .synth-hw-sequencer-chassis #synth-song-strip.synth-hw-seq-song-strip .synth-mixer-jump-btn {
  color: #1c1d1e;
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  border-color: #83868a;
}
#synth-screen .synth-hw-sequencer-chassis #synth-song-strip.synth-hw-seq-song-strip .synth-song-strip-loop-label { color: #1c1d1e; }

/* Source-selection strip -- immediately above the piano roll, per the
   Founder's own target layout. */
#synth-screen .synth-hw-sequencer-chassis .synth-hw-seq-source-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 0.4rem;
}
#synth-screen .synth-hw-sequencer-chassis .synth-hw-seq-instruction {
  font-size: 0.8125rem;
  line-height: 1.4;
  color: #45484b;
  margin: 0 0 0.4rem;
}

/* Piano-roll display bezel -- same real #synth-piano-roll grid/render
   function/scrolling (.synth-piano-roll-wrap's own overflow:auto,
   max-height, completely unchanged), now a genuinely recessed dark LCD-
   style display inset into the silver chassis instead of a thin border
   that blended into the page background. */
#synth-screen .synth-hw-sequencer-chassis .synth-hw-piano-roll-bezel {
  background: #05060a;
  border: 2px solid #6f7275;
  border-radius: 8px;
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.7), inset 0 0 0 1px #000;
  padding: 4px;
}
/* Restrained hardware scrollbar -- narrow dark track, silver/grey thumb,
   replacing the large default browser scrollbar the Founder flagged.
   Firefox via scrollbar-width/color; WebKit/Chromium/Edge via the
   ::-webkit-scrollbar family. Purely visual -- the same real overflow:auto
   scrolling behaviour is untouched. */
#synth-screen .synth-hw-sequencer-chassis .synth-hw-piano-roll-bezel {
  scrollbar-width: thin;
  scrollbar-color: #9a9da0 #1c1f1c;
}
#synth-screen .synth-hw-sequencer-chassis .synth-hw-piano-roll-bezel::-webkit-scrollbar { width: 10px; height: 10px; }
#synth-screen .synth-hw-sequencer-chassis .synth-hw-piano-roll-bezel::-webkit-scrollbar-track { background: #1c1f1c; border-radius: 4px; }
#synth-screen .synth-hw-sequencer-chassis .synth-hw-piano-roll-bezel::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, #c9cbce, #9a9da0);
  border-radius: 4px;
  border: 1px solid #05060a;
}
#synth-screen .synth-hw-sequencer-chassis .synth-hw-piano-roll-bezel::-webkit-scrollbar-thumb:hover { background: #eceeef; }

/* Piano-roll readability -- octave/note key labels raised to the accepted
   legibility floor (previous 0.65rem was below it); font-size only -- the
   real .white/.black key rules keep their own existing text colour
   (#1c2430 on a light key, #d7e3d7 on a dark one), each already correct
   for its own background, so overriding colour here would have broken
   contrast on one of the two. */
#synth-screen .synth-hw-sequencer-chassis .synth-piano-roll-key { font-size: 0.75rem; }
/* ========================================================================== */

/* Real fix (2026-09-06, founder-directed: "we need to update the sequencer
   with the vocal track and link") -- a real, jumpable sub-heading for the
   Vocals track inside the Ask Archie box, smaller than the block's own h3
   (this isn't a whole new top-level section, just a real, named landing
   point the shared Quick Links nav can scroll to). */
#synth-screen .community-block .synth-inline-heading {
  color: #d7e3d7;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.75rem;
  margin: 0.75rem 0 0.4rem;
}
/* Real fix (2026-09-01, founder-reported: "cant see 'Kit exported.' until
   its actually exported the text color is wrong") -- a real success
   confirmation needs to actually read as one, not blend in as the same
   muted grey as an empty/idle state. Same green already used for an active
   automation badge on this exact screen, for real visual consistency. */
#synth-screen .synth-status-ok { color: #6fbf73; font-weight: 600; }
#synth-screen .synth-rack-module {
  background: linear-gradient(180deg, #23261f 0%, #191b16 100%);
  border: 1px solid #3d423d;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.04);
}
#synth-screen .synth-rack-module-header strong { color: #d7e3d7; }
.synth-meter { flex: 1; max-width: 260px; height: 12px; border: 1px solid var(--border); border-radius: 999px; overflow: hidden; background: var(--surface); }
.synth-meter-bar { height: 100%; width: 0%; background: linear-gradient(90deg, #4caf50, #cddc39 70%, #f44336 92%); transition: width 0.06s linear; }
.synth-scope { width: 100%; max-width: 640px; height: 100px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: #0f1a12; display: block; margin-top: 0.5rem; }
/* @theme-component: .pricing-select-btn */
.pricing-select-btn {
  display: block;
  margin-top: 1rem;
  padding: 0.6rem 1rem;
  text-align: center;
  border-radius: var(--radius-md);
  background: var(--accent);
  color: var(--on-accent);
  text-decoration: none;
  font-weight: 600;
  transition: filter var(--transition-soft);
}
/* @theme-component: .pricing-select-btn:visited */
.pricing-select-btn:visited { color: var(--on-accent); }
/* @theme-component: .pricing-select-btn:hover */
.pricing-select-btn:hover { filter: brightness(0.92); color: var(--on-accent); }
/* @theme-component: .pricing-card-recommended */
.pricing-card-recommended { border-color: var(--accent); }
/* @theme-component: .pricing-recommended-badge */
.pricing-recommended-badge {
  position: absolute;
  top: -0.6rem;
  right: 1rem;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.7rem;
  font-weight: 600;
  padding: 0.2rem 0.6rem;
  border-radius: var(--radius-pill);
}

/* @theme-component: #public-listing-screen .topbar */
/* Social sharing (2026-08-22): every other .topbar sits 3.2rem down,
   stacking directly below the global #header-state-bar -- but that bar is
   hidden for a logged-out visitor (real, deliberate: no authenticated nav
   makes sense to show them), so on this one screen alone there is nothing
   above the topbar to offset for. Without this, the topbar's sticky
   position left a phantom 3.2rem gap and the page's own heading rendered
   overlapping underneath it -- a real bug, not a design choice. */
#public-listing-screen .topbar { top: 0; }
/* Real gap found live (2026-08-30, founder-reported: "css is obscuring some
   text"). Same real bug as public-listing-screen above, but app-store-screen
   is reachable BOTH logged in (real app header present, top: 3.2rem is
   correct so the topbar clears it) and logged out (no header at all,
   top: 3.2rem leaves a phantom gap and the sticky topbar overlaps the intro
   paragraph on scroll) -- .no-app-header is toggled by
   showPublicAppStoreScreen based on the real, current state.me, not
   assumed from the screen alone. */
#app-store-screen.no-app-header .topbar { top: 0; }
/* @theme-component: .topbar */
.topbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  padding: 0.5rem 1rem;
  min-height: 3.2rem;
  border-bottom: 1px solid var(--topbar-border);
  position: sticky;
  top: 3.2rem;
  z-index: 40;
  background: var(--topbar-bg);
  color: var(--topbar-text);
}
/* @theme-component: .topbar h1 */
.topbar h1 { font-size: 1.1rem; margin: 0; color: var(--topbar-text); }
/* @theme-component: .topbar .link-btn */
.topbar .link-btn { color: var(--topbar-text); }
/* @theme-component: .topbar .link-btn:hover */
.topbar .link-btn:hover { color: var(--accent); }
/* @theme-component: .topbar .muted */
.topbar .muted { color: var(--topbar-muted); }
/* Unified nav-links row (2026-08-15, redesigned again): the absolute
   viewport-centred version caused real text overlap with the title on
   real project names, so this goes back to flex:1 centring in the
   remaining space -- but now the two things that actually caused visible
   drift page-to-page are fixed at the root instead of patched per page:
   back-button is a fixed width regardless of its label ("Back" vs
   "Projects"), and Notifications/Org-balance moved out of the Home
   topbar entirely (see #header-state-bar) so every topbar's right side
   is symmetrically just "Log out", including Home. With both sides now
   constant width, nav's centred position stays constant too. */
.topbar-links { flex: 1 1 auto; display: flex; justify-content: center; align-items: center; gap: 1rem; flex-wrap: wrap; max-width: 1600px; margin: 0 auto; }
.topbar-right { display: flex; align-items: center; gap: 0.75rem; flex: 0 0 auto; }
.topbar > .link-btn:first-child, .topbar > button.link-btn:first-child { min-width: 7rem; }
/* Page title, positioned in-row (2026-08-15, corrected): lives in the
   same topbar row as the nav links, not a separate row, and not one of
   the centred nav-links group -- absolutely positioned so it sits at
   exactly the same x as .left-panel/.archie-panel's text (the content
   edge), on every page, regardless of that page's own content width.
   The 6.5rem floor clears the back button's own box on viewports under
   the 1600px cap (i.e. most real windows), where the raw content-edge
   formula would otherwise collapse to the same x as the back button. */
/* % (not vw) so this matches .split's own centring math exactly --
   vw includes the scrollbar's width, % of .topbar's own box doesn't,
   and that mismatch was throwing this off by several px. */
/* Title position (2026-08-15, fixed width not max-width): the last
   source of drift -- a short title ("Chat") shrank its flex box to fit
   the text, leaving MORE room for nav's flex:1 box on that screen than
   on a page with a longer title ("Settings"), so nav's centred position
   still shifted page to page even with the right side now constant.
   A fixed width (not max-width) reserves the exact same space on every
   screen regardless of how much text is actually in it -- short titles
   just leave blank space in their own box instead of giving that space
   back to nav. Left side is now genuinely constant, matching the right. */
.topbar-title, .topbar h1.topbar-title { width: 200px; flex: 0 0 200px; margin: 0 0 0 1rem; font-size: 1.1rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.topbar-title h1 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 0 1 auto; }
.topbar-title #workspace-rename-btn, .topbar-title #workspace-context-label { flex-shrink: 0; white-space: nowrap; }

/* Real fix (2026-09-01, founder-reported: "move the save session up to the
   top and add to the left side of the synth one header") -- a compact
   whole-session save/load, sized to fit the real 3.2rem-tall topbar. */
.synth-header-session { display: flex; align-items: center; gap: 0.4rem; }
.synth-header-session input[type="text"] { width: 9rem; padding: 0.3rem 0.5rem; font-size: 0.85rem; }
.synth-header-session button { padding: 0.3rem 0.6rem; font-size: 0.85rem; }
.synth-header-session .muted { font-size: 0.8rem; max-width: 12rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Real fix (2026-09-05, founder-directed: "provide a model selection in the
   synth header nav") -- same compact sizing as the rest of this header row. */
.synth-archie-model-label { display: flex; align-items: center; gap: 0.3rem; font-size: 0.8rem; margin-left: 0.4rem; }
.synth-archie-model-label select { padding: 0.3rem 0.5rem; font-size: 0.85rem; }
/* Real fix (2026-09-05, founder-directed: "lets do a tool tip next to model
   selection and add dialog") -- small, unobtrusive info trigger right next
   to the select, same real link-btn base every other small text-button on
   this header row already uses. */
.synth-archie-model-info-btn { padding: 0.1rem 0.35rem; font-size: 0.95rem; margin-left: -0.15rem; }

/* Real fix (2026-09-01, founder-directed: "put it on the right side")
   -- mirrors .synth-header-session's own compact sizing, pushed flush
   right in the same real topbar row. */
.synth-header-share { display: flex; align-items: center; gap: 0.4rem; margin-left: auto; }
.synth-header-share select { padding: 0.3rem 0.5rem; font-size: 0.85rem; }
/* Real fix (2026-09-01, founder-reported: "im only seeing group and i dont
   know which one that is") -- a real, visible "Group:" label. */
#synth-share-group-label { display: flex; align-items: center; gap: 0.3rem; font-size: 0.85rem; }
.synth-header-share button { padding: 0.3rem 0.6rem; font-size: 0.85rem; }
.synth-header-share .muted { font-size: 0.8rem; max-width: 14rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Real, live organisational OpenRouter balance -- admin-only, visible in
   the nav rather than buried in the Admin panel, colour-coded to match the
   same real 20%/10%/5% alert thresholds already checked server-side. */
/* @theme-component: .org-balance-badge */
.org-balance-badge {
  border: 1px solid currentColor;
  border-radius: var(--radius-pill);
  padding: 0.15rem 0.6rem;
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  background: transparent;
}
/* @theme-component: .org-balance-badge.org-balance-ok */
.org-balance-badge.org-balance-ok { color: var(--accent); }
/* @theme-component: .org-balance-badge.org-balance-warn */
.org-balance-badge.org-balance-warn { color: var(--warning); }
/* @theme-component: .org-balance-badge.org-balance-critical */
.org-balance-badge.org-balance-critical { color: var(--danger); }

/* @theme-component: .link-btn */
.link-btn {
  background: none;
  border: none;
  color: var(--accent);
  cursor: pointer;
  font-size: 0.9rem;
}

/* @theme-component: button */
button {
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  border-radius: var(--radius-md);
  padding: 0.5rem 1rem;
  cursor: pointer;
  transition: background-color var(--transition-soft), transform var(--transition-soft), opacity var(--transition-soft);
}
/* Chat Window Interface brief: real hover feedback on the single most
   common interactive element in the app -- previously none existed at all.
   A soft darken + a barely-there lift, not a jarring pop. */
button:hover:not(:disabled) { filter: brightness(0.92); transform: translateY(-1px); }
button:active:not(:disabled) { transform: translateY(0); filter: brightness(0.85); }
button:disabled { opacity: 0.5; cursor: not-allowed; }

.link-btn { transition: color var(--transition-soft); }
/* @theme-component: .link-btn:hover */
.link-btn:hover { color: var(--text); }

/* Real fix (2026-09-05, founder-directed: "can we add the same buttons
   throught the site where they are missing with the input prompt refering
   to the actual too/page") -- a small, generic "How do I use this?" helper
   reused next to a heading on any page outside Synth's own scoped styling
   (Image Maker, Video, Music Lab, Video Editor's command row). Built on
   .link-btn (already generic/theme-aware everywhere) rather than Synth's
   own #synth-screen-scoped pill style, which has no effect outside it. */
.how-to-use-btn { font-size: 0.75rem; font-weight: 500; margin-left: 0.6rem; vertical-align: middle; white-space: nowrap; }

.project-list-main { max-width: 640px; margin: 2rem auto; padding: 0 1rem; }
/* The real cause of the group-detail layout looking "squeezed" wasn't the
   post-card CSS -- it was this 640px cap on the shared page-width class
   itself. A 3-column layout (admin ~220px + feed up to 480px + a real
   320px chat panel, plus gaps) needs ~1050px minimum; crammed into 640px
   with flex's min-width:0 letting children shrink, every column and its
   contents got crushed instead of the whole thing just wrapping. */
/* Unconstrained, matching .community-hub-main (the profile page's own
   main column) exactly -- the group's cover banner needs to span the same
   real width the profile's own cover does for the two pages to feel like
   the same product, not narrower just because this page happens to use
   the generic .project-list-main class elsewhere. */
#groups-screen .project-list-main { max-width: none; margin: 0 auto; padding: 0 1rem; }
/* The video editor is a dense, viewport-constrained workspace -- the
   generic 2rem top margin (sized for narrow content pages) is pure wasted
   space here, pushing the preview and timeline further down for no
   benefit. */
#video-editor-screen .project-list-main { margin-top: 0.25rem; }
#video-editor-save-status { margin: 0; font-size: 0.75rem; line-height: 1.2; min-height: 0; }
#new-project-form { display: flex; gap: 0.5rem; margin-bottom: 1.5rem; flex-wrap: wrap; }
/* @theme-component: #new-project-form input */
#new-project-form input { flex: 1; padding: 0.5rem; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--panel-bg); color: var(--text); }

#project-list { list-style: none; padding: 0; }
/* @theme-component: #project-list li */
/* Rename Alignment correction (2026-08-15): the real bug was
   justify-content: space-between with Rename as the middle child of three --
   Archive (last child) always lands flush against the right edge, but
   Rename's position depended on how much space the title span ahead of it
   consumed, so it drifted row to row with title length. A real grid with
   fixed-width action columns (title gets the flexible column, each button
   gets its own fixed one) makes both buttons land in the same place on
   every row regardless of title or button-label length ("Archive" vs
   "Unarchive"). */
/* Real fix (2026-09-23, founder-reported: "try taking some styling from
   vst builder and use for the app builder page") -- the same real
   corner-screw/chassis background .vst-builder-project-row already uses,
   reused directly rather than a second, hand-matched copy. */
#project-list li {
  padding: 0.9rem 1rem;
  background:
    radial-gradient(circle at 10px 10px, #4a5058 0 1.6px, transparent 2.6px),
    radial-gradient(circle at calc(100% - 10px) 10px, #4a5058 0 1.6px, transparent 2.6px),
    radial-gradient(circle at 10px calc(100% - 10px), #4a5058 0 1.6px, transparent 2.6px),
    radial-gradient(circle at calc(100% - 10px) calc(100% - 10px), #4a5058 0 1.6px, transparent 2.6px),
    linear-gradient(180deg, rgba(255, 255, 255, 0.035), transparent 60px),
    var(--vstb-bg, #14161a);
  border: 1px solid #05060a;
  border-radius: 14px;
  box-shadow:
    0 10px 26px rgba(0, 0, 0, 0.4),
    inset 0 0 0 1px rgba(255, 255, 255, 0.04),
    inset 0 1px 0 rgba(255, 255, 255, 0.05);
  color: var(--vstb-text, #f2f2f0);
  margin-bottom: 0.5rem;
  cursor: pointer;
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: 1rem;
  transition: border-color var(--transition-soft);
}
#project-list li .muted { color: var(--vstb-muted, #9aa0a6); }
#project-list li a { color: #8ab4f8; }
#project-list li a:hover { color: #aecaff; }
/* Real fix (2026-09-23) -- label is now inside .my-app-left-col, not a
   direct child of li, since the two-column split above moved every
   existing element down one level; this selector needs to follow it. */
.my-app-left-col > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#project-list li .admin-remove-btn { margin-top: 0; }
/* @theme-component: #project-list li:hover */
#project-list li:hover { border-color: var(--accent); }

/* Real two-column split (2026-09-23, founder-directed: "do the same with
   the app cards", following the identical VST Builder project-card
   treatment) -- a true 50/50 split (flex:1, flex-basis:0 via the shorthand
   on both columns), copy/links/actions in the left column, a real
   screenshot (or the same standard "no thumbnail yet" badge) filling the
   right. */
.my-app-left-col { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.my-app-right-col { flex: 1; min-width: 0; display: flex; align-items: center; justify-content: center; }
/* Real fix (2026-09-23, founder-reported: "these corners seem to be
   bleeding through, lets fall them back to the head and foot colour" --
   same real fix as the VST Builder equivalent). Updated same day when
   #project-list li's own background changed to the VST Builder chassis
   tone -- matches that same real background now, not the generic
   surface-2/panel-bg tokens, so any exposed area around a real image (its
   own rounded corners, or object-fit:contain letterboxing) blends into
   the card instead of showing a mismatched patch. */
.my-app-thumb {
  width: 100%; height: 100%; border-radius: 8px;
  object-fit: contain; background: var(--vstb-bg, #14161a);
}
.my-app-thumb.home-card-badge { width: 100%; height: auto; aspect-ratio: 1 / 1; margin-bottom: 0; transform: none; }
/* Real fix (2026-09-23) -- same real reason as the VST Builder equivalent:
   object-fit:contain preserves a placeholder image's own opaque white
   margin (no alpha channel); cover crops it away. Scoped to the
   placeholder only, a real screenshot keeps the centred/letterboxed
   contain treatment. */
.my-app-thumb-placeholder { object-fit: cover; }

.split {
  display: flex;
  height: calc(100vh - 57px);
  max-width: 1600px;
  margin: 0 auto;
}

/* Real two-column "Ask Archie" layout for an embedded page section
   (2026-09-05, founder-annotated screenshot: "having the chat running
   like archie chat elsewhere just half this display and put the chat
   output window on the right"). Same real side-by-side/border/gap
   language as .split's own two columns (#coding-chat-mount /
   .coding-chat-result-panel), but .split itself can't be reused as-is --
   its height:calc(100vh-57px) is right for a dedicated full-screen view,
   wrong here since this section sits partway down a long, normally-
   scrolling Music Lab page with real sections below it (Sequencer, 808,
   909). Auto height, sized to its own content, same responsive stack. */
.synth-compose-split {
  display: flex;
  gap: 0;
  align-items: stretch;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  margin-top: 0.75rem;
}
.synth-compose-split-left {
  flex: 1 1 50%;
  min-width: 0;
  padding: 1rem 1.25rem;
  border-right: 1px solid var(--border);
}
.synth-compose-split-right {
  flex: 1 1 50%;
  min-width: 0;
  padding: 1rem 1.25rem;
  /* Real fix (2026-09-05, founder-directed: "lets also darken the archie
     chat response to a black", then "can we use this shade for archies
     chat background" against the Song strip's own dark panel, then "white
     text") -- this inherited the site's own light-mode panel background,
     reading as an odd bright patch against the rest of the Synth page's
     own deliberately dark, hardware-style look. Now uses the EXACT same
     real dark-rack gradient/border every other real panel on this page
     already gets (#synth-screen .community-block above), not a separate
     one-off black, with real white text for max contrast against it. */
  background: linear-gradient(180deg, #2b2f2b 0%, #1c1f1c 100%);
  border: 1px solid #3d423d;
  color: #fff;
}
.synth-compose-split-right .muted {
  /* Real fix (2026-09-05, founder-directed: "white text") -- the reply
     text itself (#synth-compose-status) carries this same .muted class, so
     a dimmed grey here was dulling the actual real reply, not just genuine
     secondary text. Real white, matching the panel's own color above. */
  color: #fff;
}
@media (max-width: 800px) {
  .synth-compose-split {
    flex-direction: column;
  }
  .synth-compose-split-left {
    border-right: none;
    border-bottom: 1px solid var(--border);
  }
}
/* Real bug found live on Android (2026-08-29, founder-reported): .split
   never had the same mobile-stack fix .community-hub-two-col and
   .project-list-two-col already have below -- side-by-side columns forced
   into a narrow phone width, each squeezed and cramped, on every real
   .split-based screen (workspace, Chat, Compute, and the new Coding Chat).
   Genuine pre-existing gap, not something a new screen introduced -- fixed
   once, here, for all of them. Height is dropped too: calc(100vh-57px)
   only makes sense for a fixed-height side-by-side row: stacked, each
   panel should size to its own real content, or the second panel becomes
   unreachable below an artificially truncated viewport. */
@media (max-width: 800px) {
  .split {
    flex-direction: column !important;
    height: auto;
  }
  .split > .left-panel,
  .split > .archie-panel,
  .split > .compute-panel,
  .split > #coding-chat-mount,
  .split > .coding-chat-result-panel {
    max-width: none;
    border-right: none;
    border-left: none;
    border-bottom: 1px solid var(--border);
  }
}
/* Mini-Brief Collation Overhaul (2026-08-15): "Archie sits fixed on the
   left... The full combined document is displayed on the right side."
   #archie-panel is appended into #workspace-split as the second DOM child
   (after .left-panel) by mountArchiePanel -- flex order reverses which
   side each renders on without touching the reparenting logic itself, so
   this stays correct regardless of DOM append order. Scoped to the main
   workspace only (#workspace-split), not the generic .split used by the
   simpler Chat/Compute model-picker pages. */
#workspace-split,
#chat-split,
#compute-split { flex-direction: row-reverse; }

.left-panel {
  flex: 1 1 50%;
  max-width: 800px;
  border-right: 1px solid var(--border);
  overflow-y: auto;
  padding: 1rem 1.5rem;
}

.tabs { display: flex; align-items: center; gap: 0.25rem; margin-bottom: 1rem; }
/* Real text-to-speech voice picker (2026-08-21), moved here from repeating
   on every message: one real, shared control, read at click time by every
   "Read aloud" link in the chat below. */
.tts-voice-select-global { margin-left: auto; font-size: 0.8rem; padding: 0.2rem 0.4rem; }
/* @theme-component: .tab */
.tab {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 0.4rem 0.9rem;
  font-size: 0.85rem;
}
/* @theme-component: .tab.active */
.tab.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }

/* @theme-component: .not-implemented */
.not-implemented {
  padding: 1rem;
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
  color: var(--muted);
}

/* Chat repurpose: the real Archie chat panel gets moved (not cloned) into
   the Chat page's mount point -- this mount div is the actual flex child
   of .split, sized the same as the panel's original home in
   workspace-screen. */
#chat-archie-mount {
  flex: 1 1 auto;
  min-width: 0;
  overflow-y: auto;
}
/* Real fix (2026-08-27): .archie-panel's own 800px cap (fine on the
   Workspace page, where it sits next to a full 50% brief column) left a
   raw, unstyled gap of bare --bg between the chat and the now-much-
   narrower Models/Previous-chats column. Given real room to grow, but
   capped (not "none") -- an uncapped chat pane on a wide screen was
   forcing the fixed sidebar off-balance and clipping its own buttons.
   1000px keeps line length sane and leaves the sidebar a stable width. */
#chat-archie-mount .archie-panel { max-width: 1000px; }

/* Coding Chat (2026-08-28): same real "reparented panel gets the flex
   sizing its old spot had" pattern as #chat-archie-mount above -- the mount
   div itself is the flex child of #coding-chat-split, not .archie-panel
   (which is now nested one level deeper). Result panel reuses .left-panel's
   sizing but needs its border on the opposite edge, since it's the RIGHT
   column here, not the left. */
#coding-chat-mount {
  flex: 1 1 50%;
  max-width: 800px;
  min-width: 0;
  overflow-y: auto;
}
.coding-chat-result-panel {
  border-right: none;
  border-left: 1px solid var(--border);
}
/* Nav moved into the right column (2026-08-30) -- narrower than its old
   full-width spot, so the four pill tabs need to wrap rather than
   overflow/clip. */
.coding-chat-result-panel > .coding-chat-workspace-nav { flex-wrap: wrap; }
.coding-chat-compile-banner {
  margin: 0 0 1rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel-bg);
}
.coding-chat-compile-banner p:first-child { margin-top: 0; }
/* @theme-component: #coding-chat-result-live p */
#coding-chat-result-live > p { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; }
/* Update app flash (2026-08-30, founder-requested: "display the text your
   app is updating and have it flash until finished") -- same real pulse
   pattern as .video-editor-record-arm.recording, just applied to text
   instead of a button background. */
.update-app-flash { color: var(--accent); font-weight: 600; animation: update-app-flash-pulse 1s ease-in-out infinite; }
@keyframes update-app-flash-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
@media (prefers-reduced-motion: reduce) {
  .update-app-flash { animation: none; }
}
/* Real, separate "Polish this brief" row (2026-08-30, founder-directed --
   a real compile must never be silently triggered by Update app, only ever
   this deliberate, visible, separately-costed action). Own row so it never
   crowds Update app's own line or lets the model <select> run off-screen. */
.coding-chat-polish-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin-top: 0.5rem; }
.coding-chat-polish-row select { max-width: 100%; }

/* Real allotted-space meter (2026-08-30, founder-requested). Plain
   colour by default; amber/red only once real usage genuinely earns it
   (set via JS, never a fixed decorative gradient regardless of the real
   number). */
.hosted-uploads-usage { margin: 0.6rem 0; }
.hosted-uploads-usage-label { display: flex; justify-content: space-between; font-size: 0.8rem; color: var(--muted-text, #888); margin-bottom: 0.25rem; }
.hosted-uploads-usage-bar { height: 8px; border-radius: 4px; background: var(--surface-2, rgba(127, 127, 127, 0.15)); overflow: hidden; }
.hosted-uploads-usage-fill { height: 100%; background: var(--accent); border-radius: 4px; transition: width 0.3s ease-out; }
.hosted-uploads-usage-fill.warn { background: #d4a017; }
.hosted-uploads-usage-fill.full { background: #c0392b; }
.coding-chat-result-frame {
  width: 100%;
  height: calc(100% - 4rem);
  min-height: 400px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  /* A token, not a literal #fff -- avoids a jarring white flash in dark
     mode before the iframe's own content (which sets its own background)
     finishes loading. */
  background: var(--surface-2);
}

/* Real 3-column pass (2026-08-27, founder's own direction): Models used to
   eat half the screen with full pricing cards right next to the chat --
   the single biggest reason the page read as a dev console instead of a
   conversation. Now its own real column (collapsed by default behind a
   <details>, so the raw $/token pricing is a click away rather than the
   first thing a new user sees), matched in width to Previous chats on the
   opposite side -- both outer columns share the one named
   --layout-col-outer token (:root block) rather than two different sizes,
   so the centre chat column stays visually balanced between them. */
#chat-split .chat-models-column {
  flex: 0 1 var(--layout-col-outer);
  min-width: 220px;
  max-width: var(--layout-col-outer);
}
.chat-models-dock summary {
  cursor: pointer;
  font-weight: 600;
  font-size: 1.05rem;
  padding: 0.25rem 0;
}
.chat-models-dock[open] summary { margin-bottom: 0.5rem; }
#chat-split .chat-models-dock #chat-page-models .model-grid { grid-template-columns: 1fr; gap: 0.5rem; margin-bottom: 0.75rem; }
#chat-split .chat-models-dock #chat-page-models .model-card { padding: 0.5rem 0.6rem; }
#chat-split .chat-models-dock #chat-page-models .model-card-detail { font-size: 0.72rem; margin: 0.3rem 0; }

.chat-history-column {
  flex: 0 1 var(--layout-col-outer);
  min-width: 220px;
  max-width: var(--layout-col-outer);
  overflow-y: auto;
  padding: 1rem 1.5rem;
  border-left: 1px solid var(--border);
}
/* Real fix (2026-09-02, founder-reported: "Previous chats on the right are
   squashed... get rid of the scroll bar fix the length to page size") --
   the list had its own separate overflow-y:auto + flex:1 1 auto, a second,
   nested scroll box inside .chat-history-column's own already-scrolling
   overflow-y:auto. That inner box was sizing to content rather than
   stretching to fill the column, so it rendered short with its own
   cramped scrollbar and a real gap of empty column below it. One scroll
   surface now, same pattern as the Models column opposite it: the outer
   .chat-history-column scrolls the whole dock, the list itself is plain
   block flow. */
.chat-history-dock { display: flex; flex-direction: column; gap: 0.75rem; }
.chat-history-dock h3 { margin: 0; }
#chat-history-search { width: 100%; padding: 0.4rem 0.6rem; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel-bg); color: var(--text); }
.chat-history-dock #chat-page-history-list { list-style: none; margin: 0; padding: 0; }
.chat-history-thumb { width: 100%; height: 90px; object-fit: cover; border-radius: var(--radius-sm); display: block; margin-bottom: 0.35rem; }
/* Real fix (2026-08-27): a title + status tag + two action buttons all in
   one unwrapped row never fit in this column's real ~260-300px width --
   they ran off the edge instead of wrapping. Stacked into two rows here,
   same real data/behavior, just laid out for the space it actually has. */
.chat-history-dock .chat-history-item { flex-wrap: wrap; align-items: flex-start; gap: 0.3rem 0.5rem; }
.chat-history-dock .chat-history-title { flex: 1 1 100%; text-align: left; white-space: normal; overflow: visible; text-overflow: clip; }
.chat-history-dock .chat-history-actions { margin-left: 0; flex-wrap: wrap; }

/* Compute page redesign (2026-08-17): a genuinely separate panel, not the
   shared #archie-panel moved in -- same sizing/scroll behavior as
   .archie-panel so the page still reads consistently, but its own real
   DOM, never reparented. */
.compute-panel {
  flex: 1 1 50%;
  max-width: 800px;
  padding: 1rem 1.5rem;
  overflow-y: auto;
  background: var(--panel-bg);
}

/* @theme-component: .archie-panel */
.archie-panel {
  flex: 1 1 50%;
  max-width: 800px;
  padding: 1rem 1.5rem;
  overflow-y: auto;
  background: var(--panel-bg);
}
.archie-block { margin-bottom: 1.5rem; }
/* @theme-component: .project-asset-vault-search */
.project-asset-vault-search { margin-top: 1rem; }
.project-asset-vault-search h4 { margin-bottom: 0.4rem; }
.project-asset-vault-search form { display: flex; gap: 0.5rem; margin-bottom: 0.6rem; }
.project-asset-vault-search input[type="text"] { flex: 1; }
/* @theme-component: .project-asset-vault-icon-grid */
.project-asset-vault-icon-grid { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.project-asset-vault-icon-tile {
  position: relative;
  width: 56px;
  height: 56px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--panel-bg);
  overflow: hidden;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.4rem;
  padding: 0;
}
.project-asset-vault-icon-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.project-asset-vault-icon-tile:hover { border-color: var(--accent); }
.project-asset-vault-icon-tile:disabled { cursor: default; opacity: 0.55; }
/* Real paint-splash badge (buildHomeCardBadge), reused here for note/video
   placeholder icons -- sized down and its own hover animation/bottom
   margin switched off, since the outer tile (56px, its own hover ring) is
   already the real interactive element; the badge is just its icon now. */
.project-asset-vault-icon-tile .home-card-badge { width: 40px; height: 40px; margin: 0; pointer-events: none; }
.project-asset-vault-icon-tile:disabled .home-card-badge:hover,
.project-asset-vault-icon-tile .home-card-badge:hover {
  transform: rotate(var(--badge-rotate, 0deg));
  box-shadow: var(--shadow-sm, 0 1px 3px rgba(0,0,0,0.15));
}
/* A small "+" overlay, only shown on hover -- the tile itself is already
   the click target (its own title carries the item's real description),
   so this is a visual cue, not a second interactive element. */
.project-asset-vault-icon-tile::after {
  content: "+";
  position: absolute;
  right: 2px;
  bottom: 1px;
  width: 16px;
  height: 16px;
  line-height: 16px;
  text-align: center;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.8rem;
  opacity: 0;
  transition: opacity var(--transition-soft);
}
.project-asset-vault-icon-tile:hover::after { opacity: 1; }
.project-asset-vault-icon-tile.added::after { content: "✓"; opacity: 1; background: var(--muted); }
.project-asset-vault-icon-tile.added { cursor: default; }

/* @theme-component: .project-asset-vault-quick-search */
/* Real bulk/type search row (2026-08-29) -- the same badge icons as the
   results grid, smaller, doubling as one-click "show me everything of
   this type" buttons. */
.project-asset-vault-quick-search { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.6rem 0; }
.project-asset-vault-quick-search-btn {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: none;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: #fff;
  box-shadow: var(--shadow-sm, 0 1px 3px rgba(0,0,0,0.15));
  transition: transform 0.15s ease-out;
}
.project-asset-vault-quick-search-btn:hover { transform: scale(1.12); }
.project-asset-vault-quick-search-btn.active { outline: 2px solid var(--text); outline-offset: 2px; }

/* @theme-component: .vault-item-popout-overlay */
/* Same real overlay convention as .vault-note-editor-overlay/
   .image-lightbox-overlay -- fixed, centered, dimmed background. */
.vault-item-popout-overlay {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.45);
  padding: 2rem;
  box-sizing: border-box;
}
.vault-item-popout-overlay.hidden { display: none; }
.vault-item-popout-card {
  position: relative;
  width: min(420px, 100%);
  background: var(--bg);
  border-radius: var(--radius-md);
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.35);
  padding: 2rem;
  box-sizing: border-box;
}
.vault-item-popout-close { position: absolute; top: 0.75rem; right: 1rem; font-size: 1.4rem; line-height: 1; }
.vault-item-popout-preview { display: flex; justify-content: center; margin-bottom: 1rem; }
.vault-item-popout-preview img { max-width: 100%; max-height: 260px; border-radius: var(--radius-sm); display: block; }
.vault-item-popout-preview .home-card-badge { width: 72px; height: 72px; margin: 0; }
.vault-item-popout-actions { display: flex; gap: 0.6rem; margin-top: 1rem; }
.panel-header-row { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; }
#compute-study-mode-block { margin-top: 1rem; }
#compute-study-goal-setup { display: flex; flex-direction: column; gap: 0.6rem; align-items: flex-start; }
#compute-study-goal-draft { display: flex; flex-direction: column; gap: 0.6rem; width: 100%; }
#compute-study-goal-active { display: flex; flex-direction: column; gap: 0.6rem; align-items: flex-start; }
#compute-results-block { margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--border); }
#compute-results-list { list-style: none; margin: 0 0 0.75rem; padding: 0; display: flex; flex-direction: column; gap: 0.6rem; }
#compute-results-list li:not(.muted) { padding: 0.5rem 0.6rem; border: 1px solid var(--border); border-radius: var(--radius-md); }
#compute-final-passes-list { display: flex; flex-direction: column; gap: 0.6rem; margin-top: 0.6rem; }
.held-pass-block { padding: 0.6rem 0.75rem; border: 1px solid var(--border); border-radius: var(--radius-md); transition: background-color 0.4s ease; }
.held-pass-block-highlighted { background-color: var(--success-bg); }
.held-pass-block h4 { margin: 0 0 0.4rem; }
.held-pass-block p { margin: 0 0 0.5rem; }
.held-pass-contradiction-warning { color: var(--warning); font-weight: 600; border: 1px solid var(--warning); border-radius: var(--radius-sm, 8px); padding: 0.5rem 0.75rem; }
.held-pass-gap-questions { border: 1px solid var(--border); border-radius: var(--radius-sm, 8px); padding: 0.6rem 0.75rem; margin: 0.5rem 0; background: var(--surface-2, rgba(127, 127, 127, 0.05)); }
.held-pass-gap-question { margin-bottom: 0.7rem; }
.held-pass-gap-question > p:first-child { font-weight: 600; margin: 0 0 0.3rem; }
.held-pass-gap-option { display: flex; align-items: flex-start; gap: 0.4rem; font-size: 0.9rem; margin-bottom: 0.25rem; cursor: pointer; }
.held-pass-gap-option input[type="radio"] { margin-top: 0.2rem; }
/* Same warning treatment, for the proactive "another project's build is
   blocking you" notice on the Coding page -- shown before the user hits
   the concurrency-lock error, not only after. */
#active-build-blocker-banner { border: 1px solid var(--warning); border-radius: var(--radius-sm, 8px); }
#active-build-blocker-text { color: var(--warning); font-weight: 600; }
#compute-past-sessions-block { margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--border); }
#compute-past-sessions-list { display: flex; flex-direction: column; gap: 0.6rem; margin-top: 0.6rem; }
.compute-session-card { padding: 0.6rem 0.75rem; border: 1px solid var(--border); border-radius: var(--radius-md); }
.compute-session-card h4 { margin: 0 0 0.3rem; }
.compute-session-card .muted { margin: 0 0 0.5rem; }
.compute-session-card-actions { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.compute-session-card-actions button { font-size: 0.85rem; padding: 0.4rem 0.7rem; }
.compute-session-card-actions .compute-session-delete-btn { background: var(--danger); }
.compute-save-to-project-row { display: flex; align-items: center; gap: 0.4rem; margin-top: 0.4rem; flex-wrap: wrap; }
.compute-save-to-project-row select { border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--panel-bg); color: var(--text); padding: 0.35rem 0.5rem; }
.compute-save-to-project-row button { font-size: 0.85rem; padding: 0.35rem 0.65rem; }
.compute-save-to-project-status { font-size: 0.8rem; }
/* Let's Study answer panel (2026-08-17): moved out of the pale chat bubble
   into its own real, high-contrast block -- the founder flagged the old
   embedded textarea as hard to read, especially for low vision. A bold
   accent-colored card (same real green used everywhere else, not a new
   color invented for this) is unmistakably legible against the page and
   reads as its own distinct "answer here" zone rather than blending into
   the conversation. */
#compute-answer-panel {
  background: var(--accent);
  color: var(--on-accent);
  border-radius: var(--radius-lg);
  padding: 1rem 1.25rem;
  margin-top: 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
#compute-answer-context { font-weight: 600; margin: 0; }
#compute-answer-panel textarea {
  width: 100%;
  min-height: 100px;
  padding: 0.65rem;
  border: 1px solid var(--on-accent);
  border-radius: var(--radius-md);
  background: var(--panel-bg);
  color: var(--text);
  font-size: 1rem;
  margin: 0;
}
#compute-answer-panel button {
  background: var(--on-accent);
  color: var(--accent);
}
/* Let's Study control explanations (2026-08-19): sits right after the
   answer panel in normal document flow (not fixed/sticky) so it naturally
   gets pushed further down the page as the real chat log grows -- exactly
   the founder's own request, and needs no extra positioning CSS since
   .compute-panel is already the scrolling container. */
.compute-controls-help { margin-top: 1rem; padding-top: 0.75rem; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 0.5rem; font-size: 0.85rem; color: var(--muted); }
.compute-controls-help p { margin: 0; }
.compute-controls-help strong { color: var(--text); }
/* @theme-component: .archie-block textarea */
.archie-block textarea {
  width: 100%;
  min-height: 80px;
  padding: 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg);
  color: var(--text);
  margin-bottom: 0.5rem;
}

/* @theme-component: #new-note-form textarea */
#new-note-form textarea {
  width: 100%;
  min-height: 60px;
  padding: 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel-bg);
  color: var(--text);
  margin-bottom: 0.4rem;
}
/* @theme-component: .scope-label */
.scope-label { display: flex; align-items: center; gap: 0.4rem; font-size: 0.85rem; color: var(--muted); margin-bottom: 0.5rem; }
.notes-controls { margin: 1rem 0; }
/* @theme-component: #notes-search */
#notes-search { width: 100%; padding: 0.5rem; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--panel-bg); color: var(--text); margin-bottom: 0.5rem; }
#notes-list { list-style: none; padding: 0; }
/* @theme-component: #notes-list li */
#notes-list li {
  padding: 0.6rem 0.8rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 0.5rem;
}
#notes-list .note-meta { display: flex; justify-content: space-between; margin-top: 0.4rem; }
#notes-list .note-actions { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.4rem; }

#source-inbox-list, #accepted-proposals-list, #source-state-list { list-style: none; padding: 0; }
/* @theme-component: #source-inbox-list li, #accepted-proposals-list li, #source-state-list li */
#source-inbox-list li, #accepted-proposals-list li, #source-state-list li {
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 0.4rem;
  font-size: 0.9rem;
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  transition: border-color var(--transition-soft);
}
/* @theme-component: #source-inbox-list li:hover, #accepted-proposals-list li:hover, #source-state-list li:hover */
#source-inbox-list li:hover, #accepted-proposals-list li:hover, #source-state-list li:hover { border-color: var(--accent); }

/* Chat Window Interface brief: a real "message appearance" transition --
   genuinely new items (a source item just added, a proposal just
   generated) ease in rather than snapping into existence. app.js adds this
   class to a freshly-created element, then removes it on the next frame so
   the transition actually plays once, not a permanently-animating element. */
.item-enter { opacity: 0; transform: translateY(4px); }
.item-enter-active { opacity: 1; transform: translateY(0); transition: opacity var(--transition-soft), transform var(--transition-soft); }

/* @theme-component: .state-tag */
.state-tag {
  font-size: 0.7rem;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.1rem 0.4rem;
}

#proposal-output { margin-top: 0.75rem; }
/* @theme-component: .proposal-card */
.proposal-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.75rem;
  margin-bottom: 0.5rem;
  transition: border-color var(--transition-soft), opacity var(--transition-soft);
}
/* @theme-component: .proposal-card:hover */
.proposal-card:hover { border-color: var(--accent); }
.proposal-card .proposal-actions { display: flex; gap: 0.5rem; margin-top: 0.5rem; }
/* @theme-component: .proposal-cost-meta */
.proposal-cost-meta { font-size: 0.75rem; color: var(--muted); margin-top: 0.25rem; }

/* @theme-component: .energy-indicator */
.energy-indicator { display: flex; align-items: center; gap: 0.5rem; font-size: 0.8rem; color: var(--muted); margin-bottom: 0.5rem; }
.energy-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
/* @theme-component: .energy-dot-green */
.energy-dot-green { background: var(--success); }
/* @theme-component: .energy-dot-red */
.energy-dot-red { background: var(--danger); }
/* @theme-component: .energy-dot-unknown */
.energy-dot-unknown { background: var(--border); }
.proposal-card.status-accepted { border-color: #7a8f6e; }
.proposal-card.status-rejected { opacity: 0.6; }

/* Chat Window Interface brief: a real idle/breathing-dot state, honestly
   placed next to the real "Archie" heading (no chat avatar exists to
   attach it to -- this is the genuine, currently-existing home for
   Archie's name in the interface). Slow and soft, not a fast alert pulse --
   matches "calm and alive, not static," never implying urgency. */
.archie-panel h2 { display: flex; align-items: center; gap: 0.5rem; }
/* @theme-component: .archie-idle-dot */
.archie-idle-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  animation: archie-breathe 3.6s ease-in-out infinite;
}
@keyframes archie-breathe {
  0%, 100% { opacity: 0.35; transform: scale(0.85); }
  50% { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .archie-idle-dot { animation: none; opacity: 0.7; }
}

/* Real gap found live (2026-09-04, founder: "it's pretty static here, take
   a long time to think") -- a real animated placeholder shown in the chat
   log while Archie's reply is genuinely in flight, replacing dead air. */
/* @theme-component: .archie-thinking-dots */
.archie-thinking-dots {
  display: inline-flex;
  gap: 5px;
  padding: 0.2rem 0;
}
.archie-thinking-dots span {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0.35;
  animation: archie-thinking-bounce 1.1s ease-in-out infinite;
}
.archie-thinking-dots span:nth-child(2) { animation-delay: 0.15s; }
.archie-thinking-dots span:nth-child(3) { animation-delay: 0.3s; }
@keyframes archie-thinking-bounce {
  0%, 60%, 100% { opacity: 0.35; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-4px); }
}
@media (prefers-reduced-motion: reduce) {
  .archie-thinking-dots span { animation: none; opacity: 0.7; }
}

/* Two-mode Archie labeling: honest badge naming the account's real, already-
   correct model-routing mode (house-pool free tier vs. own provisioned key). */
/* @theme-component: .archie-mode-badge */
.archie-mode-badge {
  font-size: 0.7rem;
  font-weight: 500;
  padding: 0.15rem 0.5rem;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  color: var(--accent);
  white-space: nowrap;
}

/* Admin Archie Mode 2: the reopening-project ping's three equal, real
   branching choices -- never presented as available when they aren't. */
.archie-ping-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.4rem;
}

/* Chat rebuild: a real online indicator, only shown once a genuine server
   round-trip has actually succeeded -- never a decorative always-on dot. */
/* @theme-component: .archie-online-indicator */
.archie-online-indicator {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.75rem;
  color: var(--success);
}
/* @theme-component: .archie-online-dot */
.archie-online-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--success);
}

/* Chat rebuild: standard chat-log styling -- Archie's messages visually
   distinct from the user's own, a real substantive reply gets room to
   breathe rather than being squeezed into a single list-item line. */
.archie-chat-log {
  list-style: none;
  padding: 0;
  margin: 0 0 0.6rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  max-height: 420px;
  overflow-y: auto;
}
/* Model picker width fix (2026-08-21): a bare <select> sizes to its widest
   option text by default -- with real, long option labels (model name +
   real pricing) that ran wider than the panel itself, spilling past the
   chat log's own right edge instead of lining up with it. */
#workspace-model-select {
  max-width: 100%;
  box-sizing: border-box;
}
/* Waiting Room layout correction (2026-08-16): a clearly separate,
   bordered block -- distinct from the chat log above it (different
   border/background, no message-bubble shape), so staged items never
   read as part of the conversation even though they now sit right next
   to the action that operates on them. */
.waiting-room-block {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.5rem 0.75rem;
  margin-bottom: 0.6rem;
  background: var(--surface-2, rgba(127, 127, 127, 0.06));
}
.waiting-room-block ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.waiting-room-block li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
/* Collapsible long lists (2026-08-30, founder-requested: "make this
   collapsable with about 4 open line... also allow collapse of Project
   assets after 4 cards") -- both the Waiting Room and Project assets
   lists render every real item unconditionally, which grows unbounded
   the longer a project runs. collapsed-list-item is applied by JS past
   the 4th real item; the toggle button (also JS-driven) is the only way
   back to seeing them, never a fixed max-height crop that clips a real
   item mid-way through its own text. */
/* !important: .waiting-room-block li's own display:flex rule (higher
   specificity -- one class + one element vs. this rule's one class alone)
   would otherwise win and keep a "collapsed" item visible regardless. */
.collapsed-list-item { display: none !important; }
.collapse-toggle-btn {
  display: block;
  margin-top: 0.4rem;
  font-size: 0.85rem;
}
/* @theme-component: .archie-chat-message */
.archie-chat-message {
  padding: 0.6rem 0.75rem;
  border-radius: var(--radius-lg);
  /* Real readability cap (2026-08-27): the chat pane now grows to fill
     the space freed up by shrinking Models, but a message bubble at 90%
     of that width would run to genuinely uncomfortable line lengths --
     capped in absolute terms too, same real reasoning as any text column. */
  max-width: min(90%, 680px);
  white-space: pre-wrap;
  /* Chat log horizontal-scroll fix (2026-08-21): a flex column's children
     default to min-width:auto, which can hold a message wider than the
     90% cap above and force the whole log to scroll sideways instead of
     wrapping -- wasting real space between the vertical and horizontal
     scrollbars. min-width:0 lets the bubble actually shrink to fit and
     wrap its text within the real available width. */
  min-width: 0;
  overflow-wrap: anywhere;
}
/* @theme-component: .archie-chat-message-user */
.archie-chat-message-user {
  align-self: flex-end;
  /* Real fix (2026-08-27): mixed into --surface-2 (the real light
     "priority" tone) rather than transparent -- previously let the page's
     own (now darker) panel show through, which made the user's own
     message read dimmer than the shell around it instead of standing out. */
  background: color-mix(in srgb, var(--accent) 14%, var(--surface-2));
}
.archie-chat-message-archie {
  align-self: flex-start;
  background: var(--surface-2);
  /* Real design experiment (2026-08-27): a genuine visual identity for
     Archie's own replies -- flagged in the design review as the single
     most-missing thing in the transcript (a flat grey box with no sense
     of who's actually replying). A small circular monogram badge, using
     the same new display face as the panel header, rather than a stock
     "AI" icon or a cartoon mascot that wouldn't fit this brand's calm,
     administrative tone. Positioned inside the bubble's own padding
     (never overhanging outside it) so it can't get clipped by the log's
     scroll container. */
  position: relative;
  padding-top: 2.5rem;
}
.archie-chat-message-archie::before {
  content: "A";
  position: absolute;
  top: 0.6rem;
  left: 0.7rem;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
  font-size: 0.9rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
.archie-chat-log-heading { font-weight: 600; }
.archie-chat-message-meta {
  font-size: 0.7rem;
  opacity: 0.7;
  margin-top: 0.3rem;
}
.archie-read-aloud-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  margin-top: 0.4rem;
}
.archie-read-aloud-btn { font-size: 0.75rem; }
.archie-read-aloud-status { font-size: 0.72rem; }
.archie-read-aloud-audio { height: 1.9rem; max-width: 220px; }
.audio-level-meter {
  width: 60px;
  height: 0.6rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-2);
  overflow: hidden;
  flex: 0 0 auto;
}
.audio-level-meter-bar {
  height: 100%;
  width: 0%;
  background: var(--accent);
  transition: width 0.08s linear;
}
.audio-level-meter-unavailable { display: none; }
.archie-chat-message-meta.archie-chat-message-contradiction {
  font-size: 0.85rem;
  opacity: 1;
  color: var(--warning);
  font-weight: 600;
  border: 1px solid var(--warning);
  border-radius: var(--radius-sm, 8px);
  padding: 0.5rem 0.75rem;
  margin-top: 0.5rem;
}
.archie-chat-add-to-brief-btn {
  margin-top: 0.4rem;
}
/* Two real lanes into the same destination (2026-09-04): "Build now" and
   "Discuss more" sit side by side, not stacked -- a real, deliberate choice
   at this one moment, not a default plus a fallback. */
.archie-chat-build-lane-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.4rem;
}
.archie-chat-build-lane-row .archie-chat-add-to-brief-btn {
  margin-top: 0;
}
.archie-chat-build-now-btn {
  background: var(--accent, #2f7a4f);
  color: #fff;
  border-color: var(--accent, #2f7a4f);
}
/* Real, genuinely optional second pass (2026-09-04, founder-directed) --
   deliberately small and muted, off by default, own row below the two
   lane buttons since it only ever applies to "Build now." */
.archie-chat-verify-full-chat-row {
  margin-top: 0.35rem;
  font-size: 0.82rem;
  color: var(--text-muted);
}
.archie-chat-verify-full-chat-row label {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  cursor: pointer;
}
.archie-chat-demo-btn, .archie-chat-demo-popout-btn, .archie-chat-demo-code-btn { margin-top: 0.4rem; margin-right: 0.6rem; display: inline-block; }
.archie-chat-demo-frame {
  display: block; width: 100%; height: 420px; margin-top: 0.5rem;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: #fff;
}
/* Chat log density fix (2026-08-16): compact one-line summary shown in
   place of the full reply/meta/buttons once an exchange is decided. */
.archie-chat-message-summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8rem;
  opacity: 0.85;
}
.archie-chat-message-summary span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.archie-chat-summary-toggle {
  flex-shrink: 0;
  font-size: 0.75rem;
}

/* Comprehensive live Billing view: six real sections, visually distinct
   but lightweight -- this is a summary, not a full accounting page. */
.billing-summary-section {
  margin-bottom: 0.75rem;
}
.billing-summary-section h4 {
  margin-bottom: 0.2rem;
}

/* Voice input: real mic control + real input-device selection, top right
   near Archie's header per the brief. A real device name can be long
   ("Microphone (Realtek(R) Audio)") -- floating this inside the <h2>
   alongside the badges broke the header layout the moment a device list
   populated, so it's a flex sibling of the heading instead, with the
   select capped so a long label truncates rather than blowing out width. */
.archie-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.archie-panel-header h2 {
  font-weight: 600;
  letter-spacing: 0.01em;
}
/* Web access toggle (2026-09-02) -- a plain small pill next to the Archie
   heading itself, real visual state (on = accent-colored, matching every
   other .active toggle in this app) rather than relying on the label
   text alone to carry the state. */
#chat-web-access-toggle-btn {
  font-size: 0.78rem;
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 0.15rem 0.6rem;
}
#chat-web-access-toggle-btn.active {
  border-color: var(--accent);
  color: var(--accent);
}
.archie-voice-controls {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  flex-shrink: 0;
}
#archie-voice-device-select {
  max-width: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* @theme-component: #archie-voice-mic-btn */
#archie-voice-mic-btn {
  border-radius: var(--radius-pill);
  padding: 0.2rem 0.5rem;
  line-height: 1;
  background: color-mix(in srgb, var(--accent) 55%, white);
}
/* @theme-component: #archie-voice-mic-btn.archie-voice-recording */
#archie-voice-mic-btn.archie-voice-recording {
  background: var(--danger);
  color: var(--on-accent);
}
#archie-voice-device-select {
  font-size: 0.75rem;
}

/* Chat Window Interface brief: real visual differentiation between warm/
   checkpoint moments and functional/active-work moments -- previously the
   whole interface was one visual mode throughout. Checkpoint-related
   content gets a genuinely warmer, softer treatment; everything else
   (Source Inbox, Proposals -- active work) stays the plain functional
   style it already had. */
/* @theme-component: .checkpoint-moment */
.checkpoint-moment {
  background: color-mix(in srgb, var(--accent) 8%, var(--panel-bg));
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
  border-radius: var(--radius-lg);
  padding: 1rem;
  margin-bottom: 1rem;
  transition: background-color var(--transition-soft);
}

/* Chat Window Interface brief: real click-to-expand for generated image
   thumbnails, reusing the exact .community-image-thumb pattern already
   established -- a real full-size lightbox, not a bare link or a new
   attachment-style download. */
.community-image-thumb { cursor: zoom-in; transition: opacity var(--transition-soft); }
.community-image-thumb:hover { opacity: 0.85; }
.image-lightbox-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.75);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  cursor: zoom-out;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-soft);
}
.image-lightbox-overlay.image-lightbox-visible { opacity: 1; pointer-events: auto; }
/* @theme-component: .image-lightbox-overlay img */
.image-lightbox-overlay img { max-width: 90vw; max-height: 90vh; border-radius: var(--radius-md); }

/* @theme-component: .checkpoint-ledger-line */
.checkpoint-ledger-line { font-size: 0.75rem; color: var(--muted); margin: 0.15rem 0; }

/* @theme-component: .contradiction-flag */
.contradiction-flag {
  border: 1px solid var(--warning);
  border-radius: var(--radius-md);
  padding: 0.6rem;
  margin-bottom: 0.5rem;
}
.contradiction-flag div { display: flex; gap: 0.5rem; margin-top: 0.4rem; }

/* @theme-component: #resume-package-output pre */
#resume-package-output pre {
  white-space: pre-wrap;
  font-size: 0.75rem;
  background: var(--panel-bg, rgba(128, 128, 128, 0.08));
  padding: 0.5rem;
  border-radius: var(--radius-sm);
}

/* @theme-component: .home-provider-independence */
.home-provider-independence { font-size: 1.25rem; color: var(--text); font-weight: 600; font-style: italic; margin: 0.75rem 0; }

/* @theme-component: .notif-badge */
.notif-badge {
  display: inline-block;
  background: var(--danger);
  color: var(--on-accent);
  border-radius: var(--radius-pill);
  padding: 0 0.4rem;
  font-size: 0.7rem;
  margin-left: 0.25rem;
}
/* @theme-component: .notif-dropdown */
.notif-dropdown {
  position: absolute;
  right: 1rem;
  top: 3.2rem;
  width: 320px;
  max-height: 400px;
  overflow-y: auto;
  background: var(--panel-bg, #fff);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: 0.5rem;
  z-index: 50;
}
.notif-dropdown-header { display: flex; justify-content: space-between; align-items: center; }
.notif-dropdown ul { list-style: none; margin: 0; padding: 0; }
.notif-dropdown li { padding: 0.5rem; border-bottom: 1px solid var(--border); font-size: 0.85rem; }
.notif-item { display: flex; align-items: center; gap: 0.5rem; }
.notif-avatar-btn { padding: 0; border: none; background: none; cursor: pointer; flex-shrink: 0; border-radius: 50%; }
.notif-avatar { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; display: block; background: var(--border); }
.notif-avatar-btn:hover .notif-avatar { outline: 2px solid var(--accent); }
.notif-dropdown li.unread { font-weight: 600; }
.notif-dropdown li:last-child { border-bottom: none; }

.community-block { margin-bottom: 1.5rem; }
.kb-article-image { display: block; max-width: 100%; border-radius: var(--radius-md); border: 1px solid var(--border-color, rgba(127, 127, 127, 0.25)); margin: 0.5rem 0 0.75rem; }

/* Real fix (2026-09-01, founder: "lets make this an actual chat input with
   archie on the left") -- the "What are you studying?" field used to be a
   bare text input; now reads as a real chat composer, Archie's own avatar
   on the left same as everywhere else he appears. */
.compute-study-input-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.6rem;
}
.compute-study-archie-avatar {
  flex: none;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: #3f9142;
  color: #fff;
  font-family: "Chewy", "Segoe Script", cursive;
  font-size: 1.15rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
.compute-study-input {
  flex: 1;
  border-radius: var(--radius-pill);
  padding: 0.6rem 1rem;
}
.vault-study-guide-share-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.4rem;
}
.vault-item-send-controls {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  margin-top: 0.3rem;
}
.chat-about-archie-card,
.chat-kb-quicklink-card {
  display: block;
  width: 100%;
  text-align: left;
  cursor: pointer;
  border: 1px solid var(--border, rgba(127, 127, 127, 0.2));
}
.chat-about-archie-card h4,
.chat-kb-quicklink-card h4 { margin: 0; }
.chat-about-archie-card.active {
  border-color: var(--accent);
  background: var(--accent-soft, rgba(63, 145, 66, 0.08));
}
/* Real fix (2026-09-02, founder-requested: "shrink the cards so only the
   title is displayed but when you click them they expand, push all
   others down the column") -- collapsed to title-only by default, the
   description only takes up real space once a card's own genuinely been
   opened. Plain block flow does the "push down" for free -- no JS layout
   needed beyond toggling one class. */
.chat-about-archie-card p,
.chat-kb-quicklink-card p {
  display: none;
  margin: 0.3rem 0 0;
}
.chat-about-archie-card.expanded p,
.chat-kb-quicklink-card.expanded p {
  display: block;
}

/* Profile-owned identity verification (2026-09-01) */
#community-hub-verification-gate {
  border: 1px solid var(--accent);
  background: var(--accent-soft, rgba(63, 145, 66, 0.08));
}
.community-hub-verified-pill {
  display: inline-block;
  color: var(--accent);
  font-weight: 600;
  margin: 0 0 1rem;
}

/* Real social share icon row (2026-09-04) -- positioned per the founder's
   own live check: "above your photos title", right under the identity
   block wherever it appears (own profile, another user's profile). */
.community-hub-social-links { display: flex; gap: 0.6rem; list-style: none; padding: 0; margin: 0 0 1rem; }
.community-hub-social-link {
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid var(--border); color: var(--muted, #888);
  transition: color 0.15s ease, border-color 0.15s ease;
}
.community-hub-social-link:hover { color: var(--accent); border-color: var(--accent); }
.community-hub-social-link svg { width: 22px; height: 22px; }

.settings-social-links-add-row { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; margin: 0.5rem 0; }
.settings-social-links-add-row input[type="url"] { flex: 1; min-width: 200px; }
.settings-social-links-list { list-style: none; padding: 0; }
.settings-social-links-item { display: flex; align-items: center; gap: 0.6rem; padding: 0.5rem 0; border-bottom: 1px solid var(--border); }
.settings-social-links-item span:nth-child(2) { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Real fix (2026-09-01, founder: "the player should be on the next line...
   lets keep it standard... theres no template corrections later") -- one
   fixed real card shape for every Music Lab Library entry: title, meta,
   player, Remix each on their own real line, regardless of title length or
   which model produced it. */
.music-lab-library-item { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 1rem; }
.music-lab-library-item audio { width: 100%; }

.community-hub-cover {
  position: relative;
  width: 100%;
  height: 220px;
  background: var(--surface-alt, var(--border));
  border-radius: var(--radius-sm, 8px);
  overflow: hidden;
}
.community-hub-cover img { width: 100%; height: 100%; object-fit: cover; user-select: none; }
.community-hub-cover.repositioning img { cursor: grab; }
.community-hub-cover.repositioning.dragging img { cursor: grabbing; }
.community-hub-cover-buttons {
  position: absolute;
  z-index: 2;
  right: 0.75rem;
  bottom: 0.75rem;
  display: flex;
  gap: 0.5rem;
}
.community-hub-cover-buttons button, .community-hub-cover-upload-btn {
  background: var(--bg, #fff);
  color: var(--text);
  padding: 0.4rem 0.75rem;
  border-radius: var(--radius-sm, 8px);
  border: 1px solid var(--border);
  cursor: pointer;
}
.community-hub-cover-reposition-controls {
  position: absolute;
  z-index: 2;
  left: 0.75rem;
  bottom: 0.75rem;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: var(--bg, #fff);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 8px);
  padding: 0.4rem 0.6rem;
}
/* Same "unconstrained, matching .community-hub-main" width pattern the
   profile and group covers already use (see the comment above
   #groups-screen .project-list-main) -- edge-to-edge within the page's own
   1rem padding, not a narrower centered box, so all three banners genuinely
   read as the same product. */
.community-feed-banner { width: auto; margin: 0 1rem 0; }

.community-hub-identity {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: flex-end;
  gap: 1rem;
  margin-top: -3rem;
  padding: 0 1rem;
}
.community-hub-avatar-wrap { position: relative; flex-shrink: 0; }
.community-hub-avatar {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  border: 3px solid var(--bg, #fff);
  object-fit: cover;
  display: block;
}
.community-hub-avatar-placeholder { background: var(--border); }
.community-hub-avatar-upload-btn {
  position: absolute;
  bottom: 0;
  right: -0.25rem;
  background: var(--bg, #fff);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 8px);
  padding: 0.15rem 0.5rem;
  font-size: 0.7rem;
  cursor: pointer;
}
.community-hub-name-block { padding-bottom: 0.5rem; }
.community-hub-name-block h2 { margin: 0; }
.community-hub-photos-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 0.5rem;
}
.community-hub-photos-grid li { position: relative; aspect-ratio: 1 / 1; border-radius: var(--radius-sm, 8px); overflow: hidden; }
.community-hub-photos-grid img, .community-hub-photos-grid video { width: 100%; height: 100%; object-fit: cover; display: block; }
.community-hub-photo-remove-btn {
  position: absolute;
  top: 0.25rem;
  right: 0.25rem;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  border: none;
  border-radius: 50%;
  width: 22px;
  height: 22px;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--transition-soft, 0.15s ease);
}
.community-hub-photos-grid li:hover .community-hub-photo-remove-btn { opacity: 1; }
.community-hub-photo-visibility-btn {
  position: absolute;
  bottom: 0.25rem;
  left: 0.25rem;
  right: 0.25rem;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  border: none;
  border-radius: var(--radius-sm, 4px);
  font-size: 0.65rem;
  padding: 0.15rem 0.3rem;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--transition-soft, 0.15s ease);
}
.community-hub-photos-grid li:hover .community-hub-photo-visibility-btn { opacity: 1; }
/* Real fix (2026-09-06, founder-directed: "the delete photos option from
   profile can we have that also on the finished videos") -- same real
   corner-overlay on-feed pill, reused on a shared video's own poster. */
.community-hub-video-media:hover .community-hub-photo-visibility-btn { opacity: 1; }
.community-hub-videos-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 0.75rem;
}
.community-hub-videos-grid li { display: flex; flex-direction: column; gap: 0.35rem; }
/* Real "Your Synth sessions" profile section (2026-09-01) -- a real
   <audio> player needs more width than a 160px video thumbnail column. */
#community-hub-synth-shares-list { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.community-hub-synth-share-item { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 0.6rem 0.75rem; }
.community-hub-synth-share-item audio { width: 100%; margin-top: 0.4rem; display: block; }
.community-hub-video-media { position: relative; aspect-ratio: 16 / 9; border-radius: var(--radius-sm, 8px); overflow: hidden; background: #000; cursor: pointer; }
.community-hub-video-media img, .community-hub-video-media video { width: 100%; height: 100%; object-fit: cover; display: block; }
.community-hub-video-media-blank { display: flex; align-items: center; justify-content: center; }
.community-hub-video-play-badge {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 36px; height: 36px; border-radius: 50%; background: rgba(0, 0, 0, 0.55); color: #fff;
  display: flex; align-items: center; justify-content: center; font-size: 0.9rem; pointer-events: none;
}
.community-hub-video-share-btn {
  border: 1px solid var(--border); border-radius: var(--radius-sm, 4px); background: var(--surface-2, rgba(127,127,127,0.06));
  color: inherit; font-size: 0.7rem; padding: 0.3rem 0.5rem; cursor: pointer; text-align: left;
}
.community-hub-photo-add-btn {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
  color: inherit;
}
.community-hub-add-photos { display: flex; align-items: center; gap: 0.75rem; margin-top: 0.75rem; flex-wrap: wrap; }

/* Same outside padding value as Image Maker's .project-list-main (padding: 0 1rem) */
.community-hub-main { padding: 0 1rem; }

.community-hub-two-col { display: flex; gap: 1.5rem; align-items: flex-start; }
.community-hub-col-left, .community-hub-col-right { flex: 1 1 50%; min-width: 0; }
@media (max-width: 800px) {
  .community-hub-two-col { flex-direction: column; }
}

.project-list-main-wide { max-width: 1300px; }
/* Real fix (2026-09-23, founder-reported: "investigate why the divs are
   not expanding outward in the app builder page" -- confirmed live: the
   App Builder screen's own main measured 1300px wide against a 1706px
   window, while VST Builder's own #vst-builder-main (already given the
   same real fix, 2026-09-11, for the identical "squeezed into a narrow
   strip" complaint) measured the full 1600px. Same exact fix, same
   pattern #synth-screen already uses below for its own wide override. */
#project-list-screen .project-list-main-wide { max-width: 1600px; }
/* Real fix (2026-09-06, founder-reported: "too much spread and the pill
   is malformed") -- moving this card above <main> (so it stays visible
   regardless of Library length) lost the horizontal padding/max-width
   <main> itself supplies, so the text ran edge-to-edge with no real
   container. Given its own real panel look instead of depending on a
   parent for containment -- same background/border/radius language
   .home-card already uses elsewhere, so it reads as one distinct card
   here too, not stretched bare text. */
.music-lab-synth-card {
  max-width: 1300px;
  margin: 1rem auto 1.5rem;
  padding: 1.25rem 1.5rem;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.music-lab-synth-card h3 { margin: 0 0 0.5rem; }
.music-lab-synth-card p { margin: 0 0 1rem; max-width: 720px; }
.music-lab-synth-card button {
  padding: 0.55rem 1.1rem;
  border-radius: 999px;
}
.project-list-two-col { display: flex; gap: 1.5rem; align-items: flex-start; }
.project-list-col-left, .project-list-col-right { flex: 1 1 50%; min-width: 0; }
@media (max-width: 800px) {
  .project-list-two-col { flex-direction: column; }
}

.community-hub-composer textarea { width: 100%; min-height: 3rem; resize: vertical; }
.community-hub-post-preview-wrap { position: relative; display: inline-block; margin-top: 0.5rem; }
.community-hub-post-preview-wrap img { max-width: 100%; max-height: 260px; border-radius: var(--radius-sm, 8px); display: block; }
.community-hub-post-preview-wrap button {
  position: absolute;
  top: 0.4rem;
  right: 0.4rem;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  border: none;
  border-radius: 50%;
  width: 24px;
  height: 24px;
  cursor: pointer;
  line-height: 1;
}
.community-hub-composer-row { display: flex; align-items: center; gap: 0.75rem; margin-top: 0.5rem; flex-wrap: wrap; }
.community-hub-composer-row .scope-label { margin-left: auto; }

.community-hub-friend-search-results { list-style: none; margin: 0.5rem 0; padding: 0; }
.community-hub-friend-search-results li {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--border);
}
.community-hub-friend-search-results li:last-child { border-bottom: none; }
.community-hub-friend-thumb {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--border);
  flex-shrink: 0;
}
.community-hub-friend-search-results .community-hub-friend-name { flex: 1 1 auto; }
.community-hub-friends-grid {
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 0.75rem;
}
.community-hub-friend-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  padding: 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 8px);
  text-align: center;
  position: relative;
}
.community-hub-friend-card img {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--border);
}
.community-hub-friend-card .muted { font-size: 0.75rem; }
.community-hub-friend-menu-btn {
  position: absolute;
  top: 0.25rem;
  right: 0.25rem;
  background: none;
  border: none;
  cursor: pointer;
  font-size: 1rem;
  color: var(--muted);
}
.community-hub-friend-menu {
  position: absolute;
  top: 1.75rem;
  right: 0.25rem;
  background: var(--bg, #fff);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 8px);
  z-index: 5;
  min-width: 110px;
}
.community-hub-friend-menu button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.4rem 0.6rem;
  background: none;
  border: none;
  cursor: pointer;
}
.community-hub-friend-menu button:hover { background: var(--border); }

.r2-storage-sparkline { color: var(--accent, #4caf7d); margin: 0.5rem 0; }

.gap-fill-banner, .gap-fill-review {
  border: 1px solid var(--accent, #4caf7d);
  border-radius: 6px;
  padding: 0.75rem;
  margin: 0.75rem 0;
  background: rgba(76, 175, 125, 0.08);
}
.gap-fill-review-text, #gap-fill-review-text { white-space: pre-wrap; }

/* Real fix (2026-09-06, founder-directed: "a timed full refresh... or
   should we add a full refresh option") -- sits fixed above everything,
   regardless of which real screen is active, since a new deploy can land
   at any time no matter what someone's doing. */
.new-version-banner {
  position: fixed; top: 0; left: 0; right: 0; z-index: 9999;
  display: flex; align-items: center; justify-content: center; gap: 0.75rem;
  border: none; border-radius: 0; margin: 0;
  padding: 0.6rem 1rem;
}
.new-version-banner p { margin: 0; font-weight: 600; }

.coding-screen-main { max-width: 900px; margin: 0 auto; padding: 1.5rem; }

.coding-terminal-block { margin-bottom: 1.5rem; }

.lead-in-actions {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.lead-in-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.lead-in-row select { min-width: 12rem; }

#reasoning-model-picker select { display: block; margin-bottom: 0.6rem; }

.info-icon {
  display: inline-flex;
  cursor: help;
  color: var(--accent, #4caf7d);
  font-size: 1.05em;
  vertical-align: middle;
}

.coding-terminal {
  white-space: pre-wrap;
  font-family: ui-monospace, "SF Mono", "Cascadia Mono", "Consolas", "Roboto Mono", monospace;
  font-size: 0.85rem;
  line-height: 1.5;
  background: #0b0f0d;
  color: #8fdaad;
  border: 1px solid #1c2620;
  border-radius: 6px;
  padding: 1rem;
  min-height: 120px;
  max-height: 480px;
  overflow-y: auto;
}

.marketing-material-viewer {
  width: 100%;
  height: 80vh;
  border: 1px solid var(--rule, #444);
  border-radius: 6px;
  margin-top: 0.75rem;
  background: #fff;
}

.admin-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.admin-table th, .admin-table td { text-align: left; padding: 0.4rem 0.6rem; border-bottom: 1px solid var(--border); }
.admin-table th { color: var(--text-muted, #888); font-weight: 600; }

.kb-search-block { position: sticky; top: 0; z-index: 5; background: var(--bg); padding-bottom: 1rem; border-bottom: 1px solid var(--border); }
#kb-search-input { width: 100%; padding: 0.6rem 0.75rem; font-size: 1rem; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text); box-sizing: border-box; }
#kb-tag-filters { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.6rem; }
.kb-tag-chip { padding: 0.25rem 0.6rem; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); color: var(--text-muted, var(--text)); font-size: 0.8rem; cursor: pointer; }
.kb-tag-chip.active { background: var(--accent, #4a7dff); color: #fff; border-color: var(--accent, #4a7dff); }
#kb-search-status { margin: 0.5rem 0 0; font-size: 0.85rem; }
#kb-screen .community-block.hidden { display: none; }
.kb-updated-badge { display: inline-block; margin: 0.15rem 0 0.5rem; font-size: 0.75rem; color: var(--text-muted, #888); }
.kb-related { margin-top: 0.75rem; padding-top: 0.5rem; border-top: 1px dashed var(--border); font-size: 0.85rem; }
.kb-related-label { color: var(--text-muted, #888); }
.kb-related-link { color: var(--accent, #4a7dff); text-decoration: none; }
.kb-related-link:hover { text-decoration: underline; }
#kb-screen .community-block.kb-highlight { outline: 2px solid var(--accent, #4a7dff); outline-offset: 4px; border-radius: 6px; }

/* Community marker: the unified feed, live on the Chill page front. */
.feed-toolbar { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 0.5rem; }
.feed-scope-toggle { display: flex; gap: 0.25rem; }
/* @theme-component: .feed-scope-btn */
.feed-scope-btn { border: 1px solid var(--border); background: var(--panel-bg); color: var(--muted); border-radius: var(--radius-pill); padding: 0.3rem 0.85rem; font-size: 0.85rem; cursor: pointer; transition: var(--transition-soft); }
/* @theme-component: .feed-scope-btn.active */
.feed-scope-btn.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
/* @theme-component: .feed-community-link-btn */
.feed-community-link-btn { display: inline-block; margin: 0.6rem 0; background: var(--accent); color: var(--on-accent); border: none; border-radius: var(--radius-md); padding: 0.45rem 0.9rem; font-size: 0.85rem; cursor: pointer; }
.feed-search-form { display: flex; gap: 0.5rem; align-items: center; margin: 0.75rem 0; }
.feed-search-form input[type="text"] { flex: 1; border-radius: var(--radius-md); border: 1px solid var(--border); padding: 0.5rem 0.75rem; background: var(--panel-bg); color: var(--text); }
/* Composer input boxes -- rounded to match the feed's own search input
   above, rather than the plain-square browser default a <textarea> gets
   with no styling of its own. */
.community-hub-composer textarea,
#group-feed-post-text {
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  padding: 0.6rem 0.75rem;
  background: var(--panel-bg);
  color: var(--text);
  font: inherit;
  resize: vertical;
}
.community-feed-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.75rem; }
/* @theme-component: .feed-card */
.feed-card { border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 0.85rem 1rem; background: var(--feed-post-bg); position: relative; }
.feed-card h4 { margin: 0.2rem 0 0.3rem 0; }
/* @theme-component: .feed-card-kind */
.feed-card-kind { display: inline-block; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 0.1rem 0.4rem; }
/* @theme-component: .feed-card-desc */
/* overflow-wrap added (Share System Consolidation visual pass, 2026-09-23)
   -- same real long-URL-with-no-spaces overflow risk found live on the
   share teaser's own text (see .share-teaser-text). */
.feed-card-desc { margin: 0.2rem 0; color: var(--text); font-size: 0.9rem; overflow-wrap: anywhere; }
.feed-card-meta { margin: 0.2rem 0 0 0; font-size: 0.8rem; }
.feed-card-tags { display: flex; gap: 0.35rem; flex-wrap: wrap; margin-top: 0.4rem; }
/* @theme-component: .feed-tag-chip */
.feed-tag-chip { font-size: 0.75rem; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-pill); padding: 0.1rem 0.5rem; color: var(--muted); }
/* @theme-component: .feed-card-thumb */
.feed-card-thumb { max-width: 100%; max-height: 220px; border-radius: var(--radius-md); display: block; margin-bottom: 0.5rem; object-fit: cover; }
.feed-card-thumb-clickable { cursor: zoom-in; transition: transform 150ms ease, box-shadow 150ms ease; }
/* @theme-component: .feed-card-thumb-clickable:hover */
.feed-card-thumb-clickable:hover { transform: scale(1.015); box-shadow: var(--shadow-md); }
/* Real "visit the live app" link on a shared brief (2026-08-28) -- only
   ever shown when the brief's snapshot hosted_url is genuinely set. */
.feed-card-hosted-link { display: inline-block; margin-top: 0.4rem; font-weight: 600; color: var(--accent); text-decoration: none; }
.feed-card-hosted-link:hover { text-decoration: underline; }
.feed-card-reactions { display: flex; gap: 0.3rem; margin-top: 0.5rem; flex-wrap: wrap; }
/* @theme-component: .feed-card-reaction-btn */
.feed-card-reaction-btn { border: 1px solid var(--border); background: var(--bg); border-radius: var(--radius-pill); padding: 0.1rem 0.5rem; font-size: 0.8rem; cursor: pointer; color: var(--muted); }
/* @theme-component: .feed-card-reaction-btn.mine */
.feed-card-reaction-btn.mine { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }

.reaction-picker-wrap { position: relative; display: inline-block; }
.reaction-picker-popover {
  position: absolute;
  bottom: 100%;
  left: 0;
  margin-bottom: 0.3rem;
  display: flex;
  gap: 0.15rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 0.25rem 0.4rem;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  z-index: 10;
}
.reaction-picker-option {
  border: none;
  background: none;
  cursor: pointer;
  font-size: 1.1rem;
  padding: 0.1rem 0.2rem;
  border-radius: 50%;
  line-height: 1;
}
.reaction-picker-option:hover { background: var(--border); transform: scale(1.15); }
.reaction-picker-option.mine { background: var(--accent); }

.feed-card-comments { margin-top: 0.5rem; }
.feed-card-comments-list { list-style: none; margin: 0.4rem 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.feed-card-comment { display: flex; align-items: flex-start; gap: 0.5rem; font-size: 0.85rem; }
.feed-card-comment-form { display: flex; gap: 0.4rem; margin-top: 0.3rem; }
.feed-card-comment-form input { flex: 1 1 auto; }

.report-btn-wrap { display: inline-block; margin-left: 0.5rem; }
.report-btn { color: var(--muted); font-size: 0.8rem; }
.report-inline-form { display: flex; align-items: center; gap: 0.4rem; margin-top: 0.3rem; flex-wrap: wrap; }
.report-inline-form input { flex: 1 1 200px; }
.report-inline-form .muted { font-size: 0.75rem; }

.header-avatar-wrap { position: relative; margin-left: 0.4rem; }
#header-avatar-btn {
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  display: block;
}
#header-avatar-img, #header-avatar-placeholder {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  background: var(--border);
  transition: box-shadow 0.25s ease;
}
/* Real illumination effect (2026-09-03, founder's own ask): the avatar
   lights up on click and stays lit until a real top-nav tool link is
   applied (see applyHeaderStateSwitch in app.js, the one place that
   clears it) -- a visible "you're in the account menu" cue that
   persists across however long you spend there, not just a hover flash.
   Real bug found and fixed same day, founder-reported ("doesnt seem to
   be any movement or illumination"): the header bar's own background IS
   var(--accent) (#header-state-bar { background: var(--accent) }), so a
   glow drawn in that exact same color was genuinely invisible against it
   -- not a "needs more" request, a real color-matches-background bug.
   Switched to var(--on-accent) (white/near-white, the header's own real
   text/icon color, guaranteed to contrast against its accent background)
   plus a real pulsing animation for the requested "movement", not a
   static ring. */
.header-avatar-wrap.illuminated #header-avatar-img,
.header-avatar-wrap.illuminated #header-avatar-placeholder {
  animation: header-avatar-illuminate-pulse 1.6s ease-in-out infinite;
}
@keyframes header-avatar-illuminate-pulse {
  0%, 100% { box-shadow: 0 0 0 2px var(--on-accent), 0 0 8px 2px var(--on-accent); }
  50% { box-shadow: 0 0 0 4px var(--on-accent), 0 0 18px 6px var(--on-accent); }
}
@media (prefers-reduced-motion: reduce) {
  .header-avatar-wrap.illuminated #header-avatar-img,
  .header-avatar-wrap.illuminated #header-avatar-placeholder {
    animation: none;
    box-shadow: 0 0 0 3px var(--on-accent), 0 0 12px 3px var(--on-accent);
  }
}
#header-avatar-menu {
  position: absolute;
  top: 2.2rem;
  right: 0;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 8px);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
  min-width: 180px;
  z-index: 50;
  display: flex;
  flex-direction: column;
  padding: 0.5rem 0;
}
.header-avatar-menu-name {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.3rem 0.9rem 0.5rem;
  font-weight: 600;
  border: none;
  border-bottom: 1px solid var(--border);
  margin-bottom: 0.3rem;
  background: none;
  cursor: pointer;
  color: inherit;
  font-size: inherit;
}
.header-avatar-menu-name:hover { background: var(--border); }
#header-avatar-menu button { text-align: left; padding: 0.4rem 0.9rem; }
#header-avatar-menu button:hover { background: var(--border); }

#chat-dock { position: fixed; bottom: 1rem; right: 1rem; z-index: 100; }
#chat-dock-toggle-btn {
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  border-radius: var(--radius-pill);
  padding: 0.6rem 1.1rem;
  cursor: pointer;
  font-weight: 600;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.chat-dock-online-badge {
  background: rgba(255, 255, 255, 0.3);
  border-radius: var(--radius-pill);
  padding: 0.05rem 0.4rem;
  font-size: 0.75rem;
}
#chat-dock-panel {
  position: absolute;
  bottom: 3.2rem;
  right: 0;
  width: 320px;
  max-height: 460px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 8px);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.chat-dock-header { display: flex; align-items: center; gap: 0.4rem; padding: 0.5rem; border-bottom: 1px solid var(--border); }
.chat-dock-channel-tabs { display: flex; gap: 0.3rem; flex: 1 1 auto; }
.chat-dock-channel-tab {
  border: 1px solid var(--border);
  background: none;
  color: var(--text);
  border-radius: var(--radius-pill);
  padding: 0.2rem 0.6rem;
  font-size: 0.8rem;
  cursor: pointer;
}
.chat-dock-channel-tab.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
#chat-dock-lock-toggle-btn, #chat-dock-close-btn { background: none; border: none; cursor: pointer; font-size: 0.9rem; }
.chat-dock-presence-line { padding: 0.3rem 0.6rem; margin: 0; font-size: 0.75rem; }
.chat-dock-messages { list-style: none; margin: 0; padding: 0 0.6rem; overflow-y: auto; flex: 1 1 auto; max-height: 260px; }
.chat-dock-messages li { padding: 0.35rem 0; border-bottom: 1px solid var(--border); font-size: 0.85rem; }
.chat-dock-messages li:last-child { border-bottom: none; }
.chat-dock-message { display: flex; align-items: flex-start; gap: 0.5rem; }
.chat-dock-message-avatar { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex-shrink: 0; background: var(--border); }
.chat-dock-message-avatar-btn { padding: 0; border: none; background: none; cursor: pointer; flex-shrink: 0; border-radius: 50%; }

/* Real chat-bubble colours (dogfooding, 2026-08-27): every chat surface
   here (DM dock, group chat dock, feed chat dock, group-detail chat) shared
   one flat, unstyled row style with no colour distinguishing your own
   messages from the other person's -- confirmed live, a real gap against
   normal chat UI conventions. Own messages get the app's own accent tint
   with white text (matches --on-accent used elsewhere for accent
   surfaces); everyone else's get a neutral surface tint -- both real theme
   tokens, so this holds in light and dark alike. */
.chat-dock-message > div:last-child { border-radius: var(--radius-sm, 8px); padding: 0.3rem 0.55rem; background: var(--surface-2, rgba(127, 127, 127, 0.08)); }
.chat-dock-message-own > div:last-child { background: var(--accent); color: var(--on-accent); }
.chat-dock-message-own > div:last-child .muted { color: var(--on-accent); opacity: 0.85; }

/* Real layout fix (2026-08-19): the read-only profile's name-block is
   shorter than the editable page's (a button instead of a status <p>),
   so the shared flex-end alignment with the avatar pushed the name up
   into the cover's bottom seam. */
#user-profile-display-name { margin-top: 0.75rem; }
#user-profile-message-btn { margin-top: 0.4rem; }
#chat-dock-form { display: flex; gap: 0.4rem; padding: 0.5rem; border-top: 1px solid var(--border); }
#chat-dock-input { flex: 1 1 auto; }
#chat-dock-lock-overlay {
  padding: 1.5rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  align-items: center;
  text-align: center;
}
#chat-dock-set-lock-panel {
  position: absolute;
  bottom: 3.2rem;
  right: 0;
  width: 260px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 8px);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
  padding: 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
/* @theme-component: .feed-card-price */
.feed-card-price { position: absolute; top: 0.85rem; right: 1rem; font-weight: 600; color: var(--accent); }
/* Real VST direct social sharing (2026-09-15) -- same small-chip language
   as .feed-tag-chip above, plus a muted/struck treatment for a platform
   that's genuinely no longer available (live-checked, never frozen). */
.feed-card-vst-share-badges { display: flex; gap: 0.4rem; margin-top: 0.5rem; flex-wrap: wrap; }
.feed-card-vst-share-badge { font-size: 0.75rem; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-pill); padding: 0.1rem 0.6rem; color: var(--text); }
a.feed-card-vst-share-badge { text-decoration: none; cursor: pointer; }
a.feed-card-vst-share-badge:hover { background: var(--accent-bg, var(--border)); }
.feed-card-vst-share-badge-unavailable { color: var(--muted); text-decoration: line-through; }
.feed-card-vst-copy-btn { display: inline-block; margin-top: 0.4rem; margin-right: 0.5rem; }
/* Card-type visual treatment: image cards read like an App Store gallery
   card, brief cards read like a book/album spine -- distinct at a glance. */
.feed-card--image { border-left: 4px solid var(--accent); }
/* @theme-component: .feed-card--brief */
.feed-card--brief { border-left: 4px solid var(--card-accent-brief); background: linear-gradient(180deg, var(--panel-bg), var(--bg)); }
.feed-card--app_store { border-left: 4px solid var(--card-accent-appstore); }
.local-ai-onboard-layout { max-width: 720px; margin: 0 auto; }

/* Admin genuinely needs real room -- data-dense tables/cards, not the
   640px cap .project-list-main uses for simple single-column pages. */
.admin-main-wide { max-width: 1400px; }
/* @theme-component: .local-ai-onboard-search-result */
.local-ai-onboard-search-result { border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.6rem; margin-bottom: 0.5rem; }
/* Search box scoped to the left (list) column's own width, not the full
   row. */
.admin-users-toolbar { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; margin-bottom: 0.75rem; max-width: 50%; }
.admin-users-toolbar input[type="text"] { flex: 1 1 160px; }

/* CMS-style user management module (2026-08-13): mirrors the real chat
   screen's own .split/.left-panel/.archie-panel layout exactly -- users
   list on the left (bordered, same 55% share), the selected user's full
   card on the right (panel-bg shaded, same 45% share), both permanently
   visible together, same visual language as the rest of the app rather
   than a one-off admin-specific layout. */
/* @theme-component: .admin-users-layout */
.admin-users-layout { display: flex; gap: 0; width: 100%; height: 80vh; border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; }
@media (max-width: 900px) { .admin-users-layout { flex-direction: column; height: auto; } }

.admin-users-list {
  flex: 1 1 55%;
  max-width: 55%;
  border-right: 1px solid var(--border);
  overflow-y: auto;
  padding: 1rem 1.25rem;
  margin: 0;
}
@media (max-width: 900px) { .admin-users-list { max-width: 100%; border-right: none; border-bottom: 1px solid var(--border); } }
/* @theme-component: .admin-user-row */
.admin-user-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: start;
  gap: 0.2rem 0.6rem;
  padding: 0.65rem 0.85rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 0.4rem;
  cursor: pointer;
  transition: border-color var(--transition-soft), background var(--transition-soft);
}
/* @theme-component: .admin-user-row:hover */
.admin-user-row:hover { border-color: var(--accent); }
/* @theme-component: .admin-user-row.selected */
.admin-user-row.selected { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
.admin-user-row-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* @theme-component: .admin-user-row-pkg */
.admin-user-row-pkg {
  font-size: 0.75rem;
  padding: 0.15rem 0.55rem;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  white-space: nowrap;
  color: var(--muted);
}
/* @theme-component: .admin-user-row-meta */
.admin-user-row-meta { grid-column: 1 / -1; font-size: 0.8rem; color: var(--muted); }

/* @theme-component: .admin-user-detail-panel */
.admin-user-detail-panel {
  flex: 1 1 45%;
  max-width: 45%;
  min-width: 0;
  overflow-y: auto;
  padding: 1rem 1.25rem;
  background: var(--panel-bg);
}
@media (max-width: 900px) { .admin-user-detail-panel { max-width: 100%; } }
.admin-user-detail-empty { padding: 2rem 1rem; text-align: center; }
.admin-user-detail-header { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }
.admin-user-detail-header h3 { margin: 0; }

/* @theme-component: .admin-card-section */
.admin-card-section {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1rem 1.2rem;
  margin-bottom: 1rem;
  background: var(--panel-bg);
}
.admin-card-section h4 { margin: 0 0 0.65rem 0; }
.admin-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0.3rem 1.5rem;
}
.admin-card-grid > div { padding: 0.15rem 0; }
.admin-card-section ul { list-style: none; padding: 0; margin: 0; }
.admin-card-section li { padding: 0.3rem 0; border-bottom: 1px solid var(--border); font-size: 0.9rem; }
.admin-card-section li:last-child { border-bottom: none; }
.admin-stat-line { font-size: 1.1rem; font-weight: 600; }
/* @theme-component: .connection-item */
.connection-item { border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.75rem; margin-bottom: 0.75rem; }
.connection-item h4 { margin: 0 0 0.35rem 0; }

/* Simplify Balance Display brief: the real spendable balance stays visually
   primary; the spend cap/usage detail stays clearly secondary and never
   blended into the same figure. */
/* @theme-component: .settings-balance-primary */
.settings-balance-primary { font-size: 1.1rem; font-weight: 600; color: var(--text); margin: 0.5rem 0 0.15rem; }
.settings-balance-secondary { margin: 0 0 0.5rem; font-size: 0.85rem; }

/* BYOK correction/governance brief: basic, low-effort visual polish for the
   real balance display -- not a redesign, just genuine hierarchy/spacing
   care applied to a section that's otherwise plain. */
#openrouter-key-status {
  font-size: 1.1rem;
  font-weight: 600;
  margin: 0.4rem 0;
}
#openrouter-key-currency-note {
  margin-top: -0.3rem;
  font-size: 0.85rem;
}
/* @theme-component: #openrouter-key-topup-btn */
#openrouter-key-topup-btn {
  background: var(--accent, #4a7c59);
  color: var(--on-accent);
  font-weight: 600;
}
.powered-by-stripe {
  font-size: 0.7rem;
  opacity: 0.7;
  margin-top: 0.5rem;
}
/* @theme-component: #openrouter-key-advanced-details summary */
#openrouter-key-advanced-details summary {
  cursor: pointer;
  font-size: 0.8rem;
  color: var(--muted-fg, #666);
  margin-top: 0.4rem;
}

/* Persistent nav balance indicator + "My Spend" pop-out. */
.spend-indicator-btn {
  font-weight: 600;
}
.my-spend-popout {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 4rem;
  z-index: 1200;
}
.my-spend-popout.hidden {
  display: none;
}
/* @theme-component: .my-spend-popout-card */
.my-spend-popout-card {
  background: var(--bg, #fff);
  border-radius: var(--radius-lg);
  padding: 1.25rem;
  max-width: 420px;
  width: 90%;
  max-height: 80vh;
  overflow-y: auto;
  position: relative;
  box-shadow: var(--shadow-md);
}
.my-spend-close-btn {
  position: absolute;
  top: 0.5rem;
  right: 0.75rem;
  font-size: 1.3rem;
}
.my-spend-popout-footer {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  margin-top: 1rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border);
}
/* @theme-component: .my-spend-topup-btn */
.my-spend-topup-btn {
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  border-radius: var(--radius-md);
  padding: 0.5rem 1rem;
  font-weight: 600;
  white-space: nowrap;
  flex-shrink: 0;
}
.my-spend-hide-balance-block { text-align: right; }
/* @theme-component: .my-spend-hide-balance-row */
.my-spend-hide-balance-row {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  color: var(--muted);
}
.my-spend-hide-balance-note {
  font-size: 0.75rem;
  margin-top: 0.25rem;
}
.my-app-actions { display: flex; gap: 0.4rem; flex-wrap: wrap; }
/* Real, direct link to a finished app's live address (2026-08-21) -- an
   <a>, not a <button>, since it's a genuine external navigation, so it
   needs the same visual treatment as its sibling buttons applied by hand. */
.my-app-open-link-btn {
  display: inline-flex;
  align-items: center;
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  border-radius: var(--radius-md);
  padding: 0.5rem 1rem;
  cursor: pointer;
  text-decoration: none;
  font-size: inherit;
}
/* Real bug found live (2026-08-30, founder-reported: "theres links not
   showing on the project cards") -- traced past getComputedStyle, which
   lies here on purpose: browsers deliberately report a :visited link's
   color to JS as if it were unvisited, a real, documented privacy
   protection against history-sniffing. The actual paint used the
   browser's own default :visited color (a dim, low-contrast purple/grey)
   instead of the real white this class sets, since nothing here ever
   overrode :visited specifically -- confirmed live by forcing an inline
   color and watching genuinely present, correctly-positioned text appear
   out of nowhere. Only ever visible on a link the visitor's own browser
   already considers visited (their own real project's app, clicked
   before) -- exactly the real, reproducible pattern reported. */
.my-app-open-link-btn:visited { color: var(--on-accent); }
.my-app-open-link-btn:hover { filter: brightness(0.92); }
/* @theme-component: .pending-build-badge */
.pending-build-badge { color: var(--danger); font-weight: 600; }
.my-app-abandon-inline { display: flex; flex-direction: column; gap: 0.4rem; width: 100%; }
.my-app-abandon-inline textarea { width: 100%; }
.model-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 0.75rem; margin-bottom: 1.5rem; }

/* Model Selection Tiering brief (2026-08-14): a real, distinct group
   heading -- not a plain divider line -- so the value/premium trade-off
   reads clearly at a glance instead of putting the reasoning burden on the
   user to spot a price difference buried in a flat list. */
.model-tier-header { margin: 0.5rem 0 0.6rem; }
/* @theme-component: .model-tier-pill */
.model-tier-pill {
  display: inline-block;
  font-size: var(--font-size-xs);
  font-weight: 600;
  padding: 0.25rem 0.75rem;
  border-radius: var(--radius-pill);
}
/* @theme-component: .model-tier-pill.model-tier-value */
.model-tier-pill.model-tier-value { background: var(--border); color: var(--text); }
/* @theme-component: .model-tier-pill.model-tier-premium */
.model-tier-pill.model-tier-premium { background: var(--accent); color: var(--on-accent); }
/* @theme-component: .model-tier-pill.model-tier-free_open */
.model-tier-pill.model-tier-free_open { background: var(--success-bg); color: var(--success); }
/* @theme-component: .model-tier-pill.model-tier-vendor_cli */
.model-tier-pill.model-tier-vendor_cli { background: var(--border); color: var(--text); }
/* @theme-component: .model-tier-pill.model-tier-vendor_account */
.model-tier-pill.model-tier-vendor_account { background: var(--warning); color: var(--on-accent); }
/* @theme-component: .model-tier-pill.model-tier-unmaintained */
.model-tier-pill.model-tier-unmaintained { background: rgba(0,0,0,0.12); color: var(--muted); }
/* @theme-component: .coding-agent-credential-tag */
.coding-agent-credential-tag {
  display: block;
  font-size: var(--font-size-xs);
  font-weight: 400;
  color: var(--muted);
  margin-top: 0.15rem;
}
/* @theme-component: .model-card-new-badge */
.model-card-new-badge {
  display: inline-block;
  font-size: var(--font-size-xs);
  font-weight: 600;
  color: var(--success);
  background: var(--success-bg);
  border-radius: var(--radius-pill);
  padding: 0.1rem 0.5rem;
  margin-left: 0.4rem;
}
/* @theme-component: .model-card */
.model-card { border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.75rem; }
/* Onboarding steps brief: a real, visible locked-in state -- every other
   model stays visible and available, this just marks which one is
   currently active. */
/* @theme-component: .model-card-selected */
.model-card-selected { border-color: var(--accent, #4a7c59); box-shadow: 0 0 0 1px var(--accent, #4a7c59); }
/* @theme-component: .model-card-selected-badge */
.model-card-selected-badge { display: inline-block; font-size: 0.7rem; font-weight: 600; color: var(--accent, #4a7c59); margin-left: 0.4rem; }
.model-card h4 { margin: 0 0 0.25rem 0; }
/* @theme-component: .model-card-detail */
.model-card-detail { font-size: 0.8rem; color: var(--muted); white-space: pre-wrap; margin: 0.5rem 0; }
.model-card-actions { display: flex; gap: 0.4rem; margin-top: 0.5rem; }
/* @theme-component: .community-block input[type="text"], .community-block textarea, .community-block select */
.community-block input[type="text"], .community-block textarea, .community-block select {
  width: 100%;
  padding: 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel-bg);
  color: var(--text);
  margin-bottom: 0.4rem;
}
.community-block textarea { min-height: 60px; }
#display-name-form { display: flex; gap: 0.5rem; }
#display-name-form input { flex: 1; margin-bottom: 0; }

#community-search-results, #community-pending-list, #community-connections-list,
#community-conversation-list, #listings-list {
  list-style: none;
  padding: 0;
}
/* @theme-component: #community-search-results li, #community-pending-list li, #community-connections-list li, #listings-list li */
#community-search-results li, #community-pending-list li, #community-connections-list li, #listings-list li {
  padding: 0.6rem 0.8rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 0.4rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
#community-connections-list li { cursor: pointer; transition: border-color var(--transition-soft); }
/* @theme-component: #community-connections-list li:hover */
#community-connections-list li:hover { border-color: var(--accent); }

#community-conversation-list { max-height: 240px; overflow-y: auto; margin-bottom: 0.5rem; }
#community-conversation-list li {
  display: block;
  border: none;
  padding: 0.3rem 0;
}
/* @theme-component: #community-conversation-list li.own */
#community-conversation-list li.own { text-align: right; color: var(--accent); }
#community-message-form { display: flex; gap: 0.5rem; }
#community-message-form input { flex: 1; margin-bottom: 0; }

#listings-list li { flex-direction: column; align-items: stretch; }
#listings-list .listing-title { font-weight: 600; }
#listings-list .listing-actions { display: flex; gap: 0.5rem; margin-top: 0.5rem; }

#new-checkpoint-form { display: flex; gap: 0.5rem; margin-bottom: 0.75rem; }
/* @theme-component: #new-checkpoint-form input */
#new-checkpoint-form input { flex: 1; padding: 0.5rem; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--panel-bg); color: var(--text); }
#checkpoints-list { list-style: none; padding: 0; }
/* @theme-component: #checkpoints-list li */
#checkpoints-list li {
  padding: 0.6rem 0.8rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 0.4rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
.status-superseded { opacity: 0.5; }

.topbar div { display: flex; gap: 1rem; align-items: center; }

.admin-panel { margin-top: 1.5rem; }
.admin-panel ul { list-style: none; padding: 0; }
/* @theme-component: .admin-panel li */
.admin-panel li {
  padding: 0.6rem 0.8rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 0.4rem;
  cursor: pointer;
  transition: border-color var(--transition-soft);
}
/* @theme-component: .admin-panel li:hover */
.admin-panel li:hover { border-color: var(--accent); }
#admin-listings-list li, #admin-audit-list li { cursor: default; }
.admin-stat-num { font-size: 1.6rem; font-weight: 600; }
/* @theme-component: #admin-user-detail */
#admin-user-detail {
  margin-top: 1rem;
  padding: 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel-bg);
}
.admin-remove-btn { margin-top: 0.5rem; }

/* VST Technical Knowledge Programme, Stage 4 (2026-09-19) -- the smallest
   usable admin table/filter layout, reusing existing --border/--radius-md/
   --panel-bg tokens rather than inventing new ones. */
.vst-knowledge-filter-row, .vst-knowledge-form-row { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-bottom: 0.5rem; }
.vst-knowledge-header-row { display: flex; justify-content: space-between; align-items: center; }
.vst-knowledge-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.vst-knowledge-table th, .vst-knowledge-table td { padding: 0.4rem 0.6rem; border-bottom: 1px solid var(--border); text-align: left; vertical-align: top; }
.vst-knowledge-table td.vst-knowledge-title-cell { max-width: 320px; }
#vst-knowledge-form-block textarea { min-height: 6rem; width: 100%; }
#vst-knowledge-form-block input[type="text"] { flex: 1; min-width: 160px; }

/* @theme-component: .ollie-message */
.ollie-message {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1rem 1.25rem;
  margin-bottom: 1.5rem;
}
.ollie-message p { margin: 0; line-height: 1.5; }
.onboarding-step { margin-bottom: 1.5rem; }
.onboarding-step form { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 0.5rem; }
/* @theme-component: .onboarding-step input */
.onboarding-step input { flex: 1; min-width: 160px; padding: 0.5rem; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--panel-bg); color: var(--text); }

#tickets-list li, #admin-tickets-list li { flex-direction: column; align-items: stretch; cursor: pointer; transition: border-color var(--transition-soft); }
/* @theme-component: #tickets-list li:hover, #admin-tickets-list li:hover */
#tickets-list li:hover, #admin-tickets-list li:hover { border-color: var(--accent); }
#ticket-detail-messages, #admin-ticket-detail-messages {
  list-style: none;
  padding: 0;
  max-height: 300px;
  overflow-y: auto;
  margin-bottom: 0.75rem;
}
/* @theme-component: #ticket-detail-messages li, #admin-ticket-detail-messages li */
#ticket-detail-messages li, #admin-ticket-detail-messages li {
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 0.4rem;
}
/* @theme-component: #ticket-detail-messages li .msg-meta, #admin-ticket-detail-messages li .msg-meta */
#ticket-detail-messages li .msg-meta, #admin-ticket-detail-messages li .msg-meta {
  display: block;
  font-size: 0.75rem;
  color: var(--muted);
  margin-bottom: 0.2rem;
}
#ticket-reply-form, #admin-ticket-reply-form { display: flex; gap: 0.5rem; }
/* @theme-component: #ticket-reply-form input, #admin-ticket-reply-form input */
#ticket-reply-form input, #admin-ticket-reply-form input { flex: 1; padding: 0.5rem; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--panel-bg); color: var(--text); }

.theme-toggle { display: flex; gap: 0.5rem; }
/* @theme-component: .theme-btn */
.theme-btn {
  background: var(--panel-bg);
  color: var(--text);
  border: 1px solid var(--border);
}
/* @theme-component: .theme-btn.active */
.theme-btn.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }

.home-footer {
  text-align: center;
  padding: 2rem 1rem 3rem;
  display: flex;
  justify-content: center;
  gap: 1.5rem;
  flex-wrap: wrap;
}
/* @theme-component: .home-footer a */
.home-footer a { color: var(--muted); text-decoration: none; font-size: 0.9rem; transition: color var(--transition-soft); }
/* @theme-component: .home-footer a:hover */
.home-footer a:hover { color: var(--accent); }
#page-content { white-space: pre-wrap; line-height: 1.6; }
.admin-kb-body { white-space: pre-wrap; line-height: 1.6; }
.page-status-badge { display: inline-block; margin-bottom: 1rem; padding: 0.3rem 0.7rem; border-radius: 999px; border: 1px solid var(--border); background: var(--surface); font-size: 0.8rem; font-weight: 600; color: var(--text-muted, #888); }

.home-title-link { display: inline-flex; align-items: center; gap: 0.5rem; background: none; border: none; padding: 0; margin: 0; font: inherit; font-weight: inherit; color: inherit; cursor: pointer; }
.home-title-link:hover { text-decoration: underline; }
.home-title-logo-slot { display: inline-block; width: 0; height: 1.5rem; }

/* Column Widths correction (2026-08-15): the real cause of the cramped
   left column and the empty space to the right of the render window was
   the outer <main> itself -- .project-list-main caps every page it's used
   on to 640px, meant for simple single-column screens (same 640px cap
   already flagged and overridden for Admin via .admin-main-wide, same
   pattern reused here). The grid below was never actually using the
   available screen width; it was fighting for room inside a 640px box. */
.image-maker-main-wide { max-width: 1300px; }
.image-maker-top-layout { display: grid; grid-template-columns: minmax(320px, 1fr) minmax(420px, 1.5fr); gap: 2rem; align-items: start; margin-bottom: 1.5rem; }
@media (max-width: 900px) { .image-maker-top-layout { grid-template-columns: 1fr; } }

/* Same 640px-cap fix as Image Maker above, same pattern reused here:
   generate form + edit-project list on the left (actions), the growing
   "Your videos" gallery on the right, so a long results list doesn't push
   the generate form miles down the page on a wide screen. */
.video-main-wide { max-width: 1300px; }
.video-top-layout { display: grid; grid-template-columns: minmax(320px, 1fr) minmax(360px, 1.2fr) minmax(280px, 0.9fr); gap: 2rem; align-items: start; }
.video-top-layout-left { display: flex; flex-direction: column; gap: 1.5rem; }
@media (max-width: 1200px) { .video-top-layout { grid-template-columns: minmax(320px, 1fr) minmax(360px, 1.2fr); } .video-top-layout-right { grid-column: 1 / -1; } }
@media (max-width: 900px) { .video-top-layout { grid-template-columns: 1fr; } .video-top-layout-right { grid-column: auto; } }

/* Real founder catch (2026-08-27): .project-list-main's own 2rem top
   margin plus every <h3>'s default browser margin stacked into a real,
   visibly wasted ~50px gap above "Real image generation"/"Render" and
   "Generate a video"/"Your videos" -- these two wide layouts only, not the
   base .project-list-main class every simple page still uses. */
.image-maker-main-wide, .video-main-wide { margin-top: 0.75rem; }
.image-maker-top-layout h3:first-child, .video-top-layout h3:first-child { margin-top: 0; }
/* Gallery moved into the render column, stacked below Render (2026-08-15):
   balances column heights and keeps reference-image drag targets (the
   prompt dropzone above) and drop sources (gallery thumbnails below) in
   the same column, close together, instead of a full-width row the user
   had to scroll past the whole page to reach. */
.image-maker-gallery-block { margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--border); }
.image-maker-models-column .model-grid { grid-template-columns: 1fr; }
.image-maker-render-window { min-height: 200px; border: 1px solid var(--border); border-radius: 8px; margin-bottom: 1rem; display: flex; align-items: center; justify-content: center; padding: 1rem; background: var(--surface); }
.image-maker-render-window img { max-width: 100%; max-height: 320px; object-fit: contain; }
.image-maker-gallery-scroll { max-height: 480px; overflow-y: auto; padding-right: 0.25rem; }
.image-maker-comparison-teaser { font-size: 0.78rem; font-style: italic; }

.image-editor-main { max-width: 1300px; display: grid; grid-template-columns: minmax(320px, 1fr) 320px; gap: 2rem; align-items: start; }
@media (max-width: 900px) { .image-editor-main { grid-template-columns: 1fr; } }
.image-editor-canvas-wrap { position: relative; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); display: flex; align-items: center; justify-content: center; padding: 1rem; min-height: 300px; }
.image-editor-canvas-wrap canvas { max-width: 100%; max-height: 70vh; object-fit: contain; }
.image-editor-crop-box { position: absolute; border: 2px dashed var(--accent); background: rgba(47, 107, 79, 0.15); cursor: move; }
.image-editor-controls { display: flex; flex-direction: column; gap: 0.85rem; }
.image-editor-controls h3:not(:first-child) { margin-top: 0.5rem; padding-top: 0.85rem; border-top: 1px solid var(--border); }
.image-editor-transform-row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.image-editor-resize-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; }
.image-editor-resize-row input[type="number"] { width: 5.5rem; }
.image-editor-lock-aspect { display: flex; align-items: center; gap: 0.3rem; font-size: 0.8rem; color: var(--muted); }
.image-editor-slider-row { display: grid; grid-template-columns: 5.5rem 1fr 2.5rem; align-items: center; gap: 0.5rem; }
.image-editor-slider-row input[type="range"] { width: 100%; }
.image-editor-slider-row output { text-align: right; font-variant-numeric: tabular-nums; color: var(--muted); }
.image-editor-actions { display: flex; gap: 0.5rem; margin-top: 0.25rem; }
.mini-brief-item { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem; padding: 0.5rem 0; border-bottom: 1px solid var(--border); cursor: pointer; }
/* Real "Project assets" panel (2026-08-28) */
.project-material-item { display: flex; align-items: center; gap: 0.6rem; padding: 0.5rem 0; border-bottom: 1px solid var(--border); }
.project-material-item:last-child { border-bottom: none; }
.project-material-thumb { width: 44px; height: 44px; object-fit: cover; border-radius: var(--radius-sm); flex-shrink: 0; }
.project-material-text { flex: 1; font-size: 0.9rem; overflow-wrap: anywhere; }
.minibrief-document-section { padding: 1rem 0; border-bottom: 1px solid var(--border); scroll-margin-top: 1rem; }
.minibrief-document-section h3 { margin-bottom: 0.25rem; }
.minibrief-document-heading-row { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.7rem; }
.minibrief-document-heading-row .archie-read-aloud-row { margin-top: 0; }
.minibrief-document-subtitle { font-style: italic; color: var(--text-muted, #888); margin: 0 0 0.5rem; }
.minibrief-document-edit-btn { font-size: 0.75rem; }
.minibrief-document-edit-form { display: flex; flex-direction: column; gap: 0.4rem; }
.minibrief-document-edit-textarea { width: 100%; min-height: 100px; box-sizing: border-box; }
.minibrief-document-edit-form button { align-self: flex-start; margin-right: 0.4rem; }

/* Offloading block reading-order pass (2026-08-15): decide/act on the
   left, observe status on the right -- same left-decide/right-observe
   principle as the Mini-Brief layout, applied to this block specifically. */
/* Real 3-row page structure, final call (2026-08-27, founder-sketched):
   full-width heading on top (see index.html, now outside this block
   entirely), then everything else -- Step 1 setup, the Offloading
   explainer, Step 3/4/5 -- as one genuine full-width flowing column, and
   a further full-width strip below (cost comparison, hosting, etc., own
   community-blocks after this one closes). Side-by-side columns were
   tried and repeatedly adjusted first; the founder's own final call was
   to drop the split altogether -- .offload-decide-col/.offload-status-col
   stay as flex items in this single column purely so their own spacing
   rules keep working, not because they're visually separate columns
   anymore. */
.offload-layout { display: flex; flex-direction: column; gap: 2rem; margin-top: 1rem; }
.offload-decide-col > *, .offload-status-col > * { margin-bottom: 1.25rem; }
.build-confirm-actions-row { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: center; }
.mini-brief-number { font-weight: 600; font-size: 0.8rem; color: var(--text-muted, #888); }
.mini-brief-title { font-size: 0.9rem; }
.mini-brief-body-preview { flex-basis: 100%; margin: 0.15rem 0 0; font-size: 0.85rem; color: var(--text-muted, #888); }
.image-maker-send-to-archie { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; padding: 0.4rem 0.5rem; margin-top: 0.4rem; }
.image-maker-send-to-archie select { max-width: 160px; }
.image-maker-send-to-archie .muted { font-size: 0.75rem; }
.archie-chat-input-row { display: flex; align-items: center; gap: 0.5rem; justify-content: flex-end; }
#archie-send-last-response-panel { justify-content: flex-start; }

/* Real gap found live (2026-08-30, founder-reported: hard to find the chat
   input again while scrolling a long conversation). A real, colored frame
   around the whole compose box (not just the bare textarea) makes it read
   as one obvious control at a glance -- deliberately not pinned/sticky
   (founder's own call: "dont pin it"), just genuinely easier to spot
   wherever it lands as you scroll past it. The Send button sits inside
   this same frame, bottom-right, rather than as a separate row below it. */
.archie-chat-input-frame {
  border: 2px solid var(--accent);
  border-radius: var(--radius-md);
  background: var(--panel-bg);
  padding: 0.6rem;
  margin: 0.5rem 0;
}
.archie-chat-input-frame textarea {
  width: 100%;
  box-sizing: border-box;
  min-height: 80px;
  border: none;
  background: transparent;
  color: var(--text);
  padding: 0.25rem;
  resize: vertical;
}
.archie-chat-input-frame textarea:focus { outline: none; }
.archie-chat-input-frame .archie-chat-input-row { margin-top: 0.4rem; }
.archie-chat-send-btn { font-weight: 600; }

/* Real gap found live (2026-09-04, founder: "are you able to move those
   bubbles with the tools calls?") -- mirrors the current live tool-call
   bubble here, always visible right above the input, instead of only in
   the scrolling log above where it could scroll out of view mid-build. */
/* @theme-component: .archie-live-status-strip */
.archie-live-status-strip {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.85rem;
  color: var(--text-muted);
  background: var(--panel-bg-subtle, var(--panel-bg));
  border: 1px solid var(--border-color, var(--accent));
  border-radius: var(--radius-md);
  padding: 0.4rem 0.7rem;
  margin: 0.4rem 0 -0.1rem;
}
.archie-live-status-strip::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
  animation: archie-thinking-bounce 1.1s ease-in-out infinite;
}
.archie-live-status-strip.hidden { display: none; }
@media (prefers-reduced-motion: reduce) {
  .archie-live-status-strip::before { animation: none; opacity: 0.7; }
}

/* Real gap found live (2026-08-30, founder-asked for a "discuss" option in
   the main chat window). #coding-chat-discuss-btn starts class="hidden"
   since the shared #archie-panel node (mountArchiePanel) also renders on
   screens where "Send" already always discusses (brief-building, standalone
   Chat) -- only meaningful once an app is live, i.e. on coding-chat-screen
   specifically, where a plain Send auto-routes into a real Amend edit. */
#coding-chat-screen:not(.hidden) #coding-chat-discuss-btn.hidden { display: inline-block !important; }

.coding-chat-instructions h4 { margin: 1.25rem 0 0.25rem; }
.coding-chat-instructions h4:first-of-type { margin-top: 0.5rem; }
.coding-chat-instructions p { margin: 0 0 0.5rem; line-height: 1.5; }
#archie-send-last-response-select { margin-left: auto; }
.chat-send-to-project { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; padding: 0.3rem 0; margin-top: 0.3rem; }
.chat-send-to-project select { max-width: 160px; }
.chat-send-to-project .muted { font-size: 0.75rem; }
#archie-chat-history-panel { border: 1px solid var(--border); border-radius: 8px; padding: 0.5rem 0.75rem; margin-bottom: 0.75rem; max-height: 220px; overflow-y: auto; }
#archie-chat-history-list { list-style: none; margin: 0; padding: 0; }
.chat-history-item { display: flex; align-items: center; gap: 0.5rem; padding: 0.45rem 0; border-bottom: 1px solid var(--border); }
.chat-history-item:last-child { border-bottom: none; }
.chat-history-title {
  flex: 1 1 auto;
  min-width: 0;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chat-history-actions { display: flex; align-items: center; gap: 0.4rem; flex-shrink: 0; margin-left: auto; }
.chat-history-actions button { white-space: nowrap; margin-top: 0; }
.image-maker-send-to-community { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; padding: 0 0.5rem 0.4rem; }
.image-maker-send-to-community .muted { font-size: 0.75rem; }
/* @theme-component: .gallery-download-btn */
.gallery-download-btn { font-size: 0.75rem; padding: 0.3rem 0.5rem; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--text); text-decoration: none; display: inline-flex; align-items: center; }
/* Card min-width widened 160px -> 220px (2026-08-27, founder's own live
   catch: the Redo/Use as reference/Download action row was genuinely
   cutting off "Download" at the old width, not just a tight fit) -- fewer,
   wider columns per row, same responsive auto-fill grid. flex-wrap on the
   action row itself is a real second layer, not just belt-and-braces: on a
   narrow viewport (mobile) three buttons still won't all fit on one line
   even at 220px, so they now wrap to a second row instead of clipping. */
.image-maker-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 1rem; margin-top: 0.75rem; }
.image-maker-gallery-item { border: 1px solid var(--border); border-radius: 8px; overflow: hidden; background: var(--surface); }
.image-maker-gallery-item img { width: 100%; height: 140px; object-fit: cover; cursor: pointer; display: block; }
.image-maker-gallery-caption { padding: 0.5rem; font-size: 0.75rem; color: var(--text-muted, #888); }
.image-maker-gallery-caption p { margin: 0 0 0.4rem; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }

.image-maker-briefs-list { list-style: none; margin: 0.75rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; max-height: 360px; overflow-y: auto; }
.image-maker-briefs-list-item { display: flex; align-items: flex-start; gap: 0.75rem; border: 1px solid var(--border); border-radius: 8px; padding: 0.6rem 0.75rem; background: var(--panel-bg); }
.image-maker-briefs-list-item img { width: 44px; height: 44px; object-fit: cover; border-radius: 6px; flex-shrink: 0; }
.image-maker-briefs-list-text { flex: 1; font-size: 0.85rem; line-height: 1.4; margin: 0; word-break: break-word; }
.image-maker-briefs-list-actions { display: flex; flex-direction: column; gap: 0.35rem; flex-shrink: 0; }
.image-maker-briefs-list-actions button { font-size: 0.75rem; padding: 0.25rem 0.5rem; white-space: nowrap; }

.image-maker-brief-popout-box { max-width: 640px; max-height: 80vh; overflow-y: auto; background: var(--panel-bg); border-radius: var(--radius-md); padding: 1.5rem; cursor: default; }
.image-maker-brief-popout-box p { white-space: pre-wrap; line-height: 1.5; margin: 0 0 1rem; color: var(--text); }
.image-maker-brief-popout-actions { display: flex; gap: 0.5rem; justify-content: flex-end; }
.image-maker-gallery-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; padding: 0 0.5rem 0.5rem; }
.image-maker-gallery-actions button { font-size: 0.75rem; padding: 0.3rem 0.5rem; }
.image-maker-dropzone { border: 1px dashed transparent; border-radius: 8px; transition: border-color 0.15s; }
.image-maker-dropzone.drag-over { border-color: var(--accent, #4a7dff); background: color-mix(in srgb, var(--accent, #4a7dff) 8%, transparent); }
.image-maker-reference-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.5rem; }
.image-maker-reference-chip { display: flex; align-items: center; gap: 0.35rem; border: 1px solid var(--border); border-radius: 999px; padding: 0.15rem 0.4rem 0.15rem 0.15rem; background: var(--surface); }
.image-maker-reference-chip img { width: 28px; height: 28px; object-fit: cover; border-radius: 999px; }
.image-maker-reference-chip button { border: none; background: none; cursor: pointer; font-size: 0.9rem; color: var(--text-muted, #888); padding: 0; }

/* A real, always-visible way back while browsing the feed/chat columns --
   the sidebar's own back button scrolls out of view independently since
   the feed and chat panels scroll on their own. */
.chill-topbar { position: sticky; top: 0; z-index: 5; }
.chill-layout { display: flex; min-height: calc(100vh - 57px); gap: 1.5rem; }
/* Real column-width parity with Groups' own three-column layout
   (.group-detail-admin-col/.group-detail-feed-col/.group-detail-chat-panel:
   220px / flex max-480px / 320px) -- left column is deliberately empty for
   now, reserving the same width rather than guessing at content that
   belongs there. */
.chill-left-col { flex: 0 0 220px; min-width: 0; padding: 1.5rem 0 1.5rem 1.5rem; }
@media (max-width: 1100px) {
  .chill-left-col { display: none; }
}
/* Facebook-style left-rail nav (2026-08-26): profile/friends/groups/
   birthdays link straight to sections that already exist on the profile
   page (Friends and Birthdays live there as real .community-block
   sections, not separate screens) -- Reminders doesn't exist yet, shown as
   a real, honestly-labeled "Soon" placeholder rather than a dead link. */
.chill-left-nav { display: flex; flex-direction: column; gap: 0.15rem; }
.chill-left-nav-item { display: flex; align-items: center; gap: 0.5rem; width: 100%; text-align: left; padding: 0.55rem 0.6rem; border: none; border-radius: var(--radius-sm); background: transparent; color: var(--text); font-size: 0.9rem; cursor: pointer; }
.chill-left-nav-item:hover { background: var(--surface-2, rgba(127, 127, 127, 0.1)); }
.chill-left-nav-item-soon { cursor: default; color: var(--muted); justify-content: space-between; }
.chill-left-nav-item-soon:hover { background: transparent; }
.chill-left-nav-soon-tag { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.03em; border: 1px solid var(--border); border-radius: var(--radius-pill); padding: 0.05rem 0.4rem; color: var(--muted); }
.chill-sidebar {
  width: 280px;
  flex-shrink: 0;
  padding: 1.5rem;
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.chill-sidebar h2 { margin-top: 0; }
.chill-sidebar ul { list-style: none; padding: 0; flex: 1; }
.chill-project-link { display: block; width: 100%; text-align: left; padding: 0; }
.chill-project-link:hover { text-decoration: underline; }
/* @theme-component: .chill-sidebar li */
.chill-sidebar li {
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 0.4rem;
  font-size: 0.9rem;
}
.chill-sidebar button { text-align: left; }
.chill-main { flex: 1; padding: 1.5rem 1.5rem; min-width: 0; overflow-y: auto; max-height: calc(100vh - 57px); }
/* Facebook-shaped centering (2026-08-26): the feed column now fills more
   of the space between the two fixed side columns and sits centered in
   it, rather than pinned to the left edge at a narrow fixed width. */
.chill-main .feed-block { max-width: 640px; margin: 0 auto; }

/* Community marker: the right-hand chat panel, standard conventions.
   Width matches Groups' own .group-detail-chat-panel exactly (320px). */
.chill-chat-panel { width: 320px; flex-shrink: 0; border-left: 1px solid var(--border); padding: 1.5rem 1.25rem; display: flex; flex-direction: column; max-height: calc(100vh - 57px); }
.feed-chat-presence-line { margin: 0 0 0.5rem 0; font-size: 0.8rem; }
/* Chill page feed chat: the input box sits just under the messages, not
   glued to the very bottom of a tall panel -- messages are capped well
   under half the panel's height, and a flexible spacer below the form
   absorbs the rest, with a soft (elastic) transition when that cap
   changes rather than an abrupt jump. */
.feed-chat-messages {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 0 1 auto;
  max-height: 34vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  min-height: 60px;
  transition: max-height 320ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.feed-chat-spacer { flex: 1 1 auto; }
/* @theme-component: .feed-chat-message */
.feed-chat-message { border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.5rem 0.65rem; background: var(--panel-bg); }
/* @theme-component: .feed-chat-message-meta */
.feed-chat-message-meta { font-size: 0.72rem; color: var(--muted); margin-bottom: 0.15rem; }
.feed-chat-message-content { font-size: 0.9rem; word-break: break-word; }
.feed-chat-reactions { display: flex; gap: 0.3rem; margin-top: 0.3rem; flex-wrap: wrap; }
/* @theme-component: .feed-chat-reaction-btn */
.feed-chat-reaction-btn { border: 1px solid var(--border); background: var(--bg); border-radius: var(--radius-pill); padding: 0.05rem 0.4rem; font-size: 0.75rem; cursor: pointer; color: var(--muted); }
/* @theme-component: .feed-chat-reaction-btn.mine */
.feed-chat-reaction-btn.mine { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.feed-chat-form { display: flex; gap: 0.4rem; margin-top: 0.75rem; }
.feed-chat-form input { flex: 1; }
/* @theme-component: #feed-chat-join-btn.joined */
#feed-chat-join-btn.joined { color: var(--accent); font-weight: 600; }

@media (max-width: 900px) {
  .chill-layout { flex-direction: column; }
  .chill-chat-panel { width: auto; border-left: none; border-top: 1px solid var(--border); max-height: none; }
}
.kb-link-inline { display: inline; padding: 0; }

/* @theme-component: .break-lock-box */
.break-lock-box {
  max-width: 360px;
  margin: 20vh auto;
  padding: 2rem;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  text-align: center;
}
/* @theme-component: #break-lock-overlay */
#break-lock-overlay { background: var(--bg); }
/* @theme-component: #break-unlock-form input */
#break-unlock-form input { display: block; width: 100%; margin-bottom: 0.75rem; padding: 0.6rem; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--bg); color: var(--text); }

/* @theme-component: .ruleset-pre */
.ruleset-pre {
  white-space: pre-wrap;
  font-family: ui-monospace, monospace;
  font-size: 0.85rem;
  line-height: 1.5;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 1rem;
  max-height: 400px;
  overflow-y: auto;
}
/* @theme-component: #ruleset-new-content */
#ruleset-new-content { width: 100%; min-height: 150px; padding: 0.5rem; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--panel-bg); color: var(--text); margin-bottom: 0.5rem; }

/* Header state selector (Chat / App Builder / Compute) — always visible, sticky */
/* @theme-component: #header-state-bar */
#header-state-bar {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  min-height: 3.2rem;
  background: var(--accent);
  border-bottom: 1px solid var(--accent);
  box-sizing: border-box;
}
/* Real balance display -- present on every page (this bar is sticky and
   shown globally once logged in), centred regardless of how wide the
   left (mode buttons) or right (theme toggle) groups are. */
/* Real fix (2026-08-28, founder's own call): "Your balance" label removed
   -- the button already carries the same text as its own tooltip, so the
   label was pure duplication. Was flex-direction:column to stack the label
   above the pill; now a single child, so it just centers like every other
   item in the topbar instead of stacking. */
.header-balance-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-left: 0.5rem;
}
/* @theme-component: .header-balance-btn */
.header-balance-btn {
  background: rgba(255, 255, 255, 0.15);
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: var(--radius-pill);
  color: var(--on-accent);
  padding: 0.25rem 0.9rem;
  font-weight: 600;
  white-space: nowrap;
  /* Overrides the generic button:hover translateY(-1px) -- that rule is
     more specific than a bare element selector expects and was shifting
     this button (and, before it was wrapped, the whole centred group)
     out of its centred position on hover. */
  transform: none !important;
}
.header-balance-btn:hover,
/* @theme-component: .header-balance-btn:hover:not(:disabled) */
.header-balance-btn:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.28);
  color: var(--on-accent);
  transform: none !important;
}
/* @theme-component: .header-state-btn */
.header-state-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.8rem;
  border-radius: var(--radius-pill);
  border: 1px solid rgba(255, 255, 255, 0.4);
  background: transparent;
  color: var(--on-accent);
  cursor: pointer;
}
.header-state-icon {
  flex: none;
  display: block;
}
/* @theme-component: .header-state-btn.active */
.header-state-btn.active {
  background: var(--on-accent);
  color: var(--accent);
  border-color: var(--on-accent);
}
/* Real "waiting for you, but less important right now" state
   (2026-09-03, founder's own ask): the pill you were on when you opened
   the avatar menu stays exactly where it is and stays clickable -- just
   visually stepped back, since the avatar itself is now the lit one. */
.header-state-btn.dimmed {
  opacity: 0.45;
  filter: saturate(0.6);
}

/* Companion button brief (2026-08-14): a real, visible state indicator on
   the nav button itself -- three honest states (none/waiting/ready), never
   blended into one, matching the same "genuinely separate real facts"
   discipline used everywhere else tonight. */
/* @theme-component: .companion-nav-badge */
.companion-nav-badge {
  display: inline-block;
  font-size: var(--font-size-xs);
  font-weight: 600;
  padding: 0.05rem 0.4rem;
  border-radius: var(--radius-pill);
  margin-left: 0.3rem;
}
/* @theme-component: .companion-badge-none */
.companion-badge-none { background: rgba(255, 255, 255, 0.25); color: var(--on-accent); }
/* @theme-component: .companion-badge-waiting */
.companion-badge-waiting { background: var(--warning); color: var(--on-accent); }
/* @theme-component: .companion-badge-ready */
.companion-badge-ready { background: var(--success); color: var(--on-accent); }
/* @theme-component: .companion-badge-stale */
.companion-badge-stale { background: rgba(255, 255, 255, 0.18); color: var(--on-accent); border: 1px solid var(--warning); }
/* @theme-component: #header-state-model-note */
#header-state-model-note { font-size: 0.85rem; color: var(--on-accent); }

/* @theme-component: .companion-setup-steps */
.companion-setup-steps {
  margin-top: 0.5rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel-bg);
}
.companion-setup-cmd-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0.4rem 0;
}
/* @theme-component: #workspace-context-label */
#workspace-context-label { display: block; font-size: var(--font-size-xs); }

/* @theme-component: .companion-setup-cmd-row code */
.companion-setup-cmd-row code {
  flex: 1 1 auto;
  padding: 0.3rem 0.5rem;
  border-radius: var(--radius-sm);
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text);
  font-family: var(--font-family-mono);
  font-size: var(--font-size-sm);
}

.header-theme-toggle { display: flex; gap: 0.3rem; }
/* Companion moved next to the theme toggle (2026-08-15) so the left
   group (Chat/Image Maker/App Builder/Compute) is lighter, letting the
   balance pill's existing absolute centring read as visually centred
   too, not just mathematically centred against a lopsided left group. */
#companion-nav-btn { margin-left: auto; }
/* Notifications + Org balance moved here from the Home-only topbar
   (2026-08-15) so every topbar's right side is symmetric everywhere.
   Both were styled for the dark .topbar text colour -- on this green
   accent bar that rendered as invisible green-on-green text (--accent
   IS this bar's own background), so they need on-accent instead. Kept
   at plain-class specificity so warn/critical (still visible enough,
   orange/red on green) keep overriding it as before; only "ok" needs a
   header-bar-specific override since --accent is exactly what it was
   invisible against. */
#header-state-bar #notifications-btn, #header-state-bar #messages-btn, #header-state-bar #header-fullscreen-btn { color: var(--on-accent); padding: 0.35rem 0.8rem; border-radius: var(--radius-pill); border: 1px solid rgba(255, 255, 255, 0.4); background: transparent; }
.org-balance-badge { color: var(--on-accent); }
#header-state-bar .org-balance-badge.org-balance-ok { color: var(--on-accent); }
/* @theme-component: .header-theme-btn */
.header-theme-btn {
  width: 1.9rem;
  height: 1.9rem;
  padding: 0;
  border-radius: var(--radius-pill);
  border: 1px solid rgba(255, 255, 255, 0.4);
  background: transparent;
  color: var(--on-accent);
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* @theme-component: .video-profile-row */
.video-profile-row { display: flex; gap: 1rem; margin: 0.5rem 0; }
.video-aspect-ratio-row { display: flex; gap: 0.75rem; margin: 0.5rem 0; }
.video-aspect-ratio-btn { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; border: 1px solid var(--border); background: var(--panel-bg); color: var(--text); border-radius: var(--radius-md); padding: 0.5rem 0.75rem; cursor: pointer; min-width: 52px; }
.video-aspect-ratio-btn svg { flex-shrink: 0; }
.video-aspect-ratio-btn span { font-size: 0.7rem; color: var(--muted, #888); }
.video-aspect-ratio-btn.active { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, var(--panel-bg)); }
.video-aspect-ratio-btn.active svg rect { stroke: var(--accent); }
.video-aspect-ratio-btn.active span { color: var(--accent); font-weight: 600; }

.video-effects-pad { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.5rem 0; }
.video-effect-btn { border: 1px solid var(--border); background: var(--panel-bg); color: var(--text); border-radius: var(--radius-pill, 999px); padding: 0.3rem 0.85rem; font-size: 0.8rem; cursor: pointer; }
.video-effect-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; }

/* Real "trigger on/trigger off" indicator (2026-08-27): sits at the top of
   "Your videos," so it's the first thing visible on the page while
   anything's still generating -- and vanishes the instant nothing is left
   processing, no manual dismiss. */
.video-jobs-block-header { display: flex; align-items: center; gap: 0.75rem; }
.video-jobs-block-header h3 { margin: 0; }
.video-processing-badge { display: inline-flex; align-items: center; gap: 0.4rem; border: 1px solid var(--accent); border-radius: var(--radius-pill, 999px); padding: 0.3rem 0.75rem; font-size: 0.8rem; background: color-mix(in srgb, var(--accent) 14%, var(--panel-bg)); color: var(--text); }
.video-processing-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); animation: video-processing-pulse 1.2s ease-in-out infinite; }
/* Idle state (2026-08-27, founder's own call): stays visible rather than
   disappearing at zero -- same badge, muted colors and no pulse instead. */
.video-processing-badge-idle { border-color: var(--border); background: var(--panel-bg); color: var(--muted, #888); }
.video-processing-badge-idle .video-processing-dot { background: var(--muted, #888); animation: none; }
.video-generate-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
/* Real "funk it up" divider + upload path (2026-09-04) -- a plain
   text-line divider so "Generate" and "Upload your own" read as two real,
   equally-valid ways to get a video started, not one primary action with a
   buried afterthought link. */
.video-generate-divider { display: flex; align-items: center; gap: 0.75rem; margin: 0.9rem 0; color: var(--muted, #888); font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.05em; }
.video-generate-divider::before, .video-generate-divider::after { content: ""; flex: 1; height: 1px; background: var(--border); }
.video-upload-own-btn { display: inline-flex; align-items: center; gap: 0.5rem; cursor: pointer; padding: 0.55rem 1rem; border-radius: var(--radius-pill); border: 2px dashed var(--accent); background: color-mix(in srgb, var(--accent) 6%, var(--panel-bg)); color: var(--text); font-weight: 600; font-size: 0.9rem; transition: background 0.15s ease, border-style 0.15s ease; }
.video-upload-own-btn:hover { background: color-mix(in srgb, var(--accent) 14%, var(--panel-bg)); border-style: solid; }
/* Real bug, found live (2026-09-04, founder: "the render pill you added
   stay after an edit and I can see it before one Generate") -- .video-
   processing-badge's own `display: inline-flex` is author CSS, which beats
   the browser's UA-level `[hidden] { display: none }` rule, so the pill
   ignored its own `hidden` attribute and stayed visible from page load.
   The [hidden] selector here is also author CSS, so it now wins instead. */
.video-processing-badge[hidden] { display: none; }
@keyframes video-processing-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.4; transform: scale(0.7); }
}
.video-job-player { width: 100%; max-width: 480px; margin-top: 0.5rem; border-radius: var(--radius-sm); display: block; }
.video-job-error-friendly { margin: 0.5rem 0 0.15rem; font-size: 0.85rem; color: var(--text); }
.send-to-editor-control { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.5rem; flex-wrap: wrap; }
.send-to-editor-control select { max-width: 220px; }
.send-to-editor-status { font-size: 0.8rem; }
.video-job-send-row { display: flex; align-items: flex-start; gap: 1rem; flex-wrap: wrap; }
.video-job-send-row .send-to-editor-control { margin-top: 0.5rem; }
.video-job-bulk-select { display: flex; align-items: center; gap: 0.4rem; font-size: 0.8rem; color: var(--muted, #888); margin-top: 0.4rem; cursor: pointer; }
.video-jobs-bulk-bar { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin: 0.5rem 0 0.75rem; padding: 0.6rem 0.75rem; border: 1px solid var(--accent); border-radius: var(--radius-sm); background: color-mix(in srgb, var(--accent) 8%, var(--panel-bg)); }
.video-jobs-bulk-bar select { max-width: 220px; }
#video-jobs-list { list-style: none; padding: 0; }
#video-jobs-list li { padding: 0.75rem 0; border-bottom: 1px solid var(--border); }
#video-model-list { list-style: none; padding: 0; }
#video-edits-list, #video-editor-scenes-list, #video-editor-scene-picker-list { list-style: none; padding: 0; }
#video-editor-scenes-list li { padding: 0.75rem 0; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.video-edit-delete-btn { flex: 0 0 auto; width: 28px; height: 28px; border: none; background: transparent; color: var(--muted-text, #888); cursor: pointer; font-size: 0.9rem; border-radius: var(--radius-sm); }
.video-edit-delete-btn:hover { background: rgba(192, 57, 43, 0.12); color: #c0392b; }

/* Real mini cards (2026-09-04, "bring the projects up... give them mini
   cards with small icon from their project where possible") */
.video-edit-card { display: flex; align-items: center; gap: 0.65rem; padding: 0.5rem; border: 1px solid var(--border); border-radius: var(--radius-sm); margin-bottom: 0.6rem; cursor: pointer; background: var(--panel-bg); }
.video-edit-card:hover { border-color: var(--accent); }
.video-edit-card-thumb { flex: 0 0 auto; width: 48px; height: 48px; border-radius: var(--radius-sm); overflow: hidden; display: flex; align-items: center; justify-content: center; background: color-mix(in srgb, var(--accent) 10%, var(--panel-bg)); }
.video-edit-card-thumb img, .video-edit-card-thumb video { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; }
.video-edit-card-thumb-placeholder { font-size: 1.3rem; }
.video-edit-card-info { flex: 1 1 auto; min-width: 0; }
.video-edit-card-title { font-weight: 600; font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.video-edit-card-status { font-size: 0.75rem; text-transform: capitalize; }
/* Real fix (2026-09-03, founder-reported: "collapse the title input box
   some because saved is too far to the right now") -- flex:1 made this
   the one element in the topbar that greedily claimed every free pixel,
   leaving "Saved." stranded way out at the far right edge with a long
   dead gap between it and the title it's actually reporting on. A
   bounded basis lets it size to a real, comfortable title width and stop
   there -- min-width:0 stays so it can still shrink on a narrow viewport
   rather than overflowing. The only real <input> using this class today
   is the Video Editor's own title, so this is safe to change directly. */
.topbar-title-input { font: inherit; font-weight: 600; background: transparent; border: none; color: inherit; flex: 0 1 320px; min-width: 0; }
.topbar-title-input:focus { outline: 1px solid var(--border); border-radius: var(--radius-sm); }
/* Real move (2026-09-03, founder-requested: "when the title is changed and
   we get the saving and saved breadcrumb can we move it from this
   position into the page nav bar to the right of the title") -- was its
   own full-width <p> as the first line of the page body, pushing
   everything else down a line every time it had text. The title input's
   own flex:1 already claims the topbar's free space, so this just needs
   to not stretch or wrap like a normal paragraph -- it settles at the
   topbar's right edge for free, right after the title. */
.video-editor-save-status-topbar { flex: 0 0 auto; margin: 0; white-space: nowrap; }

/* Real fix (2026-09-03, founder-reported: "the scroll bar needs to be an
   absolute page bottom not the player" -- meaning the timeline's own
   scrollbar, not the video preview) -- the shared .screen rule forces
   every screen to be at least a full viewport tall, which is the right
   call for most screens (a short one still looks intentional, not
   truncated) but left a real, honest-looking bug here: the timeline is
   this screen's last real element, so on a viewport taller than the
   actual editor content, that min-height padded dozens to hundreds of
   pixels of dead space below the timeline's own scrollbar instead of the
   scrollbar simply being the true bottom of the page. Scoped to this one
   screen -- every other screen keeps the shared full-height look. */
#video-editor-screen { min-height: auto; }
.video-editor-layout { display: flex; gap: 1rem; align-items: stretch; margin-bottom: 4px; }
/* Real fix (2026-09-04, founder-reported live: "reduce the padding t
   minimal and widen the icon panel on the left a smll bit so they are a
   bit bolder") -- padding cut down close to nothing (icons/labels were
   sitting in a real amount of dead space around them) and the rail
   widened slightly, letting the icon size and label size both step up a
   touch to genuinely fill the extra real room rather than leaving it
   empty. */
.video-editor-sidebar { display: flex; flex-direction: column; gap: 0.2rem; flex: 0 0 76px; overflow-y: auto; }
.video-editor-sidebar-btn { display: flex; flex-direction: column; align-items: center; gap: 0.1rem; padding: 0.3rem 0.1rem; border: 1px solid transparent; border-radius: var(--radius-sm); background: transparent; cursor: pointer; font-size: 1.25rem; color: inherit; }
.video-editor-sidebar-btn span { font-size: 0.65rem; font-weight: 600; }
.video-editor-sidebar-btn:hover { background: var(--surface-2, rgba(127, 127, 127, 0.08)); }
.video-editor-sidebar-btn.active { border-color: var(--accent); background: var(--surface-2, rgba(127, 127, 127, 0.08)); }
.video-editor-panel { flex: 0 0 260px; border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 0.75rem; overflow-y: auto; overflow-x: hidden; }
/* align-self overrides the row's align-items:stretch specifically for
   this column -- without it, the canvas (preview + transport row) was
   being stretched to match the sidebar's height (8 icon buttons make it
   taller than the canvas's own real content), leaving a real empty gap
   below the transport row instead of the timeline starting right after
   it. */
.video-editor-canvas { flex: 1; min-width: 0; display: flex; flex-direction: column; align-self: flex-start; }
/* Real fix (2026-09-04, founder-reported live: "reduce the scene pallet
   contents size and box to smaller card and the display box doesnt push
   the sequencer track down... same as the media browser thats oon the
   left"). Two real, separate things were wrong: (1) card thumbnails had
   no real size cap matching the left media browser's own real
   scene-thumb-video/image convention (70px, not however tall the source
   media happened to be) -- now matches it exactly, same class reused
   directly rather than a second near-identical size rule; (2) the column
   itself had a plain `max-height:70vh` CSS cap, which is NOT how the
   media browser stays clear of the timeline -- that column's real height
   is explicitly set in JS (videoEditSyncPanelHeight) to match the
   preview's own real measured height, with overflow doing the rest. This
   column is now included in that exact same real sync, not a separate,
   looser approximation of it. */
.video-editor-scene-palette { flex: 0 0 240px; border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 0.75rem; overflow-y: auto; overflow-x: hidden; align-self: flex-start; }
.video-editor-scene-palette h4 { margin: 0 0 0.25rem; }
.video-editor-scene-palette ul { list-style: none; margin: 0.5rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.video-editor-scene-palette-item { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 0.35rem; }
.video-editor-scene-palette-item video,
.video-editor-scene-palette-item img { width: 100%; height: 70px; object-fit: cover; border-radius: var(--radius-sm); display: block; background: #000; }
.video-editor-scene-palette-item audio { width: 100%; max-height: 40px; display: block; }
.video-editor-scene-palette-item-label { font-size: 0.65rem; margin: 0.25rem 0; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.video-editor-scene-palette-item-actions { display: flex; gap: 0.3rem; flex-wrap: wrap; }
.video-editor-scene-palette-item-actions button { flex: 1; font-size: 0.7rem; padding: 0.25rem 0.35rem; }
/* Capped by viewport height, not just the column's own width -- on a
   shorter monitor a full-width 16:9 preview pushes the timeline/audio
   lanes below the fold, forcing a scroll to reach them. The video itself
   still letterboxes cleanly inside via object-fit:contain on the <video>
   elements, so capping this shorter never crops or stretches the picture. */
/* Reduced by a fifth (2026-09-03, founder-requested: "lets equally reduce
   the main edit window by 1 5th") -- both the width and max-height terms
   scaled by 0.8 together, so whichever one is actually binding at a given
   viewport shrinks by the same real 20%, not just one axis. The fixed
   60px offset (reserved layout space, not part of the video box itself)
   stays unscaled.
   Increased back up by a real quarter (2026-09-04, founder-reported live,
   after the Scene Pallet column landed and left a real gap on the right
   and below the preview: "centre the main edit display and increase it
   size until it closes both the right gap and the bottom gap... looking
   at increase by a quarter") -- 0.8 * 1.25 = 1.0, so this is genuinely
   back to the real, un-reduced size, not an arbitrary new number.
   Centered via align-self on the flex column it lives in
   (.video-editor-canvas) -- a fixed-width item in a stretch-default flex
   column otherwise sits flush left, which was the real cause of the
   right-hand gap the founder saw, distinct from the size itself. */
/* Real fix (2026-09-04, founder-reported live: "we can increase a bit
   more will auto fill now work") -- the fixed calc-based formula above
   was calibrated for the old 2-column layout; now that a real third
   column (Scene Pallet) exists, `.video-editor-canvas`'s own real
   available width already shrinks correctly (it's `flex:1`), so this no
   longer needs its own separate math on top of that -- a genuine
   `width:100%` fills whatever real space the canvas column actually has,
   auto-adjusting if a column's own width ever changes, rather than a
   fixed formula silently going stale again. max-height bumped up too
   (44vh, was 42vh) for the real "a bit more" ask. */
#video-editor-preview-wrap { position: relative; width: 100%; aspect-ratio: 16 / 9; max-height: min(100%, 44vh); background: #000; align-self: center; }
.video-editor-preview-el { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; opacity: 0; z-index: 0; }
.video-editor-preview-el.active { opacity: 1; z-index: 1; }
#video-editor-text-overlay-layer { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.video-editor-text-overlay-live {
  position: absolute; left: 5%; right: 5%; text-align: center;
  font-weight: 700;
  text-shadow: 0 2px 6px rgba(0,0,0,0.85), 0 0 2px rgba(0,0,0,0.6);
  pointer-events: auto; cursor: text; outline: none; white-space: pre-wrap; word-break: break-word;
  touch-action: none;
}
.video-editor-text-overlay-live:focus { outline: 1px dashed rgba(255,255,255,0.6); outline-offset: 4px; }
.video-editor-text-overlay-live.pos-top { top: 6%; }
.video-editor-text-overlay-live.pos-center { top: 50%; }
.video-editor-text-overlay-live.pos-bottom { bottom: 6%; }
.video-editor-text-overlay-live.pos-left { left: 5%; right: auto; width: 45%; text-align: left; }
.video-editor-text-overlay-live.pos-right { right: 5%; left: auto; width: 45%; text-align: right; }
/* Real fix (2026-09-03, founder-reported: "this horizontal scroll bar
   doesnt seem absolute bottom") -- padding-bottom sits inside the
   scrollable box, so the native horizontal scrollbar rendered at the
   padding edge, leaving a real visible gap between the last audio lane
   and the bar itself. margin-bottom keeps the same breathing room before
   whatever comes next on the page, but outside the scrollable box, so the
   scrollbar now sits flush against the real content instead. */
#video-editor-timeline-wrap { position: relative; overflow-x: auto; margin-bottom: 0.5rem; }
/* 188px = the audio track header column's fixed width (184px, see
   .video-editor-audio-track-header -- wide enough for a longer volume
   slider (for more precise dragging) plus mute + solo + record-arm +
   split, so every track's header is the same width whether or not that
   particular track can record/split) plus its 4px flex gap -- so the
   ruler's "0s" and the clip timeline's first clip line up vertically with
   where the audio lanes' own 0s actually starts, instead of sitting flush
   left of the header column beneath them. */
#video-editor-ruler { position: relative; height: 20px; margin-left: 188px; border-bottom: 1px solid var(--border); margin-bottom: 4px; }
.timeline-ruler-tick { position: absolute; top: 0; bottom: 0; border-left: 1px solid var(--border); padding-left: 3px; font-size: 0.65rem; color: var(--muted-text, #888); white-space: nowrap; }
#video-editor-timeline { position: relative; height: 90px; min-width: 100%; margin-left: 188px; }
#video-editor-audio-lanes { display: flex; flex-direction: column; gap: 4px; margin-top: 2px; }
.video-editor-audio-track { display: flex; align-items: stretch; gap: 4px; cursor: pointer; border-radius: var(--radius-sm); }
.video-editor-audio-track.selected { outline: 2px solid var(--accent); outline-offset: -1px; }
.video-editor-audio-track-header { display: flex; align-items: center; gap: 3px; flex: 0 0 184px; width: 184px; padding: 0 4px; }
.video-editor-track-volume { width: 90px; height: 14px; margin: 0; }
.video-editor-track-toggle {
  width: 18px; height: 18px; line-height: 16px; padding: 0; font-size: 0.6rem; font-weight: 700;
  border: 1px solid var(--border, rgba(127, 127, 127, 0.35)); border-radius: var(--radius-sm);
  background: transparent; color: var(--muted-text, #888); cursor: pointer;
}
.video-editor-track-toggle.active[data-toggle="mute"] { background: #c0392b; border-color: #c0392b; color: #fff; }
.video-editor-track-toggle.active[data-toggle="solo"] { background: #d4a017; border-color: #d4a017; color: #fff; }
.video-editor-record-arm { color: #c0392b; border-color: rgba(192, 57, 43, 0.4); }
.video-editor-record-arm.armed { background: rgba(192, 57, 43, 0.25); border-color: #c0392b; }
.video-editor-record-arm.recording { background: #c0392b; border-color: #c0392b; color: #fff; animation: video-editor-record-pulse 1s ease-in-out infinite; }
@keyframes video-editor-record-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
.video-editor-record-row button.armed { color: #c0392b; font-weight: 700; }
.video-editor-audio-lane { position: relative; flex: 1 1 auto; height: 36px; background: var(--surface-2, rgba(127, 127, 127, 0.08)); border-radius: var(--radius-sm); overflow: hidden; }
.video-editor-audio-lane-empty { background: repeating-linear-gradient(45deg, var(--surface-2, rgba(127, 127, 127, 0.08)) 0 8px, transparent 8px 16px); }
.video-editor-audio-lane-empty-text { position: absolute; top: 50%; left: 8px; transform: translateY(-50%); font-size: 0.65rem; color: var(--muted-text, #888); white-space: nowrap; pointer-events: none; }
.video-editor-audio-lane-clip { background: var(--accent-muted, rgba(47, 107, 79, 0.15)); }
.video-editor-audio-lane canvas { display: block; }
.video-editor-audio-lane-label { position: absolute; top: 2px; left: 4px; font-size: 0.6rem; color: var(--muted-text, #888); pointer-events: none; z-index: 1; }
.video-editor-duck-region { position: absolute; top: 0; bottom: 0; background: rgba(198, 60, 40, 0.25); pointer-events: none; z-index: 1; }
.video-editor-audio-region { position: absolute; top: 0; bottom: 0; z-index: 2; }
.video-editor-clip-cut-mask { position: absolute; top: 0; bottom: 0; background: rgba(0, 0, 0, 0.55); pointer-events: none; z-index: 1; }
/* Real bug, found and fixed 2026-08-24: this used to sit at the region's
   own right edge -- for a region that runs on far past the video (a long
   song's trailing minutes), that edge is scrolled minutes away and
   effectively unreachable. `sticky` keeps it hugging the left edge of
   whatever's currently visible while any part of the region is in view,
   clamped to the region's own bounds, so it's always reachable without
   hunting for either end of a long region. */
.video-editor-audio-region .timeline-clip-remove { position: sticky; top: 1px; left: 4px; z-index: 3; }
.video-editor-duck-controls { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; margin: 0.4rem 0; }
.video-editor-duck-controls select { max-width: 140px; }
.video-editor-record-row { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.5rem; }
.video-editor-mic-select { min-width: 140px; max-width: 180px; }
.video-editor-record-row button.recording { color: #c0392b; font-weight: 700; }
/* Now a direct child of #video-editor-timeline-wrap (not just the video
   clip strip) so it visually spans the ruler, the clip strip, AND every
   audio lane beneath it -- top:0/bottom:0 stretches it to match the wrap's
   own height, which the ruler+timeline+audio-lanes establish between them.
   margin-left matches the ruler/timeline's own 144px offset (the audio
   track header column's width) so its "0s" position lines up with theirs;
   .style.left (set in JS) is then purely the time-based pixel offset
   within that margin, unchanged from before. */
#video-editor-playhead { position: absolute; top: 0; bottom: 0; margin-left: 188px; width: 2px; background: var(--accent); z-index: 5; cursor: ew-resize; pointer-events: none; }
#video-editor-playhead::before { content: ""; position: absolute; top: -6px; left: -5px; width: 12px; height: 12px; background: var(--accent); border-radius: 50%; pointer-events: auto; }
#video-editor-insertion-indicator { position: absolute; top: 0; bottom: 0; width: 3px; background: #2b8fd6; z-index: 6; pointer-events: none; }
.timeline-transport { display: flex; align-items: center; gap: 0.75rem; margin-top: 0.5rem; margin-bottom: 2px; flex-wrap: wrap; }
#video-editor-undo-btn:disabled { opacity: 0.4; cursor: default; }
#video-editor-time-readout { font-variant-numeric: tabular-nums; font-size: 0.8rem; }
.timeline-snap-toggle { display: flex; align-items: center; gap: 0.3rem; font-size: 0.8rem; margin-left: auto; }
.timeline-zoom { display: flex; align-items: center; gap: 0.3rem; }
.timeline-zoom button { width: 24px; height: 24px; padding: 0; line-height: 1; }
.timeline-zoom input[type="range"] { width: 90px; }
.timeline-clip { position: absolute; top: 0; height: 100%; border-radius: var(--radius-sm); overflow: hidden; background: var(--surface-2, rgba(127, 127, 127, 0.12)); border: 1px solid var(--border); cursor: grab; }
.timeline-clip.dragging { opacity: 0.4; }
.timeline-clip.selected { outline: 3px solid #2b8fd6; outline-offset: -1px; }
.timeline-clip.drag-over { outline: 2px solid var(--accent); }
.timeline-clip video { width: 100%; height: 100%; object-fit: cover; pointer-events: none; display: block; }
.timeline-clip-mask { position: absolute; top: 0; bottom: 0; background: rgba(0, 0, 0, 0.55); pointer-events: none; }
.timeline-clip-mask.left { left: 0; }
.timeline-clip-mask.right { right: 0; }
.timeline-clip-handle { position: absolute; top: 0; bottom: 0; width: 10px; background: var(--accent); cursor: ew-resize; z-index: 2; }
.timeline-clip-handle.start { left: 0; }
.timeline-clip-handle.end { right: 0; }
.timeline-clip-label { position: absolute; bottom: 2px; left: 4px; right: 4px; font-size: 0.7rem; color: #fff; text-shadow: 0 1px 2px rgba(0,0,0,0.8); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; z-index: 1; }
.timeline-clip-remove { position: absolute; top: 2px; right: 2px; z-index: 3; background: rgba(0,0,0,0.6); color: #fff; border: none; border-radius: 50%; width: 18px; height: 18px; line-height: 1; cursor: pointer; font-size: 0.85rem; }
.timeline-clip-mute-badge { position: absolute; top: 2px; left: 2px; z-index: 3; font-size: 0.85rem; pointer-events: none; text-shadow: 0 0 2px rgba(0,0,0,0.8); }
.video-editor-region-volume-canvas { position: absolute; top: 0; left: 0; z-index: 4; pointer-events: none; }
.video-editor-region-volume-point { position: absolute; width: 9px; height: 9px; margin-left: -4.5px; margin-top: -4.5px; border-radius: 50%; background: #fff; border: 1.5px solid var(--accent); z-index: 5; cursor: grab; touch-action: none; }
.video-editor-region-volume-point:active { cursor: grabbing; }
#video-editor-transitions-list, #video-editor-clip-effects-list, .clip-effects-row-list { list-style: none; padding: 0; }
.thumb-grid, #video-editor-scene-picker-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.5rem; list-style: none; padding: 0; margin-top: 0.5rem; }
.thumb-item { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.25rem; padding: 0.6rem 0.3rem; border: 1px solid var(--border); border-radius: var(--radius-sm); font-size: 0.7rem; text-align: center; cursor: grab; background: var(--surface-2, rgba(127, 127, 127, 0.06)); user-select: none; }
.thumb-item .thumb-icon { font-size: 1.3rem; }
.scene-thumb-item { cursor: pointer; padding: 0.4rem; align-items: stretch; }
.scene-thumb-item:hover { border-color: var(--accent); }
.scene-thumb-video, .scene-thumb-image { width: 100%; height: 70px; object-fit: cover; border-radius: var(--radius-sm); background: #000; pointer-events: none; }
.video-editor-audio-thumb-icon { display: flex; align-items: center; justify-content: center; font-size: 1.6rem; }
.scene-thumb-label { font-size: 0.65rem; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.timeline-transition-dropzone { position: absolute; top: 0; height: 100%; width: 18px; z-index: 4; cursor: copy; }
.timeline-transition-dropzone.drag-over { background: rgba(63, 136, 98, 0.35); outline: 2px dashed var(--accent); }
.timeline-transition-dropzone .transition-dropzone-badge { position: absolute; top: 2px; left: 50%; transform: translateX(-50%); font-size: 0.85rem; pointer-events: none; }
.timeline-context-menu { position: fixed; z-index: 50; background: var(--bg, #fff); border: 1px solid var(--border); border-radius: var(--radius-sm); box-shadow: 0 4px 16px rgba(0,0,0,0.2); display: flex; flex-direction: column; min-width: 160px; overflow: hidden; }
.timeline-context-menu button { text-align: left; padding: 0.5rem 0.8rem; border: none; background: transparent; cursor: pointer; font-size: 0.85rem; color: inherit; }
.timeline-context-menu button:hover { background: var(--surface-2, rgba(127, 127, 127, 0.1)); }
.timeline-context-menu-danger { color: #c0392b; }
.video-editor-modal-overlay { position: fixed; inset: 0; z-index: 60; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; }
.video-editor-modal { background: var(--bg, #fff); border: 1px solid var(--border); border-radius: var(--radius-sm); box-shadow: 0 8px 32px rgba(0,0,0,0.3); padding: 1.25rem; max-width: 360px; display: flex; flex-direction: column; gap: 0.75rem; }
.video-editor-modal h3 { margin: 0; font-size: 1rem; }
.video-editor-modal p { margin: 0; font-size: 0.85rem; color: var(--muted, #888); line-height: 1.5; }
.video-editor-modal button { padding: 0.55rem 0.9rem; border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--surface, #fff); color: var(--text, #1a1a1a); cursor: pointer; font-size: 0.85rem; text-align: left; }
.video-editor-modal button:first-of-type { background: var(--accent); color: #fff; border-color: var(--accent); }
.video-editor-modal button:hover { filter: brightness(1.05); }
.video-editor-modal-cancel-btn { border: none; background: transparent; text-align: center; color: var(--muted, #888); text-decoration: underline; }

.chat-dock-call-entry { display: flex; gap: 0.75rem; padding: 0 0.75rem 0.5rem; }
.call-room-modal {
  background: var(--bg, #fff); border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: 0 8px 32px rgba(0,0,0,0.3); padding: 1.25rem; width: min(640px, 90vw);
  display: flex; flex-direction: column; gap: 0.75rem;
}
.call-room-header { display: flex; align-items: center; justify-content: space-between; font-weight: 600; }
.call-room-participants { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 0.5rem; min-height: 100px; }
.call-room-participant { position: relative; background: #111; border-radius: var(--radius-sm); aspect-ratio: 4/3; overflow: hidden; display: flex; align-items: center; justify-content: center; }
.call-room-participant video { width: 100%; height: 100%; object-fit: cover; }
.call-room-participant-label { position: absolute; bottom: 4px; left: 6px; color: #fff; font-size: 0.75rem; text-shadow: 0 1px 3px rgba(0,0,0,0.8); }
.call-room-participant-avatar { color: #fff; font-size: 1.5rem; }
.call-room-participant-muted .call-room-participant-label::after { content: " 🔇"; }
.call-room-toast { background: var(--surface-2, rgba(127,127,127,0.1)); border-radius: var(--radius-sm); padding: 0.5rem 0.75rem; font-size: 0.85rem; margin: 0; }
.call-room-controls { display: flex; gap: 0.5rem; justify-content: center; }
.call-room-controls button { padding: 0.55rem 0.9rem; border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--surface-2, rgba(127,127,127,0.06)); cursor: pointer; font-size: 0.85rem; }
#call-room-leave-btn { background: #c0392b; color: #fff; border-color: #c0392b; }
.broadcast-modal { width: min(720px, 90vw); }
#broadcast-video { width: 100%; aspect-ratio: 16/9; background: #111; border-radius: var(--radius-sm); object-fit: contain; }
#broadcast-stop-btn { background: #c0392b; color: #fff; border-color: #c0392b; }

/* Matches the existing Community Feed page's own layout (.chill-layout:
   wide main + a real inline chat aside on the right) rather than
   inventing a new pattern -- admin (small) + feed (narrower) share the
   main region on the left, chat keeps the same real right-hand column
   shape already established there. */
/* .group-detail-title-row is no longer used -- the group name now lives
   in a real .community-hub-identity block reusing the profile page's own
   avatar/name layout instead of a separate title row. */
.group-detail-layout { display: flex; gap: 1.5rem; align-items: flex-start; margin-top: 0.75rem; }

/* Bottom-left, fixed -- deliberately out of normal page flow (the
   founder's own call: the old top-of-page "Your groups" list was just
   using space up most of the time) rather than a nav row competing with
   the feed/chat columns for width. */
#groups-avatar-rail {
  position: fixed; left: 1rem; bottom: 1rem; z-index: 30;
  display: flex; flex-direction: column-reverse; gap: 0.5rem; align-items: center;
}
.groups-avatar-rail-btn {
  width: 44px; height: 44px; border-radius: 50%; border: 2px solid var(--border);
  background: var(--surface-2, rgba(127,127,127,0.08)); cursor: pointer; padding: 0;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  font-size: 1.1rem; font-weight: 700; color: inherit;
}
.groups-avatar-rail-btn img { width: 100%; height: 100%; object-fit: cover; }
.groups-avatar-rail-btn.active { border-color: var(--accent); }
.group-detail-main { flex: 1; min-width: 0; display: flex; gap: 1.5rem; align-items: flex-start; }
.group-detail-admin-col { flex: 0 0 220px; min-width: 0; }
#groups-discover-section { margin-top: 1.5rem; }
#groups-discover-section select, #groups-discover-section input { width: 100%; margin-bottom: 0.5rem; }
.danger-link { color: var(--danger); }
.danger-link:hover { color: var(--danger); opacity: 0.8; }
#group-detail-delete-btn { margin-top: 1rem; }
.community-hub-calls-mute-row { display: flex; align-items: center; gap: 0.5rem; font-size: 0.9rem; margin: 0.75rem 0; cursor: pointer; }

/* Real app-wide auto-lock overlay (2026-08-28) -- deliberately opaque and
   above everything (z-index beyond the chat dock/lightboxes), so it's a
   genuine gate, not a dismissible banner. */
/* Real, per-user lock-screen background (2026-08-28, founder's own ask) --
   defaults to a genuine generated "Ask Archie" paint-splash image with
   deliberate empty space built into the prompt for the login box to sit
   over, or any real image the user picks from their own Vault (see
   appLockBackgroundUrl() in app.js, which sets background-image inline).
   background-position skews low so that empty lower-third lands roughly
   where the box actually renders, regardless of viewport shape. */
#app-lock-overlay {
  position: fixed;
  inset: 0;
  background: var(--bg) center 30% / cover no-repeat;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 12vh;
  z-index: 5000;
}
/* Real "fizzle" lock-in effect (2026-08-28, founder's own ask) -- a stepped
   opacity/blur/brightness flicker rather than a plain fade, so the moment
   of locking reads as a deliberate event, not a silent UI swap. Respects
   prefers-reduced-motion (site-wide convention already applied elsewhere). */
@keyframes app-lock-fizzle-in {
  0% { opacity: 0; filter: blur(10px) brightness(2.2); }
  12% { opacity: 0.5; filter: blur(7px) brightness(1.6); }
  24% { opacity: 0.1; filter: blur(5px) brightness(2.4); }
  38% { opacity: 0.75; filter: blur(3px) brightness(1.3); }
  52% { opacity: 0.25; filter: blur(4px) brightness(1.9); }
  68% { opacity: 0.9; filter: blur(1px) brightness(1.1); }
  100% { opacity: 1; filter: blur(0) brightness(1); }
}
#app-lock-overlay.app-lock-fizzling { animation: app-lock-fizzle-in 550ms steps(2, end); }
@media (prefers-reduced-motion: reduce) {
  #app-lock-overlay.app-lock-fizzling { animation: none; }
}
#app-lock-box {
  position: relative;
  z-index: 1;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 1.5rem;
  width: 100%;
  max-width: 260px;
  text-align: center;
  box-shadow: var(--shadow-md);
}
#app-lock-box p:first-child { font-weight: 600; margin-top: 0; }

/* Real slideshow visualizer (2026-08-28, founder's own ask): two stacked
   full-bleed layers crossfade between Vault images, each layer also
   slowly drifting/scaling (Ken Burns) so a still photo reads as alive.
   app.js toggles which layer is .app-lock-slideshow-layer-visible and
   swaps the *other* layer's background-image ahead of the next crossfade,
   so there's never a hard image-swap pop mid-transition. */
.app-lock-slideshow-layer {
  position: absolute;
  inset: 0;
  background: center 30% / cover no-repeat;
  opacity: 0;
  transition: opacity 2.5s ease-in-out;
  animation: app-lock-slideshow-drift 14s ease-in-out infinite alternate;
}
.app-lock-slideshow-layer.app-lock-slideshow-layer-visible { opacity: 1; }
@keyframes app-lock-slideshow-drift {
  0% { transform: scale(1) translate(0, 0); }
  100% { transform: scale(1.08) translate(-1.5%, -1%); }
}
@media (prefers-reduced-motion: reduce) {
  .app-lock-slideshow-layer { animation: none; transition: opacity 0.4s ease-in-out; }
}
.app-lock-slideshow-toggle-row { display: flex; align-items: center; gap: 0.5rem; font-weight: 500; margin: 0.75rem 0 0.5rem; }
.app-lock-slideshow-toggle-row input { width: auto; margin: 0; }
#app-lock-slideshow-search { width: 100%; max-width: 300px; padding: 0.4rem 0.6rem; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel-bg); color: var(--text); margin: 0.5rem 0; }
.app-lock-slideshow-picked { list-style: none; display: flex; flex-wrap: wrap; gap: 0.5rem; padding: 0; margin: 0.25rem 0 0.5rem; }
.app-lock-slideshow-picked li { position: relative; }
.app-lock-slideshow-picked img { width: 56px; height: 56px; object-fit: cover; border-radius: var(--radius-sm); border: 1px solid var(--border); display: block; }
.app-lock-slideshow-picked button { position: absolute; top: -6px; right: -6px; width: 18px; height: 18px; line-height: 16px; padding: 0; border-radius: 50%; background: var(--danger, #c0392b); color: #fff; border: none; cursor: pointer; font-size: 0.7rem; }
.app-lock-bg-preview { width: 100%; max-width: 220px; height: 100px; object-fit: cover; border-radius: var(--radius-sm); border: 1px solid var(--border); display: block; margin: 0.5rem 0; }
#app-lock-bg-search { width: 100%; max-width: 300px; padding: 0.4rem 0.6rem; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel-bg); color: var(--text); margin-bottom: 0.5rem; }
.app-lock-bg-results { list-style: none; display: flex; flex-wrap: wrap; gap: 0.5rem; padding: 0; margin: 0 0 0.5rem; }
.app-lock-bg-result-btn { padding: 0; border: 2px solid transparent; border-radius: var(--radius-sm); cursor: pointer; background: none; overflow: hidden; }
.app-lock-bg-result-btn:hover { border-color: var(--accent); }
.app-lock-bg-result-btn img { width: 64px; height: 64px; object-fit: cover; display: block; }
#app-lock-box input { width: 100%; box-sizing: border-box; margin-bottom: 0.75rem; padding: 0.5rem; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); color: var(--text); }
#app-lock-box button { width: 100%; }
/* Real independently-scrolling column, matching Community Feed's own
   .chill-main -- the post list gets its own scrollbar rather than
   scrolling the whole page, same real container-scroll pattern the chat
   panel below already uses. */
/* Real fix (Share System Consolidation visual pass, 2026-09-23,
   founder-reported "substantial wasted space to the right of the feed
   column before the right-hand sidebar"). Root cause: this column is a
   flex:1 item inside .group-detail-main (alongside the 220px admin column
   and, in .group-detail-layout, the 320px chat panel) -- its OWN
   flex-allocated box is genuinely wider than 640px on any normal desktop
   viewport (confirmed live: ~786px measured at 1422px viewport width), and
   `margin: 0 auto` centers the capped 640px content inside that wider box,
   leaving real, symmetric dead space on both sides (confirmed via
   getBoundingClientRect, not guessed). 640px itself is a real, deliberate,
   sitewide reading-width constant (feed-block, share-teaser-wrap, project
   lists, etc.) and is left alone everywhere else -- this is the one
   context with a visible right-hand sidebar immediately after the gutter,
   which is what actually made the dead space read as "stolen." Raised to
   760px (still flex:1-bounded and min-width:0, so it only ever uses this
   much space when it's genuinely available -- narrower viewports shrink
   it exactly as before, the existing 900px breakpoint below is untouched)
   -- most of the real available width is now used, without the column
   becoming unreasonably wide. */
.group-detail-feed-col { flex: 1; min-width: 0; max-width: 760px; margin: 0 auto; max-height: 80vh; overflow-y: auto; }
.group-detail-chat-panel {
  width: 320px; flex-shrink: 0; border-left: 1px solid var(--border); padding: 0 0 0 1.25rem;
  display: flex; flex-direction: column; max-height: 80vh;
}
.group-detail-chat-panel #group-detail-chat-messages { flex: 1; overflow-y: auto; }
@media (max-width: 900px) {
  .group-detail-layout { flex-direction: column; }
  .group-detail-main { flex-direction: column; }
  .group-detail-admin-col, .group-detail-feed-col { max-width: none; width: 100%; max-height: none; overflow-y: visible; }
  .group-detail-chat-panel { width: 100%; border-left: none; border-top: 1px solid var(--border); padding: 1rem 0 0; max-height: none; }
}
#group-feed-composer { display: flex; flex-direction: column; gap: 0.5rem; margin: 0.75rem 0; padding: 0.75rem; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.image-dropzone-active { outline: 2px dashed var(--accent); outline-offset: 2px; background: var(--success-bg); }
.rules-tip {
  position: relative; display: inline-flex; align-items: center; font-size: 0.8rem; color: var(--text-muted, var(--muted));
  cursor: help; border-bottom: 1px dotted currentColor; outline: none;
}
.rules-tip-popover {
  /* Real fix (2026-09-15, founder-reported: "page is breaking right hand
     side" on the profile/community composer) -- kept: this box is hidden via
     visibility/opacity (not display:none, so the opacity transition still
     works), and a fixed-width box still counts toward its scrolling
     ancestor's real layout width even while invisible.
     Real follow-up fix (Share System Consolidation visual pass, 2026-09-23,
     founder-reported horizontal scrollbar + dead gutter in Group/Community
     feed columns) -- the 2026-09-15 fix only clamped width against the
     VIEWPORT (100vw), but every real usage of this tooltip (grep-confirmed,
     4 for 4) sits at the far-right end of its own composer row, inside a
     feed column capped at ~640px, far narrower than the viewport on any
     normal desktop width. Anchored `left: 0`, a 16rem-wide popover growing
     RIGHTWARD from a trigger already near the column's own right edge
     overflowed that column (not the viewport) by real, measured pixels --
     confirmed live via getBoundingClientRect, not guessed -- which is
     exactly what an invisible absolutely-positioned descendant does to a
     `overflow-y: auto` ancestor's implicit `overflow-x` (per spec, the
     non-'visible' axis forces the other to behave as 'auto' too). Anchoring
     from the right instead so the popover grows LEFTWARD from the trigger,
     which is the correct direction for every real usage, closes this at
     its actual cause rather than hiding the scrollbar with overflow-x:hidden. */
  position: absolute; bottom: calc(100% + 0.5rem); right: 0; z-index: 20; width: 16rem; max-width: calc(100vw - 2rem);
  padding: 0.6rem 0.75rem; border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--panel-bg); color: var(--text); font-size: 0.8rem; line-height: 1.4;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25); visibility: hidden; opacity: 0; transition: opacity 0.15s ease;
}
.rules-tip:hover .rules-tip-popover, .rules-tip:focus .rules-tip-popover, .rules-tip:focus-within .rules-tip-popover {
  visibility: visible; opacity: 1;
}
#group-feed-composer textarea { min-height: 3.5rem; resize: vertical; }
.group-feed-post-list { list-style: none; margin: 0; padding: 0; }
/* Matches Community Feed's own .feed-card--image treatment: a real
   colored accent border, not a plain box, so a post visually reads as a
   card the same way across both feeds. */
.group-feed-post { display: block; border: 1px solid var(--border); border-left: 4px solid var(--accent); border-radius: var(--radius-sm); padding: 0.75rem; margin-bottom: 0.75rem; background: var(--feed-post-bg); transition: background-color 0.4s ease; }
/* Real fix (Share System Consolidation visual pass, 2026-09-23) -- the
   post's own text (a plain, unclassed <p>; a link-only post's text is the
   raw pasted URL, no spaces) had no overflow-wrap, contributing to the
   founder-reported horizontal overflow in the Group feed column. */
.group-feed-post > p { overflow-wrap: anywhere; }
.group-feed-post-highlighted { background-color: var(--success-bg); }

/* Same flash-highlight idea as .group-feed-post-highlighted, for the one
   dispatch a "Go resolve it" link (renderHandoverError) scrolls to --
   #handover-dispatches-list li has no other real styling of its own to
   hang a transition off, so it's declared here alongside the highlight. */
#handover-dispatches-list li { transition: background-color 0.4s ease; border-radius: var(--radius-sm); }
.handover-dispatch-highlighted { background-color: var(--success-bg); }
#offload-disclaimer-form { transition: background-color 0.4s ease; }
.group-feed-post-shared-from { font-size: 0.8rem; margin: 0 0 0.4rem; }
.group-feed-post-share-menu { margin-top: 0.5rem; padding-top: 0.5rem; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 0.5rem; }
.group-feed-post-share-group-row { display: flex; align-items: center; gap: 0.5rem; }
/* Real fix (2026-09-06, founder-directed: "no options in the share steps
   to add any description") -- a small, optional note field next to the
   group picker, not a full-width composer -- this is a share, not a post. */
.feed-card-share-message { width: 140px; min-height: 2rem; max-height: 5rem; font-size: 0.8rem; resize: vertical; }
/* Share System Consolidation (2026-09-23, Founder-accepted report) -- ONE
   Share control's own compact destination menu, replacing the old
   always-open copy-link+group+friend row. Ask Archie's own existing visual
   language (link-btn rows, --border/--accent tokens) throughout -- the
   Facebook screenshots that prompted this were an interaction-model
   reference only, never a visual one. */
.feed-card-share-dest-menu { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.5rem; padding-top: 0.5rem; border-top: 1px solid var(--border); }
.feed-card-share-dest-btn { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 0.3rem 0.6rem; }
/* The composer's embedded card preview is the exact same .feed-card markup
   as the real, published card (renderFeedCardPreview reuses
   buildFeedCardBody) -- this just dims interaction affordances that would
   be misleading on something not yet shared to this destination. */
.feed-card--preview { pointer-events: none; opacity: 0.92; margin: 0; }
/* Real modal-based composer (Founder UX correction, 2026-09-23) -- reuses
   the site's existing generic .modal-overlay/.modal-box (see that
   definition's own comment) so opening Share's second stage never expands
   the feed card or forces scrolling to reach it; only this box's own
   contents are new. */
.share-composer-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 0.75rem; }
.share-composer-header h3 { margin: 0; }
.share-composer-close-btn { background: none; border: none; font-size: 1.5rem; line-height: 1; cursor: pointer; color: var(--muted); padding: 0; }
.share-composer-close-btn:hover { color: var(--text); }
#share-composer-box select { width: 100%; margin-bottom: 0.75rem; }
#share-composer-box .feed-card-share-message { width: 100%; max-width: none; margin-bottom: 0.75rem; }
.share-composer-preview-host { margin-bottom: 0.75rem; }
.share-composer-actions { display: flex; align-items: center; gap: 0.75rem; }
.share-composer-actions .muted { font-size: 0.85rem; }
.group-feed-post-header { display: flex; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.4rem; }
.group-feed-post-image { max-width: 100%; border-radius: var(--radius-sm); margin-top: 0.5rem; display: block; }
/* Real mini player (2026-09-01) -- shared Synth session audio on the group
   feed and both real community feed chat renders. */
.group-feed-post-audio, .feed-chat-message-audio { width: 100%; margin-top: 0.5rem; display: block; }
/* Real, big-image link card (2026-09-15, founder's own ask: "is the big
   square available similar to this" -- comparing against the full-width
   video/image thumbnail treatment, .feed-card-thumb above; extended to the
   Group feed's own link card too, same day, founder's own ask: "we need
   that big youtube api n the groups feed also" -- one shared layout for
   every real link-preview card on the site, not a second one kept for
   Group feed specifically). */
.feed-card-link-card {
  display: flex; flex-direction: column; margin-top: 0.5rem; border: 1px solid var(--border); border-radius: var(--radius-md);
  overflow: hidden; text-decoration: none; color: inherit; background: var(--panel-bg);
}
.feed-card-link-card:hover { border-color: var(--accent); }
.feed-card-link-image { width: 100%; max-height: 320px; object-fit: cover; display: block; }
.feed-card-link-body { display: flex; flex-direction: column; gap: 0.25rem; padding: 0.75rem 1rem; min-width: 0; }
.feed-card-link-domain { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.03em; }
.feed-card-link-title { font-weight: 600; font-size: 1.05rem; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.feed-card-link-description { font-size: 0.85rem; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.group-feed-post-actions { display: flex; gap: 1rem; margin-top: 0.5rem; }
.group-feed-post-comments { margin-top: 0.5rem; padding-top: 0.5rem; border-top: 1px solid var(--border); }
.group-feed-post-comments ul { list-style: none; padding: 0; margin: 0 0 0.5rem; display: flex; flex-direction: column; gap: 0.25rem; }
.group-feed-post-comments form { display: flex; gap: 0.5rem; }
.group-feed-post-comments input[type="text"] { flex: 1; }
.call-room-controls button.active { background: var(--accent); color: #fff; border-color: var(--accent); }
.video-editor-project-settings-row { display: flex; gap: 0.6rem; margin: 0.5rem 0 1rem; }
.video-editor-project-settings-row label { display: flex; flex-direction: column; gap: 0.2rem; font-size: 0.7rem; color: var(--muted, #888); flex: 1; }
.video-editor-project-settings-row input { width: 100%; padding: 0.35rem 0.4rem; border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--surface-2, rgba(127,127,127,0.06)); color: inherit; font-size: 0.85rem; }
.video-editor-export-ratio-label { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.75rem; color: var(--muted, #888); margin: 0.5rem 0; }
.video-editor-export-ratio-label select { padding: 0.4rem; border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--surface-2, rgba(127,127,127,0.06)); color: inherit; }
.video-editor-hide-profile-label { display: flex; align-items: flex-start; gap: 0.4rem; font-size: 0.75rem; color: var(--muted, #888); margin-top: 0.75rem; cursor: pointer; }
.video-editor-hide-profile-label input { margin-top: 0.15rem; }
.clip-effects-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.4rem 0; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.clip-effects-label { font-size: 0.75rem; color: var(--muted-text, #888); min-width: 100px; max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.clip-effects-control { display: flex; align-items: center; gap: 0.35rem; }
.clip-effects-control label { font-size: 0.7rem; color: var(--muted-text, #888); }
.clip-effects-control input[type="range"] { width: 80px; }
.clip-effects-value { font-size: 0.7rem; min-width: 30px; font-variant-numeric: tabular-nums; }
.timeline-transition { display: flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0; border-bottom: 1px solid var(--border); }
.timeline-transition-label { font-size: 0.75rem; color: var(--muted-text, #888); min-width: 90px; }
.timeline-transition select { font-size: 0.8rem; }
.timeline-transition input[type="range"] { width: 100px; }
.timeline-transition-duration { font-size: 0.7rem; color: var(--muted-text, #888); min-width: 32px; }

.video-editor-text-list-title { font-size: 0.8rem; font-weight: 600; color: var(--muted-text, #888); margin: 0.5rem 0 0.25rem; text-transform: uppercase; letter-spacing: 0.02em; }
#video-editor-text-list { list-style: none; padding: 0; }
.video-editor-text-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 0.4rem; margin: 0.5rem 0 0.75rem; }
.video-editor-text-active-editor {
  border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 0.6rem;
  display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 0.75rem; max-width: 100%; box-sizing: border-box;
}
.video-editor-text-active-editor select, .video-editor-text-active-editor input { max-width: 100%; box-sizing: border-box; }
.video-editor-text-active-row { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; max-width: 100%; }
.video-editor-text-active-row input[type="number"] { width: 4.5rem; }
.video-editor-position-grid { display: grid; grid-template-columns: repeat(3, 1.8rem); grid-template-rows: repeat(3, 1.8rem); gap: 2px; }
.video-editor-pos-btn {
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: transparent;
  cursor: pointer; font-size: 0.9rem; line-height: 1; padding: 0; color: var(--text, inherit);
}
.video-editor-pos-btn:hover { border-color: var(--accent); }
.video-editor-pos-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.text-overlay-location-row { margin-bottom: 0.2rem; }
.text-overlay-location-row .link-btn { display: block; width: 100%; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; box-sizing: border-box; }
.text-overlay-location-row .link-btn.active { font-weight: 700; color: var(--accent); }

/* @theme-component: .header-icon-btn */
/* Founder's own call (2026-08-23): Messages/Notifications shrunk from text
   labels to icons, matching the theme toggle's existing icon-only pattern,
   to make real room in the header bar for the new Video pill. */
.header-icon-btn {
  font-size: 1.1rem;
  line-height: 1;
  padding: 0.3rem 0.5rem;
  display: inline-flex;
  align-items: center;
}
/* @theme-component: .header-theme-btn.active */
.header-theme-btn.active { background: var(--on-accent); color: var(--accent); border-color: var(--on-accent); }

#header-state-confirm-overlay,
#delete-project-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.4);
}
#delete-project-overlay.hidden { display: none; }
.delete-project-box { max-width: 480px; }
.delete-project-warning { color: var(--danger, #c0392b); font-weight: 600; margin-top: 0.75rem; }
#delete-project-impact-list li, #delete-project-impact-list-2 li, #delete-project-survives-list li { margin-bottom: 0.25rem; }
/* @theme-component: .header-state-confirm-box */
.header-state-confirm-box {
  max-width: 420px;
  padding: 1.5rem;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.header-state-confirm-box button { margin-right: 0.5rem; margin-top: 0.75rem; }

/* Template picker (spec §11) — a real pop-out, positioned to never cover
   the project-creation form below it (displaces, never overlays). */
#template-picker-wrap { display: flex; justify-content: flex-end; margin-bottom: 0.5rem; }
/* @theme-component: #template-picker-popout */
#template-picker-popout {
  width: 100%;
  margin-top: 0.5rem;
  padding: 1rem;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
/* @theme-component: .template-card */
.template-card {
  padding: 0.75rem;
  margin-bottom: 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.template-card.locked { opacity: 0.7; }
.template-card h4 { margin: 0 0 0.25rem; }
/* @theme-component: .template-card .template-tier-tag */
.template-card .template-tier-tag { font-size: 0.75rem; color: var(--muted); }

/* Generic reusable modal overlay -- same box/overlay pattern as
   header-state-confirm-overlay above, extracted so other popouts (App
   Store listing detail, and future ones) don't redefine it. */
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.4);
}
/* Real UX fix (2026-08-29): "My usage" (billing trickle-charge list) could
   grow unbounded -- capped to a real 10-line scrolling window in place,
   full history reachable via the "View full list" modal instead. */
.usage-full-actions {
  display: flex;
  gap: 0.5rem;
  margin: 0.5rem 0 1rem;
}
.billing-usage-scroll {
  max-height: 12.5em;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-md, 6px);
  padding: 0.25rem 0.75rem;
}

/* @theme-component: .modal-box */
.modal-box {
  max-width: 560px;
  width: 90%;
  max-height: 85vh;
  overflow-y: auto;
  padding: 1.5rem;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
/* Real fix (2026-09-01) -- expanded_content can be real, deliberate
   multi-paragraph copy (blank lines between real paragraphs); textContent
   alone collapses those, so this renders the literal line breaks the
   admin actually wrote instead of running everything into one block. */
#home-card-detail-expanded { white-space: pre-line; }
/* Real fix (2026-08-14): dialog body copy read too light at --muted's
   contrast against the panel background -- titles/links were already fine
   (full --text / --accent contrast), so this scopes darker text to plain
   dialog paragraphs specifically rather than darkening --muted globally,
   which would also affect every other, genuinely-fine use of it site-wide. */
/* @theme-component: .modal-box p, .modal-box p.muted */
.modal-box p, .modal-box p.muted {
  color: var(--text);
}
/* Real Phase 6 (2026-09-08) -- Sample Studio <-> Video Editor bridge
   picker's own project list, plain modal-box content (not scoped under
   .vocal-studio-screen -- this overlay is a top-level sibling, outside
   that dark panel's own CSS-variable scope). */
.vocal-studio-video-bridge-list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin-top: 1rem;
}
.vocal-studio-video-bridge-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.6rem 0.8rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.vocal-studio-video-bridge-row-title {
  font-weight: 600;
  color: var(--text);
}
.vocal-studio-video-bridge-row-actions {
  display: flex;
  gap: 0.4rem;
  flex-shrink: 0;
}
.vocal-studio-video-bridge-empty {
  color: var(--muted);
  font-size: 0.9rem;
}
/* Real, founder-directed (2026-09-08) -- Send to Drum Track picker, plain
   modal-box content same as the video-bridge list above. */
.vocal-studio-drum-send-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.25rem;
  margin-top: 1rem;
}
.vocal-studio-drum-send-columns h3 {
  margin: 0 0 0.5rem;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.vocal-studio-drum-send-list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.4rem;
}
.vocal-studio-drum-send-list button {
  padding: 0.5rem 0.4rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text);
  cursor: pointer;
  font-size: 0.85rem;
}
.vocal-studio-drum-send-list button:hover { border-color: var(--accent); }
.vocal-studio-drum-send-list button:disabled { opacity: 0.6; cursor: default; }
/* Real fix (2026-08-14): .auth-box's own margin:10vh auto (meant for its
   normal in-page position) fights .modal-overlay's flex centering when the
   two classes combine (google-link-confirm-box) -- reset to 0 so the
   overlay's own centering is the only thing positioning it. */
.modal-box.auth-box {
  margin: 0;
  max-width: 400px;
}
/* Full-record view (2026-08-13): an in-page view that occupies the real
   available width, not a floating modal/backdrop -- swaps in place of the
   split list+detail layout within the same Users panel. */
.admin-user-fullrecord-view { width: 100%; }
.admin-user-fullrecord-view .admin-card-grid { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.admin-user-fullrecord-view #admin-user-fullscreen-content { margin-top: 0.75rem; }

/* App Store popout foundations: category pills, card grid, listing detail
   actions -- reuses the same tokens/pattern as .template-card above. */
.category-pills { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.5rem 0; }
/* @theme-component: .category-pill */
.category-pill {
  padding: 0.3rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--panel-bg);
  color: var(--text);
  cursor: pointer;
  font-size: 0.85rem;
}
/* @theme-component: .category-pill.active */
.category-pill.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.listing-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 0.75rem;
  list-style: none;
  padding: 0;
}
/* @theme-component: .listing-grid li */
.listing-grid li {
  padding: 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel-bg);
  /* Real gap found live (2026-08-30, founder-reported: "the tables in the
     card" -- cards with a short description left their seller row/action
     buttons floating at a different height than a card with a long one,
     making the whole grid look ragged. Column flex + margin-top: auto on
     the seller row (below) pins seller+actions flush to the bottom of
     every card regardless of description length, so the grid reads as a
     real, even row of cards. */
  display: flex;
  flex-direction: column;
  cursor: pointer;
}
.listing-grid .listing-title { font-weight: 600; margin-bottom: 0.25rem; }
.listing-detail-actions { margin: 0.75rem 0; }
.listing-detail-actions button { margin-right: 0.5rem; }

.listing-seller-row { display: flex; align-items: center; gap: 0.4rem; margin: 0.5rem 0; }
.listing-grid li .listing-seller-row { margin-top: auto; }
.listing-seller-avatar { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.listing-seller-avatar-placeholder { background: var(--border); }
.listing-action-row { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.5rem; }

.app-store-login-card {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9rem;
}
/* Real bug found live (2026-08-30, founder-led mobile UX pass): on a narrow
   viewport this card's own content ("Have an account? Log in") didn't fit
   the remaining topbar space after Back/title, and genuinely overflowed
   past the visible edge (confirmed: card's real right edge sat 93px past
   the viewport). .topbar now wraps (above) as the real general fix; this
   drops the label text specifically on a narrow screen too, so the whole
   card fits on one line instead of wrapping mid-sentence. */
@media (max-width: 480px) {
  .app-store-login-card span { display: none; }
}

#public-appstore-seller-banner { display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap; margin-bottom: 0.5rem; }
#public-appstore-seller-heading { margin: 0; }
#public-appstore-share-row { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; }
#coding-appstore-share-row { display: flex; align-items: center; gap: 0.5rem; margin: 0.5rem 0; }

/* @theme-component: .listing-thumbnail */
.listing-thumbnail {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: var(--radius-sm);
  margin-bottom: 0.5rem;
  background: var(--surface-2, rgba(127, 127, 127, 0.06));
}
/* @theme-component: .listing-thumbnail-placeholder */
.listing-thumbnail-placeholder {
  width: 100%;
  aspect-ratio: 16 / 10;
  border-radius: var(--radius-sm);
  margin-bottom: 0.5rem;
  background: var(--surface-2, rgba(127, 127, 127, 0.06));
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: 1.75rem;
}
.listing-thumbnail-preview {
  max-width: 220px;
  max-height: 140px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  display: block;
  margin: 0.5rem 0;
}
/* @theme-component: .listing-thumbnail-large */
.listing-thumbnail-large {
  width: 100%;
  max-height: 280px;
  object-fit: cover;
  border-radius: var(--radius-md);
  margin-bottom: 0.75rem;
  background: var(--surface-2, rgba(127, 127, 127, 0.06));
  /* Real fix (2026-09-09, founder-requested: "can you make this image pop
     out please") -- a shadow lifts it off the page background instead of
     sitting flush, same real depth convention already used elsewhere on
     the site (box-shadow: 0 4px 20px rgba(0,0,0,0.25)). */
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
}
#listing-detail-faq-list li, #listing-detail-more-like-this-list li { margin-bottom: 0.5rem; }
#listing-detail-faq-list .faq-question { font-weight: 600; cursor: pointer; }

/* Real-time presence (spec §14) — genuine online/offline, not simulated */
.presence-dot { display: inline-block; width: 0.55rem; height: 0.55rem; border-radius: 50%; margin-right: 0.4rem; }
/* @theme-component: .presence-online */
.presence-online { background: var(--success); }
/* @theme-component: .presence-offline */
.presence-offline { background: var(--border); }

/* Sandbox exec log — real command history, not a live build viewer */
/* Real, live Deep build feed (2026-08-28) -- same real "small scrollable
   history list" shape as #sandbox-exec-log-list, populated by polling
   instead of a one-shot render. */
.deep-build-feed {
  list-style: none;
  padding: 0.5rem;
  margin: 0.5rem 0;
  max-height: 260px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-2);
}
.deep-build-feed-item { padding: 0.4rem 0.2rem; border-bottom: 1px solid var(--border); font-size: 0.9rem; }
.deep-build-feed-item:last-child { border-bottom: none; }
.deep-build-feed-image { display: block; max-width: 220px; max-height: 140px; border-radius: var(--radius-sm); margin-top: 0.4rem; object-fit: cover; }
#sandbox-exec-log-list { list-style: none; padding: 0; }
/* @theme-component: #sandbox-exec-log-list li */
#sandbox-exec-log-list li { border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.5rem 0.75rem; margin-bottom: 0.5rem; background: var(--panel-bg); }
#sandbox-exec-log-list pre { white-space: pre-wrap; word-break: break-word; overflow-x: auto; margin: 0.35rem 0 0; font-size: 0.85em; }

/* Community Hub Expansion: channel selector + image chat */
.channel-selector { display: flex; gap: 0.5rem; flex-wrap: wrap; }
/* Real bug fix (2026-08-14): base button{} sets color:var(--on-accent)
   (white) for the default accent-background button -- .channel-tab
   overrides background to var(--panel-bg) (light) but never set its own
   text color, so it silently inherited white-on-light-background =
   invisible text until the active/selected state (which does set a real
   color) took over. Same underlying class of bug as the earlier
   :visited-link specificity issue -- a base rule's colour leaking into a
   component that changes background without claiming its own text colour. */
/* @theme-component: .channel-tab */
.channel-tab { background: var(--panel-bg); color: var(--text); border: 1px solid var(--border); border-radius: var(--radius-pill); padding: 0.35rem 0.9rem; cursor: pointer; }
/* @theme-component: .channel-tab-active */
.channel-tab-active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.channel-section.hidden { display: none; }
/* @theme-component: .community-image-row */
.community-image-row { border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.5rem; margin-bottom: 0.75rem; background: var(--panel-bg); }
/* @theme-component: .community-image-thumb */
.community-image-thumb { max-width: 100%; max-height: 220px; display: block; border-radius: var(--radius-sm); margin-bottom: 0.4rem; }

/* Image Maker brief (2026-08-14): real, visible AI-generated content
   disclosure (EU AI Act Article 50) -- baked into how every generated
   image renders anywhere in the app, not a footnote. Deliberately a fixed
   dark-overlay-plus-white-text badge rather than themed tokens: this sits
   on top of arbitrary photographic image content, not app UI chrome, and
   needs to stay legible regardless of the image underneath or the current
   theme -- the same reasoning as border-radius:50% staying a literal
   elsewhere in this file, a real exception, not an oversight. */
.ai-image-frame { position: relative; display: inline-block; max-width: 100%; }
/* @theme-component: .ai-image-label */
.ai-image-label {
  position: absolute;
  bottom: 0.5rem;
  left: 0.5rem;
  background: rgba(0, 0, 0, 0.7);
  color: #fff;
  font-size: var(--font-size-xs);
  font-weight: 600;
  padding: 0.15rem 0.55rem;
  border-radius: var(--radius-pill);
  pointer-events: none;
  letter-spacing: 0.02em;
}

/* Real public share teaser (2026-08-25) -- full-bleed real photo, a
   gradient fade transitioning into the page background near the bottom
   (per the founder's own call: "most of the post up front" with the fade,
   not a locked/blurred preview), ending in a compelling sign-up/log-in CTA.
   #share-teaser-screen is a plain block (not the app's usual header+main
   shell) since this page exists purely to convert a visitor, not to feel
   like the app chrome. */
#share-teaser-screen { background: var(--bg); min-height: 100vh; }
/* Real fix (Share System Consolidation visual pass, 2026-09-23, founder-
   reported: image touching the very top of the viewport, a large dead gap
   before the attribution/CTA block, CTA sitting too far down). Root cause:
   `.share-teaser-wrap` forced `min-height: 100vh` while `.share-teaser-fade`
   below used `flex: 1; justify-content: flex-end` to pin its text to the
   BOTTOM of that box -- on any image shorter than the full viewport (the
   normal case), that left a real, empty gap between the image and the
   pinned-to-bottom text, sized however much shorter the image was than the
   viewport. Founder explicitly asked not to just vertically-centre the page
   either (same class of problem on a different viewport height). Real fix:
   drop the forced full-viewport height and the bottom-pinning entirely --
   normal top-down flow with real padding, so total height is whatever the
   actual content needs, never stretched or centred to fill the screen.
   `#share-teaser-screen` above keeps its own min-height so short content
   still sits on a full dark background, not a jarring short page. */
.share-teaser-wrap { position: relative; max-width: 640px; margin: 0 auto; padding: 2.5rem 1.25rem 2.5rem; }
/* The static /community invite page (#community-teaser-screen) reuses this
   same .share-teaser-wrap/.share-teaser-fade pair but has no image of its
   own -- it was deliberately designed to fill and vertically-centre within
   the full viewport (see .community-teaser-fade below), which needs a real
   min-height on the wrap to centre within. Scoped back on here so the
   per-item teaser fix above doesn't change that page's own, already-correct
   look. */
#community-teaser-screen .share-teaser-wrap { min-height: 100vh; padding-top: 0; padding-bottom: 0; display: flex; flex-direction: column; }
.share-teaser-image { width: 100%; max-height: 55vh; object-fit: cover; display: block; border-radius: var(--radius-md); }
/* Real fix (2026-09-07, founder-directed: "add a social share to synth
   with a decent card") -- sits right under the real cover-art image
   above, a real playable track, not just a static picture with no way to
   actually hear what was shared. */
.share-teaser-audio { width: calc(100% - 2rem); margin: 0.75rem 1rem 0; }
.share-teaser-fade {
  position: relative; margin-top: 1.5rem; padding: 0; flex: 1;
  display: flex; flex-direction: column; justify-content: flex-start; text-align: center;
}
.share-teaser-eyebrow { color: var(--accent); font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; font-size: 0.85rem; margin: 0 0 0.5rem; }
/* Real fix (Share System Consolidation visual pass, 2026-09-23) -- found
   live while checking mobile responsive safety for the layout fix above: a
   group post whose text IS a raw pasted URL (no spaces) has no natural
   break points for the browser's default word-break, so on a narrow
   viewport it overflowed the card horizontally (confirmed live at 375px).
   Same real risk class as the group-feed-post description below. */
.share-teaser-text { font-size: 1.05rem; color: var(--text); margin: 0 0 1rem; overflow-wrap: anywhere; }
.share-teaser-headline { font-size: 1.5rem; margin: 0 0 1.5rem; text-wrap: balance; }
.share-teaser-cta-row { display: flex; flex-direction: column; align-items: center; gap: 0.75rem; }
.share-teaser-vst-actions { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; margin-bottom: 1rem; }
.share-teaser-vst-actions.hidden { display: none; }
/* Real fix (2026-09-06) -- the Community teaser has no photo/audio above
   it (a static invite page, not a specific shared item), so it doesn't
   need the negative margin-top the per-item teaser uses to overlap one;
   plain, generous top padding instead, still centred and full-height. */
.community-teaser-fade { margin-top: 0; padding-top: 3rem; justify-content: center; }
.share-teaser-signup-btn {
  display: inline-block; background: var(--accent); color: var(--accent-contrast, #fff); font-weight: 700;
  padding: 0.9rem 2rem; border-radius: var(--radius-pill); text-decoration: none; font-size: 1.05rem;
  /* Real Chromium bug found live (2026-08-25): text on a rounded-corner
     element painted on the same frame it's unhidden can fail to composite
     at all -- confirmed by removing border-radius alone (text reappeared),
     then confirmed the real fix is forcing this element onto its own GPU
     layer ahead of time, not dropping the pill shape. Real fix (2026-09-06,
     founder-reported live on the new static /community teaser -- this page
     shows the button on its very first paint, same frame it's "unhidden,"
     unlike the per-item teaser which populates it slightly later via JS):
     translateZ(0) alone wasn't reliably promoting the layer early enough;
     will-change tells the browser to set the layer up ahead of paint,
     not just hint at one after the fact. */
  transform: translateZ(0);
  will-change: transform;
}
/* Real fix (Share System Consolidation visual pass, 2026-09-23,
   founder-reported: "Sign up for free button exists but its text is not
   visibly readable") -- the exact same already-documented bug pattern this
   codebase has fixed repeatedly elsewhere (.home-card-badge:visited,
   .pricing-select-btn:visited, .my-app-open-link-btn:visited, see their own
   comments above): the global `a:visited { color: var(--accent) }` rule
   (near the top of this file) has HIGHER specificity than a plain class
   selector, so it silently wins over .share-teaser-signup-btn's own
   `color: var(--accent-contrast, #fff)` the moment a visitor's browser
   already considers /?authMode=signup#get-started visited -- turning the
   button's text the same green as its own background. Never given this
   fix before now. Confirmed via CSS specificity, not a rendering glitch or
   a missing --accent-contrast value (that variable correctly falls back to
   #fff already). */
.share-teaser-signup-btn:visited { color: var(--accent-contrast, #fff); }
.share-teaser-login-link { color: var(--muted); text-decoration: underline; }

.auth-mode-highlighted {
  outline: 3px solid var(--accent); outline-offset: 2px; animation: auth-mode-pulse 1.4s ease-in-out infinite;
}
@keyframes auth-mode-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb, 62, 136, 98), 0.5); }
  50% { box-shadow: 0 0 0 8px rgba(var(--accent-rgb, 62, 136, 98), 0); }
}


/* Pre-launch privacy switch (2026-08-25) -- marketing content stays fully
   built underneath, just not rendered to an anonymous visitor while closed. */
.site-closed .marketing-content { display: none !important; }

.video-editor-fit-mode-label { display: block; margin-top: 0.5rem; font-size: 0.85rem; }
.video-editor-fit-mode-label select { display: block; width: 100%; margin-top: 0.25rem; }

/* My Vault: one real page listing every note/image/video a user owns. */
.vault-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; margin: 0.75rem 0; }
/* Real fix (2026-08-27, founder-directed): the search box used to
   flex:1 and stretch to fill the whole toolbar row, leaving no room for
   the audio-upload control to sit beside it -- given a fixed, real width
   instead so the upload button fits the same row rather than wrapping
   below it. */
.vault-toolbar .vault-search-input { flex: 0 1 260px; min-width: 160px; }
.vault-upload-btn { display: inline-flex; align-items: center; white-space: nowrap; cursor: pointer; }
.vault-type-tabs { display: flex; gap: 0.3rem; }
.vault-type-tab, .my-prompts-source-tab { padding: 0.3rem 0.75rem; border-radius: var(--radius-pill); border: 1px solid var(--border); background: transparent; color: inherit; cursor: pointer; font-size: 0.85rem; }
.vault-type-tab.active, .my-prompts-source-tab.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }

/* Real fix (2026-09-23, founder-directed: "scope the tabs in vault also
   same colour as icons alreeady established") -- each tab tinted with the
   exact same HOME_CARD_BADGE_COLORS[index] its own type's badge icon
   already uses elsewhere in Vault (renderVaultItem's note/study_guide/vst
   badges, VAULT_LINK_BADGE_INDEX's waiting_room/mini_brief/app badges) --
   never a new, independently-invented colour. Image/Video/Audio have no
   such established badge colour in Vault (they always show a real media
   thumbnail, never a badge), so they're deliberately left unstyled here
   rather than guessing one. vst and app already share the same established
   purple (VAULT_LINK_BADGE_INDEX maps app to the same index 5 as vst) --
   kept exactly as already established, not "fixed" into two colours. */
.vault-type-tab[data-vault-type="note"] { border-color: #3f9142; color: #3f9142; }
.vault-type-tab[data-vault-type="note"].active { background: #3f9142; border-color: #3f9142; color: #fff; }
.vault-type-tab[data-vault-type="mini_brief"] { border-color: #c99a2e; color: #c99a2e; }
.vault-type-tab[data-vault-type="mini_brief"].active { background: #c99a2e; border-color: #c99a2e; color: #fff; }
.vault-type-tab[data-vault-type="study_guide"] { border-color: #c15b45; color: #c15b45; }
.vault-type-tab[data-vault-type="study_guide"].active { background: #c15b45; border-color: #c15b45; color: #fff; }
.vault-type-tab[data-vault-type="waiting_room"] { border-color: #4c7fb0; color: #4c7fb0; }
.vault-type-tab[data-vault-type="waiting_room"].active { background: #4c7fb0; border-color: #4c7fb0; color: #fff; }
.vault-type-tab[data-vault-type="vst"], .vault-type-tab[data-vault-type="app"] { border-color: #8a5fae; color: #8a5fae; }
.vault-type-tab[data-vault-type="vst"].active, .vault-type-tab[data-vault-type="app"].active { background: #8a5fae; border-color: #8a5fae; color: #fff; }
.my-prompts-date-input { padding: 0.3rem 0.6rem; border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--surface-2); color: inherit; font-size: 0.85rem; }
.music-lab-share-control { margin-left: 0; flex-wrap: wrap; }
.music-lab-tag-row { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.music-lab-tag-chip { padding: 0.15rem 0.55rem; border-radius: var(--radius-pill); background: var(--success-bg); font-size: 0.78rem; }
.music-lab-gender-pills { display: flex; gap: 0.4rem; margin-top: 0.3rem; }
.music-lab-gender-pill { padding: 0.35rem 1rem; border-radius: var(--radius-pill); border: 1px solid var(--border); background: transparent; color: inherit; cursor: pointer; font-size: 0.9rem; }
.music-lab-gender-pill.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.music-lab-duration-label { font-weight: 600; font-size: 0.95rem; margin-top: 0.6rem; }
.video-model-select-label { font-weight: 600; font-size: 0.95rem; margin-top: 0.4rem; }
/* Real focal treatment (2026-09-04, founder: "lets show this pill a bit
   livelier its suppose to be a focus") -- was plain/transparent, easy to
   skim past for a real, consequential choice (model picks drive both cost
   and quality). Given the same accent-tinted treatment the processing
   badge already uses elsewhere on this page, plus a genuine focus-visible
   glow so it reads as a real focal control, not just another form field. */
#video-model-select {
  width: 100%; max-width: 32rem; margin: 0.3rem 0 0.8rem; padding: 0.55rem 0.9rem;
  border-radius: var(--radius-pill); border: 2px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--panel-bg));
  color: var(--text); font-size: 0.95rem; font-weight: 600;
  box-shadow: 0 1px 3px color-mix(in srgb, var(--accent) 25%, transparent);
  cursor: pointer; transition: box-shadow 0.15s ease, background 0.15s ease;
}
#video-model-select:hover { background: color-mix(in srgb, var(--accent) 20%, var(--panel-bg)); }
#video-model-select:focus-visible { outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent); }
/* Extends into the dropdown's own popped-out option list where the browser
   actually allows it -- a native <select>'s open popup is OS-rendered and
   only accepts background/text color on its <option>s (no border-radius,
   shadow, or padding control at this layer in any browser today), so this
   is the real ceiling of what's stylable here, not a partial effort. */
#video-model-select option { background: var(--panel-bg); color: var(--text); padding: 0.5rem; }
#video-model-select option:checked { background: color-mix(in srgb, var(--accent) 25%, var(--panel-bg)); font-weight: 600; }
.music-lab-duration-pills { display: flex; gap: 0.4rem; margin-top: 0.3rem; flex-wrap: wrap; align-items: center; }
.music-lab-duration-pill { padding: 0.35rem 1rem; border-radius: var(--radius-pill); border: 1px solid var(--border); background: transparent; color: inherit; cursor: pointer; font-size: 0.9rem; }
.music-lab-duration-pill.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.music-lab-duration-pill.disabled { opacity: 0.35; cursor: not-allowed; }
.music-lab-duration-pill.disabled:hover { background: transparent; }
#music-lab-duration-custom-input { width: 5.5rem; padding: 0.35rem 0.6rem; border-radius: var(--radius-pill); border: 1px solid var(--border); background: transparent; color: inherit; font-size: 0.9rem; }

/* ASK ARCHIE STUDIO -- MUSIC LAB HARDWARE PASS (2026-09-26, Founder-
   directed: "full silver chassis, whole page"). Music Lab is a real
   standalone screen (#music-lab-screen), not a panel inside #synth-screen's
   own rack -- every shared hardware rule elsewhere in this file is scoped
   "#synth-screen .synth-hw-instrument" / "#synth-screen .synth-hw-group"
   specifically so it never leaks onto the rest of the site, so none of it
   reaches this page. This block is a deliberate, page-scoped duplicate of
   those same two base rules (identical real values, so it reads as the
   same silver chassis/recessed-group look, not a near-miss), plus this
   page's own native controls -- selects, inputs, textarea, checkbox,
   duration/gender pills, buttons -- reskinned the same way #synth-screen's
   own converted sections were. .topbar above is untouched (same shared
   header chrome every other screen keeps). */
#music-lab-screen .synth-hw-instrument {
  background: linear-gradient(180deg, #eef0f1 0%, #cfd1d3 45%, #b6b8bb 100%);
  border: 1px solid #6f7275;
  border-radius: 10px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.65);
  padding: 0.9rem 1rem;
  color: #1c1d1e;
  max-width: 1600px;
  margin: 1.25rem auto 2rem;
}
#music-lab-screen .synth-hw-instrument label,
#music-lab-screen .synth-hw-instrument span:not(.muted) { color: #1c1d1e; }
#music-lab-screen .synth-hw-instrument .muted { color: #45484b; }
/* Real fix (2026-09-26, founder-reported: "unify the padding so its down to
   the same padding what the top want more control section has related to
   the page") -- .project-list-main's own base rule (padding: 0 1rem) was
   adding a THIRD layer of left/right inset on top of the chassis's own
   padding and each column's own .synth-hw-group padding, so Generate/
   Library sat noticeably more inset than "Want more control" (which has no
   <main> wrapper of its own, just chassis padding + its one group's
   padding) -- and, compounded on both sides, made the whole page read
   narrower than Synth's own equivalent chassis. Zeroed here so all three
   groups share the exact same two-layer inset (chassis padding + that
   group's own padding), and bumped max-width to 1600px to match Synth's
   own page width (#synth-screen .project-list-main-wide), not the
   narrower 1300px this page used before the hardware pass. */
#music-lab-screen .synth-hw-instrument main.project-list-main { margin: 0 auto; padding: 0; max-width: none; }
#music-lab-screen .synth-hw-group {
  position: relative;
  background: linear-gradient(180deg, #c9cbce, #aeb0b3);
  border: 1px solid #83868a;
  border-radius: 8px;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.25);
  padding: 0.9rem 1rem;
  margin: 0.7rem 0;
  color: #1c1d1e;
  max-width: none;
}
#music-lab-screen .synth-hw-group:first-of-type { margin-top: 0; }
/* Real fix (2026-09-26, founder-reported: "looks like that black is two
   column ... reduce it down so the text is full width of that block") --
   .music-lab-synth-card p's own max-width: 720px (set back when this card
   sat in a narrower page) was leaving roughly half of the now-1600px-wide
   chassis empty, forcing the paragraph to wrap early into a tall, narrow
   column instead of using the real available width -- taller than it
   needed to be for no reason. */
#music-lab-screen .music-lab-synth-card p { max-width: none; }
/* Real fix (2026-09-26, founder-reported: "padding above the library
   section") -- :first-of-type above only ever caught Generate (the first
   div child of <main>), not Library (the second) -- so Library kept its
   own default 0.7rem margin-top while Generate had none, sitting the two
   side-by-side columns' boxes at different heights instead of level.
   Generate and Library are a row, not a vertical stack, so neither needs
   its own top margin here. */
#music-lab-screen main.project-list-main > .synth-hw-group { margin-top: 0; }
#music-lab-screen .synth-hw-group h3 {
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 800;
  color: #1c1d1e;
  border-bottom: 1px solid rgba(0, 0, 0, 0.18);
  padding-bottom: 0.4rem;
  margin: 0 0 0.7rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
/* Native controls -- same light-bezel reskin as #synth-global-hw-chassis
   (Synth's own equivalent for its simpler, non-drum sections), duplicated
   here since that block is #synth-screen-scoped too. */
#music-lab-screen .synth-hw-group select,
#music-lab-screen .synth-hw-group input[type="text"],
#music-lab-screen .synth-hw-group input[type="number"],
#music-lab-screen .synth-hw-group textarea {
  border-radius: 5px;
  border: 1px solid #83868a;
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  color: #1c1d1e;
  /* Real fix (2026-09-26, founder-reported: "increase the size of the text
     and drop down boxes, plus the input box use the same size as the share
     dropdown on the library card") -- these had no padding/font-size of
     their own (the generic ".community-block select/input" rule that used
     to size them no longer applies now that these columns are
     .synth-hw-group, not .community-block), so they fell back to bare
     browser default sizing -- genuinely smaller than the rest of this
     page's text. Matched to the exact real size .synth-header-share select
     (the Library card's own "Share..." dropdown) already uses. */
  padding: 0.3rem 0.5rem;
  font-size: 0.85rem;
}
/* Real fix (2026-09-26, founder-directed: "increase the size of the style
   description box full width to the generate card itself so it looks like
   a chat input") -- its wrapping label already carries flex:1 to stretch
   across the row (the same row shape the Composer's own "Tell Archie..."
   input + Ask Archie button use), but the <input> itself had no width of
   its own, so it stayed at the browser's default ~20-char size regardless
   of how wide its label stretched. Sized to match that same real Composer
   prompt input's own proportions. */
#music-lab-screen #music-lab-style-input {
  width: 100%;
  box-sizing: border-box;
  font-size: 0.9rem;
  padding: 0.55rem 0.7rem;
}
/* Real fix (2026-09-26, founder-directed: "make the title box a bit longer
   and the same height as the description box") -- same real font-size/
   padding as #music-lab-style-input above (so both are genuinely the same
   height, not just visually close), plus a real fixed width wider than its
   old bare-default size -- not full-width like Style/description, which is
   the one field meant to read as a chat input; this is a single title. */
#music-lab-screen #music-lab-title-input {
  width: 22rem;
  max-width: 100%;
  box-sizing: border-box;
  font-size: 0.9rem;
  padding: 0.55rem 0.7rem;
}
#music-lab-screen .synth-hw-group input[type="checkbox"] { accent-color: #1e9a4a; }
#music-lab-screen .synth-hw-group button:not(.link-btn) {
  border-radius: 999px;
  border: 1px solid #101112;
  background: linear-gradient(180deg, #45484b, #26282a);
  color: #e3e5e7;
  cursor: pointer;
}
#music-lab-screen .synth-hw-group button:not(.link-btn):hover { border-color: #7fd992; }
#music-lab-screen .synth-hw-group button:not(.link-btn):disabled { opacity: 0.5; cursor: not-allowed; }
#music-lab-screen .synth-hw-group .link-btn:hover { color: #7fd992; }
/* Duration/gender pills -- same segmented-switch red-active convention as
   the Sequencer's own Play/Loop toggle (#synth-hw-sequencer-chassis) and
   Sample Studio's mode toggles, since these are the same kind of "which
   choice is current" control. */
#music-lab-screen .synth-hw-group .music-lab-duration-pill,
#music-lab-screen .synth-hw-group .music-lab-gender-pill,
#music-lab-screen .synth-hw-group #music-lab-duration-custom-input {
  border: 1px solid #83868a;
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  color: #1c1d1e;
}
#music-lab-screen .synth-hw-group .music-lab-duration-pill.active,
#music-lab-screen .synth-hw-group .music-lab-gender-pill.active {
  background: linear-gradient(180deg, #c14535, #8f281e);
  border-color: #5a1712;
  color: #fff;
}
/* Real fix (2026-09-26, founder-reported: "the green colour on the library
   cards ... too dark ... for the darkmode") -- .music-lab-tag-chip's own
   background: var(--success-bg) resolves to a near-black green in dark
   mode (#1e3327), meant to sit under this page's OWN dark text before the
   hardware pass -- on this panel it's dark-on-dark with this group's own
   #1c1d1e text, nearly unreadable. Fixed colour, not a variable, matching
   the same bright green the Composer's own "Ask Archie" button uses
   (#4fe38a/#1e9a4a) -- correct in both of the site's own themes now, not
   just one. */
#music-lab-screen .synth-hw-group .music-lab-tag-chip { background: #4fe38a; border: 1px solid #0a4a20; color: #06210f; }

/* Real play-button-overlay mini player + hover flourish (2026-09-01,
   founder: "can we also get the playbutton overlay so theres no player on
   the cards" / "the hover over image has a moving graphic"). */
/* Real fix (2026-09-01, founder: "style the cards now to have only a
   small image as in what they have in the library view also lets get some
   space back in profile view by doing the same") -- one shared component,
   one real CSS change fixes the size everywhere it's used (Library,
   Profile Shared audio, Community feed, Group feed) at once. A fixed small
   thumbnail, not a responsive-up-to-240px block. */
/* Real fix (2026-09-01, founder: "increase size by at least half more") --
   3.5rem was too small once the meta moved off the card and onto the image
   itself; 5.25rem is +50%, per the founder's own figure. */
/* Real size bump (2026-09-23, founder-reported: "make the audio thumbnail
   consistent to the other shared items... next size up") -- was 5.25rem,
   visibly smaller than every other card's own thumbnail treatment; one
   shared size (this is the single real cover-art mini player used in the
   Library, Profile, and every feed alike) so it stays consistent everywhere
   it's used, not just Community.
   Real follow-up (same day, founder-reported: "still quite small" after
   the first bump to 6.5rem) -- a bigger, decisive step this time, and
   matched below on My Vault's own separate audio cover element
   (.vault-item-media-audio .vault-item-cover-img was still 88px, never
   touched by the first pass), so both places genuinely look consistent. */
.audio-cover-player { position: relative; width: 9rem; height: 9rem; flex-shrink: 0; border-radius: var(--radius-sm); overflow: hidden; cursor: pointer; background: var(--surface-2); }
.audio-cover-player img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Real layout fix (2026-09-23, founder-reported: "can we get it the same
   size as the image thumbnail... shift [the badge] to the top like all
   other shared items") -- a feed/Group-post audio share's thumbnail now
   sits full-width below the badge, same position and size as
   .feed-card-thumb, instead of the old small fixed square inside a
   two-column row. Overrides the base .audio-cover-player width/height
   above; the real play/expand/hover-bar overlay controls are unaffected,
   just scaled to a bigger real box. */
.feed-card-audio-thumb { width: 100%; height: auto; aspect-ratio: 1 / 1; max-height: 220px; border-radius: var(--radius-md); margin-bottom: 0.5rem; }
.audio-cover-play-btn {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 2.1rem; height: 2.1rem; border-radius: 50%; border: none; cursor: pointer;
  background: rgba(0, 0, 0, 0.55); color: #fff; font-size: 0.85rem;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity var(--transition-soft);
}
.audio-cover-player:hover .audio-cover-play-btn, .audio-cover-player.playing .audio-cover-play-btn { opacity: 1; }
.audio-cover-expand-btn {
  position: absolute; top: 0.2rem; right: 0.2rem; width: 1.3rem; height: 1.3rem; border-radius: 4px; border: none; cursor: pointer;
  background: rgba(0, 0, 0, 0.55); color: #fff; font-size: 0.75rem; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity var(--transition-soft);
}
.audio-cover-player:hover .audio-cover-expand-btn { opacity: 1; }
/* Real fix (2026-09-01, founder: "there is a movement when you hover
   over") -- root cause: the site-wide `button:hover:not(:disabled) {
   transform: translateY(-1px) }` rule (higher specificity than a single
   class) was completely replacing this button's own centering
   `transform: translate(-50%, -50%)` the moment it became hoverable, so it
   jumped from centered to its raw top:50%/left:50% anchor point (offset by
   half its own size) on every hover. Re-declared here with matching
   specificity so the real centering transform wins instead. */
.audio-cover-play-btn:hover:not(:disabled) { transform: translate(-50%, -50%); filter: none; }
.audio-cover-expand-btn:hover:not(:disabled) { transform: none; filter: none; }
.audio-cover-hover-bars {
  position: absolute; bottom: 0.2rem; left: 0.2rem; display: flex; align-items: flex-end; gap: 2px;
  height: 0.8rem; opacity: 0; transition: opacity var(--transition-soft); pointer-events: none;
}
.audio-cover-player:hover .audio-cover-hover-bars, .audio-cover-player.playing .audio-cover-hover-bars { opacity: 1; }
.audio-cover-hover-bars span { width: 2px; background: #fff; border-radius: 1px; animation: audio-cover-bar-bounce 0.9s ease-in-out infinite; }

/* Real two-column row card shape (2026-09-01, founder-directed, matching a
   real Mureka screenshot, corrected 2026-09-01: "image in the left and the
   meat in the right"). Real cover-art mini player in the left column; real
   meta text (title, tags, stats) in the right column -- never text baked
   onto the image itself. */
.audio-card-row { display: flex; align-items: center; gap: 0.75rem; }
.audio-card-row-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.2rem; }
/* Real placeholder tile (2026-09-01) -- a clip with no real cover art gets
   a genuine, honestly-labelled music-note glyph, never a fabricated photo
   standing in for real cover art. */
.audio-cover-player-placeholder { display: flex; align-items: center; justify-content: center; }
.audio-cover-placeholder-icon { font-size: 1.6rem; color: var(--muted); }

/* Real profile housekeeping caps (2026-09-01). */
.community-hub-see-all-btn { display: block; margin-top: 0.5rem; }
.audio-cover-hover-bars span:nth-child(1) { height: 40%; animation-delay: 0s; }
.audio-cover-hover-bars span:nth-child(2) { height: 100%; animation-delay: 0.2s; }
.audio-cover-hover-bars span:nth-child(3) { height: 65%; animation-delay: 0.4s; }
@keyframes audio-cover-bar-bounce { 0%, 100% { transform: scaleY(0.4); } 50% { transform: scaleY(1); } }
@media (prefers-reduced-motion: reduce) { .audio-cover-hover-bars span { animation: none; } }

/* Real global "now playing" bar (2026-09-01). */
#global-audio-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 500;
  display: flex; align-items: center; gap: 0.75rem; padding: 0.6rem 1rem;
  background: var(--topbar-bg); border-top: 1px solid var(--topbar-border); box-shadow: var(--shadow-md);
}
#global-audio-bar-cover { width: 2.75rem; height: 2.75rem; border-radius: var(--radius-sm); object-fit: cover; }
.global-audio-bar-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.2rem; }
#global-audio-bar-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.global-audio-bar-controls { display: flex; align-items: center; gap: 0.6rem; }
#global-audio-bar-playpause { width: 2rem; height: 2rem; border-radius: 50%; border: 1px solid var(--border); background: var(--accent); color: var(--on-accent); cursor: pointer; flex-shrink: 0; }
#global-audio-bar-time { font-size: 0.78rem; font-variant-numeric: tabular-nums; flex-shrink: 0; }
#global-audio-bar-seek { flex: 1; min-width: 4rem; }
/* Real fix (2026-09-03, founder-reported: "the bottom player remains but
   theres no way to see it to the back" -- no close control existed at
   all, and nothing ever auto-hid it, not even on the track actually
   finishing). */
#global-audio-bar-close { width: 1.75rem; height: 1.75rem; border-radius: 50%; border: 1px solid var(--border); background: var(--panel-bg); color: var(--text); cursor: pointer; flex-shrink: 0; font-size: 0.85rem; line-height: 1; }
body.has-global-audio-bar { padding-bottom: 4rem; }
.my-prompts-item {
  display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem;
  background: var(--success-bg); border: 1px solid var(--border); border-radius: var(--radius-md);
  padding: 0.85rem 1rem; transition: var(--transition-soft);
}
.my-prompts-item:hover { box-shadow: var(--shadow-sm); }
.my-prompts-item-body { flex: 1; min-width: 0; }
.my-prompts-item-text { margin: 0.3rem 0 0; white-space: pre-wrap; word-break: break-word; }
.my-prompts-item-link { flex-shrink: 0; white-space: nowrap; font-weight: 600; }
.vault-items-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 0.75rem; }
.vault-item { display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.vault-item-media { aspect-ratio: 16 / 10; background: var(--surface-2, rgba(127, 127, 127, 0.08)); }
.vault-item-media img, .vault-item-media video { width: 100%; height: 100%; object-fit: cover; display: block; }
.vault-item-media-note { display: flex; align-items: center; justify-content: center; font-size: 3.5rem; background: rgba(47, 107, 79, 0.12); }
/* Real, bigger paint-splash badges in Vault specifically (2026-08-29,
   founder-requested) -- the default 44px home-card-badge size reads as
   small filling a whole 16:10 media box, so this is a real, scoped
   override, not a change to the shared home-page badge itself. */
.vault-item-media .home-card-badge { width: 88px; height: 88px; margin-bottom: 0; }
.vault-item-media-audio { aspect-ratio: auto; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.75rem; padding: 1.5rem 1rem; background: rgba(47, 107, 79, 0.12); }
.vault-item-media-audio audio { width: 100%; }
/* Real size bump (2026-09-23, founder-reported, same request as the
   .audio-cover-player bump above: "also its vault item thumbnail") --
   was 88px, genuinely smaller than every other Vault item type's own
   full-width 16:10 thumbnail (.vault-item-media img). Matched to the same
   144px (9rem) the feed/profile/group audio player now uses, for real
   visual consistency between the two, not just a bigger number picked
   independently. */
.vault-item-media-audio .vault-item-cover-img { width: 144px; height: 144px; border-radius: 8px; object-fit: cover; }
.vault-item-body { display: flex; flex-direction: column; gap: 0.35rem; padding: 0.6rem 0.75rem; }
/* Real Vault-backed Synth session save/load (2026-09-01) -- a plain
   title/date block, no media preview needed for a session snapshot. */
.vault-item-info { padding: 0.6rem 0.75rem 0; }
.vault-item-info p { margin: 0.2rem 0 0; }
#vault-synth-sessions-list .vault-item-actions { padding: 0 0.75rem 0.6rem; }
.vault-item-text { margin: 0; font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; }
.vault-item-posted-to { font-size: 0.8rem; }
.vault-item-tags { display: flex; gap: 0.4rem; }
.vault-item-tags input { flex: 1; min-width: 0; font-size: 0.8rem; }
.vault-item-actions { display: flex; flex-direction: column; align-items: stretch; gap: 0.4rem; margin-top: 0.25rem; }
.vault-item-actions .vault-item-download-btn, .vault-item-actions .link-btn.danger-link { align-self: flex-start; }
.vault-quick-action { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; }
.vault-quick-action select { max-width: 160px; }
.send-to-editor-control { margin-top: 0 !important; }

/* VST Builder Phase 0 (2026-09-09) */
#vst-builder-github-gate { max-width: 640px; }
/* Real fix (2026-09-11, founder: "split the whole page in two columns, not
   just the right hand side") -- #vst-builder-main inherited the connect-
   gate's own narrow 640px cap, so the real two-column split built inside
   it was itself squeezed into a narrow strip with most of the page empty
   to the right. Matches #vst-builder-detail's own real full-width
   convention (1600px) instead. */
#vst-builder-main { max-width: 1600px; }
#vst-builder-github-gate ol { padding-left: 1.2rem; }
.vst-builder-new-row { display: flex; gap: 0.5rem; margin-bottom: 0.5rem; }
.vst-builder-new-row input { flex: 1; }
#vst-builder-project-list { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 0.75rem; }
/* Real chassis carry-over (2026-09-15, founder-directed: "use the css for
   the card backgrounds from the vst image carry the screws over") -- the
   same corner-screw/bezel treatment #vst-builder-detail's own
   .vst-builder-col-center chassis already uses (Phase 1 instrument visual
   reconstruction, above), brought onto the plain project-list rows too, so
   the list page reads as the same piece of gear as the editor it opens
   into instead of a plain light-mode card. Uses the --vstb-* tokens with
   literal fallbacks since this row also renders outside #vst-builder-detail
   (the main list page), where those custom properties aren't in scope. */
.vst-builder-project-row {
  background:
    radial-gradient(circle at 10px 10px, #4a5058 0 1.6px, transparent 2.6px),
    radial-gradient(circle at calc(100% - 10px) 10px, #4a5058 0 1.6px, transparent 2.6px),
    radial-gradient(circle at 10px calc(100% - 10px), #4a5058 0 1.6px, transparent 2.6px),
    radial-gradient(circle at calc(100% - 10px) calc(100% - 10px), #4a5058 0 1.6px, transparent 2.6px),
    linear-gradient(180deg, rgba(255, 255, 255, 0.035), transparent 60px),
    var(--vstb-bg, #14161a);
  border: 1px solid #05060a;
  border-radius: 14px;
  box-shadow:
    0 10px 26px rgba(0, 0, 0, 0.4),
    inset 0 0 0 1px rgba(255, 255, 255, 0.04),
    inset 0 1px 0 rgba(255, 255, 255, 0.05);
  padding: 0.9rem 1rem;
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: 1rem;
  color: var(--vstb-text, #f2f2f0);
}
.vst-builder-project-row .muted { color: var(--vstb-muted, #9aa0a6); }
.vst-builder-project-row a { color: #8ab4f8; }
.vst-builder-project-row a:hover { color: #aecaff; }
.vst-builder-project-row .vst-builder-build-download { color: #6be3a3; }
.vst-builder-project-row .vst-builder-build-download:hover { color: #8ff0bb; }

/* Real two-column split (2026-09-23, founder-directed: "split the card
   into two columns. Put all the copy and links into the left column, and
   leave the logo in the right column, increase the size of the icon on
   the right to fill the right column"). Left column carries every real
   piece of copy/controls the row already built (title, status, build
   controls, links, sharing toggles, actions) unchanged, just no longer
   direct children of the row itself. */
/* Real fix (2026-09-23, founder-reported: "make the column split equal on
   the card so we can see the vst thumbnail correctly... stack links if
   you need to to reduce the div size") -- both columns now flex:1 (a true
   50/50 split, flex-basis:0 so content width never skews it), not a
   narrow fixed-width right column. The existing flex-wrap on
   .vst-builder-project-actions/.vst-builder-sharing-row, and the column's
   own flex-direction:column already stacking every real link/row one per
   line, already handle "stack links to reduce the div size" -- no
   overflow-prone inline row existed here to begin with. */
.vst-builder-project-left-col { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.3rem; }
.vst-builder-project-right-col { flex: 1; min-width: 0; display: flex; align-items: center; justify-content: center; }

/* Real narrow-width repair (2026-09-25, read-only investigation +
   founder-approved two-branch fix) -- the two-column split above was only
   ever designed and evidenced at normal desktop width. Below this
   breakpoint the same 50/50 flex:1 split, combined with the row's own
   align-items:stretch, let the left column's wrapped text height dictate
   the right column's height too, stretching the bounded square artwork
   into a tall vertical strip. Root cause and evidence: this session's own
   VST BUILD CARD LAYOUT REGRESSION investigation. Stacks the same two
   regions instead of resizing them, and gives the artwork a fixed,
   self-contained square that no longer depends on the text column's
   height at all. Desktop (above this breakpoint) is completely untouched. */
@media (max-width: 700px) {
  .vst-builder-project-row { flex-direction: column; }
  .vst-builder-project-left-col { flex: 1 1 auto; width: 100%; }
  .vst-builder-project-right-col { flex: 0 0 auto; width: 100%; max-width: 300px; aspect-ratio: 1 / 1; margin: 0 auto; }
}
/* Real second branch of the same repair (2026-09-25) -- the project
   detail/editor page's own "Build" box renders this identical row inside
   #vst-builder-detail-project-row, which lives in .vst-builder-col-left, a
   RAIL FIXED AT flex: 0 0 300px (styles.css, .vst-builder-split .vst-
   builder-col-left) -- independent of viewport width. A viewport media
   query can never correctly represent that rail's real, always-narrow
   available width, so this embedding gets the identical stacked treatment
   unconditionally instead, matched intentionally to the block above so the
   two contexts render the same way, never two independently-styled cards. */
#vst-builder-detail-project-row .vst-builder-project-row { flex-direction: column; }
#vst-builder-detail-project-row .vst-builder-project-left-col { flex: 1 1 auto; width: 100%; }
#vst-builder-detail-project-row .vst-builder-project-right-col { flex: 0 0 auto; width: 100%; max-width: 300px; aspect-ratio: 1 / 1; margin: 0 auto; }

/* Real project-card thumbnail (2026-09-23, founder-directed: "produce a
   thumbnail here on the vst project cards... only when there is a
   successful build") -- the real, server-rendered plugin screenshot (or
   the same standard "no thumbnail yet" plugin badge every other VST
   context already uses). Now fills the entire right column (the founder's
   own explicit follow-up), not a small corner badge.
   Real fix (2026-09-23, founder-reported live via an annotated screenshot:
   a real "Phase2 E2E Test" screenshot showing a large black gap on one
   side) -- root cause: generateVstThumbnail's own real render is a real
   900x571 canvas with the actual module chassis roughly centred inside
   it, genuine padding baked into the stored PNG's own pixels, not a CSS
   crop artefact (confirmed by opening the raw screenshot file directly).
   object-fit:cover was cropping that wide source image to fill a much
   taller column, which -- combined with the source's own off-centre
   content -- pushed the real chassis to one edge instead of centring it.
   contain shows the whole real image, centred, letterboxed on whichever
   axis doesn't match, rather than cropping it lopsided. Left as a real,
   scoped display fix here rather than changing generateVstThumbnail's own
   crop behaviour, which several other already-accepted contexts (Vault,
   App Store, Community/Group) also depend on unchanged. */
/* Real fix (2026-09-23, founder-reported: "these corners seem to be
   bleeding through, lets fall them back to the head and foot colour") --
   background was the generic --surface-2 token, which doesn't match this
   card's own dark chassis (--vstb-bg) -- visible wherever the real image
   itself doesn't fully cover the box (its own rounded corners, or
   object-fit:contain letterboxing on an off-aspect screenshot), showing
   as a mismatched light patch instead of blending into the card. */
.vst-builder-project-thumb {
  width: 100%; height: 100%; border-radius: 8px;
  object-fit: contain; background: var(--vstb-bg, #14161a);
}
/* Real fix (2026-09-23, founder-reported: "the outer corner is still not
   using the background colour reference, we still see white" -- the
   background fix above didn't touch it because there was nothing to fix
   there: confirmed by inspecting the actual webp file's own RIFF/VP8X
   header -- it has no alpha channel at all, so the white margin around
   the placeholder's own rounded card is real, fully opaque pixels baked
   into the image, not a transparent gap any background colour could ever
   show through. object-fit:contain (needed for a REAL screenshot, see the
   comment above) preserves that whole margin; cover crops it away instead
   -- safe here because the placeholder's own real content is centred with
   plenty of margin to spare. Scoped to the placeholder specifically so a
   real screenshot keeps the correct centred/letterboxed treatment. */
.vst-builder-project-thumb-placeholder { object-fit: cover; }
/* Real dead-selector cleanup (2026-09-25, bounded as part of the VST build
   card responsive repair) -- .vst-builder-project-thumb.home-card-badge
   used to sit here (added 2026-09-23 to cap the placeholder to a square
   and remove .home-card-badge's own rotation for this context). Confirmed
   dead: vstBuilderRenderProjectRow (public/app.js) never applies the
   home-card-badge class to this thumbnail -- only "vst-builder-project-
   thumb" and, for the placeholder, "vst-builder-project-thumb-placeholder"
   -- so this compound selector never matched anything live. Removed rather
   than left as a latent collision risk for a future edit that might add
   that class here. .home-card-badge itself is untouched; it is still used,
   unmodified, by every context that actually applies it (home marketing
   cards, Vault icon tiles, Vault search badges, My Apps thumbs, etc). */

/* Real fix (2026-09-23, founder-reported: "use a brighter but not fluro
   green for the .cm link the .ps1 link and the read me please" -- then
   "same green please as this", pointing at the Mac/Windows build download
   links just above) -- same exact bright mint green, not a new colour
   invented independently. */
.vst-local-env-download-link { color: #6be3a3; font-weight: 600; }
.vst-local-env-download-link:hover { color: #8ff0bb; }
.vst-builder-project-title-link { cursor: pointer; font-weight: 600; }
.vst-builder-project-title-link:hover { text-decoration: underline; }
.vst-builder-project-title-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.vst-builder-open-pill {
  padding: 0.25rem 0.7rem;
  border: 1px solid var(--accent);
  border-radius: var(--radius-pill, 999px);
  background: transparent;
  color: var(--accent);
  font-size: 0.75rem;
  cursor: pointer;
}
.vst-builder-open-pill:hover { background: color-mix(in srgb, var(--accent) 14%, transparent); }

/* Real connected RazorSaw view (2026-09-13, Founder APPLY) -- the real
   screenshot is never replaced or hidden by any of this; the badge/strip/
   live-view button sit as a small info line above it, only while a real,
   recently-seen connection exists. The live, interactive view itself is a
   separate popout window (see .vst-builder-live-view-* below), not an
   overlay on this image -- superseded same day it shipped once the
   founder flagged it wouldn't scale across editor layouts or bigger
   drag-and-drop chains. */
/* Real retheme (Phase 1 instrument visual reconstruction, Founder APPLY)
   -- these three sit inside the forced-dark #vst-builder-detail chassis
   (see the --vstb-* tokens above) but were still styled with the
   site-wide light-mode tokens (--surface/--text/--border), which is why
   they visually clashed with the instrument panel around them. Retheme
   only; the connect/instance/host-strip logic in app.js is untouched. */
.vst-builder-connected-badge {
  align-self: center;
  padding: 0.2rem 0.65rem;
  border-radius: var(--radius-pill, 999px);
  background: color-mix(in srgb, #4cd98a 22%, var(--vstb-panel, #1c1f1c));
  color: #6be3a3;
  border: 1px solid color-mix(in srgb, #4cd98a 45%, transparent);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
}
.vst-builder-instance-select {
  align-self: center;
  padding: 0.18rem 0.5rem;
  border-radius: var(--radius-sm, 6px);
  background: var(--vstb-panel, #1c1f1c);
  color: var(--vstb-text, #f2f2f0);
  border: 1px solid var(--vstb-border, #333);
  font-size: 0.7rem;
}
.vst-builder-host-strip {
  display: flex;
  justify-content: center;
  gap: 0.9rem;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  color: var(--vstb-muted, #9aa0a6);
  background: var(--vstb-panel, #1c1f1c);
  border: 1px solid var(--vstb-border, #2b3138);
  border-radius: 8px;
  padding: 0.35rem 0.8rem;
}
.vst-builder-open-live-view-btn {
  align-self: center;
  margin: 0.4rem 0;
  padding: 0.35rem 0.8rem;
  border-radius: var(--radius-pill, 999px);
  border: 1px solid var(--accent);
  background: transparent;
  color: var(--accent);
  font-size: 0.8rem;
  cursor: pointer;
}
.vst-builder-open-live-view-btn:hover { background: color-mix(in srgb, var(--accent) 14%, transparent); }
/* Real rotary knob widget (2026-09-13, founder-requested: "knobs") --
   drag-to-rotate, matching the visual language already established for
   RazorSaw's own in-plugin ArchieLookAndFeel knobs (dark flat body, blue
   accent pointer), so the site and the plugin read as the same instrument.
   Used in normal document flow inside the popout live view now, not
   absolutely positioned over an image. */
/* Real knob rebuild (Phase 1 instrument visual reconstruction, Founder
   APPLY: "convincing instrument controls... designed to be used, not
   excessively glossy/futuristic/decorative"). Same drag-to-rotate
   interaction and -135deg..+135deg sweep math as before (see
   vstBuilderCreateRotaryKnob in app.js) -- only the rendering changes:
   a metal-look face (radial gradient + rim), a real recessed ring
   (double box-shadow against the chassis background, not a flat 1px
   border), and a value arc drawn from the same --knob-value custom
   property app.js already sets on every rotation, so the arc can never
   drift out of sync with the pointer. */
.vst-builder-rotary-dial {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  position: relative;
  cursor: ns-resize;
  touch-action: none;
  background:
    radial-gradient(circle at 32% 26%, rgba(255, 255, 255, 0.16), transparent 46%),
    radial-gradient(circle at center, var(--vstb-knob-face, #23261f) 0%, #1a1c18 78%, #101210 100%);
  box-shadow:
    0 0 0 1px var(--vstb-knob-rim, #3d423d),
    0 0 0 3px var(--vstb-bg, #14161a),
    0 3px 6px rgba(0, 0, 0, 0.45),
    inset 0 1px 1px rgba(255, 255, 255, 0.08),
    inset 0 -2px 3px rgba(0, 0, 0, 0.5);
  transition: box-shadow 0.12s ease, transform 0.08s ease;
}
/* The value arc -- a ring (not a filled disc) via a radial-gradient
   mask, filled proportionally from -135deg by --knob-value (0-1). Sits
   just outside the knob face so it never obscures the pointer. */
.vst-builder-rotary-dial::before {
  content: "";
  position: absolute;
  inset: -5px;
  border-radius: 50%;
  background: conic-gradient(
    from -135deg,
    var(--module-accent, var(--vstb-accent-glow, #4fa8ff)) 0 calc(var(--knob-value, 0) * 270deg),
    rgba(255, 255, 255, 0.1) calc(var(--knob-value, 0) * 270deg) 270deg,
    transparent 270deg 360deg
  );
  -webkit-mask-image: radial-gradient(circle, transparent calc(50% - 3px), #000 calc(50% - 3px));
  mask-image: radial-gradient(circle, transparent calc(50% - 3px), #000 calc(50% - 3px));
  pointer-events: none;
}
.vst-builder-rotary-dial:hover {
  box-shadow:
    0 0 0 1px var(--vstb-knob-rim, #3d423d),
    0 0 0 3px var(--vstb-bg, #14161a),
    0 0 10px color-mix(in srgb, var(--module-accent, var(--vstb-accent-glow)) 55%, transparent),
    0 3px 6px rgba(0, 0, 0, 0.45),
    inset 0 1px 1px rgba(255, 255, 255, 0.1),
    inset 0 -2px 3px rgba(0, 0, 0, 0.5);
}
.vst-builder-rotary-dial.dragging {
  transform: scale(1.05);
  box-shadow:
    0 0 0 1px var(--vstb-accent-glow, #4fa8ff),
    0 0 0 3px var(--vstb-bg, #14161a),
    0 0 14px color-mix(in srgb, var(--vstb-accent-glow, #4fa8ff) 65%, transparent),
    0 3px 6px rgba(0, 0, 0, 0.45),
    inset 0 1px 1px rgba(255, 255, 255, 0.12),
    inset 0 -2px 3px rgba(0, 0, 0, 0.5);
}
/* Real drag feedback (Phase 1, founder: "strong hover/drag feedback") --
   a floating value tooltip, content sourced from the data-pct attribute
   app.js already sets alongside --knob-value on every rotation. No extra
   DOM node: pure ::after content, invisible except while dragging. */
.vst-builder-rotary-dial::after {
  content: attr(data-pct);
  position: absolute;
  left: 50%;
  bottom: calc(100% + 9px);
  transform: translateX(-50%) translateY(4px);
  background: #0d0f12;
  border: 1px solid var(--vstb-knob-rim, #3d423d);
  color: var(--vstb-text, #f2f2f0);
  font-size: 0.62rem;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  padding: 0.1rem 0.35rem;
  border-radius: 4px;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s ease, transform 0.12s ease;
  z-index: 3;
}
.vst-builder-rotary-dial.dragging::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
.vst-builder-rotary-pointer {
  position: absolute;
  top: 7px;
  left: 50%;
  width: 3px;
  height: 18px;
  background: linear-gradient(180deg, #eaf4ff, var(--vstb-accent-glow, #4fa8ff));
  border-radius: 2px;
  transform-origin: 50% 19px;
  margin-left: -1.5px;
  box-shadow: 0 0 4px rgba(79, 168, 255, 0.6);
}

.vst-connect-card {
  max-width: 420px;
  margin: 3rem auto;
  padding: 1.5rem;
  border-radius: 0.75rem;
  border: 1px solid var(--border, #ddd);
  text-align: center;
}
/* Real fix (2026-09-10, found live): no flex-wrap meant a 4th action
   ("Delete GitHub repo", added alongside Rebuild/Delete) had nowhere to go
   but overflow its own row, visually bleeding into neighbouring content
   with its text broken mid-word instead of wrapping as a whole button. */
.vst-builder-project-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.4rem; }
.vst-builder-project-actions button { white-space: nowrap; }
/* Real fix (2026-09-13, founder: the Retry button "pushed out to the
   right") -- nowrap is right for the project-list page's own wide rows,
   but inside the detail page's narrower 300px left rail a long label
   like "Retry same build (no code changes)" can't wrap and the button
   just grows past the column's own width instead. Let it wrap here. */
.vst-builder-split .vst-builder-col-left .vst-builder-project-actions button {
  white-space: normal;
  max-width: 100%;
  text-align: center;
}
.vst-builder-platform-progress { margin-top: 0.3rem; }
.vst-builder-platform-header { font-size: 0.85rem; font-weight: 600; }
.vst-builder-step-list { list-style: none; padding: 0 0 0 1rem; margin: 0.2rem 0 0; font-size: 0.8rem; color: var(--text-muted, #888); }
/* Real fix (2026-09-11, founder: notify the user when GitHub's own account
   spending limit blocks a build -- a real failure mode new/casual GitHub
   users hit, previously indistinguishable from a genuine compile error. */
.vst-builder-billing-blocked { color: #c0392b; font-size: 0.85rem; margin: 0.2rem 0; }
.vst-builder-step-list li { line-height: 1.5; }

/* VST Builder Phase 1 (2026-09-09) -- the per-project Archie chat + real,
   visible block-library reference (founder-directed: "give askarchie a bit
   more than the standard website... list what the user can have"). */
/* Real fix (2026-09-10, founder-reported: "please occupy the whole page
   and not cramped to one side") -- 960px left the whole panel squeezed
   into a narrow column on any real, normal-width screen. 1600px matches
   the same real wide-layout convention Synth's own split page already
   uses, so this genuinely fills the page instead of leaving it half
   empty. */
#vst-builder-detail { max-width: 1600px; }
/* Real Connected Instruments screen (2026-09-14, Founder APPLY) -- same
   width/padding convention as #vst-builder-detail above, not a new one. */
#vst-builder-connected-instruments { max-width: 1600px; }
#vst-builder-connected-instruments > h2, #vst-builder-connected-instruments > p.muted { padding: 0 1.25rem; }
#vst-connected-empty { padding: 0.75rem 1.25rem; }
#vst-connected-selector { padding: 0 1.25rem; }
#vst-connected-live-view { padding: 0 1.25rem; }
/* Real fix (2026-09-11, founder: "the text is bunched up to the right
   hand side... add the same padding as the current dsp chain column") --
   the title/description sat flush against the container's true edge,
   while the DSP chain column below has its own real 1.25rem padding via
   .synth-compose-split-left. Match it, same value, not a new one. */
#vst-builder-detail-title, #vst-builder-detail-description { padding: 0 1.25rem; }
.vst-builder-detail-title-row { display: flex; align-items: baseline; gap: 0.75rem; padding: 0 1.25rem; }
.vst-builder-detail-title-row #vst-builder-detail-title { padding: 0; }
#vst-builder-rename-form { display: flex; gap: 0.5rem; align-items: center; padding: 0 1.25rem 0.5rem; }
#vst-builder-rename-form input { flex: 1 1 auto; }
/* Real fix (2026-09-11, same request applied to the list page's own intro
   text -- it sat outside the split's padded left column too. */
#vst-builder-main > p.muted { padding: 0 1.25rem; }
/* Real fix (2026-09-10, founder-reported directly on a screenshot: "Too
   SMALL" / "Too WIDE" -- I'd claimed the width fix was done without
   actually looking closely at the real result). Two real, separate bugs
   found on inspection: (1) the left/right ratio was backwards -- the left
   column carries the real, dense content (screenshot, DSP chain, build
   status) and was the narrower, cramped one; (2) `height: 100%` on the
   chat column fought the parent's own `align-items: stretch`, so the dark
   chat panel stopped short instead of matching the real (taller) left
   column's height, leaving a visibly mismatched, half-empty box. */
/* Real fix (2026-09-10, founder: "reduce the width of the chat" -- 45% left
   the dark panel's real content (block library, chat log) stretched much
   wider than it needed, with lines of text running the full width instead
   of reading naturally. */
/* Real fix (2026-09-10, founder's own hand-drawn mockup): a real third
   column -- a narrow rail for DSP chain + Build on the left, the plugin's
   own screenshot centered as the actual focal point in the middle, chat
   staying on the right. */
/* Real fix (2026-09-13, founder: "a lot of sqwishing going on" once the
   left rail was carrying a real drag-and-drop chain + convolution IR
   upload row + full Build box at once) -- `flex-shrink: 1` let this
   narrow rail get squeezed even tighter than its own 260px basis
   whenever the row's total content didn't fit, which is exactly what a
   fixed-width column full of real controls (file inputs, download
   links, buttons) can't absorb without wrapping into a mess. Locking
   flex-shrink to 0 and widening the basis a little gives it a genuine
   floor instead of letting it compress indefinitely. */
.vst-builder-split .vst-builder-col-left { flex: 0 0 300px; min-width: 300px; }
.vst-builder-split .vst-builder-col-left h3 { margin-top: 1.25rem; }
.vst-builder-split .vst-builder-col-left h3:first-child { margin-top: 0; }
.vst-builder-split .vst-builder-col-center {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 1rem 1.25rem;
  border-right: 1px solid var(--border);
}
.vst-builder-split .synth-compose-split-right { flex: 1 1 38%; max-width: 480px; }

/* Real module-view rebuild (2026-09-13, Founder APPLY: full VST Builder
   detail page rebuild to match a founder-supplied reference mockup). Scoped
   entirely to #vst-builder-detail, matching the existing precedent that
   this page carries its own fixed dark theme regardless of site-wide
   light/dark mode -- does not touch the site's own :root palette.
   Recession cycle 1 (2026-10-04): the same palette is now also in scope on #vst-builder-connected-instruments (a SIBLING of the detail view, which
   previously left every var(--vstb-*) there undefined, so the chassis and title bar lost their dark skin). Tokens only; no other change. */
#vst-builder-detail,
#vst-builder-connected-instruments {
  --vstb-bg: #14161a;
  --vstb-panel: #1c1f1c;
  --vstb-panel-2: #20241f;
  --vstb-border: #2b3138;
  --vstb-text: #f2f2f0;
  --vstb-muted: #9aa0a6;
  /* Real addition (Phase 1 instrument visual reconstruction, Founder
     APPLY) -- extends the existing forced-dark module-view palette with
     the extra tokens the chassis/knob/keyboard rebuild below needs,
     rather than hardcoding these values at each use site. */
  --vstb-knob-face: #23261f;
  --vstb-knob-rim: #3d423d;
  --vstb-accent-glow: #4fa8ff;
}
/* Real chassis rebuild (Phase 1 instrument visual reconstruction, Founder
   APPLY: "give the complete instrument a recognisable outer housing") --
   the four corner radial-gradients read as recessed housing screws and the
   thin outer border + inset highlight read as a bezel edge, all on the
   existing container (no new elements) so this stays a pure presentation
   change over the same data-driven #vst-module-grid underneath. */
.vst-builder-split .vst-builder-col-center {
  background:
    radial-gradient(circle at 14px 14px, #4a5058 0 2px, transparent 3px),
    radial-gradient(circle at calc(100% - 14px) 14px, #4a5058 0 2px, transparent 3px),
    radial-gradient(circle at 14px calc(100% - 14px), #4a5058 0 2px, transparent 3px),
    radial-gradient(circle at calc(100% - 14px) calc(100% - 14px), #4a5058 0 2px, transparent 3px),
    linear-gradient(180deg, rgba(255, 255, 255, 0.035), transparent 70px),
    var(--vstb-bg);
  border: 1px solid #05060a;
  border-radius: 16px;
  border-right: none;
  box-shadow:
    0 16px 40px rgba(0, 0, 0, 0.5),
    inset 0 0 0 1px rgba(255, 255, 255, 0.04),
    inset 0 1px 0 rgba(255, 255, 255, 0.05);
  color: var(--vstb-text);
  gap: 0.85rem;
  padding: 1.25rem 1.4rem;
}
#vst-builder-module-view { display: flex; flex-direction: column; gap: 0.75rem; position: relative; }
/* Real fix (2026-09-16, founder-reported: "the display all seems a bit
   messed up around the chat input area") -- .vst-builder-split's own
   align-items:stretch (2026-09-13, deliberate: prevents content clipping)
   stretches EVERY column to match whichever is tallest. During a real
   build the left column's step checklist runs very long, so the center
   column -- empty and dimmed anyway, nothing real to show mid-build --
   was being stretched to match it (seen live: over 1500px), leaving the
   real "Archie is working..." message centered so far up the page it
   read as stray text over a giant black void.
   Real follow-up fix (same day, founder-reported: "not keen on this
   having to scroll... destroying the view of the vst and keyboard
   below") -- the first attempt capped the height and scrolled the
   overflow, which hid the real block grid/keyboard below the fold
   instead of just fixing the stray-text problem. Real fix instead:
   opt this one column OUT of the stretch itself (align-self overrides
   the parent's align-items for a single flex child) so it sizes to its
   own real content -- however tall the dimmed grid+keyboard genuinely
   are -- never a fixed number, and never scrolled. Every other state
   keeps the exact same full-height stretch the original 2026-09-13 fix
   relied on; :has() scopes this to only the processing state. */
.vst-builder-col-center:has(#vst-builder-module-view.vst-module-processing) { align-self: flex-start; }
#vst-builder-module-view.vst-module-processing #vst-module-grid,
#vst-builder-module-view.vst-module-processing .vst-module-titlebar,
#vst-builder-module-view.vst-module-processing .vst-module-timeline,
#vst-builder-module-view.vst-module-processing .vst-module-keyboard {
  opacity: 0.35;
  pointer-events: none;
  filter: grayscale(0.4);
}
/* Build status message (Round 03, Dessy-approved small fascia correction): the text used to sit as plain centred text over the dimmed pad grid. It now sits in a compact dark
   status card -- enough separation from the equipment behind it that the build status owns attention, while the dimmed instrument stays recognisable as context.
   Dark/neutral in both themes (the instrument panel is dark in both). No change to the overlay's own dimming or position. */
.vst-module-processing-msg {
  display: block;
  max-width: 30rem;
  padding: 0.85rem 1.1rem;
  background: rgba(14, 16, 20, 0.94);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 10px;
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.5);
  color: var(--vstb-text, #f2f2f0);
  line-height: 1.5;
}
#vst-module-processing-overlay {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 1.5rem;
  color: var(--vstb-text, #f2f2f0);
  font-size: 0.9rem;
  background: rgba(20, 22, 26, 0.55);
  border-radius: 10px;
}
.vst-module-titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  background: linear-gradient(180deg, var(--vstb-panel), var(--vstb-panel-2));
  border: 1px solid var(--vstb-border);
  border-radius: 10px;
  padding: 0.7rem 1rem;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 2px 8px rgba(0, 0, 0, 0.3);
}
.vst-module-titlebar-name { display: flex; flex-direction: column; line-height: 1.25; }
/* Real typography pass (Phase 1): the name reads as the instrument's own
   product identity (larger, heavier); the subtitle switches to a
   monospace "spec plate" treatment instead of matching body text, the
   same real hierarchy distinction a hardware synth's nameplate uses. */
.vst-module-titlebar-name strong { font-size: 1.2rem; font-weight: 700; letter-spacing: 0.01em; }
.vst-module-titlebar-name span {
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  color: var(--vstb-muted);
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
}
/* Real re-emphasis (2026-09-18, Founder-directed: "lets make them real")
   -- the de-emphasis below dated from when this control had no real
   preset data behind it (see migrations/0283_vst_project_presets.sql +
   /api/vst-projects/:id/presets); now that Save/Prev/Next genuinely load
   and store real live parameter snapshots, it no longer needs to hide. */
.vst-module-patch-nav { display: flex; align-items: center; gap: 0.4rem; font-size: 0.68rem; color: var(--vstb-muted); }
.vst-module-patch-nav button { background: none; border: 1px solid var(--vstb-border); border-radius: 5px; color: var(--vstb-muted); cursor: pointer; font-size: 0.68rem; padding: 0.05rem 0.4rem; }
.vst-module-patch-nav button:hover { color: var(--vstb-text); border-color: var(--vstb-text); }
.vst-module-titlebar-right { display: flex; align-items: center; gap: 0.6rem; }
/* Real fix (2026-09-14, Founder APPLY: promotional badge treatment) --
   was plain, unbadged text (0.65rem, no background/border/padding), too
   subtle for its intended promotional role. Reuses the exact same green
   pill recipe #vst-builder-connected-badge already established (same
   colour-mix ratios against --vstb-panel), so this reads as the same
   Ask Archie visual language rather than a one-off. Still a plain
   inline element (the markup has never been a link here), so nothing
   about its clickability changes -- presentation only. */
.vst-module-built-badge {
  display: inline-flex;
  align-items: center;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: #6be3a3;
  background: color-mix(in srgb, #4cd98a 22%, var(--vstb-panel));
  border: 1px solid color-mix(in srgb, #4cd98a 45%, transparent);
  border-radius: var(--radius-pill);
  padding: 0.28rem 0.7rem;
}
/* Real "power switch" treatment (Phase 1) -- same accent-red-when-active
   language as the per-module bypass icons, given real raised/pressed
   shading instead of a flat bordered text button. */
.vst-module-bypass-all {
  background: linear-gradient(180deg, #2b2f2b, #1c1f1c);
  border: 1px solid var(--vstb-border);
  border-radius: 6px;
  color: var(--vstb-text);
  cursor: pointer;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 0.32rem 0.75rem;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06), 0 1px 2px rgba(0, 0, 0, 0.4);
}
.vst-module-bypass-all.on {
  background: linear-gradient(180deg, #e2645f, #c0392b);
  border-color: #c0392b;
  color: #fff;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.35), 0 0 10px rgba(217, 83, 79, 0.55);
}
.vst-module-bypass-all:disabled { cursor: not-allowed; opacity: 0.4; box-shadow: none; }

/* Real fix (2026-09-14, Founder APPLY: Connected Instruments) -- .vst-
   module-grid class added alongside the existing #vst-module-grid id so
   this same rule also styles #vst-connected-grid (Connected Instruments'
   own grid container) without a duplicated id or a second copy of this
   rule. */
#vst-module-grid, .vst-module-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}
/* Real panel-depth pass (Phase 1 instrument visual reconstruction, Founder
   APPLY) -- same data-driven per-block-kind box this already was
   (VST_BLOCK_LIBRARY-agnostic, renders whatever vstBuilderRenderModuleView
   is handed), just with real inset/outset shading so each box reads as a
   physical panel rather than a flat card. */
.vst-module-box {
  background: linear-gradient(180deg, var(--vstb-panel), var(--vstb-panel-2));
  border: 1px solid var(--module-accent, var(--vstb-border));
  border-radius: 10px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.04);
  min-width: 160px;
  overflow: hidden;
}
.vst-module-box-header {
  align-items: center;
  background: color-mix(in srgb, var(--module-accent, var(--vstb-border)) 16%, var(--vstb-panel));
  border-bottom: 1px solid var(--module-accent, var(--vstb-border));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
  display: flex;
  font-size: 0.68rem;
  font-weight: 700;
  justify-content: space-between;
  letter-spacing: 0.06em;
  padding: 0.5rem 0.7rem;
  text-transform: uppercase;
}
.vst-module-box-header strong { color: var(--module-accent, var(--vstb-text)); }
.vst-module-box-header-right { align-items: center; display: flex; gap: 0.3rem; }
.vst-module-power-toggle, .vst-module-box-menu {
  background: none; border: none; color: var(--vstb-muted); cursor: pointer; font-size: 0.85rem; line-height: 1; padding: 0.1rem 0.25rem;
}
.vst-module-power-toggle:disabled { cursor: not-allowed; opacity: 0.35; }
.vst-module-power-toggle.bypassed { color: #d9534f; }
/* Recessed body panel -- a subtle inset shadow separates the header
   "plate" from the control surface beneath it, same visual language the
   chassis/knobs below use. */
.vst-module-box-body {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  padding: 0.75rem 0.7rem;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.14), transparent 40%);
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.25);
}
.vst-module-select-wrap select {
  background: var(--vstb-panel);
  border: 1px solid var(--vstb-border);
  border-radius: 6px;
  color: var(--vstb-text);
  font-size: 0.75rem;
  padding: 0.2rem 0.3rem;
  width: 100%;
}
.vst-module-select-wrap select option { background: var(--vstb-panel); color: var(--vstb-text); }
.vst-module-select-wrap select option:checked { background: color-mix(in srgb, #4cd98a 22%, var(--vstb-panel)); font-weight: 600; }
.vst-module-mini-waveform { background: #0e100d; border: 1px solid var(--vstb-border); border-radius: 6px; display: block; }
.vst-module-knob-row { display: flex; flex-wrap: wrap; gap: 0.9rem; }
.vst-module-knob-item { align-items: center; color: var(--vstb-muted); display: flex; flex-direction: column; font-size: 0.6rem; gap: 0.3rem; letter-spacing: 0.03em; text-transform: uppercase; width: 60px; }
.vst-module-knob-label { color: var(--vstb-muted); }
/* Real addition (Phase 1, founder: "clean parameter label/value
   relationship") -- a live normalized readout under the label, bound to
   the exact same value the dial itself reflects (see
   vstBuilderCreateKnobItem/bindValueLabel in app.js), never a separate
   source of truth. Normalized-only (0-100%) since the schema this reads
   from carries no per-parameter min/max/unit. */
.vst-module-knob-value {
  color: var(--vstb-text);
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0;
  opacity: 0.95;
  text-transform: none;
}
.vst-module-box-fixed { min-width: 180px; }
.vst-module-output-body { align-items: center; }
.vst-module-meter { display: flex; flex-direction: column-reverse; gap: 2px; height: 60px; width: 14px; }
.vst-module-meter-seg { background: #2a3128; border-radius: 2px; flex: 1; }
.vst-module-meter-seg:nth-child(-n+2) { background: #3a4a34; }

.vst-module-timeline { align-items: center; display: flex; gap: 0.6rem; }
.vst-module-timeline-track { background: var(--vstb-panel); border: 1px solid var(--vstb-border); border-radius: 999px; flex: 1 1 auto; height: 6px; position: relative; }
.vst-module-timeline-track.playing { border-color: #4cd98a; }
.vst-module-timeline-marker { background: #4fa8ff; border-radius: 50%; height: 10px; left: 0%; position: absolute; top: -2px; transform: translateX(-50%); width: 10px; transition: left 0.48s linear; }
.vst-module-timeline-track.playing .vst-module-timeline-marker { background: #4cd98a; }
.vst-module-timeline-label { font-size: 0.7rem; white-space: nowrap; }

/* Real keyboard depth pass (Phase 1 instrument visual reconstruction,
   Founder APPLY: "convincing depth, separation and pressed states").
   Same 24 keys / same pointerdown-up MIDI wiring in
   vstBuilderRenderKeyboard (app.js) -- purely a rendering change, no
   behaviour touched. A sunken keybed housing + gradient-shaded keys +
   a real pressed-state depression (translateY + deeper inset shadow). */
.vst-module-keyboard {
  display: flex;
  height: 76px;
  margin-top: 0.35rem;
  padding: 6px 6px 0;
  position: relative;
  background: linear-gradient(180deg, #0d0f0c, #17190f);
  border: 1px solid var(--vstb-border);
  border-radius: 8px;
  box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.5);
}
.vst-module-key-white {
  background: linear-gradient(180deg, #fdfdfb, #e4e4de 88%, #cfcfc8);
  border: 1px solid #0d0f0c;
  border-top: none;
  border-radius: 0 0 5px 5px;
  box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.08), 0 3px 3px rgba(0, 0, 0, 0.35);
  cursor: pointer;
  flex: 1 1 0;
  transition: transform 0.05s ease;
}
.vst-module-key-white.pressed {
  background: linear-gradient(180deg, #bdeccf, #4cd98a);
  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.35);
  transform: translateY(1px);
}
.vst-module-key-black {
  background: linear-gradient(180deg, #3a3e38, #101210 85%);
  border: 1px solid #050604;
  border-radius: 0 0 4px 4px;
  box-shadow: 0 3px 4px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.08);
  cursor: pointer;
  height: 62%;
  margin-left: -0.6rem;
  margin-right: -0.6rem;
  position: relative;
  width: 1.2rem;
  z-index: 1;
  transition: transform 0.05s ease;
}
.vst-module-key-black.pressed {
  background: linear-gradient(180deg, #7fc4ff, #2f7fd6);
  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.4);
  transform: translateY(1px);
}

/* Real search + category tabs (2026-09-13, founder reference mockup) --
   replaces the previous <details>-collapsed block library. */
.vst-builder-block-search { margin: 0.5rem 0; width: 100%; }
.vst-builder-block-tabs { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-bottom: 0.5rem; }
/* Real daylight-mode contrast fix (2026-09-14, Founder APPLY) -- this
   button previously set no color of its own, so it inherited whatever
   ambient text colour the surrounding column happened to be forcing (see
   .vst-builder-block-library's own fix note below for the real root
   cause) -- invisible white-on-light-panel text in daylight mode once
   that leak is fixed at its source. Explicit, real theme token, so this
   never has to rely on inheritance from an ancestor that has its own,
   unrelated reason to force a colour (like the always-dark chat/compose
   log further down this same column). */
.vst-builder-block-tab {
  background: none; border: 1px solid var(--border); border-radius: var(--radius-pill);
  color: var(--text);
  cursor: pointer; font-size: 0.75rem; padding: 0.2rem 0.7rem;
}
/* Real fix (2026-09-14, Founder APPLY: category-aware active filter
   chip) -- --cat/--cat-text are set per-button in app.js from the same
   VST_BUILDER_CATEGORY_ACCENTS map the chips/headings use; unset for the
   "All" tab, which correctly falls through to the original generic
   Ask Archie accent + white text below. */
.vst-builder-block-tab.active {
  background: var(--cat, var(--accent));
  border-color: var(--cat, var(--accent));
  color: var(--cat-text, var(--on-accent, #fff));
}

.vst-builder-chat-mode { flex: 0 0 auto; }
.vst-builder-solo-regenerate-btn {
  background: #2f9e5c; border: none; border-radius: 6px; color: #fff; cursor: pointer;
  font-weight: 600; padding: 0.55rem 1rem; width: 100%;
}
.vst-builder-solo-regenerate-btn:hover { background: #278a4f; }

.vst-builder-block-library summary { cursor: pointer; }
.vst-builder-proposal-actions { display: flex; gap: 0.5rem; margin-top: 0.4rem; }
/* Real daylight-mode contrast fix (2026-09-14, Founder APPLY -- root
   cause traced, not just patched per-card). This container is a direct
   descendant of .vst-builder-chat-col, which also carries the shared
   .synth-compose-split-right base class -- a genuinely correct, existing
   pattern for the SYNTH page's own permanently-dark Archie chat panel
   (see that class's own 2026-09-05 comment: fixed dark background, real
   white text, deliberately independent of site theme). That class's
   `color: #fff` was leaking straight through this container and every
   card inside it, uncontested, because nothing here ever set its own
   real colour -- harmless in dark mode (white-on-dark reads fine by
   coincidence) but broken in daylight mode, where the individual cards
   below correctly pick up a real light theme background
   (var(--surface-2)) while the inherited text stayed forced white.
   Fixed at its actual source, per the card's own instruction, rather
   than patching each card: give this container its own explicit,
   real theme-token background/colour, breaking the inherited leak here
   rather than downstream. The deliberately-dark chat log/compose input
   further down this same column (.vst-builder-chat-col
   .vst-builder-compose-input-row and friends) is untouched -- that is
   accepted, intentional, matches the rest of Ask Archie's chat surfaces
   site-wide, and was never part of the reported defect. */
.vst-builder-block-library {
  background: var(--panel-bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.75rem 1rem;
  margin: 0.75rem 0;
}
/* Real fix, same leak, one more real instance found live (2026-09-14):
   .synth-compose-split-right .muted (2026-09-05, Synth page's own
   permanently-dark Archie panel) forces color:#fff directly on any
   .muted element inside it -- a DIRECT match, which always wins over
   this container's own inherited var(--text) above regardless of
   specificity. The intro line ("Drag a block onto the chain...") was
   still invisible in daylight mode even after the container-level fix
   above, for exactly this reason. Same fix shape: constrain it back to
   a real theme token at the correct, most-specific boundary. */
.vst-builder-block-library .muted { color: var(--muted); }
/* Real collapsible panel (2026-09-16, Founder APPLY) -- the header row is
   the real toggle (click or Enter/Space, see vstBuilderToggleBlockLibrary
   in app.js); collapsing only hides #vst-builder-block-library-body
   (search/tabs/chip list -- untouched, just not displayed), releasing the
   real vertical space the search+tabs+capped chip list normally take so
   the chat/input further down this same column gets it back on shorter
   viewports. Nothing about drag/drop, scrolling, or the chip list's own
   existing max-height (below) changes -- this only ever hides/shows the
   whole body as one block. */
.vst-builder-block-library-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  cursor: pointer;
  user-select: none;
}
.vst-builder-block-library-header h3 { margin: 0; }
.vst-builder-block-library-chevron {
  flex: 0 0 auto;
  font-size: 0.7rem;
  color: var(--muted);
}
.vst-builder-block-library.collapsed #vst-builder-block-library-body { display: none; }
.vst-builder-block-library dl { margin: 0.5rem 0 0; }
.vst-builder-block-library dt { font-weight: 600; margin-top: 0.5rem; }
/* Real fix: `--text-muted` was never a real defined token anywhere in
   this file's own token set (see the Design Token Foundation block at
   the top) -- every use of it silently fell through to its hardcoded
   `#888` fallback, in both themes, never actually theme-aware. The real
   site-wide muted-text token is `--muted`. */
.vst-builder-block-library dd { margin: 0.15rem 0 0; color: var(--muted); }
/* Real drag-and-drop block library chips (2026-09-13, Founder APPLY). */
/* Real fix (2026-09-13, founder: "increase the size... to fill out this
   space") -- these used to be capped at max-width:160px in a wrapping
   row, leaving real unused whitespace once the column got wider. Now a
   real grid that fills the full row width, each card growing evenly. */
.vst-builder-block-chip-row {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}
/* 2026-09-21 (Founder: text bleeding over the sides of the block cards): a card may never be wider than its column, and long unbroken text (e.g. "sine/saw/square/...") wraps inside it. */
.vst-builder-block-chip { min-width: 0; max-width: 100%; box-sizing: border-box; overflow: hidden; overflow-wrap: anywhere; word-break: break-word; }
.vst-builder-block-chip strong, .vst-builder-block-chip span { min-width: 0; overflow-wrap: anywhere; word-break: break-word; white-space: normal; }
/* Real daylight-mode contrast fix (2026-09-14, Founder APPLY) -- this
   card's own background already correctly followed the site theme
   (var(--surface-2)); it just never set its own text colour, so it
   inherited the .vst-builder-block-library leak fixed above (or, once
   that's fixed, would have inherited plain default black -- either way,
   never guaranteed to actually contrast against its own real background
   in both themes). Explicit var(--text)/var(--muted) here means this
   card is correct for ANY future block kind rendered into this same
   markup shape (vstBuilderRenderBlockLibrary in app.js), not a
   per-Oscillator/per-Filter patch. */
/* Real category colour system (2026-09-14, Founder APPLY) -- restrained
   light tints in daylight, richer dark tints in dark mode, from ONE
   formula: color-mix() blends the fixed per-category accent (--cat) into
   the theme's own already-flipping --surface-2 token, so the same rule
   reads as "pale tint on a near-white card" in light mode and "tinted
   dark surface" in dark mode with no separate light/dark accent values
   to keep in sync. Text stays the real theme text colour throughout --
   colour identity lives in the border/tint/heading, never at the cost of
   title/description contrast. */
.vst-builder-block-chip {
  cursor: grab;
  text-align: left;
  border: 1px solid var(--cat, var(--border));
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--cat, var(--border)) 12%, var(--surface-2));
  color: var(--text);
  padding: 0.6rem 0.75rem;
  transition: background var(--transition-soft, 150ms ease), border-color var(--transition-soft, 150ms ease);
}
.vst-builder-block-chip:active { cursor: grabbing; }
/* Hover: increase emphasis without changing category identity -- same
   --cat, just a stronger mix ratio and a slightly deeper border. */
.vst-builder-block-chip:hover {
  border-color: var(--cat, var(--accent));
  background: color-mix(in srgb, var(--cat, var(--accent)) 24%, var(--surface-2));
}
.vst-builder-block-chip strong { display: block; font-size: 1rem; color: var(--text); }
.vst-builder-block-chip span { display: block; font-size: 0.82rem; margin-top: 0.15rem; color: var(--muted); }
/* Real Sampler capability cards (2026-09-30) -- informational, never a real
   draggable/click-to-add block (see vstBuilderRenderSamplerCapabilityCards'
   own comment), so the cursor and hover both stay neutral rather than
   implying a drag/click action that would do nothing. */
.vst-builder-block-chip-info { cursor: default; }
.vst-builder-block-chip-info:hover { border-color: var(--cat, var(--border)); background: color-mix(in srgb, var(--cat, var(--border)) 12%, var(--surface-2)); }
.vst-builder-chain-list { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 0.3rem; }
.vst-builder-chain-list li {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.4rem 0.6rem;
  font-size: 0.9rem;
}
/* Real fix (2026-09-13, founder: convolution row "hanging off" the left
   rail) -- five real controls (label, IR status, native file input,
   Upload IR, remove) were forced onto one unbreakable flex line
   (default flex-wrap: nowrap), so anything that didn't fit inside the
   now-300px column spilled out past its right edge instead of dropping
   to a new line. */
/* Real category colour tints (2026-09-14, Founder APPLY: "Current DSP
   chain category colour tints") -- extends the same semantic category
   language the Available building blocks library and the generated
   instrument's own module boxes already use, so the same block reads as
   the same colour in all three places. --cat is set per real block row
   in vstBuilderRenderChainList (app.js) from the exact same server-
   computed accent that already drives this row's own border-left --
   never a second, RazorSaw-specific mapping. Scoped to .vst-builder-
   chain-item specifically (never the bare "li" the drop-target/empty-
   state rows also are) so it can't fight .vst-builder-chain-drop-target-
   active's own background below, and never applies where there's no
   real block to colour. Restrained on purpose: a 9% mix into the real
   --panel-bg token means a pale tint in daylight and an already-subtle
   dark tint in dark mode from the one same rule (--panel-bg itself flips
   between the two, the mix ratio never needs to). Falls back to fully
   transparent (no visible tint at all) for any block with no mapped
   accent, exactly matching the border-left's own existing fallback. */
.vst-builder-chain-item {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.35rem 0.5rem; cursor: grab;
  background: color-mix(in srgb, var(--cat, transparent) 9%, var(--panel-bg));
}
.vst-builder-chain-item:active { cursor: grabbing; }
.vst-builder-chain-remove {
  border: none;
  background: none;
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  color: var(--muted);
  padding: 0 0.2rem;
}
.vst-builder-chain-remove:hover { color: #d9534f; }
/* Real fix (2026-09-16, founder-reported: "quite faded and not obvious")
   -- bigger, bolder, and a real accent-tinted background at rest, not
   just on hover/drag -- so this genuinely reads as a drop target instead
   of blending into the muted rows around it. */
.vst-builder-chain-drop-target {
  border: 2px dashed var(--accent) !important;
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  color: var(--accent);
  font-weight: 600;
  font-size: 0.95rem;
  text-align: center;
  padding: 0.85rem 0.5rem !important;
  border-radius: var(--radius-md);
  transition: background 0.15s ease, border-color 0.15s ease;
}
.vst-builder-chain-drop-target-active { background: color-mix(in srgb, var(--accent) 22%, transparent); border-color: var(--accent) !important; }
.vst-builder-conv-ir-status { font-size: 0.75rem; color: var(--muted); }
.vst-builder-conv-ir-input { max-width: 100%; min-width: 0; font-size: 0.75rem; }
/* Real "Build" dialog treatment (2026-09-10, founder feedback: "No Build
   Dialog" -- the build status was reusing the plain list-row style with no
   background, so it read as loose leftover text rather than its own real
   panel. Now boxed with the same visual weight as the block-library card. */
#vst-builder-detail-project-row .vst-builder-project-row {
  background: var(--surface-2, rgba(127, 127, 127, 0.06));
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.25));
  border-radius: var(--radius-md);
  padding: 1rem;
}
/* Real chat-shaped layout (2026-09-10, founder's own hand-drawn mockup):
   reference/blocks fixed at the top, the real conversation growing in a
   scrollable middle area, input + Generate pinned at the bottom -- the
   same real shape every actual chat UI (and pluginmaker.ai's own) uses. */
/* Real fix (2026-09-10, founder: "chat to scroll not to push the input box
   down out of view" -- the previous min-height-only column let
   align-items:stretch grow it to match the left column's real height with
   no ceiling, so the "sticky" input below was really just riding the page
   scroll, not pinned to a bounded panel. Giving this column a real height
   (matching the left column's own 100%) means .vst-builder-compose-log-grow's
   existing overflow-y:auto finally has something to scroll WITHIN, and the
   input -- an ordinary trailing flex child now -- stays on screen with no
   sticky/z-index trick needed at all. */
/* Real fix (2026-09-13, founder: content "pops in then disappears" once
   the chain grows tall): the ROW itself, not just the left column, was
   the actual clipping boundary. Every earlier attempt (this session)
   fixed .synth-compose-split-left's own height/overflow, but the base
   .synth-compose-split rule this row also inherits sets a hard
   `overflow: hidden` (styles.css:1570) plus this row's own fixed
   `height: min(85vh, 830px)` -- so once real content passed 830px tall,
   the ROW clipped it regardless of what the column's own CSS said.
   Overriding both here, matching the reasoning already proven for the
   column: no fixed ceiling, no clip, natural content height. */
.vst-builder-split { align-items: stretch; height: auto; max-height: none; overflow: visible; }
/* Real fix (2026-09-13, founder: "some sort of scrolling here to keep the
   chat input visible") -- this column lost its own real bounded height
   as a side effect of the earlier fix for the LEFT column's clipping bug
   (the ROW's own height moved to `auto`, which this column used to rely
   on via align-items:stretch). Giving it back a real, explicit height of
   its own (not the row's) restores a genuine ceiling for its internal
   flex children to scroll within -- overflow-y:auto here, never
   overflow:hidden, so this doesn't repeat that earlier bug. */
.vst-builder-chat-col {
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: min(85vh, 830px);
}
/* The block-library's own card list gets its own capped, independently
   scrolling region -- so a long library (all ~30 real block kinds) can't
   dominate the column and push the quick-chips/chat/input further down
   than a screen's worth of scrolling. */
#vst-builder-block-chips {
  /* 2026-09-21 (Founder): a touch taller so more of the list shows without scrolling (was 260px). */
  max-height: 400px;
  overflow-y: auto;
}
.vst-builder-block-library {
  flex: 0 0 auto;
}
/* Real fix (2026-09-13, founder-reported: blank left/center columns on
   the detail page). Five earlier attempts (explicit flex height,
   overflow-anchor, forced GPU layer, JS-computed height, plain fixed
   max-height) were each deployed, confirmed live, and each disproven by
   the Founder actually retesting -- ruled out along the way: cache/
   deploy staleness, page zoom at 100%, browser extensions (Incognito),
   and browser choice (Chrome and Brave both). The real breakthrough: the
   Founder reported having to zoom ALL THE WAY IN to bring it back --
   enough to push the effective page width under this same page's own
   pre-existing 800px mobile breakpoint below, whose own rule is `height:
   auto; overflow-y: visible`. Every attempt above kept some form of
   "constrained height + overflow-y:auto" on this column (just via a
   different mechanism each time) -- the one pattern never actually
   removed. Applying that same real, already-proven-safe mobile rule
   unconditionally (not just under 800px) removes it for good: no
   constrained height, no internal scrollbar, natural content height
   instead -- if the chain ever makes this column taller than its
   siblings, the row (and the page) simply grows to fit, exactly as it
   already does below 800px today. */
.vst-builder-split .synth-compose-split-left {
  height: auto;
  overflow-y: visible;
  /* The mobile breakpoint's own version of this rule works inside a row
     that's ALSO switched to flex-direction:column at that width
     (inherited from the shared .synth-compose-split base class), where
     "auto" is simple main-axis content-sizing. Here the row stays
     side-by-side, so align-items:stretch on the parent would otherwise
     silently override a plain height:auto right back to the stretched
     value -- align-self:flex-start is what actually disables that for
     this one column, without changing the row layout for its siblings. */
  align-self: flex-start;
}
.vst-builder-compose-log-grow { flex: 1 1 auto; overflow-y: auto; min-height: 0; }
/* Real fix (2026-09-11, founder: "the rebuild block is confusing... bringing
   the input box up one row and have them both underneath") -- Rebuild now
   pulled out of the DSP quick-chips row (it isn't a DSP request, it skips
   propose/review entirely) into its own button, paired with Ask Archie on a
   dedicated row below the full-width input. */
.vst-builder-chat-col .vst-builder-compose-input-row,
.vst-builder-chat-col .vst-builder-compose-actions-row {
  flex: 0 0 auto;
  background: linear-gradient(180deg, #2b2f2b 0%, #1c1f1c 100%);
}
.vst-builder-chat-col .vst-builder-compose-input-row {
  padding: 0.6rem 0 0;
  border-top: 1px solid #3d423d;
}
/* Real fix (2026-09-10, founder: "some frame overlap... text is a bit
   small... should be a bit more obvious when you get here from the other
   page") -- no box-sizing: border-box meant the default browser padding
   pushed this wider than its own container (the real "frame overlap"),
   font-size was whatever tiny default it inherited, and its border/
   background just fell back to a default light browser style that clashed
   with the dark panel. A visible focus glow makes it read as the obvious
   next step right after landing here from project creation. */
.vst-builder-chat-col .vst-builder-compose-input-row input {
  width: 100%;
  box-sizing: border-box;
  font-size: 1rem;
  padding: 0.65rem 0.85rem;
  margin-top: 0.5rem;
  background: #1c1f1c;
  color: #fff;
  border: 1px solid #4a5048;
  border-radius: var(--radius-sm, 6px);
}
.vst-builder-chat-col .vst-builder-compose-input-row input::placeholder { color: #8a9088; }
.vst-builder-chat-col .vst-builder-compose-input-row input:focus {
  outline: none;
  border-color: var(--accent, #2f6b4f);
  box-shadow: 0 0 0 3px rgba(47, 107, 79, 0.3);
}
.vst-builder-chat-col .vst-builder-compose-actions-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.5rem 0 0;
}
.vst-builder-chat-col .vst-builder-compose-actions-row #vst-builder-compose-btn { flex: 1 1 auto; }
.vst-builder-chat-col .vst-builder-compose-actions-row #vst-builder-rebuild-now-btn { color: #fff; opacity: 0.75; white-space: nowrap; }
.vst-builder-chat-col .vst-builder-compose-actions-row #vst-builder-rebuild-now-btn:hover { opacity: 1; }
/* Workspace reorganisation (2026-09-17, Founder APPLY): the block-library
   column no longer needs a fixed/scrolling height of its own now that
   Archie's chat isn't sharing the column with it -- same real, already-
   proven "size to natural content, no internal scrollbar" treatment
   .synth-compose-split-left already gets, just applied to this column too. */
.vst-builder-split .vst-builder-blocks-col {
  height: auto;
  overflow-y: visible;
  align-self: flex-start;
  /* 2026-09-21 (Founder): no wide padding round the edges -- the block list uses the panel's full width (was 1rem 1.25rem here, plus the card's own margin and padding below). */
  padding: 0.3rem;
}
.vst-builder-blocks-col .vst-builder-block-library {
  margin: 0;
  padding: 0.45rem 0.5rem;
}

/* Workspace reorganisation (2026-09-17, Founder APPLY): Archie's
   interaction panel, relocated below the main workspace + block-library
   row so it reads as a genuine, separate, full-width workspace section --
   not content accidentally appended under the block library. Reuses the
   real chassis look .vst-builder-col-center already established (corner
   "screws", bezel border/shadow, same --vstb-* tokens this whole detail
   page already scopes) rather than a new one-off style, so Building
   Blocks/main workspace/Archie all still read as the same hardware-panel
   family. Overrides .vst-builder-chat-col's own height (sized for a narrow
   column) with a shorter budget appropriate to a full-width bottom panel --
   every other .vst-builder-chat-col rule (input row, actions row, compose-
   log growth/scroll) is inherited completely unchanged. */
.vst-builder-archie-panel {
  height: min(48vh, 460px);
  margin-top: 1rem;
  padding: 1.1rem 1.25rem;
  background:
    radial-gradient(circle at 14px 14px, #4a5058 0 2px, transparent 3px),
    radial-gradient(circle at calc(100% - 14px) 14px, #4a5058 0 2px, transparent 3px),
    radial-gradient(circle at 14px calc(100% - 14px), #4a5058 0 2px, transparent 3px),
    radial-gradient(circle at calc(100% - 14px) calc(100% - 14px), #4a5058 0 2px, transparent 3px),
    linear-gradient(180deg, rgba(255, 255, 255, 0.035), transparent 70px),
    var(--vstb-bg);
  border: 1px solid #05060a;
  border-radius: 16px;
  box-shadow:
    0 16px 40px rgba(0, 0, 0, 0.5),
    inset 0 0 0 1px rgba(255, 255, 255, 0.04),
    inset 0 1px 0 rgba(255, 255, 255, 0.05);
  color: var(--vstb-text);
  transition: border-color 0.4s ease;
}
/* Real fix (2026-09-18, Founder-directed: "can we get archies chats to
   pulsate also like the webview") -- matches the native compiled VST's
   own chat panel exactly (archieChatPulse in vstCodeGen.ts): grey/inert
   border while disconnected, the same slow real red breathing pulse once
   genuinely connected. Toggled by the same real vstBuilderLastKnownConnected
   state every other connected-state UI on this page already reacts to --
   never a separate, invented connection signal. */
.vst-builder-archie-panel.connected { animation: vstBuilderArchiePulse 3.2s ease-in-out infinite; }
@keyframes vstBuilderArchiePulse {
  0%, 100% { border-color: #7a2020; box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5), inset 0 0 0 1px rgba(255, 255, 255, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 0 4px rgba(217, 64, 64, 0.25); }
  50% { border-color: #ff4d4d; box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5), inset 0 0 0 1px rgba(255, 255, 255, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 0 16px rgba(255, 77, 77, 0.5); }
}
.vst-builder-archie-panel-title { margin: 0 0 0.6rem; flex: 0 0 auto; color: var(--vstb-text); }
.vst-builder-archie-panel .vst-builder-compose-input-row,
.vst-builder-archie-panel .vst-builder-compose-actions-row {
  /* The inherited .vst-builder-chat-col rule sets a matching dark
     gradient here too -- fine on a narrow column, but a visible seam
     against this panel's own chassis background at full width. Blends
     into the chassis instead. */
  background: transparent;
}

@media (max-width: 800px) {
  .vst-builder-split { height: auto; }
  .vst-builder-split .synth-compose-split-left { height: auto; overflow-y: visible; }
  .vst-builder-split .vst-builder-col-center { border-right: none; }
  .vst-builder-archie-panel { height: auto; min-height: 420px; }
}

/* VST Build Programme, Stage 5 (2026-09-18, Founder APPLY): the Audition
   panel. Reuses the exact same chassis background (corner "screws",
   bezel, --vstb-* tokens) as .vst-builder-archie-panel so this reads as
   the same hardware-panel family, not a bolted-on third style. Deliberately
   compact and collapsible (Part 10: "do not turn VST Builder into another
   Music Lab") -- a single row of controls, not a second workspace. */
.vst-audition-panel {
  margin-top: 1rem;
  padding: 0.85rem 1.1rem;
  background:
    radial-gradient(circle at 14px 14px, #4a5058 0 2px, transparent 3px),
    radial-gradient(circle at calc(100% - 14px) 14px, #4a5058 0 2px, transparent 3px),
    radial-gradient(circle at 14px calc(100% - 14px), #4a5058 0 2px, transparent 3px),
    radial-gradient(circle at calc(100% - 14px) calc(100% - 14px), #4a5058 0 2px, transparent 3px),
    linear-gradient(180deg, rgba(255, 255, 255, 0.035), transparent 70px),
    var(--vstb-bg);
  border: 1px solid #05060a;
  border-radius: 16px;
  box-shadow:
    0 16px 40px rgba(0, 0, 0, 0.5),
    inset 0 0 0 1px rgba(255, 255, 255, 0.04),
    inset 0 1px 0 rgba(255, 255, 255, 0.05);
  color: var(--vstb-text);
}
.vst-audition-header { display: flex; align-items: center; justify-content: space-between; }
.vst-audition-title { margin: 0; color: var(--vstb-text); }
.vst-audition-collapse-toggle { background: transparent; border: 1px solid rgba(255,255,255,0.15); color: var(--vstb-text); border-radius: 8px; padding: 0.2rem 0.6rem; font-size: 0.8rem; cursor: pointer; }
.vst-audition-body.hidden { display: none; }
.vst-audition-mode-row { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; margin-top: 0.6rem; flex-wrap: wrap; }
.vst-audition-mode-toggle { display: inline-flex; border: 1px solid rgba(255,255,255,0.15); border-radius: 10px; overflow: hidden; }
.vst-audition-mode-btn { background: transparent; color: var(--vstb-text); border: none; padding: 0.35rem 0.75rem; font-size: 0.85rem; cursor: pointer; opacity: 0.65; }
.vst-audition-mode-btn.active { background: rgba(76, 217, 138, 0.18); opacity: 1; }
.vst-audition-mode-btn:disabled { cursor: not-allowed; opacity: 0.35; }
.vst-audition-connection-status { font-size: 0.8rem; opacity: 0.7; }
.vst-audition-truth-note { font-size: 0.78rem; opacity: 0.65; margin: 0.4rem 0 0; }
.vst-audition-controls-row { display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap; margin-top: 0.6rem; }
.vst-audition-field { display: flex; flex-direction: column; gap: 0.2rem; font-size: 0.78rem; opacity: 0.85; }
.vst-audition-field select, .vst-audition-field input[type="number"] { background: rgba(0,0,0,0.35); color: var(--vstb-text); border: 1px solid rgba(255,255,255,0.15); border-radius: 6px; padding: 0.2rem 0.4rem; }
.vst-audition-field-checkbox { flex-direction: row; align-items: center; gap: 0.35rem; }
.vst-audition-transport-row { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.65rem; flex-wrap: wrap; }
.vst-audition-play-btn, .vst-audition-stop-btn, .vst-audition-use-host-tempo-btn, .vst-audition-archie-btn {
  background: rgba(255,255,255,0.08); color: var(--vstb-text); border: 1px solid rgba(255,255,255,0.15); border-radius: 8px; padding: 0.35rem 0.75rem; font-size: 0.82rem; cursor: pointer;
}
.vst-audition-play-btn:hover, .vst-audition-archie-btn:hover { background: rgba(76, 217, 138, 0.22); }
.vst-audition-archie-request { flex: 1 1 220px; min-width: 180px; background: rgba(0,0,0,0.35); color: var(--vstb-text); border: 1px solid rgba(255,255,255,0.15); border-radius: 6px; padding: 0.3rem 0.5rem; font-size: 0.8rem; }
.vst-audition-block-support { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.6rem; }
.vst-audition-block-chip { font-size: 0.72rem; padding: 0.15rem 0.5rem; border-radius: 999px; border: 1px solid rgba(255,255,255,0.15); }
.vst-audition-block-chip[data-level="supported"] { background: rgba(76, 217, 138, 0.18); }
.vst-audition-block-chip[data-level="partial"] { background: rgba(255, 176, 32, 0.18); }
.vst-audition-block-chip[data-level="unsupported"] { background: rgba(255, 255, 255, 0.06); opacity: 0.7; }
.vst-audition-archie-status { font-size: 0.78rem; opacity: 0.75; margin: 0.4rem 0 0; }
#vst-audition-keyboard { margin-top: 0.65rem; }
.vst-audition-keep-btn { background: rgba(76, 217, 138, 0.14); }
.vst-audition-keep-btn:hover { background: rgba(76, 217, 138, 0.28); }
.vst-audition-saved-row { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.6rem; flex-wrap: wrap; }
.vst-audition-saved-row select { background: rgba(0,0,0,0.35); color: var(--vstb-text); border: 1px solid rgba(255,255,255,0.15); border-radius: 6px; padding: 0.25rem 0.5rem; font-size: 0.8rem; min-width: 220px; }
.vst-audition-saved-row button { background: rgba(255,255,255,0.08); color: var(--vstb-text); border: 1px solid rgba(255,255,255,0.15); border-radius: 8px; padding: 0.25rem 0.6rem; font-size: 0.8rem; cursor: pointer; }
.vst-audition-keep-status { font-size: 0.78rem; opacity: 0.75; margin: 0.4rem 0 0; }
.vst-audition-synth-send-panel {
  margin-top: 0.65rem;
  padding: 0.65rem 0.75rem;
  background: rgba(0,0,0,0.25);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 10px;
}
.vst-audition-synth-send-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin-top: 0.5rem; }
.vst-audition-synth-send-row:first-child { margin-top: 0; }
.vst-audition-synth-send-row label { display: flex; flex-direction: column; gap: 0.2rem; font-size: 0.78rem; opacity: 0.85; }
.vst-audition-synth-send-row select, .vst-audition-synth-send-row input[type="number"] { background: rgba(0,0,0,0.35); color: var(--vstb-text); border: 1px solid rgba(255,255,255,0.15); border-radius: 6px; padding: 0.2rem 0.4rem; width: 4.5rem; }
.vst-audition-synth-send-row select { width: auto; }
.vst-audition-synth-send-row button { background: rgba(255,255,255,0.08); color: var(--vstb-text); border: 1px solid rgba(255,255,255,0.15); border-radius: 8px; padding: 0.25rem 0.6rem; font-size: 0.8rem; cursor: pointer; }
.vst-audition-synth-send-row button:disabled { opacity: 0.4; cursor: not-allowed; }
#vst-audition-synth-confirm-send-btn:not(:disabled) { background: rgba(76, 217, 138, 0.2); }
.vst-audition-synth-analysis-result { font-size: 0.78rem; margin-top: 0.5rem; }
.vst-audition-synth-analysis-result .synth-adapter-error { color: #ff8080; }
.vst-audition-synth-analysis-result .synth-adapter-warning { opacity: 0.8; }
.vst-audition-synth-overwrite-row { display: flex; align-items: center; gap: 0.6rem; margin-top: 0.5rem; font-size: 0.8rem; color: #ffc060; flex-wrap: wrap; }
@media (max-width: 800px) {
  .vst-audition-controls-row, .vst-audition-transport-row, .vst-audition-saved-row { gap: 0.5rem; }
  .vst-audition-archie-request { flex-basis: 100%; }
  .vst-audition-saved-row select { flex: 1 1 100%; }
  .vst-audition-synth-send-row { gap: 0.4rem; }
}
/* Real fix (2026-09-10, founder: "the chat should be at the bottom" -- with
   few messages this box (taller than its content, by design, so it can
   scroll) packed them at the TOP with dead space below, floating away from
   the input instead of sitting naturally next to it. justify-content:
   flex-end packs messages against the bottom instead, matching every real
   chat UI; overflow-y:auto (already set via .vst-builder-compose-log-grow)
   keeps scrolling correct once real content overflows it.
   Real follow-up fix (2026-09-16, found live while testing the collapsible
   building-blocks panel -- founder brief: "Archie conversation/response
   area has a clear bounded region that can scroll independently"):
   justify-content:flex-end here was a genuine, real defect, not just a
   layout preference -- once real conversation content actually overflows
   this box, a flex column with justify-content:flex-end pushes the
   overflow off the START edge, and every tested Chromium build then
   reports scrollHeight === clientHeight (confirmed live: 16 real messages,
   761px of real content, scrollHeight stuck at 111px) -- scrollTop can
   never be set to anything but 0, so every message before whatever
   currently fits becomes permanently unreachable, not just visually
   scrolled past. The standard fix for this exact combination: plain
   top-down flow (justify-content's default, removed below) plus
   margin-top:auto on the first real entry -- consumes the same slack
   space justify-content:flex-end used to, so a short conversation still
   sits flush against the bottom exactly as the founder asked for above,
   but once content overflows, margin-top:auto simply resolves to 0 and
   ordinary, correctly-computed top-down overflow/scroll takes over.
   vstBuilderScrollLogToBottom() (app.js) already sets scrollTop =
   scrollHeight after every single new message (a real, already-existing
   call at every append site) -- that keeps the latest reply pinned into
   view exactly as before; this fix only restores the ability to
   genuinely scroll UP through earlier history, which the old rule
   silently made impossible. */
#vst-builder-compose-log { display: flex; flex-direction: column; gap: 0.5rem; margin: 0.5rem 0; }
#vst-builder-compose-log > *:first-child { margin-top: auto; }
.vst-builder-compose-entry {
  border-left: 3px solid var(--accent-color, #6c5ce7);
  padding: 0.3rem 0 0.3rem 0.6rem;
  font-size: 0.9rem;
}

/* Stage 4 transparency (2026-09-17): collapsed-by-default "What did this
   use?" panel, same restrained idiom as the existing Advanced/Models-dock
   <details> elements -- never open by default, never louder than the
   summary text it sits under. */
.vst-builder-usage-details { margin-top: 0.35rem; font-size: 0.8rem; color: var(--text-muted, #888); }
.vst-builder-usage-details summary { cursor: pointer; font-weight: 600; }
.vst-builder-usage-list { margin: 0.35rem 0 0 1rem; padding: 0; }
.vst-builder-usage-list li { margin: 0.15rem 0; }

/* Real "click the image to try it" screenshot control (2026-09-09,
   founder-designed): bright + clickable when ready, dimmed with a real
   processing overlay while a change is in flight. */
#vst-builder-screenshot-wrap { margin: 0.75rem 0; display: flex; flex-direction: column; align-items: center; gap: 0.5rem; }
#vst-builder-screenshot-wait-source-link { font-size: 0.85rem; text-decoration: underline; color: var(--text-muted, #888); }
#vst-builder-screenshot-btn {
  position: relative;
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.25));
  border-radius: var(--radius-md);
  padding: 0;
  background: none;
  cursor: pointer;
  overflow: hidden;
  transition: opacity 0.25s ease, transform 0.15s ease;
}
#vst-builder-screenshot-btn:hover { transform: scale(1.01); }
#vst-builder-screenshot-btn.vst-builder-screenshot-dim { cursor: default; opacity: 0.45; }
#vst-builder-screenshot-btn.vst-builder-screenshot-dim:hover { transform: none; }
/* Real fix (2026-09-10, found live: "Too SMALL") -- max-width only ever
   caps the image at its own real native pixel size, it never scales UP to
   use the real available column width, so a genuinely wider left column
   just left it looking small and lost. Now scales to fill the column
   (up to a sane real cap), aspect ratio preserved via height: auto. */
#vst-builder-screenshot-img { display: block; width: min(100%, 640px); height: auto; }
#vst-builder-screenshot-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Real fix (2026-09-10, founder: "make this text white so we can see
     it possible bigger text") -- text was already white, but 55% opacity
     let a busy backdrop image (e.g. the camo one) show through and wash
     out contrast. Darker/more opaque + bigger + bolder holds up against
     any backdrop. */
  background: rgba(0, 0, 0, 0.8);
  color: #fff;
  font-size: 1.05rem;
  font-weight: 600;
  text-align: center;
  padding: 0.5rem;
}

#vst-builder-install-popout, #vst-builder-type-popout, #vst-builder-manual-panel {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 500;
}
/* VST Builder Manual (2026-09-29) -- same popout treatment as the type
   selector above, but wide/tall/scrollable since it holds real, growing
   content instead of a couple of choices. */
.vst-builder-manual-inner { max-width: 760px; width: 90vw; max-height: 82vh; display: flex; flex-direction: column; }
.vst-builder-manual-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.vst-builder-manual-header h3 { margin: 0; }
#vst-builder-manual-body { overflow-y: auto; margin-top: 0.5rem; }
.vst-manual-category { margin: 1.1rem 0 0.4rem 0; padding-bottom: 0.25rem; border-bottom: 1px solid var(--border-color, rgba(127, 127, 127, 0.25)); color: #222; }
.vst-manual-category:first-child { margin-top: 0; }
.vst-manual-article { margin: 0.5rem 0 0.9rem 0; }
.vst-manual-article h4 { margin: 0 0 0.3rem 0; color: #222; }
.vst-manual-article p { white-space: normal; line-height: 1.5; color: #555; margin: 0 0 0.5rem 0; }
.vst-builder-type-choices { display: flex; flex-direction: column; gap: 0.6rem; margin: 0.75rem 0; }
.vst-builder-type-choice {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.2rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.25));
  border-radius: var(--radius-md);
  background: none;
  /* Real fix (2026-09-10, found live): the shared `button { color:
     var(--on-accent) }` rule assumes an accent-colored background --
     this button overrides background to none but not color, so its
     text rendered near-invisible against the light popout. A literal
     here, not var(--text) -- this card's cream background is always
     light regardless of site theme, but --text IS real and theme-driven
     (near-white in dark mode), so var(--text) picked up that value
     instead of falling back and stayed near-invisible in dark mode too
     (same root cause found and fixed the same way in the install-
     instructions popout right below, which shares this exact card). */
  color: #222;
  cursor: pointer;
  text-align: left;
}
.vst-builder-type-choice:hover { border-color: var(--accent-color, #6c5ce7); }
.vst-builder-install-popout-inner {
  background: var(--bg-color, #fdf6e3);
  border-radius: var(--radius-md);
  padding: 1.25rem 1.5rem;
  max-width: 420px;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
  /* Real fix (2026-09-10, found live): this popout's cream background is a
     hardcoded literal, not theme-aware -- but `--text` IS a real, theme-
     driven variable (redefined near-white in dark mode). Using var(--text)
     here picked up that real dark-mode value instead of falling back, so
     the heading stayed near-invisible against the always-light background
     in dark mode. Literal colors here on purpose, matching the background. */
  color: #222;
}
.vst-builder-install-popout-inner h3 { color: #222; }
.vst-builder-install-popout-inner p { white-space: pre-line; line-height: 1.5; color: #555; }

/* Video editor pop-out note taker -- floats over the editor (not a
   sidebar-panel swap) so it can stay open alongside whatever else is
   open. */
#video-editor-notes-popout { position: fixed; bottom: 1rem; left: 1rem; z-index: 100; }
#video-editor-notes-popout.hidden { display: none; }
.video-editor-notes-popout-card {
  width: 320px;
  max-height: 70vh;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.85rem;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--panel-bg, var(--bg));
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
}
.video-editor-notes-popout-header { display: flex; align-items: center; justify-content: space-between; }
.video-editor-notes-popout-header h4 { margin: 0; }
/* Real fix (2026-09-04, founder-reported: "totally collapsed at the
   moment"): rows="6" alone never actually rendered at that height -- the
   popout card is a flex column, and this textarea's default flex-shrink:1
   let the list below it (real, unbounded content once notes exist) squeeze
   it down toward a near-zero content height whenever the card's own
   max-height (70vh) got tight. flex-shrink:0 keeps its real, intended
   height fixed; the list already has its own overflow-y:auto to absorb
   any real overflow instead. */
#video-editor-notes-popout-input { resize: vertical; flex-shrink: 0; min-height: 7em; }
#video-editor-notes-popout-list { list-style: none; margin: 0; padding: 0; overflow-y: auto; flex: 1; display: flex; flex-direction: column; gap: 0.4rem; }
.video-editor-notes-popout-item { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 0.5rem; font-size: 0.85rem; }
.video-editor-notes-popout-item-text { white-space: pre-wrap; margin: 0 0 0.3rem; }
.video-editor-notes-popout-item-meta { display: flex; align-items: center; justify-content: space-between; }

.vault-item-text-editable { cursor: pointer; border-radius: var(--radius-sm); padding: 0.2rem; margin: -0.2rem; }
.vault-item-text-editable:hover { background: rgba(47, 107, 79, 0.08); }
.vault-item-edit-textarea { width: 100%; box-sizing: border-box; resize: vertical; }
.vault-item-edit-actions { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.2rem; }

/* My Vault note editor -- a real "open document" view, word-pad shaped:
   a clean page with a title bar, a meta line, and a big writing surface. */
.vault-note-editor-overlay {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.45);
  padding: 2rem;
  box-sizing: border-box;
}
.vault-note-editor-overlay.hidden { display: none; }
.vault-note-editor-card {
  position: relative;
  width: min(720px, 100%);
  height: min(80vh, 800px);
  display: flex;
  flex-direction: column;
  background: var(--bg);
  border-radius: var(--radius-md);
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.35);
  padding: 2rem 2.5rem;
  box-sizing: border-box;
}
.vault-note-editor-close-btn { position: absolute; top: 0.75rem; right: 1rem; font-size: 1.4rem; line-height: 1; }
.vault-note-editor-title {
  font-size: 1.6rem;
  font-weight: 700;
  border: none;
  background: transparent;
  padding: 0;
  color: inherit;
}
.vault-note-editor-title:focus { outline: none; }
.vault-note-editor-meta { margin: 0.25rem 0 1rem; }
.vault-note-editor-content {
  flex: 1;
  width: 100%;
  box-sizing: border-box;
  border: none;
  background: transparent;
  resize: none;
  font-size: 1.05rem;
  line-height: 1.6;
  padding: 0;
}
.vault-note-editor-content:focus { outline: none; }
.vault-note-editor-actions { display: flex; align-items: center; gap: 1rem; margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--border); }
.vault-note-editor-dictate-row { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.5rem; }
#vault-note-editor-dictate-btn.active { color: #c0392b; font-weight: 700; }
.vault-note-editor-content.dictate-target, .vault-note-editor-title.dictate-target { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Photo/video pickers (group feed, Community Feed composer) -- real
   labeled sections rather than one interleaved grid, since a still image
   and a video's first-frame thumbnail otherwise look identical at a
   glance. */
.community-hub-media-section { margin-top: 0.75rem; }

/* Vocal Studio (2026-09-07 build brief, Phase 0/1) -- originally a real,
   dedicated "vocal booth" identity, deliberately NOT looking like the rest
   of the Synth screen's modular rack (the founder's own direction at the
   time: "make it not look so modular as our synth looks") -- a warm-dark
   ground, one warm copper accent, and its own Sora/Manrope type pairing,
   all as local --vs-* tokens rather than the site's shared --bg/--accent
   chain, fixed regardless of the site's own light/dark toggle.

   SUPERSEDED (2026-09-26, Founder-directed: "full silver hardware
   chassis... drop the orange DAW theme entirely" -- explicitly choosing
   this after being shown the tension with the direction above). Sample
   Studio is now wrapped in the same .synth-hw-instrument silver chassis
   as every other Synth section (see index.html), and every one of this
   block's --vs-* tokens below is recoloured to the shared hardware
   palette -- silver panel tones for --vs-bg/--vs-panel/--vs-panel-raised
   (same three-tier recessed-vs-raised relationship as
   .synth-hw-group/.synth-hw-instrument/the reskinned select-and-button
   bezel elsewhere), dark hardware ink for --vs-ink/--vs-ink-muted, the
   shared green "on/interactive" accent for --vs-accent (was warm copper),
   the shared red "recording/destructive" accent for --vs-curve (was dusty
   rose -- this component already had two accent roles, which maps cleanly
   onto the hardware chassis's own existing red/green split), and the
   site's real font stack for --vs-font-display/--vs-font-body (was
   Sora/Manrope). This is a genuine PALETTE swap, not a rewrite -- every
   rule below still reads its colour through these same variables, so the
   actual layout/structure/spacing is completely untouched. The couple of
   hardcoded (non-variable) colours that couldn't follow a variable swap
   are called out at their own rule, below and in
   synthVocalClipDrawWaveform()'s own comment in app.js (the waveform's
   canvas fillStyle isn't CSS at all, so it's a separate, deliberate edit
   there). */
/* Real fix (founder-directed: "should we move vocal studio into the synth
   page now") -- Vocal Studio used to be its own full-page screen (its own
   min-height:100vh background, sticky topbar with a Back button); it's a
   real section of the Synth page now, so it needs to read as a contained
   panel dropped into that page's own flow instead of a second, separate
   full-bleed page -- just as a rounded card, not a full viewport. */
#synth-screen .vocal-studio-screen {
  --vs-bg: linear-gradient(180deg, #c9cbce, #aeb0b3);
  --vs-panel: linear-gradient(180deg, #d7d9db, #c2c4c7);
  --vs-panel-raised: linear-gradient(180deg, #f4f5f6, #d7d9db);
  --vs-border: #83868a;
  --vs-ink: #1c1d1e;
  --vs-ink-muted: #45484b;
  --vs-accent: #1e9a4a;
  --vs-accent-hover: #7fd992;
  --vs-accent-dim: rgba(30, 154, 74, 0.18);
  --vs-curve: #c14535;
  --vs-font-display: var(--font-family);
  --vs-font-body: var(--font-family);
  background: var(--vs-bg);
  color: var(--vs-ink);
  font-family: var(--vs-font-body);
  border-radius: 8px;
  overflow: hidden;
}
#synth-screen .vocal-studio-screen .vocal-studio-main {
  max-width: 1100px;
  margin: 0 auto;
  padding: 1.5rem 1.25rem 2rem;
}
#synth-screen .vocal-studio-screen .vocal-studio-intro {
  color: var(--vs-ink-muted);
  font-size: 0.95rem;
  line-height: 1.5;
  max-width: 62ch;
  margin: 0 0 1.5rem;
}
/* Real, honest empty-state for Phase 0/1 -- the clip lane itself is Phase
   2's work, not faked here with placeholder boxes pretending to be real
   controls. */
#synth-screen .vocal-studio-screen .vocal-studio-lane-placeholder {
  background: var(--vs-panel);
  border: 1px dashed var(--vs-border);
  border-radius: 10px;
  padding: 3rem 1.5rem;
  text-align: center;
  color: var(--vs-ink-muted);
  font-size: 0.95rem;
}
#synth-screen .vocal-studio-screen .vocal-studio-lane-placeholder strong {
  display: block;
  font-family: var(--vs-font-display);
  font-size: 1.4rem;
  font-weight: 600;
  color: var(--vs-ink);
  margin-bottom: 0.5rem;
}

/* Vocal Studio Phase 2 -- the real clip lane. */
#synth-screen .vocal-studio-screen .vocal-studio-toolbar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}
/* Real tidy-up (founder-directed: "unify the whole table now so it looks
   neat like the row below") -- same flat single-panel look as
   vocal-studio-edit-toolbar: one shared raised bar behind the whole row,
   with every child (including the nudge-group, which drops its own nested
   box below) sitting flush and transparent on top of it. */
#synth-screen .vocal-studio-screen .vocal-studio-inline-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-border);
  border-radius: 8px;
  padding: 0.4rem 0.6rem;
  margin: 0.5rem 0;
}
#synth-screen .vocal-studio-screen .vocal-studio-inline-toolbar .vocal-studio-mode-toggle-btn,
#synth-screen .vocal-studio-screen .vocal-studio-inline-toolbar .vocal-studio-audition-rewind-btn {
  background: transparent;
}
#synth-screen .vocal-studio-screen .vocal-studio-inline-toolbar .vocal-studio-nudge-group {
  background: none;
  border: none;
  padding: 0;
}
#synth-screen .vocal-studio-screen .vocal-studio-upload-btn {
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-border);
  border-radius: 8px;
  padding: 0.55rem 1rem;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--vs-ink);
  cursor: pointer;
}
#synth-screen .vocal-studio-screen .vocal-studio-upload-btn:hover { border-color: var(--vs-accent); color: var(--vs-accent-hover); }
#synth-screen .vocal-studio-screen .vocal-studio-upload-btn.recording { background: var(--vs-curve); border-color: var(--vs-curve); color: #fff; }
#synth-screen .vocal-studio-screen .vocal-studio-audition-standalone-btn {
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-accent);
  border-radius: 8px;
  color: var(--vs-accent);
  font-size: 0.9rem;
  font-family: var(--vs-font-body);
  padding: 0.5rem 1rem;
  cursor: pointer;
}
#synth-screen .vocal-studio-screen .vocal-studio-audition-standalone-btn:hover { color: var(--vs-accent-hover); border-color: var(--vs-accent-hover); }
#synth-screen .vocal-studio-screen .vocal-studio-audition-rewind-btn {
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-border);
  border-radius: 8px;
  color: var(--vs-ink-muted);
  font-size: 0.85rem;
  font-family: var(--vs-font-body);
  padding: 0.45rem 0.7rem;
  cursor: pointer;
}
#synth-screen .vocal-studio-screen .vocal-studio-audition-rewind-btn:hover { color: var(--vs-ink); border-color: var(--vs-accent); }
#synth-screen .vocal-studio-screen .vocal-studio-nudge-group {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-border);
  border-radius: 8px;
  padding: 0.3rem 0.5rem;
}
#synth-screen .vocal-studio-screen .vocal-studio-nudge-label { font-size: 0.78rem; color: var(--vs-ink-muted); margin-right: 0.15rem; }
#synth-screen .vocal-studio-screen .vocal-studio-nudge-group button {
  background: transparent;
  border: 1px solid var(--vs-border);
  border-radius: 6px;
  color: var(--vs-ink);
  font-size: 0.85rem;
  padding: 0.28rem 0.5rem;
  cursor: pointer;
}
#synth-screen .vocal-studio-screen .vocal-studio-nudge-group button:hover { border-color: var(--vs-accent); color: var(--vs-accent-hover); }
/* Real fix (2026-09-26, founder-reported: "darken the tempo text and the
   pitch text iside the boxes") -- Song BPM/Source BPM/Pitch shift all
   share this one class, and unlike this page's OTHER text inputs (e.g.
   .vocal-studio-field's own Style/Tone fields, which already include a
   real element-type selector), this rule never did -- so its specificity
   (1 id, 2 classes, 0 type) exactly TIED the generic id-having
   "#synth-screen .community-block input[type='number']" rule (1 id, 2
   classes [.community-block + the [type] attribute], 1 type) at the first
   two tiers, then LOST outright on the third (0 vs 1) -- the exact same
   real specificity-war pattern this whole hardware pass has hit before,
   just with input[type="number"] specifically instead of button/select.
   Fixed with genuine higher specificity (the missing type + attribute
   selectors), not !important. */
/* Real fix (2026-09-26, founder-reported: "and the source bp[m] please" --
   same box, still too narrow to actually show its own digits) -- 56px was
   fine at this rule's old, smaller font-size/padding; both grew as part of
   this same hardware pass (see the sizing fix above) without this width
   growing to match, so a real box-sizing:border-box box left barely any
   room for text once its own padding + border + the native number-input
   spinner arrows were accounted for. Matched to 4.5rem -- the exact real
   width the Composer's own equivalent Source BPM input
   (#synth-vocals-source-bpm) already uses. */
#synth-screen .vocal-studio-screen input[type="number"].vocal-studio-bpm-input {
  width: 4.5rem;
  background: var(--vs-panel);
  border: 1px solid var(--vs-border);
  border-radius: 6px;
  color: var(--vs-ink);
  font-family: var(--vs-font-body);
  font-size: 0.9rem;
  padding: 0.3rem 0.4rem;
}
#synth-screen .vocal-studio-screen input[type="number"].vocal-studio-bpm-input:focus { outline: none; border-color: var(--vs-accent); }
#synth-screen .vocal-studio-screen .vocal-studio-remove-btn { color: var(--vs-curve); border-color: var(--vs-curve) !important; }
#synth-screen .vocal-studio-screen #vocal-studio-audition-btn.playing { background: var(--vs-accent); color: #1c1d1e; }
/* Real Wave Edit mode + editing toolbar (2026-09-07, founder-directed:
   "this doubling up as an audio editor... reverse, anti pop click,
   export... the drag will need to be disabled some how"). */
#synth-screen .vocal-studio-screen .vocal-studio-mode-toggle-btn {
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-border);
  border-radius: 8px;
  color: var(--vs-ink-muted);
  font-family: var(--vs-font-body);
  font-size: 0.85rem;
  padding: 0.45rem 0.65rem;
  cursor: pointer;
}
#synth-screen .vocal-studio-screen .vocal-studio-mode-toggle-btn:hover { border-color: var(--vs-accent); color: var(--vs-ink); }
#synth-screen .vocal-studio-screen .vocal-studio-mode-toggle-btn.active { background: var(--vs-accent); border-color: var(--vs-accent); color: #1c1d1e; }
#synth-screen .vocal-studio-screen .vocal-studio-snap-group {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-border);
  border-radius: 8px;
  padding: 0.4rem 0.6rem;
}
#synth-screen .vocal-studio-screen .vocal-studio-snap-group select {
  background: var(--vs-panel);
  border: 1px solid var(--vs-border);
  border-radius: 6px;
  color: var(--vs-ink);
  font-family: var(--vs-font-body);
  font-size: 0.85rem;
  padding: 0.35rem 0.5rem;
}
#synth-screen .vocal-studio-screen .vocal-studio-transient-group[hidden] { display: none; }
#synth-screen .vocal-studio-screen .vocal-studio-transient-group {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-border);
  border-radius: 8px;
  padding: 0.4rem 0.6rem;
}
#synth-screen .vocal-studio-screen .vocal-studio-transient-group input[type="range"] { accent-color: var(--vs-accent); width: 100px; }
#synth-screen .vocal-studio-screen .vocal-studio-edit-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-border);
  border-radius: 8px;
  padding: 0.4rem 0.6rem;
  margin: 0.5rem 0;
}
#synth-screen .vocal-studio-screen .vocal-studio-edit-toolbar button {
  background: transparent;
  border: 1px solid var(--vs-border);
  border-radius: 6px;
  color: var(--vs-ink);
  font-size: 0.85rem;
  padding: 0.3rem 0.6rem;
  cursor: pointer;
}
#synth-screen .vocal-studio-screen .vocal-studio-edit-toolbar button:hover { border-color: var(--vs-accent); color: var(--vs-accent-hover); }
#synth-screen #vocal-studio-edit-selection-label { font-weight: 600; color: var(--vs-ink); }
/* Real drag-selected sample range (Wave Edit mode) -- an overlay on top of
   the clip's own waveform canvas, same real coordinate space (px/sec), so
   it lines up exactly with the samples it actually covers. */
#synth-screen .vocal-studio-screen .vocal-studio-wave-selection {
  position: absolute;
  top: 0;
  bottom: 0;
  background: rgba(30, 154, 74, 0.22);
  border-left: 1px solid var(--vs-accent);
  border-right: 1px solid var(--vs-accent);
  pointer-events: none;
}
#synth-screen .vocal-studio-screen .vocal-studio-clip.wave-edit-mode { cursor: crosshair; }
/* Real transient tick marks (2026-09-07) -- shown only in Wave Edit +
   Transient mode, one thin line per detected onset, so the click-to-slice
   snap points are actually visible before you click. Real fix (2026-09-26,
   part of the hardware chassis pass): this was a white line, legible
   against the old dark lane background -- the lane is silver now (see
   .vocal-studio-screen's own updated tokens), so it's a dark line instead. */
.vocal-studio-transient-tick {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: rgba(28, 29, 30, 0.45);
  pointer-events: none;
}
/* Real right-click clip menu (2026-09-07, founder-directed: "copy paste
   possibly with ctrl or right click options"). */
.vocal-studio-clip-menu[hidden] { display: none; }
.vocal-studio-clip-menu {
  position: fixed;
  z-index: 500;
  display: flex;
  flex-direction: column;
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  padding: 0.3rem;
  min-width: 160px;
}
#synth-screen .vocal-studio-clip-menu button {
  background: transparent;
  border: none;
  border-radius: 6px;
  color: var(--vs-ink);
  font-family: var(--vs-font-body);
  font-size: 0.85rem;
  text-align: left;
  padding: 0.45rem 0.6rem;
  cursor: pointer;
}
#synth-screen .vocal-studio-clip-menu button:hover { background: var(--vs-panel); color: var(--vs-accent-hover); }
#synth-screen .vocal-studio-clip-menu button:disabled { opacity: 0.4; cursor: not-allowed; }
#synth-screen .vocal-studio-clip-menu button:disabled:hover { background: transparent; color: var(--vs-ink); }
#synth-screen .vocal-studio-screen .vocal-studio-zoom-group {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  margin-left: auto;
}
#synth-screen .vocal-studio-screen .vocal-studio-zoom-group button {
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-border);
  border-radius: 6px;
  color: var(--vs-ink);
  font-size: 1rem;
  width: 32px;
  height: 32px;
  cursor: pointer;
}
#synth-screen .vocal-studio-screen .vocal-studio-zoom-group button:hover { border-color: var(--vs-accent); color: var(--vs-accent-hover); }
#synth-screen .vocal-studio-screen .vocal-studio-lane-wrap {
  background: var(--vs-panel);
  border: 1px solid var(--vs-border);
  border-radius: 10px;
  overflow-x: auto;
  overflow-y: hidden;
}
#synth-screen .vocal-studio-screen .vocal-studio-lane {
  position: relative;
  height: 120px;
  min-width: 100%;
  background-image: linear-gradient(90deg, var(--vs-border) 1px, transparent 1px);
  /* background-size set inline per render, matching the real current zoom (synthVocalStudioPxPerSecond) -- one gridline per second */
}
/* Real Song step markers (2026-09-07, founder-directed: "the step bank
   should be shown here along the timeline so the user knows which block
   they are on"). Sits directly above the clip lane, inside the SAME
   scrollable wrap, so it tracks the lane's own horizontal scroll for free. */
#synth-screen .vocal-studio-screen .vocal-studio-song-steps {
  position: relative;
  height: 22px;
  min-width: 100%;
  border-bottom: 1px solid var(--vs-border);
}
#synth-screen .vocal-studio-screen .vocal-studio-song-step {
  position: absolute;
  top: 0;
  bottom: 0;
  border-right: 1px solid var(--vs-border);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.65rem;
  font-weight: 700;
  color: var(--vs-ink-muted);
  background: var(--vs-panel-raised);
  cursor: default;
  overflow: hidden;
}
#synth-screen .vocal-studio-screen .vocal-studio-song-step.active { background: var(--vs-accent); color: #1c1d1e; }
#synth-screen .vocal-studio-screen .vocal-studio-playhead {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--vs-curve);
  box-shadow: 0 0 6px var(--vs-curve);
  pointer-events: none;
  z-index: 3;
}
/* Real scrub playhead (2026-09-07) -- a persistent "current position"
   marker, visually distinct (dimmer, no glow) from the same element while
   actively auditioning (the bright, full version above), so it's obvious
   at a glance which state you're looking at. */
#synth-screen .vocal-studio-screen .vocal-studio-playhead.scrub {
  background: var(--vs-ink-muted);
  box-shadow: none;
  opacity: 0.6;
}
#synth-screen .vocal-studio-screen .vocal-studio-lane-empty {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--vs-ink-muted);
  font-size: 0.9rem;
}
#synth-screen .vocal-studio-screen .vocal-studio-clip {
  position: absolute;
  top: 12px;
  bottom: 12px;
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-border);
  border-radius: 6px;
  cursor: grab;
  overflow: hidden;
}
#synth-screen .vocal-studio-screen .vocal-studio-clip:active { cursor: grabbing; }
#synth-screen .vocal-studio-screen .vocal-studio-clip.selected { border-color: var(--vs-accent); box-shadow: 0 0 0 1px var(--vs-accent); }
#synth-screen .vocal-studio-screen .vocal-studio-clip canvas { display: block; width: 100%; height: 100%; pointer-events: none; }
#synth-screen .vocal-studio-screen .vocal-studio-trim-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 7px;
  cursor: ew-resize;
  background: transparent;
  z-index: 2;
}
#synth-screen .vocal-studio-screen .vocal-studio-trim-handle.left { left: 0; }
#synth-screen .vocal-studio-screen .vocal-studio-trim-handle.right { right: 0; }
#synth-screen .vocal-studio-screen .vocal-studio-clip:hover .vocal-studio-trim-handle,
#synth-screen .vocal-studio-screen .vocal-studio-clip.selected .vocal-studio-trim-handle {
  background: var(--vs-accent-dim);
}
#synth-screen .vocal-studio-screen .vocal-studio-trim-handle:hover { background: var(--vs-accent) !important; }
#synth-screen .vocal-studio-screen .vocal-studio-status { margin-top: 0.75rem; font-size: 0.85rem; color: var(--vs-ink-muted); }

/* Vocal Studio Phase 3 -- the Generate panel. */
#synth-screen .vocal-studio-screen .vocal-studio-generate-panel {
  background: var(--vs-panel);
  border: 1px solid var(--vs-border);
  border-radius: 10px;
  padding: 1rem;
  margin-bottom: 1rem;
}
#synth-screen .vocal-studio-screen .vocal-studio-generate-row {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
  align-items: flex-end;
  margin-bottom: 0.75rem;
}
#synth-screen .vocal-studio-screen .vocal-studio-generate-row:last-child { margin-bottom: 0; }
#synth-screen .vocal-studio-screen .vocal-studio-field {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  font-size: 0.8rem;
  color: var(--vs-ink-muted);
  flex: 1 1 160px;
}
#synth-screen .vocal-studio-screen .vocal-studio-field-wide { flex: 2 1 260px; }
#synth-screen .vocal-studio-screen .vocal-studio-field input,
#synth-screen .vocal-studio-screen .vocal-studio-field textarea,
#synth-screen .vocal-studio-screen .vocal-studio-field select {
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-border);
  border-radius: 6px;
  color: var(--vs-ink);
  font-family: var(--vs-font-body);
  font-size: 0.9rem;
  padding: 0.45rem 0.6rem;
  resize: vertical;
}
#synth-screen .vocal-studio-screen .vocal-studio-field input:focus,
#synth-screen .vocal-studio-screen .vocal-studio-field textarea:focus,
#synth-screen .vocal-studio-screen .vocal-studio-field select:focus { outline: none; border-color: var(--vs-accent); }
#synth-screen .vocal-studio-screen .vocal-studio-generate-btn {
  background: var(--vs-accent);
  border: none;
  border-radius: 6px;
  color: #1c1d1e;
  font-weight: 700;
  font-size: 0.9rem;
  padding: 0.55rem 1.2rem;
  cursor: pointer;
  height: fit-content;
}
#synth-screen .vocal-studio-screen .vocal-studio-generate-btn:hover { background: var(--vs-accent-hover); }
#synth-screen .vocal-studio-screen .vocal-studio-generate-btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* Vocal Studio Phase 4 -- real time-based automation. */
#synth-screen .vocal-studio-screen .vocal-studio-automation-toolbar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1rem 0 0.5rem;
  flex-wrap: wrap;
}
#synth-screen .vocal-studio-screen .vocal-studio-automation-label { font-size: 0.85rem; color: var(--vs-ink-muted); }
/* Real per-lane legend (2026-09-07, founder-caught: "colour and
   persistence of the automation as in the other units") -- same
   coloured-dot chip convention as the shared Voice A-D/drum automation
   legend (#synth-screen .synth-automation-legend-chip), reimplemented
   against Vocal Studio's own design tokens instead of that legend's
   fixed dark palette. */
#synth-screen .vocal-studio-screen .vocal-studio-automation-legend { display: flex; flex-wrap: wrap; gap: 0.35rem; }
#synth-screen .vocal-studio-screen .vocal-studio-automation-legend-chip {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-border);
  border-radius: 20px;
  color: var(--vs-ink-muted);
  font-family: var(--vs-font-body);
  font-size: 0.75rem;
  padding: 0.25rem 0.6rem 0.25rem 0.4rem;
  cursor: pointer;
}
#synth-screen .vocal-studio-screen .vocal-studio-automation-legend-chip::before {
  content: "";
  display: inline-block;
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: var(--lane-color, var(--vs-accent));
  flex-shrink: 0;
}
#synth-screen .vocal-studio-screen .vocal-studio-automation-legend-chip.active {
  color: var(--vs-ink);
  border-color: var(--lane-color, var(--vs-accent));
  background: var(--vs-panel);
}
#synth-screen .vocal-studio-screen .vocal-studio-automation-toolbar select {
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-border);
  border-radius: 6px;
  color: var(--vs-ink);
  font-family: var(--vs-font-body);
  font-size: 0.85rem;
  padding: 0.35rem 0.5rem;
}
#synth-screen .vocal-studio-screen .vocal-studio-automation-toolbar button {
  background: transparent;
  border: 1px solid var(--vs-border);
  border-radius: 6px;
  color: var(--vs-ink-muted);
  font-size: 0.8rem;
  padding: 0.35rem 0.6rem;
  cursor: pointer;
}
#synth-screen .vocal-studio-screen .vocal-studio-automation-toolbar button:hover { border-color: var(--vs-curve); color: var(--vs-curve); }
#synth-screen .vocal-studio-screen .vocal-studio-automation-hint { font-size: 0.75rem; }
#synth-screen .vocal-studio-screen .vocal-studio-automation-wrap {
  background: var(--vs-panel);
  border: 1px solid var(--vs-border);
  border-radius: 10px;
  overflow-x: auto;
  overflow-y: hidden;
}
#synth-screen .vocal-studio-screen .vocal-studio-automation-svg {
  display: block;
  height: 80px;
  cursor: crosshair;
}

/* Vocal Studio Phase 4 -- the shared Vocals FX chain panel. */
#synth-screen .vocal-studio-screen .vocal-studio-fx-panel { margin-top: 1.5rem; }
#synth-screen .vocal-studio-screen .vocal-studio-fx-panel h2 {
  font-family: var(--vs-font-display);
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--vs-ink);
  margin: 0 0 0.75rem;
}
#synth-screen .vocal-studio-screen .vocal-studio-fx-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 0.75rem;
}
#synth-screen .vocal-studio-screen .vocal-studio-fx-card {
  background: var(--vs-panel);
  border: 1px solid var(--vs-border);
  border-radius: 10px;
  padding: 0.85rem;
}
#synth-screen .vocal-studio-screen .vocal-studio-fx-card h3 {
  font-size: 0.85rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--vs-ink);
  margin: 0 0 0.6rem;
}
#synth-screen .vocal-studio-screen .vocal-studio-fx-card label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  font-size: 0.8rem;
  color: var(--vs-ink-muted);
  margin-bottom: 0.5rem;
}
#synth-screen .vocal-studio-screen .vocal-studio-fx-card label:last-child { margin-bottom: 0; }
#synth-screen .vocal-studio-screen .vocal-studio-fx-card input[type="range"] { flex: 1; accent-color: var(--vs-accent); }
#synth-screen .vocal-studio-screen .vocal-studio-fx-card input[type="checkbox"] { accent-color: var(--vs-accent); }
#synth-screen .vocal-studio-screen .vocal-studio-fx-card select {
  background: var(--vs-panel-raised);
  border: 1px solid var(--vs-border);
  border-radius: 6px;
  color: var(--vs-ink);
  font-family: var(--vs-font-body);
  font-size: 0.8rem;
  padding: 0.2rem 0.4rem;
}

/* Real editor template picker (2026-09-11) -- a real two-column split on
   the VST Builder intro page (project list left, template picker right),
   reusing .synth-compose-split as-is. Auto-height here (no fixed height
   like the detail page's own .vst-builder-split) -- this page's content
   is naturally short and doesn't need a bounded/scrolling shell. */
.vst-builder-intro-split { margin-top: 0.75rem; align-items: stretch; }
.vst-builder-intro-split .vst-builder-col-left { flex: 1 1 50%; }
.vst-builder-intro-split .vst-builder-template-picker { flex: 1 1 50%; }
/* Real fix (2026-09-10, found live): was VST-Builder-scoped, but the
   founder's own next ask (a GitHub-scopes tooltip in Settings) needed the
   exact same real info-icon pattern outside VST Builder -- generalized the
   class rather than duplicating the rule, .vst-builder-info-tip kept as an
   alias so the one existing usage doesn't need touching. */
.info-tip, .vst-builder-info-tip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.1rem;
  height: 1.1rem;
  margin-left: 0.3rem;
  border-radius: 50%;
  border: 1px solid var(--border, #ccc);
  color: var(--text-muted, #888);
  font-size: 0.75rem;
  line-height: 1;
  cursor: help;
}
.info-tip:hover, .info-tip:focus-visible, .vst-builder-info-tip:hover, .vst-builder-info-tip:focus-visible { color: var(--accent, #2f6b4f); border-color: var(--accent, #2f6b4f); }
.vst-builder-source-download, .vst-builder-build-download { display: inline-block; margin-left: 0.6rem; font-size: 0.85rem; text-decoration: underline; }
.vst-builder-build-download { color: var(--accent, #2f6b4f); font-weight: 600; }
.vst-builder-sharing-row { display: flex; flex-wrap: wrap; gap: 0.9rem; margin-top: 0.5rem; font-size: 0.85rem; }
.vst-builder-sharing-toggle { display: inline-flex; align-items: center; gap: 0.35rem; cursor: pointer; }
.vst-builder-continue-build-row { margin-top: 0.5rem; }
.vst-builder-continue-build-row p { margin: 0 0 0.4rem; }
#vst-builder-github-status-line { display: flex; align-items: center; gap: 0.4rem; }
#vst-builder-github-status-line a { text-decoration: underline; cursor: pointer; }
#vst-builder-github-status-line.vst-builder-github-connected { color: var(--text-muted, #888); }
#vst-builder-github-status-line.vst-builder-github-missing { color: #c0392b; font-weight: 600; }

/* Real fix (2026-09-11, founder: "make the template bigger and also
   include some of the colours or a standard ask archie green etc like my
   303sub vst add a little bit class") -- the whole picker panel now gets
   the same real dark hardware-rack treatment the chat column already uses
   elsewhere in VST Builder (same exact gradient, not a new one), with real
   Ask Archie green (--accent, #2f6b4f -- the actual brand token, not a
   generic fallback purple) driving selection/hover, and bigger cards with
   real shape icons instead of the previous cramped small versions. */
.vst-builder-template-picker {
  background: linear-gradient(180deg, #2b2f2b 0%, #1c1f1c 100%);
  border: 1px solid #3d423d;
  border-radius: var(--radius-md);
  color: #fff;
}
.vst-builder-template-picker h3 { color: #fff; }
.vst-builder-template-picker .muted { color: #cfd6cf; }
.vst-builder-template-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 0.9rem; margin: 1rem 0; }
.vst-builder-template-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  padding: 1.25rem 0.75rem;
  border: 1px solid #3d423d;
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.03);
  cursor: pointer;
  color: #fff;
  font-size: 1rem;
  transition: border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
}
.vst-builder-template-card.active {
  border-color: var(--accent, #2f6b4f);
  background: rgba(47, 107, 79, 0.22);
  box-shadow: 0 0 0 1px var(--accent, #2f6b4f), 0 4px 16px rgba(47, 107, 79, 0.35);
}
.vst-builder-template-card:hover { border-color: var(--accent, #2f6b4f); }
.vst-builder-template-card strong { font-size: 1.05rem; }
/* Plain CSS-drawn shapes, not icon art -- just enough silhouette to tell
   the 4 layouts apart at a glance, sized up to actually read as an icon. */
.vst-builder-template-shape { display: flex; align-items: flex-end; justify-content: center; gap: 5px; width: 100%; height: 64px; }
.vst-builder-shape-grid { flex-wrap: wrap; align-items: center; align-content: center; gap: 6px; }
.vst-builder-shape-grid i { width: 15px; height: 15px; border-radius: 50%; border: 2px solid var(--accent, #2f6b4f); }
.vst-builder-shape-rack i { width: 32px; height: 50px; border: 2px solid var(--accent, #2f6b4f); border-radius: 5px; }
.vst-builder-shape-faders i { width: 11px; border-radius: 4px; background: var(--accent, #2f6b4f); }
.vst-builder-shape-faders i:nth-child(1) { height: 28px; }
.vst-builder-shape-faders i:nth-child(2) { height: 50px; }
.vst-builder-shape-faders i:nth-child(3) { height: 20px; }
.vst-builder-shape-faders i:nth-child(4) { height: 58px; }
.vst-builder-shape-faders i:nth-child(5) { height: 38px; }
.vst-builder-shape-sectioned { flex-direction: column; align-items: stretch; justify-content: center; gap: 5px; }
.vst-builder-shape-sectioned i { height: 16px; border: 2px solid var(--accent, #2f6b4f); border-radius: 4px; }
#vst-builder-template-explanation { font-size: 0.95rem; line-height: 1.5; }
.community-hub-media-section h5 { margin: 0 0 0.4rem; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted-text, #888); }

/* 2026-09-21 -- VST Builder: build method (GitHub / this PC) and the Local Build Environment panel. */
#vst-builder-new-panel[data-build-method="local"] .vst-github-only { display: none; }
.vst-build-method { border: 1px solid var(--border, #444); border-radius: var(--radius-md, 8px); padding: 0.5rem 0.75rem 0.6rem; margin: 0 0 0.75rem; }
.vst-build-method legend { font-size: 0.8rem; padding: 0 0.35rem; color: var(--muted, #8a8f98); }
.vst-build-method-option { display: flex; gap: 0.55rem; align-items: flex-start; padding: 0.3rem 0; cursor: pointer; }
.vst-build-method-option input { margin-top: 0.25rem; }
.vst-build-method-option strong { display: block; }
.vst-build-method-option small { display: block; color: var(--muted, #8a8f98); font-size: 0.78rem; }
.vst-local-env { border: 1px solid var(--border, #444); border-radius: var(--radius-md, 8px); padding: 0.6rem 0.75rem; margin: 0 0 0.85rem; }
.vst-local-env-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.35rem; }
.vst-local-env-rows { list-style: none; margin: 0; padding: 0; }
.vst-local-env-rows li { display: flex; gap: 0.6rem; justify-content: space-between; padding: 0.18rem 0; border-bottom: 1px dashed rgba(127, 127, 127, 0.25); }
.vst-local-env-rows li:last-child { border-bottom: 0; }
.vst-local-env-label { font-weight: 600; }
.vst-local-env-value { text-align: right; }
.vst-local-env-row-ok .vst-local-env-value { color: #4cd98a; }
.vst-local-env-row-warn .vst-local-env-value { color: #ffb020; }
.vst-local-env-row-missing .vst-local-env-value, .vst-local-env-row-error .vst-local-env-value { color: #ff7a7a; }
.vst-local-env-row-checking .vst-local-env-value { color: var(--muted, #8a8f98); }
.vst-local-env-detail { display: block; font-size: 0.92rem; line-height: 1.4; color: var(--muted, #8a8f98); font-weight: 400; word-break: break-word; }
.vst-local-env-headline { font-weight: 700; margin: 0.5rem 0 0.15rem; }
.vst-local-env-headline.ready { color: #4cd98a; }
.vst-local-env-headline.notready { color: #ff7a7a; }
.vst-local-env-diskspace { font-size: 0.85rem; margin: 0 0 0.6rem; }
.vst-local-env-notes { margin: 0.2rem 0 0 1.1rem; padding: 0; font-size: 0.8rem; }
.vst-local-env-help { font-size: 1rem; line-height: 1.45; margin-top: 0.4rem; }
.vst-local-env-help p { margin: 0.25rem 0; }
.vst-builder-method-row { font-size: 0.8rem; margin: 0.25rem 0; }
.vst-local-env-row-missing .vst-local-env-detail { color: var(--muted, #8a8f98); margin-top: 0.15rem; }
.vst-local-env-get, .vst-local-env-get:visited { display: inline-block; margin-top: 0.3rem; padding: 0.2rem 0.6rem; border: 1px solid currentColor; border-radius: var(--radius-sm, 6px); font-size: 0.78rem; font-weight: 600; text-decoration: none; color: #4cd98a; }
.vst-local-env-get:hover { background: rgba(76, 217, 138, 0.12); }
.vst-local-build-control { margin: 0.4rem 0; }
.vst-local-build-start { padding: 0.3rem 0.75rem; border-radius: var(--radius-sm, 6px); border: 1px solid #4cd98a; background: transparent; color: #4cd98a; font-weight: 600; font-size: 0.85rem; cursor: pointer; }
.vst-local-build-start:hover { background: rgba(76, 217, 138, 0.12); }
.vst-local-build-start:disabled { opacity: 0.6; cursor: default; }
.vst-local-build-start.hidden { display: none; }

/* Persistent build-lifecycle line (2026-09-22) -- "Built on this PC" / "Changes since last build" / "Last
   build failed", read from what the server itself remembers, so it survives a refresh instead of resetting
   to a first-time "Build on this PC" the moment the row is redrawn. */
.vst-local-build-status { font-size: 0.85rem; font-weight: 600; margin-bottom: 0.25rem; }
.vst-local-build-status.hidden { display: none; }
.vst-local-build-status-ok { color: #4cd98a; }

/* Made deliberately prominent (2026-09-22, founder: "too small/subtle... a new user should immediately
   understand: My VST has finished successfully") -- reuses .vst-module-built-badge's own established green
   success palette, scaled up into a real banner instead of an easily-missed metadata line. */
.vst-local-build-complete {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 0.55rem 0.75rem;
  border-radius: var(--radius-md, 8px);
  background: color-mix(in srgb, #4cd98a 16%, var(--vstb-panel, #1c1f1c));
  border: 1px solid color-mix(in srgb, #4cd98a 45%, transparent);
}
.vst-local-build-complete-heading { font-size: 1rem; font-weight: 800; letter-spacing: 0.01em; color: #6be3a3; }
.vst-local-build-complete-sub { font-size: 0.78rem; font-weight: 600; color: color-mix(in srgb, #6be3a3 70%, var(--muted, #8a8f98)); }

/* "Add to Vault" / "New build available -> Update Vault version" / "In your Vault" (2026-09-22) --
   deliberately separate from the build-lifecycle control above: building/regenerating never touches this
   on its own, only the button here does (Founder: "building != publishing, regenerating != republishing"). */
.vst-vault-control { margin: 0.4rem 0; }
.vst-vault-status { font-size: 0.85rem; font-weight: 600; margin-bottom: 0.25rem; }
.vst-vault-status.hidden { display: none; }
.vst-vault-status-ok { color: #4cd98a; }
.vst-vault-publish-btn { padding: 0.3rem 0.75rem; border-radius: var(--radius-sm, 6px); border: 1px solid #4cd98a; background: transparent; color: #4cd98a; font-weight: 600; font-size: 0.85rem; cursor: pointer; }
.vst-vault-publish-btn:hover { background: rgba(76, 217, 138, 0.12); }
.vst-vault-publish-btn:disabled { opacity: 0.6; cursor: default; }
.vst-vault-publish-btn[hidden] { display: none; }
.vst-vault-external-btn { display: inline-block; margin-top: 0.25rem; font-size: 0.82rem; }
.vst-vault-external-btn[hidden] { display: none; }
.vst-vault-external-status { display: block; font-size: 0.78rem; margin-top: 0.15rem; }

/* While this row's build/regenerate is running, dim the controls that would otherwise let someone navigate
   away or switch build methods mid-compile -- same idea as the GitHub build card's own dimming. */
.vst-builder-project-row.vst-local-building .vst-builder-project-title-link,
.vst-builder-project-row.vst-local-building .vst-builder-method-row { opacity: 0.5; pointer-events: none; }

/* A real in-progress card while the build runs -- matching the GitHub build's own "dimmed and notified" idea,
   but with a spinner + heading + description since a local compile happens on this very machine. */
.vst-local-build-progress { display: flex; align-items: center; gap: 0.75rem; margin: 0.4rem 0; padding: 0.75rem; border: 1px solid var(--border, #444); border-radius: var(--radius-md, 8px); background: rgba(255, 255, 255, 0.03); }
.vst-local-build-progress.hidden { display: none; }
.vst-local-build-spinner { flex: none; width: 1.6rem; height: 1.6rem; border-radius: 50%; border: 3px solid rgba(76, 217, 138, 0.25); border-top-color: #4cd98a; animation: vst-local-build-spin 0.8s linear infinite; }
@keyframes vst-local-build-spin { to { transform: rotate(360deg); } }
.vst-local-build-progress-heading { font-weight: 700; font-size: 1rem; }
.vst-local-build-progress-detail { font-size: 0.9rem; margin-top: 0.2rem; line-height: 1.4; }

/* The finished result -- kept compact deliberately (founder: "do not display the full filesystem path as
   uncontrolled text across the narrow card") -- only a failure note shows inline; everything else (per-stage
   log, the real path) lives collapsed inside "Build details". */
.vst-local-build-result { margin: 0.4rem 0; font-size: 0.9rem; line-height: 1.5; }
.vst-local-build-result.hidden { display: none; }
.vst-local-build-details summary { cursor: pointer; font-size: 0.85rem; color: var(--muted, #8a8f98); }
.vst-local-build-stage-list { list-style: none; margin: 0.3rem 0 0.4rem; padding: 0; }
.vst-local-build-stage-list li { padding: 0.1rem 0; }
.vst-local-build-artifact-path { font-family: ui-monospace, "Cascadia Code", Consolas, monospace; font-size: 0.88rem; word-break: break-all; overflow-wrap: anywhere; white-space: normal; color: var(--muted, #8a8f98); margin: 0.15rem 0 0.5rem; }
.vst-local-build-error { color: #ff7a7a; word-break: break-word; }

.vst-local-build-reveal { margin-top: 0.3rem; }
.vst-local-build-reveal[hidden] { display: none; }

/* ==========================================================================
   ASK ARCHIE STUDIO -- DRUM MACHINE HARDWARE REDESIGN (2026-09-26,
   Founder-directed, design-reference-only mockup). Same real silver
   chassis language as Composer/Mixer/Voice A-D. Every selector below is
   scoped to the shared ".synth-drum-hw-chassis" class (carried by both
   Drum 808 and Drum 909's own wrapper -- one real implementation, not two
   copies) or an id unique to one instrument -- never the bare
   ".synth-drum-row"/".synth-drum-step"/".synth-drum-mixer"/
   ".synth-unit-fx-group" classes, which stay untouched for any future
   non-hardware use elsewhere. No control id/behaviour changes anywhere in
   this block -- geometry and colour only. */

/* Band 1: Pattern/Kit (narrow) beside Instruments & Steps (wide). */
#synth-screen .synth-drum-hw-chassis .synth-hw-drum-pattern-kit { flex: 2.6 1 230px; min-width: 210px; }
#synth-screen .synth-drum-hw-chassis .synth-hw-drum-grid-group { flex: 9 1 700px; min-width: 0; }
/* ASK ARCHIE STUDIO -- DRUM 808 LEFT CONTROL RAIL / HEIGHT RECOVERY PASS
   (2026-09-26). Real, measured finding: once Distortion/PCF/Delay/
   Compression/Output moved into this rail, its own real content (12
   knobs + pattern/kit) needs genuinely more height (1301px) than the 11
   drum rows + toolbar do (784px) -- the shared ".synth-hw-row" row's own
   base "align-items: stretch" (used by Voice A-D, unaffected here) would
   then stretch the shorter Instruments & Steps panel to match, recreating
   the exact same "large empty panel" problem this pass exists to remove,
   just on the opposite side. The Founder's own instruction ("terminate at
   approximately the same line IF PRACTICAL... rather than artificially
   stretching") allows this: each column keeps its own real natural
   height instead. */
#synth-screen .synth-drum-hw-chassis .synth-hw-row-top { align-items: flex-start; }

/* Pattern/Kit panel internals -- same real pattern-bank/kit-picker/export-
   import controls, just restyled to sit on the silver panel. */
#synth-screen .synth-drum-hw-chassis .synth-drum-kit-bank-picker { margin-top: 0.5rem; }
#synth-screen .synth-drum-hw-chassis .synth-drum-kit-bank-picker select {
  width: 100%;
  font-size: 0.8rem;
  padding: 0.25rem 0.4rem;
  border-radius: 4px;
  border: 1px solid #83868a;
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  color: #1c1d1e;
}
#synth-screen .synth-drum-hw-chassis .synth-drum-kit-io {
  flex-direction: column;
  align-items: stretch;
  gap: 0.4rem;
  margin-top: 0.5rem;
}
#synth-screen .synth-drum-hw-chassis .synth-drum-kit-io button {
  font-size: 0.8rem;
  padding: 0.35rem 0.6rem;
  border-radius: 5px;
  border: 1px solid #101112;
  background: linear-gradient(180deg, #45484b, #26282a);
  color: #e3e5e7;
  cursor: pointer;
}
#synth-screen .synth-drum-hw-chassis .synth-drum-kit-io button:hover { border-color: #7fd992; }
#synth-screen .synth-drum-hw-chassis .synth-drum-kit-io .muted { font-size: 0.72rem; color: #45484b; }

/* Instruments & Steps: one real row per voice, identical column geometry
   every time -- label/knobs/sample buttons are all fixed-width regardless
   of their own current value or loaded-sample state (the numeric-readout
   standard from the Voice A-D pass, reused here via the same shared
   vstBuilderCreateHardwareKnobItem/data-readout-size mechanism -- not a
   second implementation), so nothing in one row can ever push a
   neighbouring row's step grid out of column alignment with the rest. */
#synth-screen .synth-drum-hw-chassis .synth-drum-grid-scroll { overflow-x: auto; }
#synth-screen .synth-drum-hw-chassis .synth-drum-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0;
  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}
#synth-screen .synth-drum-hw-chassis .synth-drum-row:first-child { margin-top: 0; }
#synth-screen .synth-drum-hw-chassis .synth-drum-row:last-child { border-bottom: none; }
#synth-screen .synth-drum-hw-chassis .synth-drum-row-label {
  width: 2.8rem;
  flex: 0 0 2.8rem;
  font-size: 0.78rem;
  font-weight: 700;
  color: #1c1d1e;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
#synth-screen .synth-drum-hw-chassis .synth-drum-mixer { flex: 0 0 auto; gap: 0.35rem; align-items: flex-end; }
/* Per-row Vol/Pan/Dec/Pitch -- a real compact slider (kept near its
   original height, not a full-height ArchieKnob -- see the real measured
   reason in synthDrumMountRowKnob's own comment), given the exact same
   fixed-width readout box the knobs use elsewhere, so a value's own width
   still can never move a neighbouring row's step grid out of alignment. */
#synth-screen .synth-drum-hw-chassis .synth-drum-hw-control {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  width: 3.4rem;
}
#synth-screen .synth-drum-hw-chassis .synth-drum-hw-control-label {
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #1c1d1e;
}
#synth-screen .synth-drum-hw-chassis .synth-drum-hw-control input[type="range"] {
  width: 3.2rem;
  height: 0.9rem;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
}
#synth-screen .synth-drum-hw-chassis .synth-drum-hw-control input[type="range"]::-webkit-slider-runnable-track {
  height: 0.3rem;
  border-radius: 999px;
  background: linear-gradient(90deg, #1e9a4a, #83868a);
}
#synth-screen .synth-drum-hw-chassis .synth-drum-hw-control input[type="range"]::-moz-range-track {
  height: 0.3rem;
  border-radius: 999px;
  background: linear-gradient(90deg, #1e9a4a, #83868a);
}
#synth-screen .synth-drum-hw-chassis .synth-drum-hw-control input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 0.75rem;
  height: 0.75rem;
  margin-top: -0.24rem;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #f4f5f6, #9a9da0 60%, #45484b);
  border: 1px solid #101112;
  cursor: pointer;
}
#synth-screen .synth-drum-hw-chassis .synth-drum-hw-control input[type="range"]::-moz-range-thumb {
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #f4f5f6, #9a9da0 60%, #45484b);
  border: 1px solid #101112;
  cursor: pointer;
}
/* Real fix, found by inspecting the actual live computed style rather than
   assuming (the same recurring specificity bug class this session already
   found and fixed for the ArchieKnob readouts themselves): the generic
   "#synth-screen .synth-hw-instrument span:not(.muted) { color: #1c1d1e }"
   rule (specificity 1-2-1, its type selector "span" outweighs this rule's
   plain class list at 1-2-0) was silently winning over the #7fe3a3 colour
   below, rendering the LCD readout in near-invisible dark-on-dark. Fixed
   with genuinely higher specificity (an extra real ancestor class,
   1-3-1) rather than !important. */
#synth-screen .synth-drum-hw-chassis .synth-drum-hw-control span.synth-drum-hw-readout {
  /* Same real dark-LCD colours as the ArchieKnob readouts elsewhere in the
     Synth (#synth-screen .synth-hw-instrument .acKnobItem .acKnobValue) --
     duplicated here, not reused directly, because that rule requires an
     .acKnobItem ancestor this slider-based control genuinely doesn't have. */
  background: #14171a;
  color: #7fe3a3;
  border: 1px solid #05060a;
  border-radius: 3px;
  display: inline-block;
  box-sizing: border-box;
  font-size: 0.68rem;
  height: 1.05rem;
  line-height: 0.85rem;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  font-variant-numeric: tabular-nums;
  padding: 0.08rem 0.3rem;
}
#synth-screen .synth-drum-hw-chassis .synth-drum-hw-control span.synth-drum-hw-readout[data-readout-size="compact"] { min-width: 2.3rem; }
#synth-screen .synth-drum-hw-chassis .synth-drum-hw-control span.synth-drum-hw-readout[data-readout-size="wide"] { min-width: 3.2rem; }
#synth-screen .synth-drum-hw-chassis .synth-drum-sample-controls { flex: 0 0 auto; gap: 0.3rem; margin-right: 0.5rem; }
#synth-screen .synth-drum-hw-chassis .synth-drum-sample-controls .sample-btn,
#synth-screen .synth-drum-hw-chassis .synth-drum-sample-controls .sample-reset-btn {
  font-size: 0.72rem;
  padding: 0.3rem 0.5rem;
  border-radius: 5px;
  border: 1px solid #101112;
  background: linear-gradient(180deg, #45484b, #26282a);
  color: #e3e5e7;
  cursor: pointer;
  white-space: nowrap;
}
#synth-screen .synth-drum-hw-chassis .synth-drum-sample-controls .sample-btn.sample-loaded {
  background: linear-gradient(180deg, #2f4a34, #1c2e20);
  border-color: #7fd992;
  color: #7fd992;
}

/* 16-step grid -- the strongest visual feature, per the Founder's own
   brief. Cream/off-white inactive steps (with the existing every-4th-step
   beat marker kept, just recoloured for the light panel), green illuminated
   active state matching Voice A's own step-on colour exactly, orange accent
   ring, red current-step outline -- all colours already proven legible on
   this exact silver/cream language elsewhere in the Synth. */
#synth-screen .synth-drum-hw-chassis .synth-drum-step {
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 4px;
  border: 1px solid #83868a;
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  color: #1c1d1e;
  font-size: 0.72rem;
  font-weight: 700;
}
#synth-screen .synth-drum-hw-chassis .synth-drum-row > button.synth-drum-step:nth-of-type(4n+1):not(.active) {
  background: linear-gradient(180deg, #e3e6e8, #c4c7ca);
}
#synth-screen .synth-drum-hw-chassis .synth-drum-step.active {
  background: linear-gradient(180deg, #4fe38a, #1e9a4a);
  border-color: #0a4a20;
  color: #06210f;
  box-shadow: 0 0 6px rgba(79, 227, 138, 0.6);
}
#synth-screen .synth-drum-hw-chassis .synth-drum-step.accent { box-shadow: 0 0 0 2px #ffb020 inset; }
#synth-screen .synth-drum-hw-chassis .synth-drum-step.accent.active { box-shadow: 0 0 0 2px #ffb020 inset, 0 0 6px rgba(79, 227, 138, 0.6); }
#synth-screen .synth-drum-hw-chassis .synth-drum-step.synth-seq-step-current { border-color: #c14535; box-shadow: 0 0 0 2px #c14535; outline-offset: 1px; }
#synth-screen .synth-drum-hw-chassis .synth-drum-step.accent.synth-seq-step-current { box-shadow: 0 0 0 2px #c14535, 0 0 0 4px #ffb020; }

/* Pattern Tools toolbar -- reuses the exact same .synth-pattern-tools/
   .synth-pattern-tool-btn markup and Shift/Copy/Paste/Randomize/Alter
   handlers Voice A's own toolbar strip uses; Paste's disabled state is the
   same real button.disabled, kept visually readable (not just greyed to
   invisibility) via a real disabled-specific rule. */
#synth-screen .synth-drum-hw-chassis .synth-pattern-tool-btn {
  font-size: 0.78rem;
  padding: 0.3rem 0.6rem;
  border-radius: 5px;
  border: 1px solid #83868a;
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  color: #1c1d1e;
  cursor: pointer;
}
#synth-screen .synth-drum-hw-chassis .synth-pattern-tool-btn:disabled {
  background: #b6b8bb;
  color: #6f7275;
  cursor: not-allowed;
}

/* Effects & Processing -- one real hardware band, five real groups
   (Distortion/PCF/Delay/Compression/Output) laid out horizontally instead
   of as separate web cards. Numeric sliders became real ArchieKnobs
   (utility/secondary size, same fixed-width readout standard); Shape/Type/
   Steps stay real selects, Synced/Enabled stay real checkboxes -- restyled
   for legibility only, same bound state/listeners. */
#synth-screen .synth-drum-hw-chassis .synth-unit-fx { display: flex; flex-wrap: wrap; gap: 0.7rem; align-items: flex-start; }
#synth-screen .synth-drum-hw-chassis .synth-unit-fx-group {
  background: linear-gradient(180deg, #d7d9db, #c2c4c7);
  border: 1px solid #83868a;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
  border-radius: 6px;
  padding: 0.5rem 0.6rem;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.4rem;
  flex: 1 1 auto;
}
#synth-screen .synth-drum-hw-chassis .synth-unit-fx-group strong {
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #1c1d1e;
  flex-basis: 100%;
}
/* The only real remaining <label> elements in this group (once sliders are
   knobs) are the Shape/Type/Steps select rows and the Synced/Enabled
   checkbox rows -- a plain descendant selector reaches both, no :has()
   needed. */
#synth-screen .synth-drum-hw-chassis .synth-unit-fx-group > label {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.78rem;
  color: #1c1d1e;
}
#synth-screen .synth-drum-hw-chassis .synth-unit-fx-group select {
  font-size: 0.78rem;
  padding: 0.15rem 0.3rem;
  border-radius: 4px;
  border: 1px solid #83868a;
  background: linear-gradient(180deg, #f4f5f6, #d7d9db);
  color: #1c1d1e;
}
#synth-screen .synth-drum-hw-chassis .synth-unit-fx-group input[type="checkbox"] {
  width: 1rem;
  height: 1rem;
  accent-color: #1e9a4a;
}
#synth-screen .synth-drum-hw-chassis .synth-unit-fx-group .synth-mute-solo { margin-left: 0.4rem; }

/* ASK ARCHIE STUDIO -- DRUM 808 LEFT CONTROL RAIL / HEIGHT RECOVERY PASS
   (2026-09-26, Founder-directed). #synth-unit-fx-808 (Distortion/PCF/
   Delay/Compression/Output -- same real ids/state/DSP, only its position
   in the page changed) now lives inside the Pattern/Kit rail instead of
   its own separate horizontal band. The existing ".synth-unit-fx" row
   layout is designed for a wide band; ".synth-drum-rail-fx" narrows it to
   the real rail width and stacks the five groups vertically instead --
   each group's OWN existing flex-wrap already reflows its knobs into
   however many columns actually fit at this width (measured live: every
   group's widest row, PCF's 5 knobs, needs ~326px; the rail's real usable
   width comfortably clears that), so no group-specific column count had
   to be hand-picked. */
#synth-screen .synth-drum-hw-chassis .synth-drum-rail-fx .synth-unit-fx {
  flex-direction: column;
  flex-wrap: nowrap;
  gap: 0.5rem;
  margin-top: 0.6rem;
}
#synth-screen .synth-drum-hw-chassis .synth-drum-rail-fx .synth-unit-fx-group {
  flex: 0 0 auto;
  width: 100%;
  box-sizing: border-box;
}

/* Automation -- reuses the exact same shared .synth-hw-group/.synth-
   automation-lanes styling Voice A-D already established (class-based, no
   new rule needed for the panel itself); this just gives the group its own
   title like every other 808 band above. */

.vst-local-build-reveal:disabled { opacity: 0.45; cursor: default; text-decoration: none; }


/* ============================================================================================================================================
   BUILDER RECESSION, CYCLE 1 (Dessy, 2026-10-04). SKIN ONLY. While a live instrument is connected, the instrument is the stage and the Builder around
   it is the workbench: the workbench and the environment get quieter, the stage and its stack are untouched or tidied. Nothing here changes geometry,
   position, DOM, behaviour or ids, and nothing uses opacity on the header (its dropdowns are children).

   The condition is pure CSS and keyed on DOM state that already exists, so it clears itself the moment that state goes away (leaving the screen, going
   back to the list, the instrument disconnecting). No JS state, no class to forget to clear:
     GB  = Builder project detail with the CONNECTED badge showing  (#vst-builder-connected-badge not hidden)
     GC  = Connected Instruments with its live view showing         (#vst-connected-live-view not hidden)
   Both are anchored on ids and kept shallow because :has() under body is re-evaluated when classes change anywhere in the page.
   ============================================================================================================================================ */

/* ENVIRONMENT: the global header and chat dock. Moderate: a darker, calmer green (always mixed toward a dark neutral so the white header text stays
   readable in both themes). Geometry, children and menus are not touched. */
body:has(#vst-builder-screen:not(.hidden) #vst-builder-detail:not(.hidden) #vst-builder-connected-badge:not(.hidden)) #header-state-bar,
body:has(#vst-builder-screen:not(.hidden) #vst-builder-connected-instruments:not(.hidden) #vst-connected-live-view:not(.hidden)) #header-state-bar {
  background: color-mix(in srgb, var(--accent) 62%, #14161a);
  border-bottom-color: color-mix(in srgb, var(--accent) 45%, #14161a);
}
body:has(#vst-builder-screen:not(.hidden) #vst-builder-detail:not(.hidden) #vst-builder-connected-badge:not(.hidden)) #chat-dock-toggle-btn,
body:has(#vst-builder-screen:not(.hidden) #vst-builder-connected-instruments:not(.hidden) #vst-connected-live-view:not(.hidden)) #chat-dock-toggle-btn {
  background: color-mix(in srgb, var(--accent) 55%, #14161a);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}

/* WORKBENCH and SUPPORTING TOOLS (Builder project detail only; Connected Instruments has none of these): the DSP/Build rail, the block library, the Audition
   panel and the Archie panel lose a little colour and brightness. Readable and obviously available, never disabled-looking: no opacity, and each one returns
   to full strength the moment you hover or focus inside it ("quiet until used"). */
body:has(#vst-builder-screen:not(.hidden) #vst-builder-detail:not(.hidden) #vst-builder-connected-badge:not(.hidden)) #vst-builder-detail .vst-builder-col-left,
body:has(#vst-builder-screen:not(.hidden) #vst-builder-detail:not(.hidden) #vst-builder-connected-badge:not(.hidden)) #vst-builder-detail .vst-builder-blocks-col,
body:has(#vst-builder-screen:not(.hidden) #vst-builder-detail:not(.hidden) #vst-builder-connected-badge:not(.hidden)) #vst-audition-panel,
body:has(#vst-builder-screen:not(.hidden) #vst-builder-detail:not(.hidden) #vst-builder-connected-badge:not(.hidden)) #vst-builder-detail .vst-builder-archie-panel {
  filter: saturate(0.6) brightness(0.9);
  transition: filter 0.2s ease;
}
body:has(#vst-builder-screen:not(.hidden) #vst-builder-detail:not(.hidden) #vst-builder-connected-badge:not(.hidden)) #vst-builder-detail .vst-builder-col-left:is(:hover, :focus-within),
body:has(#vst-builder-screen:not(.hidden) #vst-builder-detail:not(.hidden) #vst-builder-connected-badge:not(.hidden)) #vst-builder-detail .vst-builder-blocks-col:is(:hover, :focus-within),
body:has(#vst-builder-screen:not(.hidden) #vst-builder-detail:not(.hidden) #vst-builder-connected-badge:not(.hidden)) #vst-audition-panel:is(:hover, :focus-within),
body:has(#vst-builder-screen:not(.hidden) #vst-builder-detail:not(.hidden) #vst-builder-connected-badge:not(.hidden)) #vst-builder-detail .vst-builder-archie-panel:is(:hover, :focus-within) {
  filter: none;
}
/* The Audition panel is the largest bright block under the stage (a second piano keyboard of white keys), so it recedes a little further than the rest; it still
   reads clearly and returns to full strength on hover or focus. */
body:has(#vst-builder-screen:not(.hidden) #vst-builder-detail:not(.hidden) #vst-builder-connected-badge:not(.hidden)) #vst-audition-panel {
  filter: saturate(0.5) brightness(0.8);
}
/* The Archie panel's endless red pulse is a "connected" signal that competes with the stage; it stops ONLY while this recession condition applies. Anywhere
   else the pulse is exactly as before. The panel keeps its static border. */
body:has(#vst-builder-screen:not(.hidden) #vst-builder-detail:not(.hidden) #vst-builder-connected-badge:not(.hidden)) #vst-builder-detail .vst-builder-archie-panel.connected {
  animation: none;
}

/* STAGE SUPPORT: the stack under the instrument (timeline, keyboard, contextual action) stays where it is, in the same order. It reads as the stage's own
   support layer: a hairline separates it from the instrument, the timeline is quieter, the keyboard is left as the performance surface it is, and the
   Connected Instruments "Open in VST Builder" becomes a contextual bridge rather than another primary control. Scoped by id/context, so the shared
   .vst-builder-open-live-view-btn class (also the Builder's "Pop out") is untouched. */
body:has(#vst-builder-screen:not(.hidden) #vst-builder-detail:not(.hidden) #vst-builder-connected-badge:not(.hidden)) #vst-builder-module-view .vst-module-timeline,
body:has(#vst-builder-screen:not(.hidden) #vst-builder-connected-instruments:not(.hidden) #vst-connected-live-view:not(.hidden)) #vst-connected-live-view .vst-module-timeline {
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  padding-top: 0.65rem;
  opacity: 0.8;
}
#vst-connected-open-in-builder-btn {
  border-color: var(--vstb-border, #2b3138);
  color: var(--vstb-muted, #9aa0a6);
  font-size: 0.75rem;
}
#vst-connected-open-in-builder-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}


/* LIGHT THEME, Build card in the project detail's left rail (2026-10-04). Two rules above disagree in light mode: `#vst-builder-detail-project-row .vst-builder-project-row`
   (the "Build dialog" box) takes the SITE theme's background (--surface-2, cream in light mode), while the row's own text colour, muted colour and link colours come from the
   forced-dark palette and were chosen for a dark card. Result: near-white text on cream. In light mode only, this card now takes the site's text, muted and link colours.
   The dark theme, and the project-list rows (which keep their dark chassis in both themes), are untouched. Two selector groups because the light theme is either chosen
   (data-theme="light") or inherited from the OS (no data-theme and the OS is not dark). */
:root[data-theme="light"] #vst-builder-detail-project-row .vst-builder-project-row {
  color: var(--text);
}
:root[data-theme="light"] #vst-builder-detail-project-row .vst-builder-project-row .muted {
  color: var(--muted);
}
:root[data-theme="light"] #vst-builder-detail-project-row .vst-builder-project-row a,
:root[data-theme="light"] #vst-builder-detail-project-row .vst-builder-project-row .vst-builder-build-download {
  color: var(--accent);
}
:root[data-theme="light"] #vst-builder-detail-project-row .vst-builder-project-row a:hover,
:root[data-theme="light"] #vst-builder-detail-project-row .vst-builder-project-row .vst-builder-build-download:hover {
  color: var(--accent-hover);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) #vst-builder-detail-project-row .vst-builder-project-row {
    color: var(--text);
  }
  :root:not([data-theme]) #vst-builder-detail-project-row .vst-builder-project-row .muted {
    color: var(--muted);
  }
  :root:not([data-theme]) #vst-builder-detail-project-row .vst-builder-project-row a,
  :root:not([data-theme]) #vst-builder-detail-project-row .vst-builder-project-row .vst-builder-build-download {
    color: var(--accent);
  }
  :root:not([data-theme]) #vst-builder-detail-project-row .vst-builder-project-row a:hover,
  :root:not([data-theme]) #vst-builder-detail-project-row .vst-builder-project-row .vst-builder-build-download:hover {
    color: var(--accent-hover);
  }
}

/* VST Builder project list (2026-10-04): the first project card and the "New project" panel now start on the same horizontal line. The card column had 1rem of top padding
   (.synth-compose-split-left) and the list its browser-default 1rem top margin (the list rule sets padding, not margin), so the first card sat 2rem below the panel. Scoped to the
   list page's intro split only; the project detail view's own columns are untouched. */
.vst-builder-intro-split .vst-builder-col-left { padding-top: 0; }
.vst-builder-intro-split #vst-builder-project-list { margin-top: 0; }

/* ---- Archie's Studio, Slice 1 (Round 03, 2026-10-04) --------------------------------------------------------------------------------------------------
   The first real Studio surface: identity, transport, one pattern + one device, a real stereo Master. Dark equipment panels (the same instrument-panel family as the
   Sampler / Builder module view) on whichever page theme is active; nothing here is drawn for a capability the runtime does not have. Hierarchy follows Dessy's
   hypothesis: STUDIO IDENTITY, TRANSPORT, MUSICAL WORKSPACE (pattern + device), SIGNAL DESTINATION (Master), then one honest line about what is not part of Studio. */
.daw-shell { max-width: 62rem; margin: 0 auto; padding: 1rem 1rem 3rem; }
.studio {
  --st-panel: #16191d; --st-panel-2: #1c2025; --st-border: #2b3036; --st-text: #e6e8e6; --st-muted: #8b929a;
  --st-accent: #4cd98a; --st-warn: #e0a548; --st-bad: #e04848; --st-step: #242a30;
  display: flex; flex-direction: column; gap: 0.75rem; color: var(--st-text); font-size: 0.9rem;
}
.studio section { background: var(--st-panel); border: 1px solid var(--st-border); border-radius: 10px; padding: 0.75rem 0.9rem; }
.studio-identity { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.studio-brand { font-weight: 700; letter-spacing: 0.04em; margin-right: 0.25rem; }
.studio-title { flex: 1 1 12rem; min-width: 8rem; background: var(--st-panel-2); color: var(--st-text); border: 1px solid var(--st-border); border-radius: 6px; padding: 0.4rem 0.6rem; font: inherit; }
.studio-saved { color: var(--st-muted); font-size: 0.8rem; white-space: nowrap; }
.studio-saved.ok { color: var(--st-accent); }
.studio-saved.dirty { color: var(--st-warn); }
.studio-identity-actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.studio-btn { background: var(--st-panel-2); color: var(--st-text); border: 1px solid var(--st-border); border-radius: 6px; padding: 0.4rem 0.9rem; font: inherit; cursor: pointer; }
.studio-btn:hover { border-color: #5a626b; }
.studio-btn:focus-visible, .studio-step:focus-visible, .studio-key:focus-visible, .studio-title:focus-visible, .studio-select:focus-visible, .studio-num:focus-visible { outline: 2px solid var(--st-accent); outline-offset: 1px; }
.studio-btn-primary { border-color: var(--st-accent); color: var(--st-accent); }
.studio-btn-quiet { color: var(--st-muted); }
.studio-play.playing { background: var(--st-accent); color: #0d0f0d; border-color: var(--st-accent); font-weight: 700; }
.studio-open, .studio-select { background: var(--st-panel-2); color: var(--st-text); border: 1px solid var(--st-border); border-radius: 6px; padding: 0.35rem 0.5rem; font: inherit; max-width: 16rem; }
.studio-transport { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.studio-field { display: inline-flex; align-items: center; gap: 0.4rem; }
.studio-label { color: var(--st-muted); font-size: 0.75rem; letter-spacing: 0.06em; text-transform: uppercase; }
.studio-unit { color: var(--st-muted); font-size: 0.78rem; min-width: 3.2rem; }
.studio-num { width: 4.5rem; background: var(--st-panel-2); color: var(--st-text); border: 1px solid var(--st-border); border-radius: 6px; padding: 0.35rem 0.4rem; font: inherit; text-align: right; }
.studio-position { font-variant-numeric: tabular-nums; color: var(--st-text); min-width: 9rem; }
.studio-loopnote { color: var(--st-muted); font-size: 0.8rem; margin-left: auto; }
.studio-head { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin-bottom: 0.5rem; }
.studio-title-label { font-size: 0.72rem; letter-spacing: 0.14em; color: var(--st-accent); font-weight: 700; }
.studio-device-name, .studio-engine { color: var(--st-muted); font-size: 0.8rem; }
.studio-steps { display: grid; grid-template-columns: repeat(16, minmax(0, 1fr)); gap: 4px; }
.studio-step { position: relative; height: 3.6rem; background: var(--st-step); border: 1px solid var(--st-border); border-radius: 5px; color: var(--st-muted); cursor: pointer; display: flex; flex-direction: column; align-items: center; justify-content: space-between; padding: 0.25rem 0; font: inherit; }
.studio-step.beat { border-left: 2px solid #4b535c; }
.studio-step-n { font-size: 0.62rem; opacity: 0.7; }
.studio-step-note { font-size: 0.72rem; font-weight: 700; min-height: 1em; }
.studio-step.on { background: #1f4a37; border-color: var(--st-accent); color: var(--st-text); }
.studio-step.now { box-shadow: 0 0 0 2px #fff inset; }
.studio-hint { color: var(--st-warn); font-size: 0.8rem; margin-top: 0.4rem; }
.studio-device { margin-top: 0.9rem; padding-top: 0.75rem; border-top: 1px solid var(--st-border); }
.studio-device-row { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.studio-range { width: 8rem; accent-color: var(--st-accent); }
.studio-keyrow { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin-top: 0.75rem; }
.studio-keys { display: flex; gap: 3px; }
.studio-key { width: 2.3rem; height: 3.2rem; background: #d9dcd9; color: #14161a; border: 1px solid #aab0aa; border-radius: 0 0 5px 5px; cursor: pointer; display: flex; align-items: flex-end; justify-content: center; padding-bottom: 0.2rem; font: inherit; touch-action: none; }
.studio-key.black { background: #23282d; color: #cfd3d6; border-color: #14161a; height: 2.4rem; }
.studio-key.down { background: var(--st-accent); color: #0d0f0d; }
.studio-key-cap { font-size: 0.65rem; opacity: 0.7; pointer-events: none; }
.studio-master { display: flex; flex-direction: column; gap: 0.5rem; }
.studio-meter { display: flex; flex-direction: column; gap: 0.3rem; max-width: 40rem; }
.studio-meter-row { display: flex; align-items: center; gap: 0.5rem; }
.studio-meter-ch { width: 1rem; color: var(--st-muted); font-weight: 700; }
.studio-meter-track { position: relative; flex: 1 1 auto; height: 0.9rem; background: #0d0f0d; border: 1px solid var(--st-border); border-radius: 4px; overflow: hidden; }
.studio-meter-fill { position: absolute; inset: 0 auto 0 0; width: 0; background: linear-gradient(90deg, #2fae6c 0%, #4cd98a 60%, #e0a548 85%, #e04848 100%); background-size: 40rem 100%; }
.studio-meter-hold { position: absolute; top: 0; bottom: 0; width: 2px; background: #fff; left: 0; }
.studio-meter-row.clip .studio-meter-db { color: var(--st-bad); font-weight: 700; }
.studio-meter-db { width: 3.2rem; text-align: right; font-variant-numeric: tabular-nums; color: var(--st-text); }
.studio-meter-scale { position: relative; height: 0.9rem; margin: 0 3.7rem 0 1.5rem; }
.studio-meter-tick { position: absolute; transform: translateX(-50%); font-size: 0.62rem; color: var(--st-muted); }
.studio-truth { color: var(--st-muted); font-size: 0.82rem; margin: 0.25rem 0 0; }
.studio-status { min-height: 1.2em; color: var(--st-muted); font-size: 0.82rem; }
@media (max-width: 640px) {
  .studio-steps { grid-template-columns: repeat(8, minmax(0, 1fr)); }
  .studio-step { height: 3rem; }
  .studio-loopnote { margin-left: 0; }
}
