/* ===========================================================================
   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-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; }

.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. */
.vst-builder-chip-group-label { margin: 0.5rem 0 0.3rem; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted, #888); }
.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; }
/* 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; }
/* 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"). */
#project-list li {
  padding: 0.75rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 0.5rem;
  cursor: pointer;
  background: var(--panel-bg);
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  transition: border-color var(--transition-soft);
}
#project-list li > 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); }

.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; }

/* 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-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 */
.feed-card-desc { margin: 0.2rem 0; color: var(--text); font-size: 0.9rem; }
.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); }
/* 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; }

/* @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; }
.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. */
.group-detail-feed-col { flex: 1; min-width: 0; max-width: 640px; 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 {
  position: absolute; bottom: calc(100% + 0.5rem); left: 0; z-index: 20; width: 16rem;
  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; }
.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; }
.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; }
.group-feed-post-link-card {
  display: flex; gap: 0; margin-top: 0.5rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
  overflow: hidden; text-decoration: none; color: inherit; background: var(--panel-bg);
}
.group-feed-post-link-card:hover { border-color: var(--accent); }
.group-feed-post-link-image { width: 8rem; min-width: 8rem; height: 8rem; object-fit: cover; }
.group-feed-post-link-body { display: flex; flex-direction: column; gap: 0.2rem; padding: 0.6rem 0.75rem; min-width: 0; }
.group-feed-post-link-domain { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.03em; }
.group-feed-post-link-title { font-weight: 600; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.group-feed-post-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; }
.share-teaser-wrap { position: relative; max-width: 640px; margin: 0 auto; min-height: 100vh; display: flex; flex-direction: column; }
.share-teaser-image { width: 100%; max-height: 70vh; object-fit: cover; display: block; }
/* 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: -18vh; padding: 22vh 1.25rem 2.5rem;
  background: linear-gradient(to bottom, transparent 0%, var(--bg) 55%, var(--bg) 100%);
  flex: 1; display: flex; flex-direction: column; justify-content: flex-end; 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; }
.share-teaser-text { font-size: 1.05rem; color: var(--text); margin: 0 0 1rem; }
.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; }
/* 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;
}
.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); }
.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; }

/* 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. */
.audio-cover-player { position: relative; width: 5.25rem; height: 5.25rem; 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; }
.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%; }
.vault-item-media-audio .vault-item-cover-img { width: 88px; height: 88px; 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; }
.vst-builder-project-row {
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.2));
  border-radius: var(--radius-md);
  padding: 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.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 same
   project view, shown instead of the static screenshot only while a
   real, recently-seen connection exists. */
#vst-builder-connected-panel { display: flex; flex-direction: column; align-items: center; gap: 0.75rem; }
.vst-builder-connected-badge {
  padding: 0.2rem 0.6rem;
  border-radius: var(--radius-pill, 999px);
  background: color-mix(in srgb, #4cd98a 20%, transparent);
  color: #2f8f5f;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.05em;
}
.vst-builder-host-strip {
  display: flex;
  gap: 0.75rem;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted, #666);
}
.vst-builder-live-knobs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  justify-content: center;
}
.vst-builder-live-knob { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.05em; }

.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; }
.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 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. */
.vst-builder-split .vst-builder-col-left { flex: 0 1 260px; }
.vst-builder-split .vst-builder-col-center {
  flex: 1 1 auto;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 1rem 1.25rem;
  border-right: 1px solid var(--border);
}
.vst-builder-split .synth-compose-split-right { flex: 1 1 38%; max-width: 480px; }
.vst-builder-block-library summary { cursor: pointer; }
.vst-builder-proposal-actions { display: flex; gap: 0.5rem; margin-top: 0.4rem; }
.vst-builder-block-library {
  border: 1px solid var(--border-color, rgba(127, 127, 127, 0.2));
  border-radius: var(--radius-md);
  padding: 0.75rem 1rem;
  margin: 0.75rem 0;
}
.vst-builder-block-library dl { margin: 0.5rem 0 0; }
.vst-builder-block-library dt { font-weight: 600; margin-top: 0.5rem; }
.vst-builder-block-library dd { margin: 0.15rem 0 0; color: var(--text-muted, #888); }
.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-color, rgba(127, 127, 127, 0.15));
  border-radius: var(--radius-sm, 6px);
  padding: 0.4rem 0.6rem;
  font-size: 0.9rem;
}
/* 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. */
.vst-builder-split { align-items: stretch; height: min(85vh, 830px); }
.vst-builder-chat-col {
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.vst-builder-split .synth-compose-split-left { overflow-y: auto; min-height: 0; }
.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; }
@media (max-width: 800px) {
  .vst-builder-split { height: auto; }
  .vst-builder-chat-col { min-height: 480px; }
  .vst-builder-split .synth-compose-split-left { height: auto; overflow-y: visible; }
  .vst-builder-split .vst-builder-col-center { border-right: none; }
}
/* 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. */
#vst-builder-compose-log { display: flex; flex-direction: column; justify-content: flex-end; gap: 0.5rem; margin: 0.5rem 0; }
.vst-builder-compose-entry {
  border-left: 3px solid var(--accent-color, #6c5ce7);
  padding: 0.3rem 0 0.3rem 0.6rem;
  font-size: 0.9rem;
}

/* 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 {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 500;
}
.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) -- a real, dedicated
   page for the Vocals clip lane, deliberately NOT another cream
   .community-block stacked into the Synth screen's own module rack (the
   founder's own direction: "make it not look so modular as our synth
   looks"). A genuine "vocal booth" identity: a warm-dark ground (not a
   cold near-black), one warm copper accent carried through the waveform
   fill, the playhead, and clip selection -- distinct from the site-wide
   forest-green --accent used everywhere else, and NOT swapped by the
   site's own light/dark toggle (same deliberate fixed-identity precedent
   as .synth-rack-module's own always-dark panel inside an otherwise cream
   Synth page) -- local tokens, not the global --bg/--panel-bg/--accent
   chain, so this page reads as one considered surface, not a themed
   component. Real type pairing: Sora (a warm, slightly rounded
   geometric display face) for the page title/section headers, Manrope
   for controls/labels -- deliberately not Fraunces, tried and reverted
   elsewhere in this codebase for reading too formal against this app's
   own informal tone; neither Sora nor Manrope is the site's own default
   UI stack, both loaded via the same Google Fonts CSP path the "Chewy"
   logo face already uses.
   Phase 0 covers the palette/type/chrome below; the clip lane itself is
   Phase 2's real content, not built here. */
/* 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. Keeps its own real dark theme/colors (still genuinely
   distinct from Synth's own light Quick Links chrome) -- just as a
   rounded card, not a full viewport. */
.vocal-studio-screen {
  --vs-bg: #161210;
  --vs-panel: #221c19;
  --vs-panel-raised: #2b2420;
  --vs-border: #3a2f29;
  --vs-ink: #f2e9df;
  --vs-ink-muted: #a89a8c;
  --vs-accent: #e08a3c;
  --vs-accent-hover: #ee9c52;
  --vs-accent-dim: rgba(224, 138, 60, 0.18);
  --vs-curve: #c9605a;
  --vs-font-display: "Sora", -apple-system, "Segoe UI", system-ui, sans-serif;
  --vs-font-body: "Manrope", var(--font-family);
  background: var(--vs-bg);
  color: var(--vs-ink);
  font-family: var(--vs-font-body);
  border-radius: 12px;
  overflow: hidden;
}
.vocal-studio-screen .vocal-studio-main {
  max-width: 1100px;
  margin: 0 auto;
  padding: 1.5rem 1.25rem 2rem;
}
.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. */
.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;
}
.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. */
.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. */
.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;
}
.vocal-studio-screen .vocal-studio-inline-toolbar .vocal-studio-mode-toggle-btn,
.vocal-studio-screen .vocal-studio-inline-toolbar .vocal-studio-audition-rewind-btn {
  background: transparent;
}
.vocal-studio-screen .vocal-studio-inline-toolbar .vocal-studio-nudge-group {
  background: none;
  border: none;
  padding: 0;
}
.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;
}
.vocal-studio-screen .vocal-studio-upload-btn:hover { border-color: var(--vs-accent); color: var(--vs-accent-hover); }
.vocal-studio-screen .vocal-studio-upload-btn.recording { background: var(--vs-curve); border-color: var(--vs-curve); color: #1a1210; }
.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;
}
.vocal-studio-screen .vocal-studio-audition-standalone-btn:hover { color: var(--vs-accent-hover); border-color: var(--vs-accent-hover); }
.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;
}
.vocal-studio-screen .vocal-studio-audition-rewind-btn:hover { color: var(--vs-ink); border-color: var(--vs-accent); }
.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;
}
.vocal-studio-screen .vocal-studio-nudge-label { font-size: 0.78rem; color: var(--vs-ink-muted); margin-right: 0.15rem; }
.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;
}
.vocal-studio-screen .vocal-studio-nudge-group button:hover { border-color: var(--vs-accent); color: var(--vs-accent-hover); }
.vocal-studio-screen .vocal-studio-bpm-input {
  width: 56px;
  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;
}
.vocal-studio-screen .vocal-studio-bpm-input:focus { outline: none; border-color: var(--vs-accent); }
.vocal-studio-screen .vocal-studio-remove-btn { color: var(--vs-curve); border-color: var(--vs-curve) !important; }
.vocal-studio-screen #vocal-studio-audition-btn.playing { background: var(--vs-accent); color: #1a1210; }
/* 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"). */
.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;
}
.vocal-studio-screen .vocal-studio-mode-toggle-btn:hover { border-color: var(--vs-accent); color: var(--vs-ink); }
.vocal-studio-screen .vocal-studio-mode-toggle-btn.active { background: var(--vs-accent); border-color: var(--vs-accent); color: #1a1210; }
.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;
}
.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;
}
.vocal-studio-screen .vocal-studio-transient-group[hidden] { display: none; }
.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;
}
.vocal-studio-screen .vocal-studio-transient-group input[type="range"] { accent-color: var(--vs-accent); width: 100px; }
.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;
}
.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;
}
.vocal-studio-screen .vocal-studio-edit-toolbar button:hover { border-color: var(--vs-accent); color: var(--vs-accent-hover); }
#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. */
.vocal-studio-screen .vocal-studio-wave-selection {
  position: absolute;
  top: 0;
  bottom: 0;
  background: rgba(224, 138, 60, 0.28);
  border-left: 1px solid var(--vs-accent);
  border-right: 1px solid var(--vs-accent);
  pointer-events: none;
}
.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. */
.vocal-studio-transient-tick {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: rgba(255, 255, 255, 0.35);
  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;
}
.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;
}
.vocal-studio-clip-menu button:hover { background: var(--vs-panel); color: var(--vs-accent-hover); }
.vocal-studio-clip-menu button:disabled { opacity: 0.4; cursor: not-allowed; }
.vocal-studio-clip-menu button:disabled:hover { background: transparent; color: var(--vs-ink); }
.vocal-studio-screen .vocal-studio-zoom-group {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  margin-left: auto;
}
.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;
}
.vocal-studio-screen .vocal-studio-zoom-group button:hover { border-color: var(--vs-accent); color: var(--vs-accent-hover); }
.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;
}
.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. */
.vocal-studio-screen .vocal-studio-song-steps {
  position: relative;
  height: 22px;
  min-width: 100%;
  border-bottom: 1px solid var(--vs-border);
}
.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;
}
.vocal-studio-screen .vocal-studio-song-step.active { background: var(--vs-accent); color: #1a1210; }
.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. */
.vocal-studio-screen .vocal-studio-playhead.scrub {
  background: var(--vs-ink-muted);
  box-shadow: none;
  opacity: 0.6;
}
.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;
}
.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;
}
.vocal-studio-screen .vocal-studio-clip:active { cursor: grabbing; }
.vocal-studio-screen .vocal-studio-clip.selected { border-color: var(--vs-accent); box-shadow: 0 0 0 1px var(--vs-accent); }
.vocal-studio-screen .vocal-studio-clip canvas { display: block; width: 100%; height: 100%; pointer-events: none; }
.vocal-studio-screen .vocal-studio-trim-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 7px;
  cursor: ew-resize;
  background: transparent;
  z-index: 2;
}
.vocal-studio-screen .vocal-studio-trim-handle.left { left: 0; }
.vocal-studio-screen .vocal-studio-trim-handle.right { right: 0; }
.vocal-studio-screen .vocal-studio-clip:hover .vocal-studio-trim-handle,
.vocal-studio-screen .vocal-studio-clip.selected .vocal-studio-trim-handle {
  background: var(--vs-accent-dim);
}
.vocal-studio-screen .vocal-studio-trim-handle:hover { background: var(--vs-accent) !important; }
.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. */
.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;
}
.vocal-studio-screen .vocal-studio-generate-row {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
  align-items: flex-end;
  margin-bottom: 0.75rem;
}
.vocal-studio-screen .vocal-studio-generate-row:last-child { margin-bottom: 0; }
.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;
}
.vocal-studio-screen .vocal-studio-field-wide { flex: 2 1 260px; }
.vocal-studio-screen .vocal-studio-field input,
.vocal-studio-screen .vocal-studio-field textarea,
.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;
}
.vocal-studio-screen .vocal-studio-field input:focus,
.vocal-studio-screen .vocal-studio-field textarea:focus,
.vocal-studio-screen .vocal-studio-field select:focus { outline: none; border-color: var(--vs-accent); }
.vocal-studio-screen .vocal-studio-generate-btn {
  background: var(--vs-accent);
  border: none;
  border-radius: 6px;
  color: #1a1210;
  font-weight: 700;
  font-size: 0.9rem;
  padding: 0.55rem 1.2rem;
  cursor: pointer;
  height: fit-content;
}
.vocal-studio-screen .vocal-studio-generate-btn:hover { background: var(--vs-accent-hover); }
.vocal-studio-screen .vocal-studio-generate-btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* Vocal Studio Phase 4 -- real time-based automation. */
.vocal-studio-screen .vocal-studio-automation-toolbar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1rem 0 0.5rem;
  flex-wrap: wrap;
}
.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. */
.vocal-studio-screen .vocal-studio-automation-legend { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.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;
}
.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;
}
.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);
}
.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;
}
.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;
}
.vocal-studio-screen .vocal-studio-automation-toolbar button:hover { border-color: var(--vs-curve); color: var(--vs-curve); }
.vocal-studio-screen .vocal-studio-automation-hint { font-size: 0.75rem; }
.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;
}
.vocal-studio-screen .vocal-studio-automation-svg {
  display: block;
  height: 80px;
  cursor: crosshair;
}

/* Vocal Studio Phase 4 -- the shared Vocals FX chain panel. */
.vocal-studio-screen .vocal-studio-fx-panel { margin-top: 1.5rem; }
.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;
}
.vocal-studio-screen .vocal-studio-fx-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 0.75rem;
}
.vocal-studio-screen .vocal-studio-fx-card {
  background: var(--vs-panel);
  border: 1px solid var(--vs-border);
  border-radius: 10px;
  padding: 0.85rem;
}
.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-accent);
  margin: 0 0 0.6rem;
}
.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;
}
.vocal-studio-screen .vocal-studio-fx-card label:last-child { margin-bottom: 0; }
.vocal-studio-screen .vocal-studio-fx-card input[type="range"] { flex: 1; accent-color: var(--vs-accent); }
.vocal-studio-screen .vocal-studio-fx-card input[type="checkbox"] { accent-color: var(--vs-accent); }
.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-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); }
