/* =============================================================================
   studio-system.css — THE single holistic studio layout system. Loaded LAST.
   One source of truth for the four global rules Rob wants everywhere:
     · generous rounded corners on every surface (no square corners)
     · zero border/divider lines (borderless + no seams)
     · content centered in one column w/ consistent max-width + symmetric gutters
     · (bottom-dock + full-bleed reactive bg live in studio-elevate/ambient)
   Scoped under .app so it governs the whole studio without touching landing.
   ========================================================================== */

/* 1) ONE SHAPE SCALE — re-declare the shape tokens for the entire studio app.
      Every component that reads var(--sn-r-*) inside .app inherits the scale
      automatically → single knob for the whole room.

      §132 — THE SCALE IS FIVE STEPS, AND EVERY SURFACE IS ONE OF THEM:
        pill 999 · panel 18 · card 14 · control 10 · xs 6
      The census that forced this counted TWELVE distinct radii live in the app
      (6,7,8,9,10,11,12,13,14,16,18,20) — four button shapes alone. A control's
      SIZE may change; its SHAPE does not. That one discipline — uniform corner
      language at each tier — is most of the difference between Linear/Vercel
      and "AI-generated". (§96 already recorded 18px-where-14-was-intended on
      the Billing hero; 14 IS the card, panel keeps 18.) */
.app {
  --sn-r-panel: 18px;   /* the room's own panels: stage, modals, hero tiles */
  --sn-r-card: 14px;    /* cards, menus, tables, media-in-cards */
  --sn-r-input: 10px;   /* every control: buttons, inputs, selects, menu rows */
  --sn-r-media: 14px;
  /* --sn-r-pill stays 999px */
}
/* Round the DS atoms that HARDCODE a radius, so nothing stays sharp — every
   control takes the ONE control shape regardless of its size. */
.app .sn-btn, .app .sn-btn-sm, .app .sn-btn-lg, .app .sn-btn-icon { border-radius: var(--sn-r-input); }
.app .sn-badge, .app .sn-badge-accent { border-radius: 999px; }
.app .sn-menu { border-radius: var(--sn-r-card); }
.app .sn-menu-item { border-radius: var(--sn-r-input); }
.app .sn-seg { border-radius: 999px; }
.app .sn-seg > button { border-radius: 999px; }
.app .sn-input, .app .sn-textarea, .app .sn-select, .app .sn-input-affix { border-radius: var(--sn-r-input); }
.app .sn-modal { border-radius: var(--sn-r-panel); }
.app .sn-table-wrap { border-radius: var(--sn-r-card); }
.app .card, .app figure.card, .app .xp-card, .app .cam-card, .app .sn-card { border-radius: var(--sn-r-card); }
.app .tool-entry, .app .tool-studio, .app .an-pair, .app .scn-preview { border-radius: var(--sn-r-panel); }
.app .media, .app .cam-media, .app .sg-thumb, .app .att-thumb, .app .ref-thumb,
.app .pair-still, .app .voice-tile { border-radius: var(--sn-r-card); }
.app .sl-avatar { border-radius: 999px; }

/* 2) NO LINES — global sweep of borders/dividers/seams across the studio.
      Curved + filled surfaces, never lined. (Composer glass keeps its shadow.) */
.app .side { border-right: none !important; }        /* the line to the LEFT of the search icon */
.app .sn-topbar { border-bottom: none !important; }  /* no seam under the top bar */
.app .side-foot { border-top: none !important; }     /* no divider above the sidebar footer */
.app .sn-topdiv { display: none !important; }         /* vertical divider in the top-bar right group */
.app .cmp-div { display: none !important; }
.app .sec-head, .app .band-head, .app .tl-board-head,
.app .an-pair, .app .scn-preview, .app .cam-lib, .app .tl-board { border-top: none !important; border-bottom: none !important; }
.app hr { display: none !important; }

/* 3) CENTERED CONTENT — one column, consistent max-width, symmetric gutters, every tool. */
.app .gen-chat .gen-scroll { display: flex; flex-direction: column; align-items: center; }
.app .gen-chat .gen-scroll > * { width: 100%; max-width: 1080px; margin-left: auto; margin-right: auto;
  padding-left: 32px; padding-right: 32px; box-sizing: border-box; }
.app .gen-chat .gen-head { text-align: center; }
/* non-chat views (Explore, Library, Settings, Billing, Voice) center their .wrap too */
.app #stage > .wrap, .app .stage > .wrap { max-width: 1160px; margin-left: auto; margin-right: auto; }

@media (max-width: 640px) {
  .app .gen-chat .gen-scroll > * { padding-left: 16px; padding-right: 16px; }
}

/* =============================================================================
   AD CENTER (V.ad) — house concept board, in the Resend design vocabulary.
   ========================================================================== */
.ad-center { max-width: 1200px; }
.ad-head { text-align: center; }
.ad-kicker { display: inline-block; font: 600 .6875rem var(--sn-mono); letter-spacing: .16em; text-transform: uppercase; color: var(--sn-accent-ink); margin-bottom: 8px; }
.ad-cta { margin-top: 18px; }
.ad-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 18px; margin-top: 26px; }
.ad-card { padding: 0; overflow: hidden; display: flex; flex-direction: column; border-radius: var(--sn-r-panel); }
.ad-media { position: relative; aspect-ratio: 16/9; background: var(--sn-bg-deep); overflow: hidden; }
.ad-media > video { width: 100%; height: 100%; object-fit: cover; display: block; }
.ad-slot { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; color: var(--sn-ink-3); font: 500 .8125rem var(--sn-sans); }
.ad-slot-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--sn-accent); box-shadow: 0 0 0 4px var(--sn-accent-glow); animation: ad-pulse 1.8s ease-in-out infinite; }
@keyframes ad-pulse { 0%,100% { opacity: .4; } 50% { opacity: 1; } }
.ad-status { position: absolute; top: 10px; left: 10px; font: 600 .5625rem var(--sn-mono); letter-spacing: .06em; text-transform: uppercase; padding: 3px 8px; border-radius: 999px; background: rgba(10,9,8,.6); backdrop-filter: blur(6px); color: var(--sn-ink-2); }
.ad-status.is-live { color: var(--sn-success, #7bd6a2); }
.ad-status.is-muted { color: var(--sn-ink-3); }
.ad-body { padding: 16px 18px 18px; display: flex; flex-direction: column; gap: 6px; }
.ad-genre { font: 600 .625rem var(--sn-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--sn-ink-3); }
.ad-title { font: 600 1.1875rem/1.2 var(--sn-sans); letter-spacing: -.01em; color: var(--sn-ink); margin: 0; }
.ad-hook { font: 700 .8125rem var(--sn-sans); letter-spacing: .02em; color: var(--sn-accent-ink); margin: 2px 0; }
.ad-idea { font: .8125rem/1.55 var(--sn-sans); color: var(--sn-ink-2); margin: 0; }
.ad-share { font: italic .75rem/1.5 var(--sn-serif); color: var(--sn-ink-3); margin: 2px 0 4px; }
.ad-make { align-self: flex-start; margin-top: 6px; }

/* =============================================================================
   AD CENTER — the working surface (AC.*): five stages, product in → ad out.
   Ported from the v5 shell's create-ad.js into this design vocabulary. Sizing is
   fluid; the phone/viewer honour the ad's real aspect via [data-aspect].
   ========================================================================== */
.ac-note { margin: 14px auto 0; max-width: 720px; text-align: center; font: .8125rem/1.5 var(--sn-sans); color: var(--sn-ink-2); }
.ac-note.is-err { color: var(--sn-danger, #e0796b); }
.ac-stage { padding: 22px 24px 24px; margin-top: 18px; }
.ac-stage.is-done { opacity: .78; }
.ac-stage.is-done:hover, .ac-stage.is-done:focus-within { opacity: 1; }
.ac-stage-head { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
.ac-stage-head h2 { font: 600 1.125rem var(--sn-serif); margin: 0; color: var(--sn-ink); }
.ac-step { display: grid; place-items: center; width: 24px; height: 24px; border-radius: 999px;
  font: 600 .75rem var(--sn-mono); background: var(--sn-accent-soft); color: var(--sn-accent-ink); }
.ac-stage.is-current .ac-step { background: var(--sn-accent); color: var(--sn-on-accent); }
.ac-stage-r, .ac-free { font: 500 .75rem var(--sn-mono); color: var(--sn-ink-3); }
.ac-sub { display: flex; align-items: center; gap: 8px; font: 600 .6875rem var(--sn-mono); letter-spacing: .1em;
  text-transform: uppercase; color: var(--sn-ink-3); margin-bottom: 8px; }
.ac-hint { font: .78125rem/1.5 var(--sn-sans); color: var(--sn-ink-3); margin: 6px 0 0; }
.ac-cta-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 18px; }
.ac-cta-sub { font: .78125rem var(--sn-sans); color: var(--sn-ink-3); }

/* Stage 1 — product in */
.ac-in { display: grid; grid-template-columns: minmax(200px, 300px) 1fr; gap: 20px; align-items: start; }
.ac-in-col { min-width: 0; }
.ac-desc { width: 100%; resize: vertical; min-height: 76px; font: .875rem/1.5 var(--sn-sans); }
.ac-samples { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 8px;
  font: .75rem var(--sn-sans); color: var(--sn-ink-3); }
.ac-sample { border: 1px solid var(--sn-hairline-strong); background: transparent; color: var(--sn-ink-2);
  border-radius: 999px; padding: 3px 10px; font: .75rem var(--sn-sans); cursor: pointer; }
.ac-sample:hover { border-color: var(--sn-accent); color: var(--sn-ink); }
.ac-or { display: flex; align-items: center; gap: 10px; margin: 16px 0 8px; color: var(--sn-ink-3);
  font: .75rem var(--sn-sans); }
.ac-or::before, .ac-or::after { content: ""; flex: 1; height: 1px; background: var(--sn-hairline); }
.ac-url-row { display: flex; gap: 8px; }
.ac-url-row .sn-input { flex: 1; min-width: 0; }
.ac-ghost { list-style: none; margin: 22px 0 0; padding: 18px 0 0; border-top: 1px solid var(--sn-hairline);
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px; }
.ac-ghost li { display: flex; gap: 10px; }
.ac-ghost-n { flex: none; display: grid; place-items: center; width: 20px; height: 20px; border-radius: 999px;
  border: 1px dashed var(--sn-hairline-strong); font: 600 .6875rem var(--sn-mono); color: var(--sn-ink-3); }
.ac-ghost b { display: block; font: 600 .8125rem var(--sn-sans); color: var(--sn-ink-2); }
.ac-ghost span { font: .75rem/1.45 var(--sn-sans); color: var(--sn-ink-3); }
.ac-prod { display: flex; gap: 18px; align-items: flex-start; }
.ac-prod-media { flex: none; width: 92px; aspect-ratio: 1; border-radius: var(--sn-r-card); overflow: hidden;
  background: var(--sn-bg-deep); display: grid; place-items: center; color: var(--sn-ink-3); }
.ac-prod-media img { width: 100%; height: 100%; object-fit: cover; }
.ac-prod-media.is-drop { background: none; }
.ac-prod-media.is-drop .sl-drop { width: 92px; height: 92px; }
.ac-prod-fields { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.ac-prod-acts { display: flex; gap: 8px; }
/* Brand colour on the product card — the swatch, its hex, and what it actually affects. "auto" (null)
   is a real state, so the hex reads as a label rather than pretending a colour has been chosen. */
.ac-brandrow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font: .75rem/1.4 var(--sn-sans); color: var(--sn-ink-2); }
.ac-brandrow .ac-color { width: 30px; height: 30px; flex: none; padding: 0; cursor: pointer;
  border: 1px solid var(--sn-hairline); border-radius: var(--sn-r-input); background: none; }
.ac-brandrow .ac-color::-webkit-color-swatch-wrapper { padding: 2px; }
.ac-brandrow .ac-color::-webkit-color-swatch { border: none; border-radius: var(--sn-r-xs); }
.ac-brand-hex { font-size: .6875rem; color: var(--sn-ink-3); min-width: 4.5em; }
.ac-import { border: 1px solid var(--sn-accent); border-radius: var(--sn-r-card); padding: 16px; background: var(--sn-accent-soft); }
.ac-import-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px;
  font: .75rem var(--sn-mono); color: var(--sn-ink-2); }
.ac-import-flag { font: 600 .625rem var(--sn-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--sn-accent-ink); }
.ac-import-body { display: flex; gap: 14px; align-items: flex-start; }
.ac-import-media { flex: none; width: 84px; aspect-ratio: 1; border-radius: var(--sn-r-card); overflow: hidden; background: var(--sn-bg-deep); }
.ac-import-media img { width: 100%; height: 100%; object-fit: cover; }
.ac-import-fields { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.ac-recent { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--sn-hairline); }
.ac-recent-lbl { font: 600 .6875rem var(--sn-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--sn-ink-3); margin-bottom: 10px; }
.ac-recent-row { display: flex; gap: 10px; overflow-x: auto; padding-bottom: 4px; }
.ac-recent-card { flex: none; width: 140px; text-align: left; border: 1px solid var(--sn-hairline); border-radius: var(--sn-r-card);
  background: var(--sn-surface); padding: 8px; cursor: pointer; display: flex; flex-direction: column; gap: 8px; }
.ac-recent-card:hover { border-color: var(--sn-accent); }
.ac-recent-thumb { display: grid; place-items: center; aspect-ratio: 9/16; max-height: 110px; border-radius: var(--sn-r-input);
  overflow: hidden; background: var(--sn-bg-deep); color: var(--sn-ink-3); }
.ac-recent-thumb img { width: 100%; height: 100%; object-fit: cover; }
.ac-recent-meta { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.ac-recent-meta b { font: 600 .75rem var(--sn-sans); color: var(--sn-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ac-recent-chip { font: 600 .5625rem var(--sn-mono); letter-spacing: .06em; text-transform: uppercase; color: var(--sn-ink-3); }
.ac-recent-chip.st-ready { color: var(--sn-success, #7bd6a2); }
.ac-recent-chip.st-failed { color: var(--sn-danger, #e0796b); }

/* Stage 2 — the angle rail */
.ac-angles { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px; }
.ac-angle { border: 1px solid var(--sn-hairline); border-radius: var(--sn-r-card); padding: 14px; cursor: pointer;
  display: flex; flex-direction: column; gap: 6px; background: var(--sn-surface); transition: border-color .12s, background .12s; }
.ac-angle:hover { border-color: var(--sn-hairline-strong); }
.ac-angle.on { border-color: var(--sn-accent); background: var(--sn-accent-soft); }
.ac-angle-top { display: flex; align-items: center; gap: 8px; }
.ac-angle-top b { font: 600 .9375rem var(--sn-sans); color: var(--sn-ink); }
.ac-chip-ai { font: 600 .5625rem var(--sn-mono); letter-spacing: .06em; padding: 2px 6px; border-radius: 999px;
  border: 1px solid var(--sn-hairline-strong); color: var(--sn-ink-3); }
.ac-angle-meta { font: .75rem/1.45 var(--sn-sans); color: var(--sn-ink-3); }
.ac-angle-hook { font: italic .8125rem/1.45 var(--sn-serif); color: var(--sn-ink-2); }
.ac-hookwrap { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
.ac-hook-alts { display: flex; flex-wrap: wrap; gap: 5px; }
.ac-hook-alt { border: 1px solid var(--sn-hairline-strong); background: transparent; color: var(--sn-ink-2);
  border-radius: 999px; padding: 3px 9px; font: .6875rem var(--sn-sans); cursor: pointer; text-align: left; }
.ac-hook-alt:hover { border-color: var(--sn-accent); color: var(--sn-ink); }

/* Stage 3 — the scene strip */
.ac-prog { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; font-size: .75rem; color: var(--sn-ink-3); }
.ac-prog-bar { flex: 1; height: 4px; border-radius: 999px; background: var(--sn-hairline); overflow: hidden; }
.ac-prog-bar > span { display: block; height: 100%; background: var(--sn-accent); transition: width .4s ease; }
.ac-alert { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 12px 14px; margin-bottom: 14px;
  border: 1px solid var(--sn-hairline-strong); border-radius: var(--sn-r-card); font: .8125rem/1.5 var(--sn-sans); color: var(--sn-ink-2); }
.ac-alert.is-err { border-color: var(--sn-danger, #e0796b); }
.ac-alert b { display: block; width: 100%; color: var(--sn-ink); }
.ac-strip { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 6px; }
.ac-sc { flex: none; width: 190px; margin: 0; display: flex; flex-direction: column; gap: 8px; }
.ac-sc-frame { position: relative; aspect-ratio: 9/16; max-height: 220px; border-radius: var(--sn-r-card); overflow: hidden;
  background: var(--sn-bg-deep); display: grid; place-items: center; color: var(--sn-ink-3); }
.ac-strip[data-aspect="16:9"] .ac-sc-frame { aspect-ratio: 16/9; }
.ac-strip[data-aspect="1:1"] .ac-sc-frame { aspect-ratio: 1; }
.ac-strip[data-aspect="4:5"] .ac-sc-frame { aspect-ratio: 4/5; }
.ac-sc-frame img { width: 100%; height: 100%; object-fit: cover; }
.ac-sc-badge { position: absolute; top: 8px; left: 8px; font: 600 .5625rem var(--sn-mono); letter-spacing: .08em;
  padding: 2px 7px; border-radius: 999px; background: rgba(10,9,8,.66); color: var(--sn-ink-2); backdrop-filter: blur(6px); }
.ac-sc-badge.b-hook { color: var(--sn-accent-ink); }
.ac-sc-prompt { width: 100%; resize: vertical; font: .75rem/1.45 var(--sn-sans); }
.ac-sc-ro { font: .75rem/1.45 var(--sn-sans); color: var(--sn-ink-2); margin: 0; }
.ac-sc-foot { display: flex; align-items: center; gap: 6px; font-size: .6875rem; color: var(--sn-ink-3); }
/* Per-beat actions: quiet until hovered, so the strip still reads as pictures rather than a toolbar.
   Each is a real spend except `take`, which re-points a clip already paid for. */
.ac-sc-act { display: inline-flex; align-items: center; gap: 3px; padding: 3px 5px; border-radius: var(--sn-r-xs);
  border: 1px solid transparent; background: none; color: var(--sn-ink-3); cursor: pointer; line-height: 1; }
.ac-sc-act:hover { border-color: var(--sn-hairline); background: var(--sn-bg-raised); color: var(--sn-ink); }
.ac-sc-act:focus-visible { outline: none; box-shadow: var(--sn-ring); }
.ac-sc-act .sn-ic { width: 13px; height: 13px; }
.ac-sc-act i { font-style: normal; font-size: .625rem; }
.ac-sc-del { border: 0; background: transparent; color: var(--sn-ink-3); cursor: pointer; padding: 2px; line-height: 0; }
.ac-sc-del:hover { color: var(--sn-danger, #e0796b); }
.ac-sc-spin { display: grid; place-items: center; }
.ac-beat-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 14px; }
.ac-beat-row .ac-hint { margin: 0; }
.ac-beat-cr { color: var(--sn-accent-ink); }

/* Stage 4 — the ad layer */
.ac-layer { display: grid; grid-template-columns: minmax(180px, 250px) 1fr; gap: 22px; align-items: start; }
.ac-phone-col { display: flex; flex-direction: column; gap: 10px; align-items: center; }
.ac-phone { position: relative; width: 100%; max-width: 220px; aspect-ratio: 9/16; border-radius: var(--sn-r-panel); overflow: hidden;
  background: var(--sn-bg-deep); border: 1px solid var(--sn-hairline-strong); }
.ac-phone[data-aspect="16:9"] { aspect-ratio: 16/9; max-width: 100%; }
.ac-phone[data-aspect="1:1"] { aspect-ratio: 1; }
.ac-phone[data-aspect="4:5"] { aspect-ratio: 4/5; }
.ac-phone img { width: 100%; height: 100%; object-fit: cover; }
.ac-phone-ph { position: absolute; inset: 0; display: grid; place-items: center; color: var(--sn-ink-3); }
.ac-phone-cap { position: absolute; left: 10%; right: 10%; text-align: center; font: 700 .8125rem/1.3 var(--sn-sans);
  color: #fff; text-shadow: 0 2px 10px rgba(0,0,0,.75); }
.ac-phone-cap.pos-top { top: 12%; } .ac-phone-cap.pos-center { top: 46%; } .ac-phone-cap.pos-bottom { bottom: 20%; }
.ac-phone-cap.emph { color: var(--sn-accent); }
.ac-phone-ec { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; background: var(--sn-bg-deep); text-align: center; padding: 16px; }
.ac-phone-ec b { font: 600 1.0625rem var(--sn-serif); color: var(--sn-ink); }
.ac-phone-ec i { font: italic .8125rem var(--sn-serif); color: var(--sn-ink-3); }
.ac-safezone { position: absolute; inset: 0; pointer-events: none; }
.ac-safezone .ac-sz-rail { position: absolute; right: 6px; bottom: 26%; display: flex; flex-direction: column; gap: 12px; }
.ac-safezone .ac-sz-rail span { display: block; width: 18px; height: 18px; border-radius: 50%; background: rgba(255,255,255,.34); }
.ac-safezone .ac-sz-line { position: absolute; left: 0; right: 0; bottom: 20%; border-top: 1px dashed rgba(255,255,255,.6); }
.ac-safezone .ac-sz-line span { position: absolute; right: 4px; top: 2px; font-size: .5rem; color: rgba(255,255,255,.7); }
.ac-safezone .ac-sz-bottom { position: absolute; left: 8px; right: 40px; bottom: 8px; display: flex; flex-direction: column; gap: 5px; }
.ac-safezone .ac-sz-bottom span { display: block; height: 5px; border-radius: 3px; background: rgba(255,255,255,.3); }
.ac-safezone .ac-sz-bottom span:nth-child(1) { width: 45%; }
.ac-safezone .ac-sz-bottom span:nth-child(2) { width: 80%; }
.ac-safezone .ac-sz-bottom span:nth-child(3) { width: 60%; }
.ac-layer-ctrl { min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.ac-cap-track { position: relative; height: 26px; border-radius: var(--sn-r-input); background: var(--sn-bg-deep); overflow: hidden;
  display: flex; align-items: center; padding: 0 6px; font-size: .6875rem; color: var(--sn-ink-3); }
.ac-cap-chip { position: absolute; top: 4px; bottom: 4px; border-radius: var(--sn-r-xs); background: var(--sn-hairline-strong);
  color: var(--sn-ink-2); font: .625rem/18px var(--sn-sans); padding: 0 5px; overflow: hidden; white-space: nowrap;
  cursor: pointer; }
.ac-cap-chip.sel { background: var(--sn-accent); color: var(--sn-on-accent); }
.ac-cap-list { display: flex; flex-direction: column; gap: 6px; }
.ac-cap-row { display: flex; align-items: center; gap: 6px; }
.ac-cap-row .ac-cap-text { flex: 1; min-width: 0; }
.ac-pos { display: flex; gap: 2px; }
.ac-pos-chip, .ac-cap-emph { width: 26px; height: 28px; border: 1px solid var(--sn-hairline-strong); background: transparent;
  color: var(--sn-ink-3); border-radius: var(--sn-r-input); font: 600 .6875rem var(--sn-mono); cursor: pointer; }
.ac-pos-chip.on, .ac-cap-emph.on { border-color: var(--sn-accent); color: var(--sn-accent-ink); background: var(--sn-accent-soft); }
.ac-cap-del { border: 0; background: transparent; color: var(--sn-ink-3); cursor: pointer; padding: 2px; line-height: 0; }
.ac-cap-del:hover { color: var(--sn-danger, #e0796b); }
.ac-ec-toggle { display: flex; align-items: center; gap: 8px; font: .8125rem var(--sn-sans); color: var(--sn-ink-2); margin-top: 4px; }

/* Stage 5 — ship it */
.ac-ship { display: grid; grid-template-columns: minmax(230px, 320px) 1fr; gap: 22px; align-items: start; }
.ac-ship-render { display: flex; flex-direction: column; gap: 10px; }
.ac-quality { display: flex; flex-direction: column; gap: 6px; }
.ac-q { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 12px;
  border: 1px solid var(--sn-hairline); border-radius: var(--sn-r-card); background: var(--sn-surface); cursor: pointer;
  font: 500 .8125rem var(--sn-sans); color: var(--sn-ink-2); }
.ac-q.on { border-color: var(--sn-accent); background: var(--sn-accent-soft); color: var(--sn-ink); }
.ac-q .mono { color: var(--sn-ink-3); font-size: .75rem; }
.ac-ship-out { min-width: 0; display: flex; flex-direction: column; gap: 10px; }
.ac-viewer { border-radius: var(--sn-r-card); overflow: hidden; background: var(--sn-bg-deep); aspect-ratio: 9/16; max-height: 420px;
  margin: 0 auto; width: 100%; max-width: 260px; }
.ac-viewer[data-aspect="16:9"] { aspect-ratio: 16/9; max-width: 100%; }
.ac-viewer[data-aspect="1:1"] { aspect-ratio: 1; max-width: 360px; }
.ac-viewer[data-aspect="4:5"] { aspect-ratio: 4/5; max-width: 320px; }
.ac-viewer video { width: 100%; height: 100%; object-fit: contain; display: block; background: #000; }
/* The in-page viewer stays as the compact result strip; this is the way INTO the premiere, where the ad
   actually plays at size with sound. It sits over the media rather than stealing a row from the stage. */
.ac-viewer { position: relative; }
.ac-premiere { position: absolute; left: 10px; bottom: 10px; z-index: 2;
  appearance: none; border: none; cursor: pointer;
  padding: 7px 12px; border-radius: var(--sn-r-pill);
  background: var(--sn-glass-strong); color: var(--sn-on-media);
  font: 600 .75rem/1 var(--sn-sans);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  transition: background var(--sn-t-hover) var(--sn-ease); }
.ac-premiere:hover { background: var(--sn-glass-solid); }
.ac-viewer.is-empty { display: grid; place-items: center; padding: 20px; text-align: center;
  font: .8125rem/1.5 var(--sn-sans); color: var(--sn-ink-3); }
.ac-aspects { display: flex; flex-wrap: wrap; gap: 6px; }
.ac-filename { font-size: .6875rem; color: var(--sn-ink-3); overflow: hidden; text-overflow: ellipsis; }
.ac-ship-acts { display: flex; flex-wrap: wrap; gap: 8px; }
.ac-vars { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--sn-hairline); }
.ac-var { display: flex; align-items: center; gap: 10px; padding: 8px 0; font: .8125rem var(--sn-sans); color: var(--sn-ink-2); }
.ac-copy { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--sn-hairline); }
.ac-copy-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }
.ac-copy-item { border: 1px solid var(--sn-hairline); border-radius: var(--sn-r-card); padding: 10px 12px; background: var(--sn-surface); }
.ac-copy-item.big { grid-column: 1 / -1; }
.ac-copy-meta { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 4px;
  font-size: .625rem; color: var(--sn-ink-3); letter-spacing: .06em; text-transform: uppercase; }
.ac-copy-val { font: .8125rem/1.5 var(--sn-sans); color: var(--sn-ink); }

.ac-concepts { margin-top: 42px; padding-top: 26px; border-top: 1px solid var(--sn-hairline); }

@media (max-width: 860px) {
  .ac-in, .ac-layer, .ac-ship { grid-template-columns: 1fr; }
  .ac-phone, .ac-viewer { max-width: 260px; }
}

/* ==========================================================================
   MOTION GRAPHICS (MG.*) — the Ad Center's second lane: real product photos
   composited with animated typography by ffmpeg, no video model. It borrows
   the stage chrome above (.ac-stage / .ac-ship / .ac-viewer) and adds only a
   template picker, a product row and the brief.
   ========================================================================== */
.ac-modes { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 20px; }
.ac-mode { display: grid; gap: 3px; text-align: left; cursor: pointer; padding: 12px 14px;
  border: 1px solid var(--sn-hairline-strong); border-radius: var(--sn-r-card); background: var(--sn-surface);
  transition: border-color .12s, background .12s; }
.ac-mode:hover { border-color: var(--sn-accent); }
.ac-mode.on { border-color: var(--sn-accent); background: var(--sn-accent-soft); }
.ac-mode b { font: 600 .875rem var(--sn-sans); color: var(--sn-ink); }
.ac-mode span { font: .75rem/1.45 var(--sn-sans); color: var(--sn-ink-3); }

.mg-tpls { display: grid; grid-template-columns: repeat(auto-fit, minmax(258px, 1fr)); gap: 12px; }
.mg-tpl { display: grid; gap: 7px; align-content: start; cursor: pointer; padding: 14px;
  border: 1px solid var(--sn-hairline-strong); border-radius: var(--sn-r-card); background: var(--sn-surface);
  transition: border-color .12s, background .12s; }
.mg-tpl:hover { border-color: var(--sn-accent); }
.mg-tpl.on { border-color: var(--sn-accent); background: var(--sn-accent-soft); }
.mg-tpl-top { display: flex; align-items: baseline; gap: 8px; }
.mg-tpl-top b { font: 600 .9375rem var(--sn-sans); color: var(--sn-ink); }
.mg-tpl-top .mono { margin-left: auto; flex: none; font-size: .6875rem; color: var(--sn-ink-3); }
.mg-tpl-blurb { font: .8125rem/1.5 var(--sn-sans); color: var(--sn-ink-2); }
.mg-tpl-wants { display: flex; flex-wrap: wrap; gap: 5px; }
.mg-tpl-wants span { border: 1px solid var(--sn-hairline); border-radius: 999px; padding: 2px 8px;
  font: .6875rem var(--sn-sans); color: var(--sn-ink-3); }

.mg-prods { display: grid; gap: 10px; }
.mg-prod { display: grid; grid-template-columns: 84px minmax(0, 1fr) auto; gap: 12px; align-items: start;
  padding: 10px; border: 1px solid var(--sn-hairline); border-radius: var(--sn-r-card); background: var(--sn-surface); }
.mg-prod-media { position: relative; flex: none; width: 84px; height: 84px; border-radius: var(--sn-r-card);
  overflow: hidden; background: var(--sn-bg-deep); }
.mg-prod-media img { display: block; width: 100%; height: 100%; object-fit: cover; }
.mg-prod-media .sl-drop { width: 84px; height: 84px; }
.mg-prod-x { position: absolute; top: 4px; right: 4px; display: grid; place-items: center;
  width: 22px; height: 22px; border: none; border-radius: 999px; cursor: pointer;
  background: rgba(8, 10, 13, .72); color: #fff; }
.mg-prod-x .sn-ic-slot { width: 12px; height: 12px; }
.mg-prod-fields { display: grid; grid-template-columns: minmax(0, 1fr) 104px; gap: 8px; }
.mg-prod-fields .mg-feat { grid-column: 1 / -1; }
.mg-prod-del { align-self: center; display: grid; place-items: center; width: 28px; height: 28px;
  border-radius: 999px; border: 1px solid var(--sn-hairline-strong); background: transparent;
  color: var(--sn-ink-3); cursor: pointer; transition: color .12s, border-color .12s; }
.mg-prod-del:hover { color: var(--sn-danger, #e0796b); border-color: var(--sn-danger, #e0796b); }
.mg-prod-del .sn-ic-slot { width: 13px; height: 13px; }
.mg-prod-acts { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 12px; }
.mg-prod-acts .ac-hint { margin: 0; }

.mg-samples { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 8px;
  font: .75rem var(--sn-sans); color: var(--sn-ink-3); }
.mg-brief-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 14px; }
.mg-flag { font: 500 .6875rem var(--sn-mono); color: var(--sn-ink-3); padding: 3px 10px;
  border: 1px dashed var(--sn-hairline-strong); border-radius: 999px; }
.mg-flag.is-ok { color: var(--sn-accent-ink); background: var(--sn-accent-soft);
  border: 1px solid var(--sn-accent); }
.mg-copy { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 18px; }
.mg-lbl { display: grid; gap: 5px; font: 600 .6875rem var(--sn-mono); letter-spacing: .1em;
  text-transform: uppercase; color: var(--sn-ink-3); }
.mg-aspects { margin-top: 18px; }
.mg-aspects .ac-sub { margin-bottom: 8px; }
.mg-work { display: grid; gap: 6px; margin-top: 12px; }
.mg-work .ac-hint { margin: 0; }
.mg-work-bar { display: block; height: 3px; border-radius: 999px; overflow: hidden;
  background: var(--sn-hairline); }
.mg-work-bar span { display: block; height: 100%; width: 32%; border-radius: 999px;
  background: var(--sn-accent); animation: mg-slide 1.6s ease-in-out infinite; }
@keyframes mg-slide { from { transform: translateX(-110%); } to { transform: translateX(420%); } }

@media (prefers-reduced-motion: reduce) {
  .mg-work-bar span { animation: none; width: 100%; opacity: .5; }
}
@media (max-width: 860px) {
  .ac-modes, .mg-copy { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  .mg-prod { grid-template-columns: 84px minmax(0, 1fr); }
  .mg-prod-fields { grid-template-columns: minmax(0, 1fr); }
  .mg-prod-del { grid-column: 2; justify-self: end; }
}

/* the ⋯ advanced button — round icon button (visual). Layout/sizing live in the COMPOSER block below. */
.cmp-adv { display: grid; place-items: center; border-radius: 999px;
  border: 1px solid var(--sn-hairline-strong); background: var(--sn-surface); color: var(--sn-ink-2); cursor: pointer;
  transition: color .12s, border-color .12s, background .12s; }
.cmp-adv:hover, .composer.adv-open .cmp-adv { color: var(--sn-ink); border-color: var(--sn-accent); background: var(--sn-accent-soft); }
.cmp-adv .sn-ic-slot { width: 18px; height: 18px; }
.composer.is-slim .cmp-prompt { order: 0; }
.composer.is-slim .cmp-note { order: 2; }

/* =============================================================================
   MEDIA CARDS — show the FULL result, never cover-cropped so a subject gets
   amputated. Respect the real aspect ratio (object-fit: contain) on a matte well.
   (Caption .over is already opacity:0 at rest = off the media; toolbar is inset.)
   ========================================================================== */
.app .card .media > img, .app .card .media > video { object-fit: contain !important; object-position: center !important; }
.app .card .media { background: var(--sn-bg-deep); }

/* =============================================================================
   TWO ROUNDED PANELS (Rob 2026-07-21) — the CHROME (left rail + top bar + right
   controls) is ONE grouped, borderless glass unit; the MAIN CONTENT is a SEPARATE
   rounded panel set apart from it by a clean gap. No 90° corners; both internally
   clean + rounded; works in light + dark. Desktop; ≤860px reverts to page-scroll.
   ========================================================================== */
@media (min-width: 861px) {
  html, body { height: 100%; }
  body { overflow: hidden; background: var(--sn-bg-deep); }   /* ambient shows around; scroll moves INSIDE */

  /* the CHROME unit = the app frame; rail + top bar share its one glass surface (no seam) */
  .app {
    position: fixed; inset: 10px; width: auto; height: auto; min-height: 0;
    grid-template-rows: 100%;
    border-radius: var(--sn-r-panel); overflow: hidden; isolation: isolate;
    background: color-mix(in srgb, var(--sn-sidebar-bg) 94%, transparent);
    -webkit-backdrop-filter: blur(18px) saturate(1.05); backdrop-filter: blur(18px) saturate(1.05);
    box-shadow: var(--sn-elev-3), 0 0 0 1px color-mix(in srgb, var(--sn-hairline) 55%, transparent);
  }
  .app .side { height: 100%; position: static; background: transparent !important; border-right: 0 !important;
    -webkit-backdrop-filter: none !important; backdrop-filter: none !important; overflow: hidden; }
  .app .sn-topbar { position: static; background: transparent !important; border-bottom: 0 !important;
    -webkit-backdrop-filter: none !important; backdrop-filter: none !important; }
  .app .sn-topbar .sn-topdiv { display: none !important; }   /* no divider in the right cluster */
  .app .main { height: 100%; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }

  /* the CONTENT is its OWN rounded panel — a clean gap of chrome shows around it (top from the top
     bar, left from the rail, right/bottom from the frame), so it reads as a distinct surface. */
  .app #stage.stage {
    min-height: 0; overflow-y: auto; overflow-x: hidden;
    margin: 4px 10px 10px 6px; border-radius: var(--sn-r-panel);
    /* the content panel is its OWN surface — THEME-AWARE (var(--sn-bg) flips light/dark), distinct from
       the chrome (which is var(--sn-sidebar-bg)). id-specificity beats studio-ambient's transparent #stage. */
    background: var(--sn-bg) !important;
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sn-hairline) 70%, transparent);
  }
  /* chrome vs content need a real tonal difference in BOTH modes — nudge each theme so they read distinct */
  :root[data-theme="studio"] .app #stage.stage { background: color-mix(in srgb, var(--sn-bg) 78%, #000) !important; }
  :root[data-theme="light"] .app #stage.stage { background: #FFFFFF !important; }
  #stage > .gen.gen-chat { min-height: 100%; height: auto; }   /* fill the panel; sticky dock pins to its bottom */
  /* COLLAPSE = grid-template-columns (the proven mechanism that reliably narrows the rail), driven by
     Cloudflare's clean ease-in-out (--rail-t, cubic-bezier(.77,0,.175,1), .25s) → NO overshoot/bounce.
     !important guarantees the collapsed 66px wins over the inline expanded default. */
  .app { grid-template-columns: 236px 1fr; transition: grid-template-columns var(--rail-t) !important; }
  html[data-rail="collapsed"] .app { grid-template-columns: 66px 1fr !important; }
}
/* =============================================================================
   RAIL COLLAPSE — perfectly SYMMETRIC, BOUNCE-FREE open & close. One clean ease-in-out (--rail-t),
   ~210ms, identical both directions. The rail WIDTH glides (not the grid), and the labels fade on the
   exact same clock. No spring/overshoot anywhere on the collapse path.
   ========================================================================== */
/* Cloudflare's EXACT sidebar easing + duration (observed live in their dashboard: their --sidebar-easing
   is cubic-bezier(.77,0,.175,1) at .25s) — smooth ease-in-out, zero overshoot, so the open can't bounce. */
:root { --rail-t: 250ms cubic-bezier(.77, 0, .175, 1); }
.side .nav-lb { transition: opacity var(--rail-t), max-width var(--rail-t), transform var(--rail-t) !important; }
/* the toggle icon must NOT spring — kill the overshoot easing on its wrap (studio-elevate used --el-spring) */
.rail-toggle .rail-ic-wrap { transition: transform var(--rail-t) !important; }
/* the icon = Cloudflare's panel + vertical line; the LINE translates on toggle (no chevron, no rotation) */
.rail-ic .rail-line { transition: transform var(--rail-t); transform-box: fill-box; transform-origin: center; }
html[data-rail="collapsed"] .rail-ic .rail-line { transform: translateX(2.5px); }

/* broken/empty media → a clean labeled placeholder instead of a blank or broken-image box */
.media.is-empty > img, .media.is-empty > video { display: none; }
.media.is-empty::after { content: "unavailable"; }
.media.is-empty::after, .media-ph { position: absolute; inset: 0; display: grid; place-items: center;
  background: var(--sn-bg-raised); color: var(--sn-ink-3); font: 500 .76rem var(--sn-mono); letter-spacing: .04em; }

/* STUDIO-AS-LANDING (anon / try-before-signup): logged-out visitors see NO account/asset/billing UI —
   just the inviting studio + a Log in / Sign up entry. Signed-in users see everything as normal. */
[data-anon-only] { display: none; }
html[data-anon] [data-auth-only] { display: none !important; }
html[data-anon] [data-anon-only] { display: inline-flex; }

/* the SIGNUP GATE — the "moment of value" wall. Enticing, warm, one clear action + the perks. */
.signup-modal { max-width: 440px; text-align: center; padding: 0; position: relative; overflow: hidden; }
.signup-modal .sn-modal-x { position: absolute; top: 12px; right: 12px; z-index: 2; }
.sg-body { padding: 42px 34px 34px; }
.sg-spark { width: 54px; height: 54px; border-radius: var(--sn-r-card); margin: 0 auto 18px; display: grid; place-items: center;
  background: var(--sn-accent-soft); color: var(--sn-accent); }
.sg-spark .sn-ic-slot { width: 27px; height: 27px; }
.sg-title { font: 600 1.5rem/1.2 var(--sn-serif); color: var(--sn-ink); margin: 0 0 8px; }
.sg-sub { color: var(--sn-ink-2); font-size: .95rem; line-height: 1.5; margin: 0 auto 22px; max-width: 344px; }
.sg-perks { display: flex; flex-direction: column; gap: 11px; text-align: left; max-width: 300px; margin: 0 auto 24px; }
.sg-perk { display: flex; align-items: center; gap: 10px; color: var(--sn-ink); font-size: .9rem; }
.sg-perk .sn-ic-slot { width: 18px; height: 18px; color: var(--sn-accent); flex: none; }
.sg-cta { width: 100%; justify-content: center; height: 46px; font-size: 1rem; text-decoration: none; }
.sg-alt { margin: 14px 0 0; color: var(--sn-ink-3); font-size:.8125rem; }
.sg-alt a, .sg-alt .am-swap { color: var(--sn-accent); text-decoration: none; font-weight: 600; }
.sg-alt .am-swap { background: none; border: 0; padding: 0; font: inherit; cursor: pointer; }

/* INLINE AUTH MODAL — the in-studio login/create-account popup (replaces the full-page /login redirect).
   Reuses the signup-modal shell + DS field tokens, so it themes light + dark for free. */
.auth-modal { max-width: 428px; }
.auth-modal .sg-body { padding: 34px 32px 30px; max-height: 90vh; overflow-y: auto; }
.auth-modal .sg-title { margin-bottom: 6px; }
.auth-modal .sg-sub { margin-bottom: 20px; }
/* social buttons */
.am-social { display: flex; flex-direction: column; gap: 10px; margin-bottom: 4px; }
.am-oauth { display: flex; align-items: center; justify-content: center; gap: 10px; width: 100%; height: 44px;
  border: 1px solid var(--sn-hairline-strong); border-radius: 999px; background: var(--sn-surface); color: var(--sn-ink);
  font: 600 .9375rem var(--sn-sans); cursor: pointer; transition: border-color var(--sn-t-hover), background var(--sn-t-hover); }
.am-oauth:hover { border-color: var(--sn-accent); background: var(--sn-bg-raised); }
.am-oauth svg { flex: none; }
.am-gbtn { display: flex; justify-content: center; min-height: 0; }
.am-gbtn:empty { display: none; }
.am-divider { display: flex; align-items: center; gap: 12px; margin: 14px 0 2px; color: var(--sn-ink-3);
  font: .75rem var(--sn-mono); text-transform: uppercase; letter-spacing: .05em; }
.am-divider::before, .am-divider::after { content: ""; flex: 1; height: 1px; background: var(--sn-hairline); }
/* form */
.am-form { display: flex; flex-direction: column; gap: 13px; text-align: left; margin-top: 14px; }
.am-form .sn-field { gap: 5px; }
.am-err { padding: 9px 12px; border-radius: var(--sn-r-input); background: var(--sn-error-soft, rgba(179,64,46,.12));
  border: 1px solid rgba(179,64,46,.32); color: var(--sn-error); font: 500 .8125rem/1.4 var(--sn-sans); }
.am-pw { position: relative; }
.am-pw .sn-input { padding-right: 58px; width: 100%; }
.am-pw-toggle { position: absolute; top: 50%; right: 8px; transform: translateY(-50%); background: none; border: 0;
  padding: 4px 8px; color: var(--sn-ink-3); font: 600 .8125rem var(--sn-sans); cursor: pointer; border-radius: var(--sn-r-xs); }
.am-pw-toggle:hover { color: var(--sn-ink); }
.am-consent { display: flex; gap: 9px; align-items: flex-start; font: .8125rem/1.45 var(--sn-sans); color: var(--sn-ink-2); }
.am-consent input { margin-top: 2px; flex: none; accent-color: var(--sn-accent); }
.am-consent a { color: var(--sn-accent); text-decoration: none; }
.auth-modal .sg-cta { margin-top: 4px; }
.sg-cta.is-busy { opacity: .7; pointer-events: none; }
.am-forgot { align-self: center; background: none; border: 0; padding: 2px; margin-top: -2px; color: var(--sn-ink-3);
  font: .8125rem var(--sn-sans); cursor: pointer; }
.am-forgot:hover { color: var(--sn-accent); }
/* mode toggling: sign-in hides the signup-only rows, and vice-versa */
.auth-modal[data-mode="signin"] .am-signup-only { display: none; }
.auth-modal[data-mode="signup"] .am-signin-only { display: none; }

/* every menu / popover / modal / card keeps a curve — full sweep, zero hard corners */
.sn-menu, .model-menu, .style-gallery, .sl-acct-menu, .sn-scrim > *, .cmp-adv-panel { border-radius: var(--sn-r-card); }
.sn-modal { border-radius: var(--sn-r-panel); }

/* =============================================================================
   COMPOSER — ONE component across EVERY tool: tight, aligned, optimized. The
   +, prompt, ⋯ and Generate/run share one centered baseline; 38px controls; even
   8px gaps; consistent 12px padding + rounded box. Applies to ALL .composer.
   ========================================================================== */
.composer.composer { padding: 8px 10px; border-radius: var(--sn-r-card); }
.composer .cmp-prompt { padding: 0; }
/* ONE single horizontal line: + | text | ⋯ | Generate. NEVER wraps to a second line (nowrap). */
.composer .cmp-prompt-row { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; }
.composer .cmp-prompt-row > textarea, .composer .cmp-prompt-row > input { flex: 1 1 auto; min-width: 60px;
  align-self: center; min-height: 40px; max-height: 40px; height: 40px; padding: 9px 8px; line-height: 1.4;
  box-sizing: border-box; overflow-y: auto; resize: none; }
/* CRITICAL: the id rules #cm-ta{min-height:72px} + .tl-pitch #tl-brief{min-height:96px} in studio-live.css
   made the text box 72–96px next to 38px controls → the misalignment Rob sees. Override at id+ specificity
   + !important so the input is a true single 40px line, level with +, ⋯ and Generate. */
.composer .cmp-prompt-row > #cm-ta, .tl-pitch .cmp-prompt-row > #tl-brief,
.composer.composer .cmp-prompt-row > textarea, .composer.composer .cmp-prompt-row > input {
  min-height: 40px !important; max-height: 40px !important; height: 40px !important; }
.composer .cmp-attach, .composer .cmp-adv { width: 38px; height: 38px; flex: none; align-self: center; padding: 0; }
.composer .cmp-prompt-row > .cmp-gen, .composer .cmp-prompt-row > [data-run] { height: 38px; flex: none;
  align-self: center; border-radius: var(--sn-r-input); white-space: nowrap; }
/* ChatGPT/Claude-style attachment strip ABOVE the one-line row; hidden (:empty) when nothing attached,
   so the composer stays a single line until the user attaches. Multiple = a horizontal strip that wraps. */
.cmp-prompt { display: flex; flex-direction: column; gap: 10px; }
/* the chip strip renders ABOVE the input, on its own line, and stacks in FRONT of the composer's glass
   (backdrop-filter creates a stacking context — without this the chips can hide behind it / read as
   "something in the background"). width:100% forces it onto its own row above the prompt. */
.cmp-attach-strip { display: flex; flex-wrap: wrap; gap: 8px; width: 100%; position: relative; z-index: 3; order: -1; }
.cmp-attach-strip:empty { display: none; }
.cmp-attach-strip .att-thumb { position: relative; width: 54px; height: 54px; border-radius: var(--sn-r-input); overflow: hidden;
  flex: none; border: 1px solid var(--sn-hairline-strong); box-shadow: var(--sn-elev-1); }
.cmp-attach-strip .att-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cmp-attach-strip .att-x { position: absolute; top: 3px; right: 3px; width: 18px; height: 18px; padding: 0;
  border-radius: 999px; border: 1.5px solid #fff; background: rgba(20,18,16,.72); color: #fff;
  display: grid; place-items: center; cursor: pointer; }
.cmp-attach-strip .att-x:hover { background: #000; }
.cmp-attach-strip .att-x .sn-ic-slot { width: 11px; height: 11px; }

/* THROUGHLINE start/end-frame + text-with-reference selector (the live-site model, on the staging composer) */
.tl-frame-sel { display: flex; flex-direction: column; gap: 10px; margin-bottom: 12px; }
.tl-mode { align-self: flex-start; }
.tl-anchors { display: flex; align-items: center; gap: 10px; }
.tl-anchor { position: relative; width: 82px; height: 82px; border-radius: var(--sn-r-card); border: 1px dashed var(--sn-hairline-strong);
  background: var(--sn-bg-raised); display: grid; place-items: center; align-content: center; gap: 4px; cursor: pointer;
  overflow: hidden; flex: none; color: var(--sn-ink-3); padding: 0; }
.tl-anchor:hover { border-color: var(--sn-accent); color: var(--sn-ink); }
.tl-anchor.is-set { border-style: solid; border-color: var(--sn-hairline-strong); }
.tl-anchor.is-set img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.tl-anchor .sn-ic-slot { width: 20px; height: 20px; }
.tl-anchor-lbl { font: 600 .7rem var(--sn-mono); letter-spacing: .02em; position: relative; z-index: 1; }
.tl-anchor.is-set .tl-anchor-lbl { position: absolute; bottom: 0; left: 0; right: 0; padding: 3px 4px; text-align: center;
  background: linear-gradient(transparent, rgba(0,0,0,.72)); color: #fff; }
.tl-anchor-x { position: absolute; top: 3px; right: 3px; z-index: 2; width: 18px; height: 18px; border-radius: 999px; padding: 0;
  border: 1.5px solid #fff; background: rgba(20,18,16,.72); color: #fff; display: grid; place-items: center; cursor: pointer; }
.tl-anchor-x .sn-ic-slot { width: 10px; height: 10px; }
.tl-swap { width: 30px; height: 30px; border-radius: 999px; border: 1px solid var(--sn-hairline-strong);
  background: var(--sn-surface); color: var(--sn-ink-2); cursor: pointer; font-size: 15px; flex: none; }
.tl-swap:hover { color: var(--sn-ink); border-color: var(--sn-accent); }
.tl-refstrip { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tl-refstrip .cmp-attach-strip { margin: 0; }
.tl-ref-add { display: inline-flex; align-items: center; gap: 7px; height: 40px; padding: 0 14px; border-radius: var(--sn-r-input);
  border: 1px dashed var(--sn-hairline-strong); background: var(--sn-bg-raised); color: var(--sn-ink-2); cursor: pointer;
  font: 500 .85rem var(--sn-sans); }
.tl-ref-add:hover { border-color: var(--sn-accent); color: var(--sn-ink); }
.tl-ref-add .sn-ic-slot { width: 16px; height: 16px; }
.tl-frame-note { margin: 0; color: var(--sn-ink-3); font-size: .78rem; line-height: 1.4; }

/* MODEL PICKER (full registry): "Coming soon" divider, disabled + soon rows */
.model-gallery .mg-sep { padding: 10px 12px 5px; font: 600 .68rem var(--sn-mono); letter-spacing: .08em;
  text-transform: uppercase; color: var(--sn-ink-3); border-top: 1px solid var(--sn-hairline); margin-top: 4px; }
.model-gallery .sn-menu-item.is-disabled { opacity: .52; cursor: default; }
.model-gallery .mg-chip.is-soon { color: var(--sn-ink-3); border-color: var(--sn-hairline); }
.model-gallery .mg-empty { padding: 16px; text-align: center; color: var(--sn-ink-3); font-size: .875rem; }

/* CLEAN + premium composer (Rob) — a quiet neutral hairline + soft elevation, ZERO orange at rest OR on
   focus. This OVERRIDES the id-based orange rings that outranked the class rule: `.gen-chat #sl-composer`
   in studio-ambient/elevate + `#sl-composer:focus-within` in studio-live all added 0 0 0 3px accent-glow.
   Match their specificity (id) + !important so the orange is gone everywhere, both themes. */
.composer.composer, #sl-composer.composer, .gen-chat #sl-composer {
  border: 1px solid var(--sn-hairline-strong) !important; box-shadow: var(--sn-elev-2) !important; }
.composer.composer:focus-within, #sl-composer.composer:focus-within, .gen-chat #sl-composer:focus-within {
  border-color: color-mix(in srgb, var(--sn-ink-3) 55%, transparent) !important; box-shadow: var(--sn-elev-3) !important; }
/* the text caret stays neutral too (was orange) */
.composer .cmp-prompt textarea, .composer #cm-ta, .composer #tl-brief { caret-color: var(--sn-ink) !important; }
/* the ⋯ advanced panel: tucked-away controls, a clean rounded sub-panel above the prompt */
.composer.is-slim { display: flex; flex-direction: column; }
.composer.is-slim .cmp-adv-panel { display: none; order: -1; margin: 0 0 12px; padding: 12px; border-radius: var(--sn-r-card);
  background: var(--sn-bg-raised); }
.composer.is-slim.adv-open .cmp-adv-panel { display: flex; }

/* ============================================================================
   Image → 3D model (V.model3d): the src→viewer pair, the WebGL viewer, exports.
   studio-system.css loads LAST, so these win the cascade. The viewer MUST carry
   an explicit height or the WebGL canvas collapses to 0px and renders nothing.
   ============================================================================ */
.m3d-pair { margin: 0 0 32px; padding: 0 0 28px; border-bottom: 1px solid var(--sn-hairline); }
.m3d-grid { display: grid; grid-template-columns: minmax(0,0.85fr) 44px minmax(0,1.15fr); gap: 14px; align-items: stretch; }
.m3d-cell { min-width: 0; display: flex; flex-direction: column; }
.m3d-cell .pair-k { font-size: 12px; font-weight: 600; color: var(--sn-ink-3); margin: 0 0 8px; letter-spacing: .02em; text-transform: uppercase; }
.m3d-out { gap: 12px; }
.m3d-src .pair-still { flex: 1; min-height: 200px; border-radius: var(--sn-r-card); overflow: hidden; display: flex; }
.m3d-src .pair-still.has-img { border: 1px solid var(--sn-hairline); background: var(--sn-bg-raised); }
.m3d-src .pair-still > img { width: 100%; height: 100%; object-fit: contain; display: block; }
.pair-link { align-self: center; display: grid; place-items: center; }
.pair-link .pair-coin { width: 30px; height: 30px; border-radius: 999px; display: grid; place-items: center; background: var(--sn-surface); border: 1px solid var(--sn-hairline-strong); color: var(--sn-ink-3); box-shadow: var(--sn-elev-1); }
.pair-link .pair-coin .sn-ic { width: 15px; height: 15px; }

/* WebGL hero viewer — a soft studio backdrop, rounded, with floating orbit/reset controls. */
.m3d-viewer { flex: 1; min-height: 400px; border: 1px solid var(--sn-hairline); border-radius: var(--sn-r-card); overflow: hidden;
  position: relative; background: radial-gradient(130% 120% at 50% 12%, #f7f8fb 0%, #eceef4 52%, #dde1ea 100%);
  box-shadow: var(--sn-elev-1), inset 0 1px 0 rgba(255,255,255,.5); cursor: grab; }
.m3d-viewer:active { cursor: grabbing; }
.m3d-viewer canvas { display: block; width: 100% !important; height: 100% !important; }
:root[data-theme="dark"] .m3d-viewer, html[data-anon][data-theme="dark"] .m3d-viewer {
  background: radial-gradient(130% 120% at 50% 12%, #20242e 0%, #15171e 58%, #0d0e13 100%); box-shadow: var(--sn-elev-1), inset 0 1px 0 rgba(255,255,255,.04); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .m3d-viewer {
  background: radial-gradient(130% 120% at 50% 12%, #20242e 0%, #15171e 58%, #0d0e13 100%); box-shadow: var(--sn-elev-1), inset 0 1px 0 rgba(255,255,255,.04); } }

/* Empty / idle / error / pending share the same rounded card; each state paints its own contents. */
.m3d-out-empty { flex: 1; min-height: 220px; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px; text-align: center; color: var(--sn-ink-3); font-size: 14px; border: 1px dashed var(--sn-hairline-strong);
  border-radius: var(--sn-r-card); padding: 28px 24px; background:
    radial-gradient(120% 100% at 50% 0%, color-mix(in srgb, var(--sn-accent) 5%, transparent), transparent 60%), var(--sn-bg-raised); }
.m3d-out-empty.is-src { min-height: 200px; gap: 8px; }
.m3d-out-empty.is-src .sn-ic { width: 24px; height: 24px; opacity: .5; }
.m3d-idle-copy { display: flex; flex-direction: column; gap: 4px; max-width: 320px; }
.m3d-idle-copy b { font-size: 15px; font-weight: 600; color: var(--sn-ink); }
.m3d-idle-copy span { font-size: 13px; line-height: 1.5; color: var(--sn-ink-3); }
.m3d-idle-orb { width: 56px; height: 56px; border-radius: var(--sn-r-panel); display: grid; place-items: center; color: var(--sn-on-accent);
  background: linear-gradient(150deg, var(--sn-accent), color-mix(in srgb, var(--sn-accent) 55%, #000)); box-shadow: var(--sn-elev-2), 0 0 0 6px color-mix(in srgb, var(--sn-accent) 12%, transparent); }
.m3d-idle-orb .sn-ic { width: 26px; height: 26px; }
.m3d-fmt-row { display: flex; gap: 7px; }
.m3d-fmt { font: 600 11px/1 var(--sn-mono, ui-monospace, monospace); letter-spacing: .06em; padding: 5px 9px; border-radius: var(--sn-r-input);
  color: var(--sn-ink-2); background: var(--sn-surface); border: 1px solid var(--sn-hairline); }
.m3d-out-empty.is-err { color: var(--sn-error); border-color: color-mix(in srgb, var(--sn-error) 40%, transparent); background: var(--sn-error-soft); }
.m3d-out-empty.is-err > .sn-ic { width: 26px; height: 26px; }

/* Sculpting state — a rotating wireframe cube + a moving progress track + cycling status, never a dead spinner. */
.m3d-sculpt-stage { width: 72px; height: 72px; display: grid; place-items: center; perspective: 260px; }
.m3d-sculpt-cube { position: relative; width: 40px; height: 40px; transform-style: preserve-3d; animation: m3d-tumble 4.4s linear infinite; }
.m3d-sculpt-cube i { position: absolute; inset: 0; border: 1.5px solid color-mix(in srgb, var(--sn-accent) 70%, transparent); background: color-mix(in srgb, var(--sn-accent) 9%, transparent); }
.m3d-sculpt-cube i:nth-child(1) { transform: rotateY(0deg) translateZ(20px); }
.m3d-sculpt-cube i:nth-child(2) { transform: rotateY(90deg) translateZ(20px); }
.m3d-sculpt-cube i:nth-child(3) { transform: rotateY(180deg) translateZ(20px); }
.m3d-sculpt-cube i:nth-child(4) { transform: rotateY(-90deg) translateZ(20px); }
.m3d-sculpt-cube i:nth-child(5) { transform: rotateX(90deg) translateZ(20px); }
.m3d-sculpt-cube i:nth-child(6) { transform: rotateX(-90deg) translateZ(20px); }
@keyframes m3d-tumble { from { transform: rotateX(-24deg) rotateY(0); } to { transform: rotateX(-24deg) rotateY(360deg); } }
.m3d-sculpt-track { width: min(220px, 80%); height: 5px; border-radius: 999px; overflow: hidden; background: color-mix(in srgb, var(--sn-ink-3) 22%, transparent); }
.m3d-sculpt-fill { display: block; height: 100%; width: 40%; border-radius: 999px; background: linear-gradient(90deg, transparent, var(--sn-accent), transparent); animation: m3d-scan 1.5s var(--sn-ease, ease) infinite; }
@keyframes m3d-scan { from { transform: translateX(-120%); } to { transform: translateX(320%); } }
.m3d-sculpt .m3d-sculpt-lbl { color: var(--sn-ink); }
@media (prefers-reduced-motion: reduce) { .m3d-sculpt-cube { animation-duration: 9s; } .m3d-sculpt-fill { animation-duration: 3s; } }

/* Result toolbar: labeled export groups + floating orbit controls. */
.m3d-bar { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.m3d-dl { display: flex; gap: 18px; flex-wrap: wrap; }
.m3d-dl-group { display: flex; flex-direction: column; gap: 6px; }
.m3d-dl-k { font: 600 10.5px/1 var(--sn-mono, ui-monospace, monospace); letter-spacing: .05em; text-transform: uppercase; color: var(--sn-ink-3); }
.m3d-dl-btns { display: flex; gap: 7px; }
.m3d-meta { display: flex; align-items: center; gap: 10px; }
.m3d-hint { font-size: 12px; color: var(--sn-ink-3); }
.m3d-view-ctl { display: inline-flex; gap: 6px; }
.m3d-ctl { width: 32px; height: 32px; display: grid; place-items: center; border-radius: var(--sn-r-input); cursor: pointer; color: var(--sn-ink-2);
  background: var(--sn-surface); border: 1px solid var(--sn-hairline-strong); transition: background var(--sn-t-micro, .12s), color var(--sn-t-micro, .12s), border-color var(--sn-t-micro, .12s); }
.m3d-ctl svg { width: 17px; height: 17px; }
.m3d-ctl:hover { color: var(--sn-ink); border-color: var(--sn-ink-3); }
.m3d-ctl.is-on { color: var(--sn-on-accent); background: var(--sn-accent); border-color: var(--sn-accent); }
.m3d-badge { font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: 999px; white-space: nowrap; }
.m3d-badge-ok { background: color-mix(in srgb, #1e9e5a 16%, transparent); color: #1e9e5a; }
.m3d-badge-warn { background: color-mix(in srgb, #d98a1f 18%, transparent); color: #b9760f; }
.m3d-badge-info { background: var(--sn-bg-raised); color: var(--sn-ink-3); border: 1px solid var(--sn-hairline); }
.card.is-3d .badge-type { background: color-mix(in srgb, var(--sn-accent) 85%, #000); color: var(--sn-on-accent); }
@media (max-width: 720px) { .m3d-grid { grid-template-columns: 1fr; } .m3d-grid .pair-link { display: none; } }

/* ============================================================================
   Billing & credits (V.billing) — a balance hero with usage meter, plan card,
   and transactions card. Resend-grade: rounded, calm, both themes.
   ============================================================================ */
.bill-k { font: 600 11px/1 var(--sn-mono, ui-monospace, monospace); letter-spacing: .05em; text-transform: uppercase; color: var(--sn-ink-3); margin: 0 0 6px; }
.bill-hero { display: flex; align-items: center; justify-content: space-between; gap: 24px; flex-wrap: wrap; padding: 24px 26px; margin: 0 0 18px;
  background: radial-gradient(120% 140% at 0% 0%, color-mix(in srgb, var(--sn-accent) 10%, transparent), transparent 55%), var(--sn-surface); }
.bill-hero-main { display: flex; align-items: center; gap: 30px; flex: 1; min-width: 260px; flex-wrap: wrap; }
.bill-bal { flex: 0 0 auto; }
.bill-bal-num { font: 600 3rem/1 var(--sn-serif); color: var(--sn-ink); letter-spacing: -.02em; }
.bill-bal-lbl { font-size: 13px; color: var(--sn-ink-3); margin-top: 4px; }
.bill-meter { flex: 1; min-width: 220px; max-width: 460px; }
.bill-meter-track { height: 8px; border-radius: 999px; background: color-mix(in srgb, var(--sn-ink-3) 20%, transparent); overflow: hidden; }
.bill-meter-track > span { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--sn-accent), color-mix(in srgb, var(--sn-accent) 60%, #fff)); transition: width .5s var(--sn-ease, ease); }
.bill-meter-cap { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 9px; font-size: 12px; color: var(--sn-ink-3); flex-wrap: wrap; }
.bill-meter-cap b { color: var(--sn-ink-2); font-weight: 600; }
.bill-reset { display: inline-flex; align-items: center; gap: 5px; }
.bill-reset .sn-ic { width: 13px; height: 13px; opacity: .7; }
.bill-topup { display: inline-flex; align-items: center; gap: 6px; margin-top: 10px; font-size: 12px; font-weight: 500; color: var(--sn-accent);
  padding: 4px 10px; border-radius: 999px; background: color-mix(in srgb, var(--sn-accent) 12%, transparent); }
.bill-topup .sn-ic { width: 13px; height: 13px; }
.bill-hero-cta { display: flex; flex-direction: column; gap: 9px; flex: 0 0 auto; }
.bill-hero-cta .sn-btn { justify-content: center; min-width: 168px; }
@media (max-width: 640px) { .bill-hero-cta { flex-direction: row; width: 100%; } .bill-hero-cta .sn-btn { flex: 1; min-width: 0; } }

/* Inline quick-buy: the $10/$25/$50/$100 top-up amounts shown at a glance (full-width row in the hero),
   so the flexible top-up is visible without first clicking "Buy credits". */
.bill-quick { flex: 1 0 100%; display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin-top: 4px; padding-top: 18px; border-top: 1px solid var(--sn-hairline); }
.bill-quick-lbl { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; font-weight: 600; color: var(--sn-ink-2); }
.bill-quick-lbl .sn-ic { width: 14px; height: 14px; color: var(--sn-accent); }
.bill-quick-lbl span { color: var(--sn-ink-3); font-weight: 500; }
.bill-quick-chips { display: flex; gap: 8px; flex-wrap: wrap; }
.bill-quick-chip { height: 40px; min-width: 62px; padding: 0 16px; border-radius: var(--sn-r-input); border: 1px solid var(--sn-hairline-strong);
  background: var(--sn-surface); color: var(--sn-ink); font: 600 15px/1 inherit; cursor: pointer;
  transition: border-color .15s var(--sn-ease, ease), background .15s var(--sn-ease, ease); }
.bill-quick-chip:hover { border-color: var(--sn-accent); background: color-mix(in srgb, var(--sn-accent) 10%, transparent); }
.bill-quick-more { font-weight: 500; color: var(--sn-ink-2); }
@media (max-width: 640px) { .bill-quick { gap: 10px; } .bill-quick-chips { width: 100%; } .bill-quick-chip { flex: 1; min-width: 0; padding: 0 8px; } }

.bill-cols { display: grid; grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.5fr); gap: 18px; align-items: start; }
@media (max-width: 820px) { .bill-cols { grid-template-columns: 1fr; } }
.bill-plan { padding: 20px 22px; }
.bill-plan-name { font: 600 1.375rem/1.1 var(--sn-serif); color: var(--sn-ink); display: flex; align-items: center; gap: 9px; }
.bill-plan-rows { margin: 16px 0 18px; }
.bill-plan-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 11px 0; border-top: 1px solid var(--sn-hairline); font-size: 14px; }
.bill-plan-row:first-child { border-top: none; }
.bill-plan-row span { color: var(--sn-ink-3); }
.bill-plan-row b { color: var(--sn-ink); font-weight: 600; }
.bill-manage { width: 100%; justify-content: center; }
.bill-txns { padding: 20px 22px; }
.bill-empty { display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; padding: 34px 20px; color: var(--sn-ink-3); font-size: 13px;
  border: 1px dashed var(--sn-hairline-strong); border-radius: var(--sn-r-card); }
.bill-empty .sn-ic { width: 22px; height: 22px; opacity: .5; }

/* ============================================================================
   Out-of-credits / upgrade paywall (openOutOfCredits) — a large, media-forward
   modal in the OpenArt paywall idiom: an autoplaying muted looping showcase clip
   dominant on one side, the value copy + cost-and-short line + CTAs on the other.
   Two columns on a laptop; stacks (clip on top) on narrow screens. Light + dark.
   ============================================================================ */
.ooc-modal { width: min(880px, calc(100vw - 32px)); max-height: calc(100vh - 32px); padding: 0; overflow: hidden; position: relative; }
.ooc-modal .ooc-x { position: absolute; top: 12px; right: 12px; z-index: 4; color: #F5F4EF;
  background: rgba(10, 9, 8, .42); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.ooc-modal .ooc-x:hover { background: rgba(10, 9, 8, .64); color: #fff; }
.ooc-layout { display: grid; grid-template-columns: 1.04fr 1fr; align-items: stretch; min-height: 384px; }

/* Media side — the showcase clip fills the column edge-to-edge. */
.ooc-hero { position: relative; margin: 0; overflow: hidden; background: #0B0A09; min-height: 220px; }
.ooc-hero > .ooc-video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; background: #0B0A09; }
.ooc-hero-badge { position: absolute; top: 14px; left: 14px; z-index: 2; display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 11px 5px 9px; border-radius: 999px; font: 600 11px/1 var(--sn-sans); letter-spacing: .01em; color: #F5F4EF;
  background: rgba(10, 9, 8, .5); backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px); }
.ooc-dot { width: 6px; height: 6px; border-radius: 50%; background: #46D48A; box-shadow: 0 0 0 3px rgba(70, 212, 138, .24); }
.ooc-hero-cap { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; padding: 40px 16px 14px; display: flex; flex-direction: column; gap: 2px;
  background: linear-gradient(transparent, rgba(8, 7, 6, .82)); }
.ooc-hero-cap b { font: 600 14px/1.3 var(--sn-sans); color: #F7F6F1; }
.ooc-hero-cap span { font: 12px/1.35 var(--sn-sans); color: rgba(247, 246, 241, .74); }

/* Content side. */
.ooc-content { display: flex; flex-direction: column; padding: 30px 30px 26px; min-width: 0; }
.ooc-title { font: 600 1.5rem/1.15 var(--sn-serif); letter-spacing: -.01em; color: var(--sn-ink); margin: 0 0 12px; }
.ooc-copy { font: .9375rem/1.6 var(--sn-sans); color: var(--sn-ink-2); margin: 0; }
.ooc-copy b { color: var(--sn-ink); font-weight: 600; }
.ooc-cta { margin-top: auto; padding-top: 22px; display: flex; flex-direction: column; gap: 9px; }
.ooc-btn { width: 100%; justify-content: center; }
.ooc-rec { margin-left: 8px; background: rgba(255, 255, 255, .22); color: #fff; }
.ooc-btn-tertiary { color: var(--sn-ink-3); }

@media (max-width: 640px) {
  .ooc-modal { width: calc(100vw - 24px); overflow-y: auto; }
  .ooc-layout { grid-template-columns: 1fr; min-height: 0; }
  .ooc-hero { aspect-ratio: 16 / 9; min-height: 0; }
  .ooc-hero > .ooc-video { position: static; width: 100%; height: 100%; }
  .ooc-content { padding: 20px 22px 22px; }
  .ooc-title { font-size: 1.3rem; }
}

/* ============================================================================
   Buy-credits modal (openBuyCredits) — clean uniform rectangle cards on a
   responsive grid, DS type. Replaces the cramped 3-col stat grid.
   ============================================================================ */
/* Moderately-wide LANDSCAPE modal — tiers in a comfortable 2-column grid, scrollable. Balanced rectangle:
   not the old cramped 3-across, not the narrow portrait. */
.bc-modal { max-width: 680px; width: min(680px, 94vw); max-height: 88vh; display: flex; flex-direction: column; }
.bc-modal .sn-modal-body { overflow-y: auto; }
.bc-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 16px; flex-wrap: wrap; }
.bc-save { font: 600 11px/1 var(--sn-mono, ui-monospace, monospace); letter-spacing: .04em; text-transform: uppercase; color: var(--sn-accent); }
.bc-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
@media (max-width: 560px) { .bc-list { grid-template-columns: 1fr; } }
.bc-row { display: flex; align-items: center; gap: 12px; padding: 12px 16px; border-radius: var(--sn-r-card);
  background: var(--sn-surface); border: 1px solid var(--sn-hairline);
  transition: border-color var(--sn-t-micro, .12s), box-shadow var(--sn-t-micro, .12s); }
.bc-row:hover { border-color: var(--sn-hairline-strong); box-shadow: var(--sn-elev-1); }
.bc-row.is-pop { border-color: color-mix(in srgb, var(--sn-accent) 55%, transparent); box-shadow: 0 0 0 1px color-mix(in srgb, var(--sn-accent) 40%, transparent); }
.bc-row-main { flex: 1; min-width: 0; }
.bc-row-cr { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font: 600 1.0625rem/1.1 var(--sn-serif); color: var(--sn-ink); letter-spacing: -.01em; }
.bc-row-cr > span:not(.bc-pop) { font: 500 .8125rem var(--sn-sans); color: var(--sn-ink-3); letter-spacing: 0; }
.bc-row-sub { font-size: 12px; line-height: 1.4; color: var(--sn-ink-3); margin-top: 3px; }
.bc-row-price { font: 600 1.0625rem/1 var(--sn-sans); color: var(--sn-ink); white-space: nowrap; text-align: right; }
.bc-row-price > span { font: 500 .6875rem var(--sn-sans); color: var(--sn-ink-3); }
.bc-row .bc-buy { flex: 0 0 auto; min-width: 88px; justify-content: center; }
/* Popular — an INLINE pill (never clipped by the scroll container), high-contrast WHITE on accent in both modes. */
.bc-pop { display: inline-block; font: 700 10px/1 var(--sn-mono, ui-monospace, monospace); letter-spacing: .06em; text-transform: uppercase;
  padding: 3px 7px; border-radius: 999px; background: var(--sn-accent); color: var(--sn-on-accent); white-space: nowrap; }
@media (max-width: 380px) { .bc-row { flex-wrap: wrap; } .bc-row .bc-buy { width: 100%; } }
/* Current plan — the tier the user is already on: muted, non-buyable (can't re-buy your own plan). */
.bc-row.is-current { opacity: .72; background: var(--sn-bg-raised); border-color: var(--sn-hairline); box-shadow: none; }
/* A NAMED plan (pro/team/custom grant) matches no ladder row — the modal says what the user is on in
   words, so the picker never reads as plan-blind to the one user most likely to be shopping it. */
.bc-current-note { margin: -6px 0 12px; font-size: .8125rem; line-height: 1.5; color: var(--sn-ink-2); }
.bc-current-note b { color: var(--sn-ink); }
.bc-row.is-current:hover { border-color: var(--sn-hairline); box-shadow: none; }
.bc-pop.bc-cur { background: var(--sn-ink-3); color: var(--sn-surface); }

/* ============================================================================
   Flexible top-up (openTopUp) — fal-style: a dollar amount (quick chips or a
   custom value, min $10) that buys never-expiring credits. Compact, both modes.
   ============================================================================ */
.tu-modal { max-width: 440px; width: min(440px, calc(100vw - 32px)); }
.tu-lede { margin: 0 0 14px; font: .9375rem/1.5 var(--sn-sans); color: var(--sn-ink-2); }
.tu-quick { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin: 0 0 10px; }
.tu-chip { height: 42px; border-radius: var(--sn-r-input); border: 1px solid var(--sn-hairline-strong); background: var(--sn-surface);
  font: 600 .9375rem var(--sn-sans); color: var(--sn-ink); cursor: pointer;
  transition: border-color var(--sn-t-micro, .12s), background var(--sn-t-micro, .12s), color var(--sn-t-micro, .12s); }
.tu-chip:hover { border-color: var(--sn-accent); }
.tu-chip.is-active { border-color: var(--sn-accent); background: color-mix(in srgb, var(--sn-accent) 14%, transparent); color: var(--sn-ink); }
.tu-custom { display: flex; align-items: center; gap: 2px; height: 48px; padding: 0 14px; border-radius: var(--sn-r-card);
  border: 1px solid var(--sn-hairline-strong); background: var(--sn-surface); }
.tu-custom:focus-within { border-color: var(--sn-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--sn-accent) 18%, transparent); }
.tu-cur { font: 600 1.125rem var(--sn-sans); color: var(--sn-ink-3); }
.tu-input { flex: 1; min-width: 0; border: none; background: none; outline: none; color: var(--sn-ink);
  font: 600 1.125rem var(--sn-sans); -moz-appearance: textfield; }
.tu-input::-webkit-outer-spin-button, .tu-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.tu-est { margin: 12px 0 0; font: .9375rem/1.5 var(--sn-sans); color: var(--sn-ink-2); }
.tu-est b { color: var(--sn-ink); font-weight: 600; }
.tu-err { margin: 6px 0 0; font: .8125rem var(--sn-sans); color: var(--sn-error, #C6493B); }
.tu-note { display: flex; align-items: center; gap: 7px; margin: 14px 0 0; padding-top: 13px; border-top: 1px solid var(--sn-hairline);
  font: .8125rem/1.4 var(--sn-sans); color: var(--sn-ink-3); }
.tu-note .sn-ic { width: 15px; height: 15px; color: var(--sn-accent); flex: 0 0 auto; }
.tu-confirm[disabled] { opacity: .5; cursor: not-allowed; }

/* Inline Sound toggle in the composer prompt row (video + Producer) — always visible, directly selectable.
   Was buried in the ⋯ advanced panel ("can't select sound"). Active (sound on) reads as an accent chip. */
.cmp-snd { flex: 0 0 auto; white-space: nowrap; }
.cmp-snd[aria-pressed="true"] { background: var(--sn-accent-soft); border-color: var(--sn-accent); color: var(--sn-accent-ink); }
.cmp-snd[aria-pressed="true"] .sn-ic { color: var(--sn-accent-ink); }

/* =============================================================================
   SINGLE-BOX FIELDS (Rob) — wherever a user types, the PILL/container is the ONE
   box. A text control nested inside a field-shell draws NO second border, fill,
   focus ring or outline (at rest OR focused, both themes) — so it can never read
   as "a box inside a box" / double orange border. The shell keeps its own styling
   + focus-within indicator. Standalone fields (.sn-field > .sn-input, form inputs)
   are the box themselves and are deliberately untouched.
   Shells: the composer (URL→ad bar, Producer/Image/Video prompt), the affix input,
   and any element opting in with .sn-fieldshell.
   ========================================================================== */
.app .composer textarea, .app .composer .sn-input, .app .composer .sn-textarea,
.app .composer input[type="text"], .app .composer input[type="url"],
.app .composer input[type="search"], .app .composer input[type="email"],
.app .cmp-prompt textarea, .app .cmp-prompt .sn-input,
.app .sn-input-affix .sn-input, .app .tl-url input, .app .tl-url .sn-input,
.app .sn-fieldshell textarea, .app .sn-fieldshell input, .app .sn-fieldshell .sn-input {
  border: 0 !important; background: transparent !important; box-shadow: none !important; outline: none !important; }
.app .composer textarea:focus, .app .composer .sn-input:focus, .app .composer .sn-textarea:focus,
.app .composer input[type="text"]:focus, .app .composer input[type="url"]:focus,
.app .composer input[type="search"]:focus, .app .composer input[type="email"]:focus,
.app .cmp-prompt textarea:focus, .app .cmp-prompt .sn-input:focus,
.app .sn-input-affix .sn-input:focus, .app .tl-url input:focus, .app .tl-url .sn-input:focus,
.app .sn-fieldshell textarea:focus, .app .sn-fieldshell input:focus, .app .sn-fieldshell .sn-input:focus {
  border: 0 !important; background: transparent !important; box-shadow: none !important; outline: none !important; }

/* ==== BUSINESS SUITE — Real Estate + Product Shots (native /studio views) ===========================
   Only what the .sn-* system does not already ship. Sections are .sn-card, fields are
   .sn-field/.sn-label/.sn-input, and buttons are .sn-btn. Everything below is layout plus the four
   things with no .sn-* equivalent ON THIS PAGE: the picker card, a photo grid, a three-state compliance
   banner, and an export rack. Every value is an --sn-* token, so light mode and the ambient theme
   follow for free.

   THE PICKER IS SELF-CONTAINED, deliberately. The first pass built it on .sn-pack, which looks like the
   obvious fit — it even ships an aria-current selected state. But .sn-pack lives in billing.css, and
   studio-live.html does not load billing.css: the cards fell back to raw <button> chrome, a pale grey
   slab with the ink tokens rendering white-on-white. A class existing in the repo is not the same as a
   class being IN SCOPE on the page you are styling. So .biz-pack resets the button and draws itself.  */

.biz-view .biz-card { padding: 22px 24px; margin-bottom: 18px; }
.biz-note { font: 500 .8125rem/1 var(--sn-sans); }

.biz-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px; }
.biz-grid > * { min-width: 0; }
.biz-color { padding: 4px; height: 38px; cursor: pointer; }
.sn-input-sm { height: 32px; font-size: .8125rem; padding: 0 9px; }

/* ---- dropzone: the .sn-card border language, dashed, with a live drag state ---- */
.biz-drop { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  min-height: 132px; padding: 20px; text-align: center; cursor: pointer;
  border: 1px dashed var(--sn-hairline); border-radius: var(--sn-r-card); background: var(--sn-bg);
  transition: border-color .16s ease, background .16s ease; }
.biz-drop:hover, .biz-drop:focus-visible { border-color: var(--sn-ink-3); background: var(--sn-bg-raised); outline: none; }
.biz-drop.is-over { border-color: var(--sn-accent); background: var(--sn-bg-raised); }
.biz-drop b { font: 600 .9375rem/1.3 var(--sn-sans); color: var(--sn-ink); }
.biz-drop .sn-ic-slot { opacity: .6; }
.biz-drop.has-img { min-height: 0; padding: 14px; }
.biz-hero { max-width: 220px; max-height: 170px; object-fit: contain; border-radius: var(--sn-r-media); display: block; }

/* ---- photo grid ---- */
.biz-photos { display: grid; grid-template-columns: repeat(auto-fill, minmax(164px, 1fr)); gap: 12px; margin-top: 14px; }
.biz-photos > * { min-width: 0; }
.biz-photo { position: relative; display: flex; flex-direction: column; gap: 6px; padding: 8px;
  border: 1px solid var(--sn-hairline); border-radius: var(--sn-r-card); background: var(--sn-bg-raised); }
.biz-photo.is-bad { border-color: var(--sn-error); }
.biz-photo b { font: 600 .8125rem/1.3 var(--sn-sans); color: var(--sn-ink); }
.biz-thumb { position: relative; aspect-ratio: 4/3; border-radius: var(--sn-r-media); overflow: hidden; background: var(--sn-bg);
  display: grid; place-items: center; }
.biz-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.biz-ord { position: absolute; left: 6px; top: 6px; min-width: 20px; height: 20px; padding: 0 6px;
  display: grid; place-items: center; border-radius: var(--sn-r-media); background: rgba(0,0,0,.62); color: #fff;
  font: 600 .6875rem/1 var(--sn-mono, monospace); }
.biz-rm { position: absolute; right: 6px; top: 6px; width: 22px; height: 22px; border: 0; border-radius: var(--sn-r-input);
  background: rgba(0,0,0,.62); color: #fff; font-size: 15px; line-height: 1; cursor: pointer; }
.biz-rm:hover { background: var(--sn-error); }
/* The three per-photo controls. They are JS hooks too, but they carry real style: a <select> inside a
   164px card will happily overflow it without an explicit width. */
.biz-room, .biz-cap, .biz-pace { width: 100%; min-width: 0; }
.biz-disc { accent-color: var(--sn-accent); }
.biz-stage { display: flex; align-items: center; gap: 7px; font: 500 .75rem/1.3 var(--sn-sans); color: var(--sn-ink-2); cursor: pointer; }
/* The brokerage logo well — a drop target that becomes the mark once one is set. Sized to the same row
   height as the fields beside it so the identity block stays on one grid. */
.re-logo { display: grid; place-items: center; min-height: 40px; padding: 6px 10px; cursor: pointer;
  border: 1px dashed var(--sn-hairline); border-radius: var(--sn-r-input); background: var(--sn-bg-raised);
  font-size: .75rem; text-align: center; }
.re-logo:hover, .re-logo:focus-visible { border-color: var(--sn-accent); outline: none; }
.re-logo.is-set { border-style: solid; padding: 4px; background: var(--sn-surface); }
.re-logo img { max-height: 46px; max-width: 100%; object-fit: contain; }
.biz-spin { margin-top: 14px; font-size: .8125rem; }
.biz-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }

/* ---- the paced cost sheet (Walkthrough) ----
   A walkthrough has no cap on its length, which is only fair if its size is legible BEFORE the button is
   pressed. This is that itemisation: one row per clip, the rooms it joins, how long it holds, and what
   that length costs. Deliberately a table rather than a prose summary — an agent reconciling a four-figure
   number on a thirty-photo listing is reading down a column, not reading a sentence. */
/* The lifestyle room checklist — a wrapping row of per-room toggles, not a select: an agent is choosing
   several rooms at once and needs to see all of them and their state together. */
.biz-rooms { display: flex; flex-wrap: wrap; gap: 8px 18px; margin-top: 12px; }
.biz-life { accent-color: var(--sn-accent); }

.biz-sheet { width: 100%; border-collapse: collapse; margin-top: 12px; font-size: .8125rem; }
.biz-sheet td { padding: 7px 0; border-bottom: 1px solid var(--sn-hairline); vertical-align: baseline; }
.biz-sheet td:nth-child(2) { width: 5.5rem; text-align: right; color: var(--sn-ink-2); }
.biz-sheet td:last-child { width: 8.5rem; text-align: right; white-space: nowrap; }
.biz-sheet tr:last-child td { border-bottom: 0; }
.biz-sheet tr.is-total td { padding-top: 11px; border-top: 1px solid var(--sn-ink-3); border-bottom: 0; }

/* The rights + accuracy attestation. Given its own card rather than tucked under the button: it is a
   statement the agent is making, not a control they are operating, and burying it would be the whole
   problem with every checkbox nobody reads. */
.biz-attest { border-color: var(--sn-accent-soft); }
.biz-attest .biz-stage { align-items: flex-start; font-size: .8125rem; color: var(--sn-ink); }
.biz-attest .biz-stage input { margin-top: 2px; flex: 0 0 auto; accent-color: var(--sn-accent); }
.biz-attest p { margin: 8px 0 0 24px; }

/* ---- tier / pack picker (self-contained; see the header) ---- */
.biz-packs { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; }
.biz-packs > * { min-width: 0; }
.biz-pack { appearance: none; -webkit-appearance: none; font: inherit; margin: 0;
  display: flex; flex-direction: column; gap: 6px; text-align: left; padding: 14px 15px; cursor: pointer;
  border: 1px solid var(--sn-hairline); border-radius: var(--sn-r-card); background: var(--sn-bg);
  transition: border-color .16s ease, background .16s ease; }
.biz-pack:hover:not([disabled]) { border-color: var(--sn-ink-3); background: var(--sn-bg-raised); }
.biz-pack:focus-visible { outline: none; box-shadow: var(--sn-focus); }
.biz-pack[aria-current="true"] { border-color: var(--sn-accent); box-shadow: 0 0 0 1px var(--sn-accent); background: var(--sn-bg-raised); }
.biz-pack-h { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.biz-pack-h b { font: 600 .9375rem/1.2 var(--sn-sans); color: var(--sn-ink); }
.biz-pack-h i { font-style: normal; font-size: .6875rem; color: var(--sn-accent); white-space: nowrap; }
.biz-pack-b { font: 400 .8125rem/1.5 var(--sn-sans); color: var(--sn-ink-2); }
.biz-pack .sn-muted, .biz-pack .biz-warn { font-size: .6875rem; line-height: 1.45; }
.biz-pack[disabled] { opacity: .55; cursor: not-allowed; }

/* ---- compliance banner: three states, because the screen returns three verdicts.
       A refusal is not a suggestion, and a suggestion must not read as a refusal. ---- */
.biz-banner { margin-top: 14px; padding: 12px 14px; border: 1px solid var(--sn-hairline);
  border-left: 3px solid var(--sn-hairline); border-radius: var(--sn-r-card);
  background: var(--sn-bg-raised); font: 400 .8125rem/1.55 var(--sn-sans); color: var(--sn-ink-2); }
.biz-banner b { display: block; margin-bottom: 5px; font-weight: 600; color: var(--sn-ink); }
.biz-banner ul { margin: 5px 0 0; padding-left: 18px; }
.biz-banner li { margin: 3px 0; }
.biz-banner i { font-style: normal; color: var(--sn-ink-3); }
.biz-banner p { margin: 8px 0 0; font-size: .75rem; }
.biz-banner.is-bad { border-left-color: var(--sn-error); }
.biz-banner.is-bad b { color: var(--sn-error); }
.biz-banner.is-warn { border-left-color: var(--sn-warn); }
.biz-banner.is-warn b { color: var(--sn-warn); }
.biz-banner.is-ok { border-left-color: var(--sn-success); color: var(--sn-ink-3); }
.biz-warn { color: var(--sn-warn); }

/* ---- the money row ---- */
.biz-ship { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.biz-ship-l { flex: 1; min-width: 200px; }
.biz-price { font: 600 1.375rem/1.1 var(--sn-serif); color: var(--sn-ink); margin-bottom: 3px; }
.biz-hint { width: 100%; margin: 0; font-size: .75rem; }

/* ---- export rack ---- */
.biz-cuts { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 14px; align-items: start; }
.biz-cuts > * { min-width: 0; }
/* Each cut is now a BUTTON that opens the Premiere modal — the three deliverables no longer play
   auto-muted and looping in three unbalanced cells. The tile is a poster; the film plays in the modal
   with sound, once, at the aspect it was rendered for. */
.biz-cut { display: flex; flex-direction: column; gap: 6px; padding: 10px; width: 100%; text-align: left;
  appearance: none; cursor: pointer; font: inherit;
  border: 1px solid var(--sn-hairline); border-radius: var(--sn-r-card); background: var(--sn-bg-raised);
  transition: border-color var(--sn-t-hover) var(--sn-ease), background var(--sn-t-hover) var(--sn-ease); }
.biz-cut:hover { border-color: var(--sn-hairline-strong); background: var(--sn-surface); }
.biz-cut-media { position: relative; display: grid; place-items: center; width: 100%; aspect-ratio: 16/9;
  border-radius: var(--sn-r-media); background: #000 center/cover no-repeat; }
.biz-cut.is-vert .biz-cut-media { aspect-ratio: 9/16; }
.biz-cut-play { width: 40px; height: 40px; display: grid; place-items: center; border-radius: var(--sn-r-pill);
  background: var(--sn-glass-strong); color: var(--sn-on-media); }
.biz-cut-play .sn-ic { width: 18px; height: 18px; }
.biz-cut b { font: 600 .875rem/1.2 var(--sn-sans); color: var(--sn-ink); }
.biz-cut span.sn-muted { font-size: .75rem; }
.biz-cut-foot { margin: 12px 0 0; font-size: .75rem; }

@media (max-width: 720px) {
  .biz-view .biz-card { padding: 16px 15px; }
  .biz-grid { grid-template-columns: 1fr 1fr; gap: 10px; }
  .biz-photos { grid-template-columns: repeat(auto-fill, minmax(138px, 1fr)); }
  .biz-packs { grid-template-columns: 1fr; }
  .biz-ship { flex-direction: column; align-items: stretch; }
  .biz-ship .sn-btn { width: 100%; }
}

/* =============================================================================
   §74 — THE BRAND COMES BACK TO THE STUDIO, AS A FINISH.
   =============================================================================
   §72 neutralised every accent in the studio at Rob's instruction; §74 reverses that at Rob's
   instruction. What SURVIVES from §72 is the part that was structural rather than stylistic, and it
   is the reason this reversal is nine lines instead of another fourteen-rule sweep:

     · `--sn-on-accent` / `--pd-on-accent` — the token pair that did not exist before §72. Fourteen
       rules used to hard-code `#fff` onto the accent fill. They now all read the token, so flipping
       the primary from near-white back to a dark brand ramp moves the ink with it, automatically.
     · the file-level scoping — this sheet is loaded by studio pages only, so `:root` here still
       cannot reach the marketing site.

   The accent tokens simply go back to the shared brand values, and the ink on them goes back to
   white — which the contrast work in senova-ui.css verified at 4.60:1 under the glyph band, or
   5.39:1 with the deepened first stop the gloss actually uses. */
:root {
  --sn-on-accent: var(--sn-gloss-ink);
  --sn-ring: var(--sn-accent);
  --sn-focus: 0 0 0 2px var(--sn-bg), 0 0 0 4px color-mix(in srgb, var(--sn-accent) 55%, transparent);
}

/* §73 CHUNK 3 — THE SIDEBAR GROUP LABEL, to shadcn's actual spec.
   SidebarGroupLabel is `flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium
   text-sidebar-foreground/70` — plainly NOT uppercase and NOT letter-spaced. Ours was 11px uppercase with
   .06em tracking, which is the dashboard-chrome idiom shadcn avoids on purpose and part of the texture
   Rob called AI slop.

   ⚠️ Overridden HERE rather than edited in senova-ds.css for the §72 reason: that sheet is shared with the
   marketing site, and this sheet is loaded by the studio pages only. Same containment, same argument. */
:root .sn-nav-label {
  display: flex; align-items: center; height: 32px; padding: 0 8px;
  font: 500 .75rem/1 var(--sn-sans); color: var(--sn-ink-3);
  text-transform: none; letter-spacing: normal;
}

/* ── §99 MATH — scoped to .sn-math, which the view puts on its own wrapper. The §96 lesson applied
   before it could bite again: a scope class is worthless unless something emits it, and this one is
   emitted by mathHTML() (asserted by test). ─────────────────────────────────────────────────────── */
.sn-math .math-compose { display: flex; flex-direction: column; gap: 12px; }
.sn-math .sn-textarea { min-height: 84px; resize: vertical; line-height: 1.5; }
.sn-math .math-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
/* 🔴 §92's LESSON, APPLIED BEFORE IT BIT: the chip carries `.sn-badge`, which is `display:inline-flex`,
   and a class-level display DEFEATS the user-agent `[hidden] { display: none }` rule — that is exactly
   how every gated nav row leaked its link. The chip paints only once the quote lands, so without this
   an empty accent pill would sit beside the button on every load and on every failed quote. */
.sn-math .math-price[hidden] { display: none; }
.sn-math .math-note { font-size: 12px; color: var(--sn-ink-3); line-height: 1.5; max-width: 52ch; }
.sn-math .math-eg { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding-top: 4px; }
.sn-math .math-eg-lb { font-size: 12px; color: var(--sn-ink-3); }

/* §119 — THE TYPE PICKER, THE DATA LANE AND THE NARRATION TOGGLE.
   Every colour is a token, so this is correct in both themes with no theme-specific
   rule — the lesson from the Create picker, applied at the start rather than after. */
.gp-types { display: grid; gap: 14px; margin: 16px 0 4px; }
.gp-grp-h { font: 600 .6875rem/1 var(--sn-sans); text-transform: uppercase;
  letter-spacing: .06em; color: var(--sn-ink-3); margin-bottom: 8px; }
.gp-grp-b { display: flex; flex-wrap: wrap; gap: 6px; }
.gp-type { display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  height: 30px; padding: 0 11px; border-radius: 999px; background: none;
  border: 1px solid var(--sn-hairline-strong); color: var(--sn-ink-2);
  font: 500 .8125rem/1 var(--sn-sans);
  transition: background var(--sn-t-hover) var(--sn-ease), border-color var(--sn-t-hover) var(--sn-ease), color var(--sn-t-hover) var(--sn-ease); }
.gp-type:hover { background: var(--sn-hover); color: var(--sn-ink); }
.gp-type.is-on { background: var(--sn-accent-soft); border-color: var(--sn-accent); color: var(--sn-accent-ink); }
/* Soon reads as unavailable, not as a control: it is a <span>, so there is nothing to click. */
.gp-type.is-soon { opacity: .45; cursor: default; }
.gp-type.is-soon:hover { background: none; color: var(--sn-ink-2); }
.gp-soon { font: 600 .5625rem/1 var(--sn-sans); text-transform: uppercase; letter-spacing: .05em; color: var(--sn-ink-3); }

.sn-math .math-data { margin: 14px 0 4px; padding: 14px; border-radius: var(--sn-r-card);
  background: var(--sn-bg-raised); border: 1px solid var(--sn-hairline); }
.sn-math .math-csv { font: .8125rem/1.5 var(--sn-mono); min-height: 96px; }
.sn-math .math-data-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 10px; }
.sn-math .math-upload { cursor: pointer; }

.sn-math .math-narr { margin: 14px 0 4px; }
.sn-math .math-narr-lb { display: flex; align-items: baseline; gap: 9px; cursor: pointer; font: .875rem/1.4 var(--sn-sans); }
.sn-math .math-narr-lb b { font-weight: 600; color: var(--sn-ink); }
.sn-math .math-narr-lb span { color: var(--sn-ink-3); font-size: .8125rem; }

/* The price, one row per line the SERVER sent — one row today, two when narration is charged. */
.gp-price-lines { margin-top: 14px; display: grid; gap: 6px; max-width: 380px; }
.gp-pr-row { display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  font: .8125rem/1.4 var(--sn-sans); color: var(--sn-ink-2); }
.gp-pr-row i { font-style: normal; color: var(--sn-ink-3); font-size: .75rem; }
.gp-pr-row b { font: 600 .8125rem var(--sn-mono); color: var(--sn-ink); font-variant-numeric: tabular-nums; }
.gp-pr-row.is-total { border-top: 1px solid var(--sn-hairline); padding-top: 6px; margin-top: 2px; }

/* The renderer's own notes — "200 rows clamped to 12" and friends. Shown because a chart that quietly
   drops most of a spreadsheet is worse than one that says it did. */
.sn-math .math-notes { margin: 12px 0 0; padding-left: 18px; }
.sn-math .math-notes li { font: .8125rem/1.6 var(--sn-sans); color: var(--sn-ink-3); }
.sn-math .math-out { margin-top: 18px; }
.sn-math .math-working { display: flex; align-items: center; gap: 14px; }
.sn-math .math-working b { display: block; font-size: 15px; }
.sn-math .math-fail { display: flex; align-items: flex-start; gap: 12px; }
.sn-math .math-fail .sn-ic { width: 18px; height: 18px; opacity: .7; margin-top: 2px; }
.sn-math .math-video { width: 100%; border-radius: var(--sn-r-card); background: #000; display: block; }
.sn-math .math-done-row { display: flex; align-items: center; justify-content: space-between;
  gap: 16px; margin-top: 14px; flex-wrap: wrap; }
.sn-math .math-done-cta { display: flex; gap: 8px; }


/* §120 — ANIMATE, and the bits the shared picker does not cover. Every colour is a token, so both
   themes are right by construction — the Create-picker lesson, applied up front. */
.gp-intent { margin: 8px 0 0; font: .8125rem/1.5 var(--sn-sans); color: var(--sn-ink-3); max-width: 62ch; }
.gp-check { display: flex; align-items: baseline; gap: 9px; cursor: pointer; font: .875rem/1.4 var(--sn-sans); }
.gp-check b { font-weight: 600; color: var(--sn-ink); }
.gp-check span { color: var(--sn-ink-3); font-size: .8125rem; }
.sn-animate .an-compose { display: grid; gap: 4px; }
.sn-animate .an-brand { margin: 16px 0 4px; }
.sn-animate .an-brand-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.sn-animate .an-brand-row input[type="color"] { width: 38px; height: 32px; padding: 2px; cursor: pointer;
  background: none; border: 1px solid var(--sn-hairline-strong); border-radius: var(--sn-r-input); }
.sn-animate .an-hex { width: 108px; font: 500 .8125rem var(--sn-mono); text-transform: lowercase; }
/* The refusal sits BESIDE the field, because a colour that is not six hex digits should be caught here
   rather than twenty seconds into a render. */
.sn-animate .an-brand-warn { font: .8125rem/1.4 var(--sn-sans); color: var(--sn-error); }
.sn-animate .an-brand-warn[hidden] { display: none; }
.sn-animate .an-steps { margin: 16px 0 4px; }
.an-beats { font: .8125rem/1.7 var(--sn-sans); min-height: 118px; }
.sn-animate .an-opts { display: grid; gap: 10px; margin: 16px 0 4px; }

/* §123 — UPLOAD YOUR OWN SCREENSHOTS. Same rule as §120 and for the same reason: every colour here is a
   token, so light and dark are both right by construction rather than by a second theme-specific rule
   somebody has to remember to write. */
.sn-animate .an-modes { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 8px; margin-bottom: 4px; }
.sn-animate .an-mode { display: grid; gap: 2px; text-align: left; cursor: pointer; padding: 11px 13px;
  border-radius: var(--sn-r-card); background: none; border: 1px solid var(--sn-hairline-strong);
  transition: background var(--sn-t-hover) var(--sn-ease), border-color var(--sn-t-hover) var(--sn-ease); }
.sn-animate .an-mode:hover { background: var(--sn-hover); }
.sn-animate .an-mode.is-on { background: var(--sn-accent-soft); border-color: var(--sn-accent); }
.sn-animate .an-mode b { font: 600 .875rem/1.3 var(--sn-sans); color: var(--sn-ink); }
.sn-animate .an-mode.is-on b { color: var(--sn-accent-ink); }
.sn-animate .an-mode span { font: .8125rem/1.4 var(--sn-sans); color: var(--sn-ink-3); }

.sn-animate .an-upload { margin: 16px 0 4px; border-radius: var(--sn-r-card); }
.sn-animate .an-upload.over { outline: 2px dashed var(--sn-accent); outline-offset: 6px; }
.sn-animate .an-up-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.sn-animate .an-up-k { display: grid; gap: 2px; }
.sn-animate .an-up-k b { font: 600 .875rem/1.3 var(--sn-sans); color: var(--sn-ink); }
.sn-animate .an-up-k span { font-size: .8125rem; }
/* Refusals name the file they are about — "that upload failed" tells a customer with six screenshots
   nothing at all. */
.sn-animate .an-up-err { display: grid; gap: 4px; margin-bottom: 12px; padding: 10px 12px;
  border-radius: var(--sn-r-input); border: 1px solid var(--sn-hairline); background: var(--sn-bg-raised); }
.sn-animate .an-up-err div { font: .8125rem/1.5 var(--sn-sans); color: var(--sn-error); }
.sn-animate .an-drop { min-height: 168px; }

.sn-animate .an-states { display: grid; gap: 14px; }
.sn-animate .an-state { padding: 12px; border-radius: var(--sn-r-card);
  border: 1px solid var(--sn-hairline); background: var(--sn-bg-raised); }
.sn-animate .an-state-h { display: flex; align-items: center; gap: 9px; margin-bottom: 10px; }
.sn-animate .an-state-n { display: grid; place-items: center; width: 22px; height: 22px; border-radius: 50%;
  background: var(--sn-accent-soft); color: var(--sn-accent-ink);
  font: 600 .75rem/1 var(--sn-mono); font-variant-numeric: tabular-nums; }
.sn-animate .an-state-fn { font: .8125rem/1.3 var(--sn-sans); color: var(--sn-ink-2);
  max-width: 26ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sn-animate .an-state-d { font: .6875rem/1 var(--sn-mono); color: var(--sn-ink-3); }
.sn-animate .an-state-b { display: grid; place-items: center; width: 26px; height: 26px; flex: none;
  border-radius: var(--sn-r-input); background: none; border: 1px solid var(--sn-hairline-strong);
  color: var(--sn-ink-3); cursor: pointer; }
.sn-animate .an-state-b:hover:not(:disabled) { background: var(--sn-hover); color: var(--sn-ink); }
.sn-animate .an-state-b:disabled { opacity: .35; cursor: default; }
.sn-animate .an-state-b .sn-ic { width: 14px; height: 14px; }

/* The shot the customer marks. `position: relative` on the wrapper and the dot placed in PERCENTAGES is
   what makes the mark survive every rendered size — the fractions we send are the fractions drawn.
   🔴 …AND THAT IS ONLY TRUE WHILE THE WRAPPER IS EXACTLY THE IMAGE. The 460px cap used to sit on the
   IMG while the wrapper took its width from the layout, so on a wide card the wrapper ran ~515px against
   a 460px picture. The dot's `left: 89.8%` then resolved against the wrapper and painted past the right
   edge of the screenshot — a mark on the "+ New Deal" button drawn 55px off it. The CLICK was stored
   correctly (the handler measures the img), so the animation put the cursor in the right place and only
   the customer was lied to — who would then "correct" a mark that was already right. The cap belongs on
   the positioning context; the image fills it, and the two boxes cannot drift apart again. */
.sn-animate .an-shot { position: relative; display: inline-block; max-width: min(100%, 460px);
  border-radius: var(--sn-r-input); overflow: hidden; border: 1px solid var(--sn-hairline-strong); }
.sn-animate .an-shot img { display: block; width: 100%; height: auto; cursor: crosshair; }
.sn-animate .an-shot.is-last img { cursor: default; }
.sn-animate .an-shot img:focus-visible { outline: 2px solid var(--sn-accent); outline-offset: -2px; }
/* 🎯 §126 THE MARKER. Rob: "the part I click doesn't work — it doesn't show to click where I press it."
   Half of that was arithmetic (see anPointFrom: a mark computed against an image that had not decoded
   yet), and half was this: a 12px dot on a 460px thumbnail of a 2560px screenshot is a speck, and a
   speck is not an answer to "show me where I pressed". So the mark is a RING with a NUMBERED TAG —
   unmistakable at a glance, and it says WHICH click it is, which matters the moment there are four.

   🔴 pointer-events: none stays, and stays on the whole subtree — WITHOUT IT, CLICKING THE MARKER TO
   MOVE IT MEASURES FROM INSIDE THE MARKER and the mark jumps to wherever the ring's own box begins.
   The element itself is a zero-size anchor at the marked point; everything visible hangs off it with
   negative margins, so the ring's SIZE can change without moving the point it marks. */
.sn-animate .an-dot { position: absolute; width: 0; height: 0; pointer-events: none; }
.sn-animate .an-dot-ring { position: absolute; left: 0; top: 0; width: 30px; height: 30px;
  margin: -15px 0 0 -15px; border-radius: 50%; border: 2px solid var(--sn-accent);
  background: color-mix(in srgb, var(--sn-accent) 20%, transparent);
  /* Two haloes: the surface colour first so the ring reads on a dark screenshot, the accent outside it
     so it reads on a light one. The mark must survive a screenshot of ANY colour — we never see them. */
  box-shadow: 0 0 0 2px var(--sn-surface), 0 0 0 3px color-mix(in srgb, var(--sn-accent) 55%, transparent); }
.sn-animate .an-dot-ring::after { content: ""; position: absolute; left: 50%; top: 50%;
  width: 8px; height: 8px; margin: -4px 0 0 -4px; border-radius: 50%;
  background: var(--sn-accent); box-shadow: 0 0 0 2px var(--sn-surface); }
/* The pulse is the CONFIRMATION that the press registered — anPaintMark replaces the subtree on every
   click, which restarts it. One shot, not a loop: a marker that never stops moving is noise on a card
   the customer is trying to read.
   ⚠️ IT IS A SEPARATE RIPPLE, NOT A SCALE ON THE RING. Animating the ring itself means its FIRST
   keyframe is its resting appearance until the animation advances — and a tab that is not compositing
   never advances one (§110). Measured on staging mid-build: a ring specced at 30px sat at 66px, which
   is scale(2.2), the from-frame. The ring is now always exactly the size it claims; the ripple is
   decoration that can stall at any frame without lying about where the click is. */
.sn-animate .an-dot-ring::before { content: ""; position: absolute; inset: -2px; border-radius: 50%;
  border: 2px solid var(--sn-accent); animation: an-mark-in .5s cubic-bezier(.2, .9, .3, 1) 1 forwards; }
@keyframes an-mark-in { from { transform: scale(.5); opacity: .9; } to { transform: scale(2.1); opacity: 0; } }
.sn-animate .an-dot-tag { position: absolute; left: 21px; top: 9px; white-space: nowrap;
  font: 600 .6875rem/1 var(--sn-sans); letter-spacing: .01em; padding: 4px 7px; border-radius: var(--sn-r-xs);
  background: var(--sn-accent); color: var(--sn-on-accent); box-shadow: 0 1px 5px rgba(0, 0, 0, .35); }
/* `.an-shot` clips, so near an edge a tag that did not flip would simply be gone — which reads as the
   marker being broken again. Flipped by the painter from the stored fraction, not by measuring. */
.sn-animate .an-dot.is-left .an-dot-tag { left: auto; right: 21px; }
.sn-animate .an-dot.is-up .an-dot-tag { top: auto; bottom: 9px; }
@media (prefers-reduced-motion: reduce) { .sn-animate .an-dot-ring::before { animation: none; opacity: 0; } }
.sn-animate .an-state-r { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 10px; }
.sn-animate .an-cap { max-width: 260px; font-size: .8125rem; }
.sn-animate .an-chips { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.sn-animate .an-chip { display: flex; align-items: center; gap: 8px; }
.sn-animate .an-chip > b { font: 600 .75rem/1 var(--sn-sans); color: var(--sn-accent-ink); min-width: 14px; text-align: center; }
.sn-animate .an-mlabel { max-width: 200px; font-size: .8125rem; }
.sn-animate .an-mtext { max-width: 200px; font-size: .8125rem; }
.sn-animate .an-direct-row { max-width: 560px; }
.sn-animate .an-plan { border: 1px solid var(--sn-hairline); border-radius: var(--sn-r-input); padding: 10px 12px; margin-top: 4px; font: .8125rem/1.55 var(--sn-sans); color: var(--sn-ink-2); }
.sn-animate .an-plan > b { display: block; color: var(--sn-ink); margin-bottom: 4px; }
.sn-animate .an-mark-note { font: .8125rem/1.4 var(--sn-sans); color: var(--sn-ink-3); }
.sn-animate .an-mark-note.is-need { color: var(--sn-accent-ink); }
.sn-animate .an-title-row { display: grid; gap: 6px; margin-top: 14px; max-width: 380px; }
