/* ============================================================
   STOCHOS live demonstrators, design system
   Mirrors the probaligence.com marketing site's site.css token set:
   near-black, a single amber accent, Space Grotesk/IBM Plex, a crisp
   "scientific instrument" feel. Extend this system, do not fork it.
   ============================================================ */
:root {
  /* canonical marketing-site tokens (site.css) */
  --bg: #060607;
  --bg-warm: #0A0908;
  --surface: #101012;
  --hairline: rgba(255, 255, 255, .09);
  --text: #F4F3EF;
  --muted: #9C9C96;
  --faint: #82827C;
  --amber: #FFB006;
  --amber-lt: #FFCA4D;
  --ease-out: cubic-bezier(.16, .84, .32, 1);

  /* type: Space Grotesk (display/headings), IBM Plex Sans (body/UI),
     IBM Plex Mono (labels, data readouts, tracked kickers) */
  --display: 'Space Grotesk', sans-serif;
  --sans: 'IBM Plex Sans', sans-serif;
  --mono: 'IBM Plex Mono', monospace;

  /* aliases: this file's original token names, kept so every existing
     var() reference below (and in the JS style blocks) still resolves */
  --bg-2: var(--bg-warm);
  --panel: var(--surface);
  --panel-2: #16161a;              /* one step up from --surface, for stacked panels */
  --line: var(--hairline);
  --line-2: rgba(255, 255, 255, 0.19);
  --txt: var(--text);
  --accent: var(--amber);          /* the one accent */
  --accent-2: var(--amber-lt);
  --accent-soft: rgba(255, 176, 6, 0.12);
  --accent-line: rgba(255, 176, 6, 0.5);
  --warm: var(--amber);
  --warm-2: var(--amber-lt);
  --good: var(--amber);
  --bad: #ff6b6b;
  --shadow: 0 24px 60px rgba(0, 0, 0, 0.6);   /* floating surfaces only: dropdowns, lightboxes */
  --r: 4px;

  /* fixed chrome above every route: the 66px topbar plus the 20px demonstration
     ticker below it. Every full-height panel measures itself against this, so
     changing either strip's height means changing only this one number. */
  --chrome-h: 86px;
}

@font-face { font-family: 'Space Grotesk'; src: url('../assets/fonts/space-grotesk-500.woff2') format('woff2'); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: 'Space Grotesk'; src: url('../assets/fonts/space-grotesk-700.woff2') format('woff2'); font-weight: 700; font-style: normal; font-display: swap; }
@font-face { font-family: 'IBM Plex Sans'; src: url('../assets/fonts/ibm-plex-sans-400.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: 'IBM Plex Sans'; src: url('../assets/fonts/ibm-plex-sans-500.woff2') format('woff2'); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: 'IBM Plex Sans'; src: url('../assets/fonts/ibm-plex-sans-600.woff2') format('woff2'); font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: 'IBM Plex Mono'; src: url('../assets/fonts/ibm-plex-mono-400.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: 'IBM Plex Mono'; src: url('../assets/fonts/ibm-plex-mono-500.woff2') format('woff2'); font-weight: 500; font-style: normal; font-display: swap; }

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  font-family: var(--sans);
  background: var(--bg);      /* flat near-black; no ambient glow gradients */
  color: var(--txt);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
a { color: inherit; text-decoration: none; }
em { font-style: normal; }
::selection { background: #FFB006; color: #000; }
a:focus-visible, button:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--amber-lt); outline-offset: 2px; border-radius: 2px;
}
/* Accessibility floor (2026-07-14 a11y pass): several component styles (the
   three challenge instruments' own injected sliders, the studio's kappa/deploy
   sliders) explicitly set outline:none on input[type=range] with no visible
   replacement, and some are injected in their own <style> tag at mount time,
   so a plain override placed here could still lose a specificity/order tie.
   This is the one global backstop that restores a visible brand ring on every
   native range input and custom [role=slider] widget, load-order-proof.
   !important is deliberate: an invisible focus state is worse than an
   over-specific override, and this must win over any outline:none, current or
   future. .rx-knob-dial (the reactor's rotary knobs) already has its own
   bespoke ring below, keyed off the same accent, so it is excluded here to
   avoid a redundant double ring on a circular widget. */
input[type=range]:focus-visible,
[role="slider"]:focus-visible:not(.rx-knob-dial) {
  outline: 2px solid var(--amber-lt) !important;
  outline-offset: 3px !important;
  border-radius: 3px !important;
}
/* visually hidden but still announced by assistive tech: used for aria-live
   score/verdict announcements that must not show on screen (the visible copy
   already carries the same information, styled). */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ---------- top bar ----------
   Mirrors the site's scrolled header. Height is 66px (not the site's 68px):
   the app's layout math is built on calc(100vh - var(--chrome-h)) in many
   places (including a !important override injected by studio.js), so 66px
   keeps every full-height panel fitting exactly; the 2px difference is not
   perceptible. Sticky is required, the app layout depends on it. */
.topbar {
  position: sticky; top: 0; z-index: 50;
  height: 66px;
  background: rgba(6, 6, 7, 0.78);
  backdrop-filter: saturate(140%) blur(16px);
  -webkit-backdrop-filter: saturate(140%) blur(16px);
  border-bottom: 1px solid var(--hairline);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .topbar { background: rgba(6, 6, 7, 0.97); }
}

/* ---------- demonstration ticker ----------
   A 20px running strip under the topbar, present on every route: this app
   simulates STOCHOS, it is not the product and the numbers are synthetic.
   Sticky (never fixed) so it stays visible without leaving the flow, and its
   height is counted in --chrome-h so calc(100vh - var(--chrome-h)) panels
   still fit the viewport exactly. Track duplicated exactly once; the -50%
   loop depends on it. Reduced motion stops the scroll and leaves a readable,
   scrollable row. */
.demo-ticker {
  position: sticky; top: 66px; z-index: 49;
  height: 20px; min-width: 0; overflow: hidden;
  background: rgba(255, 255, 255, 0.045);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.demo-ticker-view {
  height: 100%; margin-right: 20px; overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 4%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 4%, #000 92%, transparent);
}
/* pause control (WCAG 2.2.2: text that moves on its own must be stoppable
   without hovering it). Sits outside .demo-ticker-view so the edge mask on the
   scrolling text does not fade the button out. Wired up in main.js. */
.demo-ticker-pause {
  position: absolute; right: 0; top: 0; z-index: 2; width: 20px; height: 19px;
  border: 0; border-left: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(10, 9, 8, 0.72); color: var(--muted);
  font-size: 7.5px; line-height: 1; padding: 0; cursor: pointer;
}
.demo-ticker-pause:hover { background: rgba(255, 255, 255, 0.1); color: var(--txt); }
.demo-ticker-track {
  display: flex; width: max-content; will-change: transform;
  animation: demoTickerScroll 46s linear infinite;
}
.demo-ticker-set { display: flex; flex: 0 0 auto; }
.demo-ticker-set b {
  padding: 0 18px; white-space: nowrap;
  font: 500 9.5px/19px var(--mono); letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--muted);
}
.demo-ticker-set b::before { content: '\25C6'; margin-right: 18px; opacity: 0.5; }
@keyframes demoTickerScroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
  .demo-ticker-view {
    margin-right: 0; overflow-x: auto; scrollbar-width: none;
    -webkit-mask-image: none; mask-image: none;
  }
  .demo-ticker-view::-webkit-scrollbar { display: none; }
  .demo-ticker-track { animation: none; }
  .demo-ticker-set + .demo-ticker-set { display: none; }
  .demo-ticker-set b:nth-child(n+4) { display: none; }  /* one pass, not the looped copies */
  .demo-ticker-pause { display: none; }
}
@media (max-width: 560px) {
  .demo-ticker-set b { padding: 0 12px; font-size: 9px; letter-spacing: 0.1em; }
  .demo-ticker-set b::before { margin-right: 12px; }
}

.topbar-inner {
  height: 100%; max-width: 1280px; margin: 0 auto; padding: 0 24px;
  display: flex; align-items: center; gap: 28px;
  flex-wrap: nowrap; /* keep brand + nav + cta on one row */
  min-width: 0;      /* allow flex children to shrink below their content width */
}
.brand { display: flex; align-items: center; gap: 11px; flex-shrink: 0; }
.brand img { height: 34px; display: block; }
.brand .brand-word { height: 15px; }
.brand span { font: 600 17px/1 var(--display); color: var(--text); letter-spacing: -0.01em; }
.domnav {
  display: flex; gap: 6px; margin-left: 10px;
  /* allow horizontal scroll on narrow viewports without spilling the page */
  overflow-x: auto; flex-shrink: 1; min-width: 0;
  /* momentum scroll on iOS */
  -webkit-overflow-scrolling: touch;
  /* hide the scrollbar track while keeping the scroll behaviour */
  scrollbar-width: none; /* Firefox */
  /* a label must not come to rest half-cut: proximity snapping lines the
     nearest tab's left edge up with the nav's left edge on every scroll that
     is not pinned to an extreme. No effect at all when the nav does not
     overflow, so desktop is untouched. */
  scroll-snap-type: x proximity;
}
.domnav::-webkit-scrollbar { display: none; } /* Chrome/Safari/Edge */
/* fade the clipped edge while more tabs sit off-screen (classes toggled by main.js);
   without it the hidden scrollbar makes the nav look cut off mid-word on phones.
   Right edge while more tabs sit to the right; left edge once scrolled fully
   right, where snapping cannot help (the end position is clamped, so a tab is
   cut mid-glyph there: measured 15px of "Beat STOCHOS" at 390px). */
.domnav.can-scroll:not(.at-end) {
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 44px), transparent);
  mask-image: linear-gradient(90deg, #000 calc(100% - 44px), transparent);
}
.domnav.can-scroll.at-end {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 32px);
  mask-image: linear-gradient(90deg, transparent, #000 32px);
}
.domnav a {
  padding: 9px 13px; border-radius: 2px; font: 500 14.5px/1 var(--sans); color: var(--muted);
  border: 1px solid transparent; transition: color 0.18s;
  white-space: nowrap; flex-shrink: 0;
  scroll-snap-align: start;
}
.domnav a:hover { color: var(--text); }
.domnav a.active { color: var(--amber); font-weight: 600; }
.cta {
  margin-left: auto; flex-shrink: 0; white-space: nowrap;
  background: var(--amber); color: #08070A;
  font: 600 14.5px/1 var(--sans);
  padding: 11px 18px; border-radius: 2px; border: none;
  transition: filter 0.18s, transform 0.18s;
}
.cta:hover { filter: brightness(1.08); transform: translateY(-1px); }
/* full label by default; a short "Demo" label takes over once the header row
   gets too tight for both nav links plus the full CTA text (see the 420px
   breakpoint below). */
.cta-short { display: none; }

/* ---------- app shell ----------
   Content determines height and the footer follows it (the marketing site does
   the same: no route is force-stretched to a full viewport, which would float a
   viewport of dead black space below short screens like the hub or the pick).
   The one full-height need left is the error placeholder, handled on its own. */
.app { min-height: 0; }

/* ---------- hub / landing ----------
   No width constraint here: the hero is a full-bleed section managing its own
   centered inner container so its background can span the full viewport
   (the site's alternating warm/near-black band rhythm continues below, into
   the footer; see "section rhythm" near the footer rules). */
.hub { }

/* ---------- hub HERO: the Beat STOCHOS challenge as the flagship ----------
   Full-bleed on the page background, no outer box. The illustrative panel on
   the right is the only boxed element. A soft radial amber glow sits behind
   the headline, mirroring the marketing site's hero treatment (static, so it
   needs no reduced-motion guard). */
.hero {
  position: relative; overflow: hidden;
  padding: 96px 24px 72px;
}
.hero::before {
  content: ""; position: absolute; z-index: 0; pointer-events: none;
  top: -12%; left: 50%; transform: translateX(-50%);
  width: 1100px; height: 560px; max-width: 120vw;
  background: radial-gradient(rgba(255, 176, 6, 0.10), transparent 62%);
  filter: blur(6px);
}
.hero-inner {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: 1.55fr 1fr; align-items: center; gap: 36px;
  max-width: 1120px; margin: 0 auto;
}
.hero-body { position: relative; z-index: 1; min-width: 0; }
/* the hero eyebrow; matches the reusable .eyebrow recipe below (see "shared
   card + eyebrow system") but kept as its own rule since main.js renders this
   markup and cannot be handed an extra class. */
.hero-kicker {
  display: block; font: 500 12px/1 var(--mono); letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--amber);
}
.hero-title {
  font-family: var(--display); font-weight: 700;
  font-size: clamp(36px, 5.4vw, 64px); line-height: 1.03; letter-spacing: -0.025em;
  margin: 18px 0 16px;
}
.hero-hot { color: var(--amber); }
.hero-pitch { font-size: 17px; color: var(--muted); line-height: 1.55; max-width: 560px; margin: 0; }
.hero-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 14px 18px; margin-top: 26px; }
.hero-cta {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 15px 28px; border-radius: 2px; font: 600 16px/1 var(--sans);
  background: var(--amber); color: #08070A; border: none;
  transition: filter 0.18s, transform 0.18s;
}
.hero-cta:hover { filter: brightness(1.08); transform: translateY(-1px); }
.hero-sub { font-size: 13px; color: var(--faint); }
.hero-cases { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 22px; }
.hero-chip {
  font-size: 13px; padding: 8px 15px; border-radius: 999px; color: var(--muted);
  border: 1px solid rgba(255, 255, 255, 0.14); background: transparent; transition: 0.16s;
}
.hero-chip:hover { color: var(--amber); border-color: var(--accent-line); background: var(--accent-soft); }
/* touch target: the chips are links, and at 8px block padding they measured 35px
   tall on a phone. Grow the box, not the text, to the 44px minimum. */
@media (max-width: 480px) {
  .hero-chip {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: 44px; padding: 8px 16px;
  }
}

/* illustrative "you vs STOCHOS" race strip: a small instrument panel, labelled
   and decorative only. Sized to the site's card recipe (6px radius, generous
   padding, hairline border; see "shared card + eyebrow system" below). */
.hero-illus {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; justify-content: center;
  border: 1px solid var(--hairline); border-radius: 6px; padding: 28px 26px 24px;
  background: var(--surface); min-height: 160px;
}
.hero-illus-label {
  position: absolute; top: 14px; right: 16px; font: 500 10px/1 var(--mono); letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--faint);
}
.hi-grid { display: flex; flex-direction: column; gap: 20px; margin-top: 4px; }
.hi-row { display: flex; align-items: center; gap: 12px; }
.hi-row-label {
  flex: 0 0 58px; font: 500 10.5px/1 var(--mono); letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--muted);
}
.hi-bar-track { position: relative; flex: 1; height: 8px; border-radius: 2px; background: var(--panel-2); overflow: hidden; }
/* faint hairline gridline ticks behind the fill: 8 even divisions */
.hi-bar-track::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image: repeating-linear-gradient(90deg,
    rgba(255, 255, 255, 0.10) 0, rgba(255, 255, 255, 0.10) 1px,
    transparent 1px, transparent 12.5%);
}
.hi-you, .hi-st {
  position: absolute; left: 0; top: 0; height: 100%; border-radius: 2px;
  animation: hi-fill 2.6s var(--ease-out) infinite;
}
.hi-you { width: 64%; background: linear-gradient(90deg, rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.45)); }
.hi-st { width: 92%; background: linear-gradient(90deg, var(--accent-2), var(--accent)); animation-delay: 0.35s; }
@keyframes hi-fill { 0% { transform: scaleX(0.04); transform-origin: left; } 55%, 100% { transform: scaleX(1); transform-origin: left; } }
.hi-row-val { flex: 0 0 26px; text-align: right; font: 600 13px/1 var(--mono); color: var(--muted); }
.hi-row-val.hot { color: var(--accent); }

@media (max-width: 880px) {
  .hero { padding: 64px 24px 48px; }
  .hero-inner { grid-template-columns: 1fr; gap: 26px; }
  .hero-illus { order: 2; min-height: 130px; }
  .hero-body { order: 1; }
}
@media (max-width: 480px) {
  .hero { padding: 48px 18px 40px; }
  .hero-title { font-size: clamp(27px, 8.5vw, 38px); }
  .hero-pitch { font-size: 15px; }
  .hero-cta { width: 100%; justify-content: center; font-size: 15px; padding: 14px 22px; }
  .hero-actions { gap: 10px; }
}

/* ---------- shared card + eyebrow system ----------
   Mirrors the marketing site's hairline card + mono eyebrow recipe (see
   site.css: the /challenge/ "How it works" cards and the hero's tracked
   kicker). Utility classes for any demo markup (challenge.js included) that
   wants the same look; .hero-illus above hand-matches this recipe directly
   since main.js renders that markup and cannot be handed an extra class. */
.card {
  border: 1px solid var(--hairline); border-radius: 6px; padding: 28px;
  background: transparent;
}
.eyebrow {
  display: block; font: 500 12px/1 var(--mono); letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--amber);
}

/* boot */
.boot { position: fixed; inset: 0; display: grid; place-items: center; background: var(--bg); z-index: 100; transition: opacity .5s; }
.boot.hidden { opacity: 0; pointer-events: none; }
.boot-ring { width: 46px; height: 46px; border-radius: 50%; border: 3px solid var(--line); border-top-color: var(--accent); animation: spin 0.9s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

.placeholder { display: grid; place-items: center; min-height: 60vh; color: var(--muted); text-align: center; padding: 40px; }
.placeholder .big { font-size: 26px; color: var(--txt); margin-bottom: 10px; }

.fade-in { animation: fade .5s ease both; }
@keyframes fade { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

@media (max-width: 768px) {
  /* Tighten topbar so the scrollable nav gets maximum width */
  .topbar-inner { gap: 16px; }
  /* CTA shrinks to just its text: no extra padding eating nav space */
  .cta { padding: 8px 12px; font-size: 12px; flex-shrink: 0; }
}

@media (max-width: 480px) {
  body { overflow-x: hidden; }
  .topbar-inner { padding: 0 14px; gap: 8px; }
  .brand span, .brand .brand-word { display: none; }
  /* both nav links + the CTA must share a very tight row here: tighten the
     link padding/size and shrink the CTA further so nothing clips behind it. */
  .domnav a { padding: 8px 9px; font-size: 13px; }
  .cta { padding: 9px 12px; font-size: 13px; }
}
@media (max-width: 420px) {
  /* still not enough room for both full nav labels and "Request a Demo":
     the CTA drops to a short "Demo" label instead of clipping a nav link. */
  .cta-full { display: none; }
  .cta-short { display: inline; }
}
@media (max-width: 360px) {
  /* out of room even at the tightened sizes above: drop the CTA rather than
     let it clip a nav label (the CTA is still one tap away via the hub hero). */
  .cta { display: none; }
}

/* ---------- site footer ----------
   Verbatim port of the marketing site's .site-footer (site.css), minus the
   cookie-consent JS hooks (no site.js in this app, so "Cookie settings" and
   the live copyright-year script are dropped; plain links + a static year
   stand in). One deliberate deviation from the literal site value: this
   footer sits on --bg-warm rather than the site's flat --bg. The app hub has
   no middle "how it works" band to alternate against (that content lives on
   the marketing site's own /challenge/ bridge page, not in this app), so the
   warm tone here is what carries the site's alternating-band rhythm between
   the hero and the page's end. Section rhythm, subtle not heavy. */
.site-footer {
  background: var(--bg-warm); border-top: 1px solid var(--hairline);
  padding: 80px 24px 40px; font-family: var(--sans);
}
.footer-inner { max-width: 1280px; margin: 0 auto; }
.footer-grid { display: grid; grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr; gap: 48px 32px; align-items: start; }
.footer-brand { min-width: 220px; }
.footer-brand .brand { margin-bottom: 18px; }
.footer-brand .brand img { height: 32px; }
.footer-brand .brand .brand-word { height: 14px; }
.footer-brand .brand span { font-size: 16px; }
.footer-brand p { color: var(--muted); font-size: 14px; line-height: 1.6; max-width: 300px; margin: 0 0 18px; }
.footer-cred { display: flex; flex-direction: column; gap: 14px; align-items: flex-start; }
.footer-cred img { display: block; width: auto; }
.footer-cred .cred-ansys { height: 40px; }
.footer-cred .cred-cadfem { height: 19px; }
.footer-grid .f-h {
  font: 500 11.5px/1 var(--mono); color: var(--faint);
  letter-spacing: 0.16em; text-transform: uppercase; margin: 0 0 18px;
}
.footer-grid ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 11px; }
.footer-grid li a { color: var(--muted); text-decoration: none; font-size: 14px; }
.footer-grid li a:hover { color: var(--text); }
.footer-grid li a.amber { color: var(--amber); }
.footer-bottom {
  display: flex; flex-wrap: wrap; gap: 16px; justify-content: space-between; align-items: center;
  margin-top: 64px; padding-top: 24px; border-top: 1px solid var(--hairline);
}
.footer-bottom .copy { font: 400 13px/1 var(--mono); color: var(--faint); }
.footer-bottom .legal { display: flex; gap: 18px; flex-wrap: wrap; }
.footer-bottom .legal a { color: var(--faint); text-decoration: none; font-size: 13px; }
.footer-bottom .legal a:hover { color: var(--text); }
.footer-social { display: flex; gap: 10px; margin-top: 22px; }
.social-btn {
  display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px;
  border: 1px solid var(--hairline); border-radius: 9px; color: var(--muted);
  background: rgba(255, 255, 255, 0.02);
  transition: color 0.18s var(--ease-out), border-color 0.18s, background 0.18s, transform 0.18s;
}
.social-btn svg { height: 18px; width: auto; fill: currentColor; display: block; }
.social-btn:hover { color: var(--amber); border-color: rgba(255, 176, 6, 0.5); background: rgba(255, 176, 6, 0.09); transform: translateY(-2px); }
.social-btn:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
.social-btn--pending { opacity: 0.4; cursor: default; }
.social-btn--pending:hover { color: var(--muted); border-color: var(--hairline); background: rgba(255, 255, 255, 0.02); transform: none; }
@media (max-width: 860px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 520px) { .footer-grid { grid-template-columns: 1fr; } }

/* ---------- reduced motion (site standard: motion is never mandatory) ----------
   Collapses every keyframe animation to a single instant pass, which stops the
   infinite decorative loops (hero race strip, pulses, dashes) while keeping all
   content visible; nothing in the app gates visibility on an animation, and no
   JS listens for animationend. One-shot transitions are left alone. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* ============================================================
   STUDIO (Build-it-yourself) CSS, restored 2026-07-13 from the
   pre-trim backup. Verified on-brand (all colors resolve through
   the :root tokens above; no off-brand hexes, no gradient text,
   no bounce easing). The .sf-app block deliberately replicates
   the Flow desktop product's grey/green look per the contract;
   the amber site chrome wraps it. Picker cards aligned to the
   site 6px card recipe 2026-07-13.
   ============================================================ */
/* touch: the drag-to-build surfaces must own the pointer stream. Without
   touch-action none, a touch browser's scroll/pan gesture pre-empts the
   pointerdrag (pointercancel) and silently aborts placing or wiring a node.
   The canvas sits in a fixed-height app layout, so no page scroll is lost. */
.pal-node, .snode-card, .snode .port, .port, .sf-canvas { touch-action: none; }
/* ---------- demo layout ---------- */
.demo { display: grid; grid-template-columns: 360px 1fr; gap: 0; height: calc(100vh - var(--chrome-h)); }
.panel {
  border-right: 1px solid var(--line); background: linear-gradient(180deg, var(--bg-2), var(--bg));
  padding: 22px 22px 26px; overflow-y: auto;
}
.stage { position: relative; overflow: hidden; }

.eyebrow { font-size: 11.5px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent); margin-bottom: 8px; }
.panel h2 { margin: 0 0 6px; font-size: 22px; letter-spacing: -0.01em; }
.panel .sub { color: var(--muted); font-size: 13.5px; line-height: 1.5; margin: 0 0 18px; }

.section-label { font-size: 11.5px; letter-spacing: 0.13em; text-transform: uppercase; color: var(--faint); margin: 22px 0 10px; }

/* control chips */
.chips { display: flex; flex-wrap: wrap; gap: 7px; }
.chip {
  font-size: 12.5px; padding: 7px 12px; border-radius: 9px; cursor: pointer;
  border: 1px solid var(--line); background: var(--panel); color: var(--muted); transition: 0.15s;
}
.chip:hover { color: var(--txt); border-color: var(--line-2); }
.chip.on { background: var(--accent); color: var(--bg); border-color: var(--accent); font-weight: 600; }

/* sliders */
.slider { margin: 13px 0; }
.slider .row { display: flex; justify-content: space-between; font-size: 12.5px; margin-bottom: 5px; }
.slider .row .v { font-family: var(--mono); color: var(--accent); }
.slider .row .name { color: var(--muted); }
.slider input[type=range] { width: 100%; appearance: none; height: 4px; border-radius: 4px; background: var(--line-2); outline: none; }
.slider input[type=range]::-webkit-slider-thumb {
  appearance: none; width: 15px; height: 15px; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 12px -1px var(--accent); cursor: pointer; border: 2px solid var(--bg);
}
.slider.axis .row .name::after { content: " · axis"; color: var(--accent); opacity: 0.8; }

/* readout card */
.readout { margin-top: 16px; border: 1px solid var(--line); border-radius: 12px; background: var(--panel); padding: 16px; }
.readout .big { font-family: var(--mono); font-size: 30px; font-weight: 600; letter-spacing: -0.01em; }
.readout .unit { font-size: 14px; color: var(--muted); margin-left: 4px; }
.readout .band { font-family: var(--mono); font-size: 13px; color: var(--warm); margin-top: 2px; }
.readout .band .conf { color: var(--muted); }
.readout .bandbar { height: 6px; border-radius: 4px; margin-top: 10px; background: var(--panel-2); position: relative; overflow: hidden; }
.readout .bandbar .fill { position: absolute; height: 100%; background: linear-gradient(90deg, transparent, var(--warm), transparent); opacity: 0.85; transition: 0.18s; }
.readout .lbl { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.1em; }

/* buttons */
.btn {
  display: inline-flex; align-items: center; gap: 9px; justify-content: center;
  width: 100%; margin-top: 14px; padding: 13px 18px; border-radius: 999px; cursor: pointer;
  font-size: 14px; font-weight: 700; border: 1px solid var(--accent); transition: 0.2s;
  background: var(--accent); color: #111;
}
.btn:hover { transform: translateY(-2px); box-shadow: 0 14px 34px rgba(255, 176, 6, 0.2); }
.btn.ghost { background: transparent; color: var(--muted); border: 1px solid var(--line-2); }
.btn.ghost:hover { color: var(--txt); border-color: var(--line-2); }
.btn .dot { width: 8px; height: 8px; border-radius: 50%; background: #1a0d05; }

/* stage canvas + overlays */
.field-wrap { position: absolute; inset: 0; }
canvas.field { position: absolute; inset: 0; width: 100%; height: 100%; }
.axis-label { position: absolute; font-size: 12px; color: var(--muted); letter-spacing: 0.04em; }
.axis-label.x { bottom: 16px; left: 50%; transform: translateX(-50%); }
.axis-label.y { top: 50%; left: 16px; transform: translateY(-50%) rotate(-90deg); transform-origin: left; }
.legend {
  position: absolute; right: 20px; top: 20px; background: rgba(12, 12, 14, 0.84); border: 1px solid var(--line);
  border-radius: 10px; padding: 12px 14px; backdrop-filter: blur(8px); font-size: 12px; min-width: 168px;
}
.legend .lr { display: flex; justify-content: space-between; color: var(--muted); margin-bottom: 7px; }
.legend .ramp { height: 8px; border-radius: 4px; margin-bottom: 6px; }
.legend .scale { display: flex; justify-content: space-between; font-family: var(--mono); font-size: 11px; color: var(--faint); }
.legend .fog-key { display: flex; align-items: center; gap: 8px; margin-top: 11px; color: var(--muted); font-size: 11.5px; }
.legend .fog-swatch { width: 26px; height: 12px; border-radius: 3px; background: linear-gradient(90deg, transparent, rgba(168,160,148,0.6)); border: 1px solid var(--line); }

.hint { position: absolute; left: 50%; bottom: 64px; transform: translateX(-50%); font-size: 12.5px; color: var(--muted); background: rgba(12,12,14,0.78); border: 1px solid var(--line); padding: 7px 13px; border-radius: 999px; backdrop-filter: blur(6px); pointer-events: none; transition: opacity .4s; }

/* swatch (paint) */
.swatch-wrap { margin-top: 16px; }
.swatch {
  position: relative; height: 120px; border-radius: 12px; overflow: hidden; border: 1px solid var(--line);
  background-image: linear-gradient(45deg, #2e2b26 25%, transparent 25%), linear-gradient(-45deg, #2e2b26 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #2e2b26 75%), linear-gradient(-45deg, transparent 75%, #2e2b26 75%);
  background-size: 18px 18px; background-position: 0 0, 0 9px, 9px -9px, -9px 0;
  background-color: #1a1714;
}
.swatch .coat { position: absolute; inset: 0; transition: 0.25s; }
.swatch .sheen { position: absolute; inset: 0; mix-blend-mode: screen; pointer-events: none; transition: 0.25s; }
.swatch .tagline { position: absolute; left: 12px; bottom: 10px; font-size: 11px; color: rgba(255,255,255,0.7); text-shadow: 0 1px 4px rgba(0,0,0,.6); }

/* boot */
.boot { position: fixed; inset: 0; display: grid; place-items: center; background: var(--bg); z-index: 100; transition: opacity .5s; }
.boot.hidden { opacity: 0; pointer-events: none; }
.boot-ring { width: 46px; height: 46px; border-radius: 50%; border: 3px solid var(--line); border-top-color: var(--accent); animation: spin 0.9s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

.placeholder { display: grid; place-items: center; height: 100%; color: var(--muted); text-align: center; padding: 40px; }
.placeholder .big { font-size: 26px; color: var(--txt); margin-bottom: 10px; }

.fade-in { animation: fade .5s ease both; }
@keyframes fade { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

@media (max-width: 880px) {
  .demo { grid-template-columns: 1fr; height: auto; }
  /* Reorder: hero stage first, then controls panel. Markup is panel-then-stage
     so order:-1 on .stage pulls it above .panel (which defaults to order:0). */
  .stage { order: -1; height: 64vh; }
  .panel { border-right: none; border-bottom: 1px solid var(--line); }
  .cards { grid-template-columns: 1fr; }
  /* nav stays visible and scrolls horizontally — do NOT hide it */
}

@media (max-width: 768px) {
  /* Tighten topbar so the scrollable nav gets maximum width */
  .topbar { gap: 16px; }
  /* CTA shrinks to just its text — no extra padding eating nav space */
  .cta { padding: 8px 12px; font-size: 12px; flex-shrink: 0; }
}

@media (max-width: 480px) {
  body { overflow-x: hidden; }
  .hub { padding: 36px 14px 56px; }
  .hub h1 { font-size: clamp(24px, 8vw, 34px); }
  .hub .lead { font-size: 15px; }
  .panel { padding: 16px 14px 20px; }
  .intro-card { padding: 22px 18px 18px; }
  .intro-card h2 { font-size: 22px; }
  .topbar { padding: 12px 14px; gap: 10px; }
  .brand-text { display: none; }
  .readout .big { font-size: 24px; }
}

/* ---------- stage caption (always-on framing) ---------- */
.stage-caption {
  position: absolute; left: 20px; top: 18px; z-index: 6; max-width: 460px;
  background: rgba(12, 12, 14, 0.80); border: 1px solid var(--line); backdrop-filter: blur(10px);
  border-radius: 12px; padding: 12px 15px;
}
.stage-caption .sc-step { font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent); margin-bottom: 4px; }
.stage-caption .sc-title { font-size: 15px; font-weight: 600; }
.stage-caption .sc-sub { font-size: 12.5px; color: var(--muted); margin-top: 3px; line-height: 1.45; }
/* plain-language decode key: tells a cold visitor what color / fog / pins mean.
   Sits under the caption so the three pieces of the Confidence Field are named in one place. */
.decode-key { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--line); }
.decode-key .dk { display: flex; align-items: center; gap: 7px; font-size: 11.5px; color: var(--muted); line-height: 1.3; }
.decode-key .dk b { color: var(--txt); font-weight: 600; }
.decode-key .dk-sw { width: 16px; height: 12px; border-radius: 3px; flex: none; }
.decode-key .dk-sw.color { background: linear-gradient(90deg, #2a2118, var(--accent)); }
.decode-key .dk-sw.fog { background: radial-gradient(circle at 50% 50%, rgba(168,160,148,0.65), transparent 72%); border: 1px solid var(--line); }
.decode-key .dk-sw.pin { background: radial-gradient(circle at 50% 38%, #fff 0%, var(--accent) 42%, transparent 72%); }
.decode-key .dk-sw.slide { background: linear-gradient(90deg, transparent 0 30%, var(--accent) 30% 70%, transparent 70%); position: relative; }
.decode-key .dk-sw.slide::after { content: ''; position: absolute; top: 50%; left: 50%; width: 5px; height: 5px; border-radius: 50%; background: var(--txt); transform: translate(-50%, -50%); }

/* ---------- intro modal ---------- */
.intro-modal {
  position: fixed; inset: 0; z-index: 95; display: grid; place-items: center;
  background: rgba(5, 5, 7, 0.78); backdrop-filter: blur(6px);
  opacity: 0; transition: opacity 0.28s;
}
.intro-modal.show { opacity: 1; }
.intro-card {
  width: min(540px, 92vw); border: 1px solid var(--line-2); border-radius: 18px; padding: 30px 30px 26px;
  background: linear-gradient(180deg, var(--panel-2), var(--panel)); box-shadow: var(--shadow);
  transform: translateY(14px) scale(0.98); transition: 0.32s cubic-bezier(.2,.8,.2,1);
}
.intro-modal.show .intro-card { transform: none; }
.intro-eyebrow { font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent); margin-bottom: 10px; }
.intro-card h2 { margin: 0 0 12px; font-size: 27px; letter-spacing: -0.01em; line-height: 1.12; }
.intro-card p { margin: 0 0 18px; color: var(--muted); font-size: 15px; line-height: 1.6; }
/* plain-language framing inside the intro modal: what you see + what to do */
.intro-frame { margin: 0 0 22px; border: 1px solid var(--line); border-radius: 12px; padding: 4px 0; background: rgba(255, 176, 6, 0.04); }
.intro-frame .if-row { display: flex; gap: 12px; padding: 9px 14px; align-items: baseline; }
.intro-frame .if-row + .if-row { border-top: 1px solid var(--line); }
.intro-frame .if-tag { flex: none; width: 132px; font-size: 10.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--faint); }
.intro-frame .if-tag.try { color: var(--accent); }
.intro-frame .if-txt { font-size: 13.5px; line-height: 1.5; color: var(--txt); }
.intro-frame .if-txt b { color: var(--accent); font-weight: 600; }
.intro-actions { display: flex; gap: 10px; }
.intro-actions .btn { margin-top: 0; }

/* ---------- guided tour ---------- */
.tour-veil { position: fixed; inset: 0; z-index: 88; background: rgba(5, 5, 7, 0.80); backdrop-filter: blur(3px); opacity: 0; pointer-events: none; transition: opacity 0.25s; }
.tour-veil.on { opacity: 1; }
.tour-spot {
  position: fixed; z-index: 89; display: none; pointer-events: none;
  box-shadow: 0 0 0 9999px rgba(5, 8, 13, 0.74), 0 0 28px -2px var(--accent) inset;
  border: 1.5px solid var(--accent); transition: all 0.32s cubic-bezier(.3,.8,.2,1);
}
.tour-card {
  position: fixed; z-index: 92; width: 320px;
  background: linear-gradient(180deg, var(--panel-2), var(--panel)); border: 1px solid var(--line-2);
  border-radius: 14px; padding: 17px 18px 14px; box-shadow: var(--shadow);
  opacity: 0; transform: translateY(8px); transition: 0.26s cubic-bezier(.2,.8,.2,1);
}
.tour-card.show { opacity: 1; transform: none; }
.tour-card .tour-step { font-family: var(--mono); font-size: 11px; color: var(--faint); }
.tour-card h4 { margin: 5px 0 7px; font-size: 17px; }
.tour-card p { margin: 0 0 14px; font-size: 13.5px; line-height: 1.55; color: var(--muted); }
.tour-actions { display: flex; align-items: center; justify-content: space-between; }
.tour-actions .tlink { background: none; border: none; color: var(--faint); font-size: 12px; cursor: pointer; }
.tour-actions .tlink:hover { color: var(--muted); }
.tour-actions .tbtn { background: var(--accent); color: var(--bg); border: none; padding: 8px 14px; border-radius: 9px; font-size: 13px; font-weight: 600; cursor: pointer; }
.tour-actions .tbtn:hover { filter: brightness(1.08); }
.tour-dots { display: flex; gap: 5px; }
.tour-dots span { width: 6px; height: 6px; border-radius: 50%; background: var(--line-2); }
.tour-dots span.on { background: var(--accent); }

/* illustrative-data footnote (house rule: label illustrative visuals) */
.demo-note { margin-top: 22px; padding-top: 14px; border-top: 1px solid var(--line); font-size: 11.5px; line-height: 1.5; color: var(--muted-2, var(--faint)); }

/* 3D terrain (chemistry / engineering) */
.terrain-wrap { position: absolute; inset: 0; cursor: grab; }
.terrain-wrap:active { cursor: grabbing; }

/* ===================== Flow editor demonstrator ===================== */
.flow-app { display: grid; grid-template-rows: auto 1fr auto; height: calc(100vh - var(--chrome-h)); background: var(--bg); }
.flow-toolbar { display: flex; align-items: center; gap: 12px; padding: 8px 16px; border-bottom: 1px solid var(--line); background: var(--bg-2); }
.flow-toolbar .ftitle { font-size: 13px; color: var(--muted); }
.flow-toolbar .ftitle b { color: var(--txt); font-weight: 600; }
.flow-menu { display: flex; gap: 15px; font-size: 12.5px; color: var(--faint); margin-left: 6px; }
.flow-run { display: inline-flex; align-items: center; gap: 8px; background: linear-gradient(100deg, var(--warm), var(--warm-2)); color: #1a0d05; border: none; border-radius: 9px; padding: 8px 15px; font-weight: 600; font-size: 13px; cursor: pointer; box-shadow: 0 0 20px -8px var(--warm); }
.flow-run:hover { filter: brightness(1.07); }
.flow-run.busy { opacity: 0.6; pointer-events: none; }
.flow-run .tri { width: 0; height: 0; border-left: 9px solid #1a0d05; border-top: 6px solid transparent; border-bottom: 6px solid transparent; }
.flow-btn { background: transparent; border: 1px solid var(--line); color: var(--muted); border-radius: 8px; padding: 7px 12px; font-size: 12.5px; cursor: pointer; }
.flow-btn:hover { color: var(--txt); border-color: var(--line-2); }

.flow-body { display: grid; grid-template-columns: 208px 1fr 256px; min-height: 0; }
.flow-palette { border-right: 1px solid var(--line); background: var(--bg-2); overflow-y: auto; padding: 11px; }
.flow-search { width: 100%; background: var(--panel); border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; color: var(--txt); font-size: 12.5px; margin-bottom: 6px; }
.pal-cat { font-size: 10px; letter-spacing: 0.13em; text-transform: uppercase; color: var(--faint); margin: 13px 0 5px; }
.pal-node { display: flex; align-items: center; gap: 9px; padding: 6px 7px; border-radius: 7px; cursor: grab; font-size: 12.5px; color: var(--muted); }
.pal-node:hover { background: var(--panel); color: var(--txt); }
.pal-node img { width: 22px; height: 22px; flex: none; }

.flow-canvas-wrap { position: relative; overflow: hidden; background:
  radial-gradient(circle at center, rgba(255,255,255,0.03) 1px, transparent 1.4px) 0 0 / 22px 22px, #050505; }
.flow-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
.edge { fill: none; stroke: rgba(170,170,170,0.32); stroke-width: 1.6; transition: stroke 0.2s; }
.edge.hot { stroke: var(--warm); stroke-dasharray: 5 6; animation: dash 0.55s linear infinite; }
@keyframes dash { to { stroke-dashoffset: -22; } }
.packet { fill: var(--warm); filter: drop-shadow(0 0 6px var(--warm)); }

.fnode { position: absolute; width: 84px; text-align: center; }
.fnode-card { position: relative; width: 72px; height: 72px; margin: 0 auto; border-radius: 14px;
  background: linear-gradient(160deg, #3a3a38, #242320); border: 1px solid rgba(255,255,255,0.12);
  box-shadow: 0 0 0 1.5px var(--cat, transparent), 0 7px 18px -9px #000; display: grid; place-items: center; cursor: grab; transition: box-shadow 0.18s, transform 0.12s; }
.fnode-card:active { cursor: grabbing; }
.fnode-card img { width: 52px; height: 52px; pointer-events: none; }
.fnode-label { margin-top: 6px; font-size: 11.5px; font-weight: 600; color: #eeece8; white-space: nowrap; }
.fnode-sub { font-size: 9.5px; color: var(--faint); }
.port { position: absolute; width: 9px; height: 9px; border-radius: 50%; background: #8a8880; border: 2px solid #050503; }
.fnode.running .fnode-card { box-shadow: 0 0 0 2px var(--warm), 0 0 26px -4px var(--warm); }
.fnode.running .fnode-card::after { content: ""; position: absolute; inset: -3px; border-radius: 16px; border: 2px solid var(--warm); opacity: 0.6; animation: npulse 1s ease-out infinite; }
@keyframes npulse { from { transform: scale(1); opacity: 0.6; } to { transform: scale(1.2); opacity: 0; } }
.fnode-badge { position: absolute; right: -6px; top: -6px; width: 20px; height: 20px; border-radius: 50%; display: grid; place-items: center; font-size: 12px; background: var(--good); color: #05140a; opacity: 0; transform: scale(0.4); transition: 0.2s; z-index: 3; }
.fnode.done .fnode-badge { opacity: 1; transform: scale(1); }
.fnode.done .fnode-card { box-shadow: 0 0 0 1.5px var(--good), 0 7px 18px -9px #000; }

.flow-output { position: absolute; z-index: 4; background: rgba(14,14,14,0.96); border: 1px solid var(--line-2); border-radius: 10px; padding: 8px; box-shadow: var(--shadow); opacity: 0; transform: translateY(6px); transition: 0.25s; }
.flow-output.show { opacity: 1; transform: none; }
.flow-output .fo-title { font-size: 10.5px; color: var(--muted); margin-bottom: 5px; text-align: left; }
.flow-output canvas { display: block; border-radius: 4px; }

.flow-agent { border-left: 1px solid var(--line); background: var(--bg-2); display: flex; flex-direction: column; min-height: 0; }
.agent-head { padding: 11px 13px; border-bottom: 1px solid var(--line); font-size: 13px; color: var(--txt); display: flex; align-items: center; gap: 8px; font-weight: 600; }
.agent-head .dotmark { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 8px var(--accent); }
.agent-body { flex: 1; overflow-y: auto; padding: 12px; display: flex; flex-direction: column; gap: 10px; }
.agent-msg { font-size: 12.5px; line-height: 1.5; padding: 9px 11px; border-radius: 11px; max-width: 92%; }
.agent-msg.user { background: var(--panel-2); color: var(--txt); align-self: flex-end; border-bottom-right-radius: 3px; }
.agent-msg.bot { background: rgba(255,176,6,0.07); border: 1px solid rgba(255,176,6,0.18); color: #e8dfc8; align-self: flex-start; border-bottom-left-radius: 3px; }
.agent-foot { padding: 10px; border-top: 1px solid var(--line); }
.agent-foot .btn { margin-top: 0; }

.flow-bottom { border-top: 1px solid var(--line); background: var(--bg-2); height: 168px; overflow: auto; }
.vars-head { display: flex; gap: 16px; align-items: center; padding: 8px 13px; border-bottom: 1px solid var(--line); font-size: 12px; color: var(--faint); position: sticky; top: 0; background: var(--bg-2); z-index: 2; }
.vars-head .on { color: var(--txt); border-bottom: 2px solid var(--accent); padding-bottom: 6px; }
.vars-head .count { margin-left: auto; color: var(--faint); }
.vars-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.vars-table th { text-align: left; color: var(--faint); font-weight: 500; padding: 5px 13px; position: sticky; top: 33px; background: var(--bg-2); }
.vars-table td { padding: 4px 13px; border-top: 1px solid rgba(255,255,255,0.04); color: var(--muted); }
.vars-table td.node { color: var(--txt); font-weight: 600; }
.vars-table td.type { color: var(--accent-2); font-family: var(--mono); }
.vars-table td.val { font-family: var(--mono); color: var(--muted); }
.vars-row { animation: fade 0.3s ease both; }

@media (max-width: 1100px) { .flow-body { grid-template-columns: 170px 1fr; } .flow-agent { display: none; } }

/* compact readouts + pareto chart (engineering) */
.readout.compact { padding: 12px 15px; margin-top: 0; }
.readout.compact .big { font-size: 24px; }
.readout.compact .band { font-size: 12px; margin-top: 1px; }
.pareto { width: 100%; height: 168px; display: block; margin-top: 6px; border: 1px solid var(--line); border-radius: 10px; background: var(--panel); cursor: pointer; }

/* reaction scheme chip */
.rxn { display: flex; align-items: center; gap: 12px; margin: 4px 0 4px; font-size: 20px; color: var(--txt); }
.rxn b { font-weight: 700; }
.rxn .prod { color: var(--accent); }
.rxn .arrow { position: relative; font-size: 11px; color: var(--muted); letter-spacing: 0.04em; padding: 0 22px 6px; }
.rxn .arrow::before { content: ""; position: absolute; left: 0; right: 6px; bottom: 0; height: 1px; background: var(--line-2); }
.rxn .arrow::after { content: ""; position: absolute; right: 6px; bottom: -2px; width: 5px; height: 5px; border-top: 1px solid var(--line-2); border-right: 1px solid var(--line-2); transform: rotate(45deg); }

/* ===== studio (Stochos Flow Web, guided builder) ===== */
/* One guided experience that should feel like driving Stochos Flow itself: a top
   progress rail, a left node palette, a center canvas where nodes drop in and wire
   up with bezier edges, a right inspector for the per-step decision, and a bottom
   Variables/Console dock. Mirrors the flow editor's visual language (.flow-*,
   .fnode*, .pal-*, .vars-*) on the same brand tokens. The one extra color is the
   green execution edge (#4CAF50), required by the real product. */

.studio { min-height: calc(100vh - var(--chrome-h)); }

/* ---------------------------------------------------------- industry picker */
/* Full-bleed, centered selection. Cards echo .hub .card but tuned for picking:
   stronger hover lift and an accent glow keyed off each card's own accent. */
.studio-pick {
  min-height: calc(100vh - var(--chrome-h));
  display: flex; flex-direction: column; justify-content: center;
  max-width: 1180px; margin: 0 auto; padding: 56px 26px 72px;
}
.pick-head { text-align: center; margin-bottom: 8px; }
.pick-head h1 {
  font-size: clamp(30px, 4.6vw, 50px); line-height: 1.05; margin: 0 0 16px;
  letter-spacing: -0.02em;
}
.pick-head .lead {
  font-size: 17.5px; color: var(--muted); line-height: 1.55;
  max-width: 640px; margin: 0 auto;
}
.pick-cards {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; margin-top: 44px;
}
.pick-card {
  position: relative; overflow: hidden; cursor: pointer;
  border: 1px solid var(--line); border-radius: 6px;
  background: var(--panel); padding: 26px; min-height: 230px;
  display: flex; flex-direction: column;
  transition: transform 0.2s var(--ease-out), border-color 0.2s, box-shadow 0.2s;
}
.pick-card:hover {
  transform: translateY(-2px); border-color: var(--accent-line); box-shadow: var(--shadow);
}
.pick-card .tag {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.14em;
  color: var(--accent); font-weight: 600;
}
.pick-card h3 { margin: 13px 0 8px; font-size: 21px; letter-spacing: -0.01em; line-height: 1.18; }
.pick-card p { color: var(--muted); font-size: 14px; line-height: 1.5; margin: 0; }
.pick-card .go {
  margin-top: auto; padding-top: 16px; color: var(--accent);
  font-size: 13px; font-weight: 600; transition: gap 0.18s;
}
.pick-card:hover .go { color: var(--warm); }
/* quiet corner mark, the same treatment as the challenge picker's .ch-mark:
   26px line art top-right. Without a size the inline SVG inflated to fill the
   card (the giant-icon bug, fixed 2026-07-14). */
.pick-card .mark {
  position: absolute; top: 22px; right: 22px; width: 26px; height: 26px;
  pointer-events: none;
}
.pick-card .mark svg { width: 100%; height: 100%; display: block; }
/* full-bleed still header (Jason's brand stills, 2026-07-14), matching the
   challenge picker's .ch-card-img; the card's overflow:hidden rounds the top. */
.pick-card .pick-card-img {
  display: block; margin: -26px -26px 18px; width: calc(100% + 52px);
  height: clamp(140px, 12vw, 180px); object-fit: cover;
  border-bottom: 1px solid var(--line);
}

/* ----------------------------------------------------------------- the app */
/* Full-height 3-row grid (rail / main / dock), like .flow-app. */
.studio-stage {
  display: grid; grid-template-rows: auto 1fr auto;
  height: calc(100vh - var(--chrome-h)); background: var(--bg); min-height: 0;
  animation: fade 0.45s ease both;
}

/* ----- top progress rail: 7 numbered steps with a connecting line ----- */
.studio-rail {
  display: flex; align-items: flex-start; gap: 0;
  padding: 12px 22px 11px; border-bottom: 1px solid var(--line);
  background: var(--bg-2); overflow-x: auto;
}
.rail-step {
  position: relative; flex: 1 1 0; min-width: 76px;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  text-align: center; color: var(--faint);
}
/* the connecting line, drawn from each step toward the next */
.rail-step::after {
  content: ""; position: absolute; top: 12px; left: 50%; width: 100%; height: 2px;
  background: var(--line); z-index: 0;
}
.rail-step:last-child::after { display: none; }
.rail-step.done::after { background: var(--accent-line); }
.rail-step .rs-dot {
  position: relative; z-index: 1; width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center; font-size: 12px; font-weight: 600;
  font-family: var(--mono); background: var(--panel-2); color: var(--faint);
  border: 1px solid var(--line-2); transition: 0.22s;
}
.rail-step .rs-label {
  font-size: 11px; letter-spacing: 0.04em; max-width: 96px; line-height: 1.25;
  transition: color 0.22s;
}
.rail-step.active .rs-dot {
  background: var(--accent); color: #1a0d05; border-color: var(--accent);
  box-shadow: 0 0 16px -3px var(--accent); transform: scale(1.06);
}
.rail-step.active .rs-label { color: var(--txt); font-weight: 600; }
.rail-step.done .rs-dot {
  background: var(--accent-soft); color: var(--accent); border-color: var(--accent-line);
  font-size: 0;
}
.rail-step.done .rs-dot::after {
  content: "\2713"; font-size: 13px; font-weight: 700; line-height: 1;
}
.rail-step.done .rs-label { color: var(--muted); }

/* ----- main: palette | center | inspector (mirrors .flow-body) ----- */
.studio-main { display: grid; grid-template-columns: 208px 1fr 300px; min-height: 0; }

/* palette: reuse the flow palette look (search + .pal-cat + .pal-node) */
.studio-palette {
  border-right: 1px solid var(--line); background: var(--bg-2);
  overflow-y: auto; padding: 11px;
}

/* ----- center: story banner (top) + graph + live field, with the per-industry
   branch panel docked beside the field. v2 puts FOUR children here, so the old
   two-row split is replaced by an explicit grid in the studio v2 block below. ----- */
.studio-center { position: relative; min-height: 0; }

/* the node canvas: dark, dotted, like .flow-canvas-wrap */
.studio-graph {
  position: relative; overflow: hidden; min-height: 0;
  border-bottom: 1px solid var(--line);
  background:
    radial-gradient(circle at center, rgba(255,255,255,0.03) 1px, transparent 1.4px) 0 0 / 22px 22px,
    #050503;
}
svg.studio-edges {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; overflow: visible;
}
/* bezier edges, idle then GREEN on execution (real-product cue) */
.studio-edges .edge { fill: none; stroke: rgba(170,170,170,0.32); stroke-width: 1.6; transition: stroke 0.2s; }
.studio-edges .edge.running { stroke: rgba(255,210,120,0.85); }
.studio-edges .edge.hot {
  stroke: #4CAF50; stroke-width: 2; stroke-dasharray: 5 6;
  filter: drop-shadow(0 0 5px rgba(76,175,80,0.7));
  animation: dash 0.55s linear infinite;
}
.studio-edges .packet { fill: var(--warm); filter: drop-shadow(0 0 6px var(--warm)); }

/* graph node cards: mirror .fnode*, header accent via inline --cat */
.snode { position: absolute; width: 78px; text-align: center; }
.snode-card {
  position: relative; width: 72px; height: 72px; margin: 0 auto; border-radius: 14px;
  background: linear-gradient(160deg, #3a3a38, #242320);
  border: 1px solid rgba(255,255,255,0.12);
  border-top: 3px solid var(--cat, #888);
  box-shadow: 0 0 0 1.5px transparent, 0 7px 18px -9px #000;
  display: grid; place-items: center; cursor: grab;
  transition: box-shadow 0.18s, transform 0.12s;
}
.snode-card:active { cursor: grabbing; }
.snode-card img { width: 46px; height: 46px; pointer-events: none; }
.snode-label {
  margin-top: 6px; font-size: 11px; font-weight: 600; color: #eeece8;
  white-space: nowrap; font-family: var(--mono); letter-spacing: -0.01em;
}
.snode .port {
  position: absolute; width: 9px; height: 9px; border-radius: 50%;
  background: #8a8880; border: 2px solid #050503;
}
/* drop-in reveal (scale/opacity), crisp not bouncy */
.snode.reveal { animation: snode-pop 0.34s cubic-bezier(.2,.8,.2,1) both; }
@keyframes snode-pop { from { opacity: 0; transform: scale(0.78); } to { opacity: 1; transform: none; } }
.snode.running .snode-card { box-shadow: 0 0 0 2px var(--warm), 0 0 26px -4px var(--warm); }
.snode.running .snode-card::after {
  content: ""; position: absolute; inset: -3px; border-radius: 16px;
  border: 2px solid var(--warm); opacity: 0.6; animation: npulse 1s ease-out infinite;
}
.snode-badge {
  position: absolute; right: -6px; top: -6px; width: 20px; height: 20px;
  border-radius: 50%; display: grid; place-items: center; font-size: 12px;
  background: #4CAF50; color: #05140a; opacity: 0; transform: scale(0.4);
  transition: 0.2s; z-index: 3;
}
.snode.done .snode-badge { opacity: 1; transform: scale(1); }
.snode.done .snode-card { box-shadow: 0 0 0 1.5px #4CAF50, 0 7px 18px -9px #000; }

/* the live field host: relative, with room reserved so the field always shows */
.studio-result {
  position: relative; overflow: hidden; min-height: 280px;
  background: #050503;
}
/* renderer-injected canvases (set inline too; these are crisp fallbacks) */
.studio-result .studio-field-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.studio-result .studio-field-acq {
  position: absolute; left: 12px; bottom: 12px;
  border-radius: 8px; border: 1px solid rgba(255,176,6,0.28);
  background: rgba(7,7,7,0.78); box-shadow: 0 6px 20px rgba(0,0,0,0.45);
  pointer-events: none;
}
/* optional output title + readout chips, if the renderer or studio.js add them */
.studio-result .fo-title {
  position: absolute; left: 14px; top: 12px; z-index: 3; font-size: 11px;
  letter-spacing: 0.04em; color: var(--muted);
  background: rgba(7,7,7,0.6); border: 1px solid var(--line); border-radius: 8px;
  padding: 5px 10px; backdrop-filter: blur(6px);
}
.studio-result .readout-chip {
  position: absolute; z-index: 3; font-family: var(--mono); font-size: 12px;
  color: var(--warm); background: rgba(7,7,7,0.66); border: 1px solid var(--line);
  border-radius: 8px; padding: 6px 10px; backdrop-filter: blur(6px);
}

/* ----- inspector: the per-step decision panel ----- */
.studio-inspector {
  border-left: 1px solid var(--line); background: var(--bg-2);
  padding: 18px 18px 20px; overflow-y: auto;
  display: flex; flex-direction: column; gap: 0; min-height: 0;
}
.insp-kicker {
  font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--accent); font-weight: 600; margin-bottom: 8px;
  animation: fade 0.3s ease both;
}
.insp-title {
  font-size: 21px; line-height: 1.2; letter-spacing: -0.01em; margin-bottom: 16px;
  animation: fade 0.3s ease both;
}
.insp-decision { display: flex; flex-direction: column; gap: 10px; animation: fade 0.32s ease both; }
.decision-label { font-size: 13px; color: var(--muted); line-height: 1.45; }

/* choice chips: selectable pills, selected = amber fill */
.chip-row { display: flex; flex-direction: column; gap: 8px; }
.choice-chip {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  text-align: left; width: 100%; cursor: pointer;
  padding: 11px 13px; border-radius: 11px; font-family: var(--sans);
  border: 1px solid var(--line); background: var(--panel); color: var(--txt);
  transition: 0.16s;
}
.choice-chip:hover { border-color: var(--line-2); transform: translateY(-1px); }
.choice-chip:active { transform: translateY(0) scale(0.99); }
.choice-chip .chip-label { font-size: 13.5px; font-weight: 600; }
.choice-chip .chip-hint { font-size: 11.5px; color: var(--muted); }
.choice-chip.on {
  background: var(--accent); border-color: var(--accent); color: #1a0d05;
  box-shadow: 0 0 18px -6px var(--accent);
}
.choice-chip.on .chip-hint { color: rgba(26,13,5,0.72); }

/* transform toggle: a 2-segment selector */
.toggle-row {
  display: inline-flex; padding: 3px; gap: 3px; border-radius: 11px;
  border: 1px solid var(--line); background: var(--panel); align-self: flex-start;
}
.toggle-opt {
  padding: 8px 18px; border-radius: 8px; cursor: pointer; font-size: 13px;
  font-weight: 600; border: none; background: transparent; color: var(--muted);
  transition: 0.15s;
}
.toggle-opt:hover { color: var(--txt); }
.toggle-opt.on { background: var(--accent); color: #1a0d05; }

/* fit score readout (R2 / RMSE / CI) shown after validate */
.fit-score {
  display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 4px;
  padding: 11px 13px; border-radius: 11px;
  border: 1px solid var(--line); background: var(--panel);
  font-size: 12.5px; color: var(--muted); font-family: var(--mono);
}
.fit-score b { color: var(--accent); font-weight: 600; }

/* optimize strategy: kappa slider + iterations action + convergence readout */
.strategy-ctl { display: flex; flex-direction: column; gap: 12px; }
.kappa-row { display: flex; align-items: center; gap: 10px; }
.kappa-end { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--faint); flex: none; }
.kappa-slider {
  flex: 1; appearance: none; height: 4px; border-radius: 4px; outline: none;
  background: linear-gradient(90deg, var(--accent), var(--line-2));
}
.kappa-slider::-webkit-slider-thumb {
  appearance: none; width: 15px; height: 15px; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 12px -1px var(--accent); cursor: pointer; border: 2px solid var(--bg);
}
.kappa-slider::-moz-range-thumb {
  width: 15px; height: 15px; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 12px -1px var(--accent); cursor: pointer; border: 2px solid var(--bg);
}
.kappa-readout { font-size: 12.5px; color: var(--muted); font-family: var(--mono); }
.kappa-readout b { color: var(--accent); }
.opt-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.iter-label { font-size: 12.5px; color: var(--muted); display: inline-flex; align-items: center; gap: 7px; }
.iter-input {
  width: 54px; background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
  padding: 7px 8px; color: var(--txt); font-family: var(--mono); font-size: 12.5px;
}
.run-iter {
  flex: 1; min-width: 120px; cursor: pointer; padding: 10px 16px; border-radius: 999px;
  font-size: 13px; font-weight: 700; border: 1px solid var(--accent);
  background: var(--accent); color: #1a0d05; transition: 0.18s;
}
.run-iter:hover { transform: translateY(-2px); box-shadow: 0 12px 30px rgba(255,176,6,0.2); }
.run-iter.busy { opacity: 0.55; pointer-events: none; }
.opt-readout {
  font-size: 12.5px; color: var(--muted); line-height: 1.5;
  padding: 11px 13px; border-radius: 11px;
  border: 1px solid var(--line); background: var(--panel);
}
.opt-readout b { color: var(--accent); font-family: var(--mono); }

/* "why it matters" helper (the v1 "what changed" callout, .insp-changed, is
   retired: studio.js now renders that copy inside the single .sfg-result
   card's .result-callout instead, see the guide-panel block below). */
.insp-why { margin-top: 12px; font-size: 12.5px; line-height: 1.55; color: var(--muted); }
.insp-why:empty { display: none; }

/* primary amber pill: the Next / Restart button */
.insp-next {
  margin-top: auto; width: 100%; cursor: pointer;
  padding: 13px 18px; border-radius: 999px; border: 1px solid var(--accent);
  font-size: 14px; font-weight: 700; background: var(--accent); color: #1a0d05;
  transition: 0.2s;
}
.insp-next:hover { transform: translateY(-2px); box-shadow: 0 14px 34px rgba(255,176,6,0.2); }

/* ----- bottom Variables/Console dock (mirrors .flow-bottom + .vars-*) ----- */
.studio-dock { border-top: 1px solid var(--line); background: var(--bg-2); height: 150px; overflow: auto; }
.studio-dock .vars-head {
  display: flex; gap: 16px; align-items: center; padding: 8px 13px;
  border-bottom: 1px solid var(--line); font-size: 12px; color: var(--faint);
  position: sticky; top: 0; background: var(--bg-2); z-index: 2;
}
.studio-dock .vars-head span { cursor: pointer; }
.studio-dock .vars-head .on { color: var(--txt); border-bottom: 2px solid var(--accent); padding-bottom: 6px; }
.studio-dock .vars-head .count { margin-left: auto; color: var(--faint); cursor: default; }
.studio-console { padding: 10px 13px; font-family: var(--mono); font-size: 11.5px; color: var(--muted); }
.console-line { padding: 2px 0; line-height: 1.5; animation: fade 0.25s ease both; }
.console-line::before { content: "› "; color: var(--accent); }

/* ----- step 7: the deployed-web-app preview (a shipped product panel) ----- */
/* Anchored INSIDE the center column so the preview overlays the field, never the
   inspector (whose Restart button must stay clickable). */
.studio-center { position: relative; }
.studio-deploy {
  position: absolute; left: 16px; right: 16px; bottom: 16px; z-index: 8;
  max-height: calc(100% - 24px); overflow: auto;
  border: 1px solid var(--line-2); border-radius: 16px;
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  box-shadow: var(--shadow);
  opacity: 0; transform: translateY(18px) scale(0.98);
  transition: opacity 0.34s cubic-bezier(.2,.8,.2,1), transform 0.34s cubic-bezier(.2,.8,.2,1);
}
.studio-deploy.show { opacity: 1; transform: none; }
.deploy-head { position: relative; padding: 16px 44px 12px 18px; border-bottom: 1px solid var(--line); }
.deploy-close {
  position: absolute; top: 12px; right: 12px; width: 26px; height: 26px;
  display: grid; place-items: center; border-radius: 50%;
  border: 1px solid var(--line); background: var(--panel); color: var(--muted);
  font-size: 17px; line-height: 1; cursor: pointer; transition: 0.15s;
}
.deploy-close:hover { color: var(--txt); border-color: var(--line-2); background: var(--panel-2); }
.deploy-badge {
  display: inline-block; font-size: 10px; text-transform: uppercase; letter-spacing: 0.13em;
  color: var(--accent); border: 1px solid var(--accent-line); border-radius: 999px;
  padding: 3px 10px; margin-bottom: 9px;
}
.deploy-head h3 { margin: 0 0 5px; font-size: 18px; letter-spacing: -0.01em; }
.deploy-blurb { margin: 0; font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.deploy-body {
  display: grid; grid-template-columns: 1fr 1fr; gap: 16px; padding: 16px 18px;
}
.deploy-controls { display: flex; flex-direction: column; gap: 13px; }
.deploy-slider { display: grid; grid-template-columns: 1fr auto; gap: 4px 8px; align-items: center; }
.deploy-slider label { grid-column: 1 / -1; font-size: 12px; color: var(--muted); }
.deploy-slider input[type=range] {
  appearance: none; width: 100%; height: 4px; border-radius: 4px; background: var(--line-2); outline: none;
}
.deploy-slider input[type=range]::-webkit-slider-thumb {
  appearance: none; width: 14px; height: 14px; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 10px -1px var(--accent); cursor: pointer; border: 2px solid var(--bg);
}
.deploy-slider input[type=range]::-moz-range-thumb {
  width: 14px; height: 14px; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 10px -1px var(--accent); cursor: pointer; border: 2px solid var(--bg);
}
.deploy-slider-val { font-family: var(--mono); font-size: 12px; color: var(--accent); }
.deploy-out {
  display: flex; flex-direction: column; justify-content: center;
  border: 1px solid var(--line); border-radius: 12px; background: var(--panel); padding: 14px;
}
.deploy-out-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); }
.deploy-out-val { margin-top: 4px; font-family: var(--mono); font-size: 26px; font-weight: 600; letter-spacing: -0.01em; }
.deploy-band { font-size: 13px; font-weight: 500; color: var(--warm); margin-left: 2px; }
.deploy-bar { position: relative; height: 6px; border-radius: 4px; margin-top: 12px; background: var(--panel-2); overflow: hidden; }
.deploy-bar-fill { position: absolute; left: 0; top: 0; height: 100%; background: var(--warm); opacity: 0.85; transition: width 0.18s; }
.deploy-bar-band { position: absolute; top: 0; height: 100%; background: linear-gradient(90deg, transparent, rgba(255,176,6,0.5), transparent); transition: 0.18s; }
.deploy-note { margin-top: 9px; font-size: 11px; color: var(--faint); line-height: 1.4; }
.deploy-foot {
  display: flex; align-items: center; gap: 12px; padding: 12px 18px;
  border-top: 1px solid var(--line); background: var(--bg-2);
}
.deploy-download {
  cursor: pointer; padding: 9px 16px; border-radius: 999px; font-size: 13px; font-weight: 700;
  border: 1px solid var(--accent); background: var(--accent); color: #1a0d05; transition: 0.18s;
}
.deploy-download:hover { transform: translateY(-2px); box-shadow: 0 12px 28px rgba(255,176,6,0.2); }
.deploy-disclaimer { font-size: 11px; color: var(--faint); }

/* --------------------------------------------------------------- responsive */
/* Mirror the flow media rule: collapse the inspector and tighten the palette,
   but keep the live field large and visible. */
@media (max-width: 1100px) {
  .studio-main { grid-template-columns: 170px 1fr; }
  .studio-inspector {
    grid-column: 1 / -1; border-left: none; border-top: 1px solid var(--line);
    max-height: 300px;
  }
  .pick-cards { grid-template-columns: 1fr; }
  .studio-deploy { left: 10px; right: 10px; bottom: 10px; }
  .deploy-body { grid-template-columns: 1fr; }
}


/* studio: flash a Variables row when its value updates in place */
@keyframes varFlash { from { background: rgba(255,176,6,0.22); } to { background: transparent; } }
.studio-dock .vars-row.var-flash { animation: varFlash 0.7s ease-out; }

/* studio: validate step pred-vs-true mini scatter */
.validate-viz { margin-top: 13px; }
.validate-viz .vv-title { font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); margin-bottom: 6px; }
.validate-scatter { width: 100%; height: auto; display: block; border: 1px solid var(--line); border-radius: 10px; background: var(--panel); }


/* ===== studio v2 (drag-build) ===== */
/* The headline interaction: the visitor BUILDS the Stochos Flow pipeline by
   dragging palette nodes onto dashed ghost slots, then wiring ports with a
   rubber-band edge that glows green on execution. A story banner narrates each
   beat; a per-industry branch chart pays it off. Pure CSS over the v1 studio
   chrome above. Brand tokens only; the one non-brand color is the green
   execution edge / done state (#4CAF50), as in the real product. */

/* ----- center layout: banner row, graph row, field row; branch docks over the
   field. The live Confidence Field stays the hero (it gets the tallest row);
   the banner sits in its own auto row so it never covers the graph nodes. ----- */
.studio-center {
  display: grid;
  grid-template-rows: auto minmax(0, 0.44fr) minmax(0, 0.56fr);
  grid-template-areas: "banner" "graph" "field";
}
.studio-center > .story-banner { grid-area: banner; }
.studio-center > .studio-graph { grid-area: graph; }
/* the field row owns a real minimum so the surface is always the hero */
.studio-center > .studio-result { grid-area: field; min-height: 240px; }

/* ----- story banner: a glassy narrative strip, like .stage-caption, pinned at
   the top of the canvas column. An amber eyebrow rule + the animated beat line,
   with the secondary Auto-build button parked at the right. ----- */
.story-banner {
  position: relative; z-index: 6;
  display: flex; align-items: center; gap: 14px;
  margin: 10px 12px 0; padding: 10px 14px;
  border: 1px solid var(--line); border-radius: 12px;
  background: linear-gradient(120deg, rgba(255,176,6,0.07), rgba(12,12,14,0.82));
  backdrop-filter: blur(7px);
  box-shadow: 0 8px 24px -16px #000;
}
/* the amber eyebrow tab on the left edge */
.story-banner::before {
  content: "Story"; flex: none;
  font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase;
  font-weight: 700; color: #1a0d05;
  background: var(--accent); border-radius: 6px; padding: 3px 8px;
  box-shadow: 0 0 14px -4px var(--accent);
}
.story-beat {
  flex: 1; min-width: 0;
  font-size: 13px; line-height: 1.45; color: var(--txt);
}
/* animate the beat changing: a crisp fade + slide-up, retriggered via .pulse */
.story-beat.pulse { animation: beatChange 0.4s cubic-bezier(.2,.8,.2,1) both; }
@keyframes beatChange {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* ----- ghost slots: a faint dashed blueprint of what to build. Each is the
   exact NW x NH of a node card, positioned by studio.js. ----- */
.studio-ghost {
  position: absolute; z-index: 1;
  border: 1.6px dashed rgba(255,255,255,0.20); border-radius: 14px;
  background: rgba(255,255,255,0.012);
  display: grid; place-items: center; text-align: center;
  opacity: 0.7; transition: opacity 0.25s, border-color 0.25s, box-shadow 0.25s, transform 0.25s;
  pointer-events: none;
}
.ghost-label {
  font-size: 10px; line-height: 1.2; letter-spacing: 0.04em;
  color: var(--faint); padding: 0 4px;
}
/* the slot the current step wants: amber dashed + a soft idle pulse */
.studio-ghost.want {
  border-color: var(--accent-line); opacity: 0.92;
  animation: ghostPulse 1.7s ease-in-out infinite;
}
.studio-ghost.want .ghost-label { color: var(--accent); }
@keyframes ghostPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255,176,6,0.0); }
  50%      { box-shadow: 0 0 0 4px rgba(255,176,6,0.10), 0 0 18px -4px rgba(255,176,6,0.45); }
}
/* a correct node is hovering this slot: lock an amber glow */
.studio-ghost.target {
  border-color: var(--accent); border-style: solid; opacity: 1;
  background: rgba(255,176,6,0.06); transform: scale(1.03);
  box-shadow: 0 0 0 3px rgba(255,176,6,0.16), 0 0 24px -4px var(--accent);
  animation: none;
}
/* once a node lands the slot fills: hide it so the card reads clean */
.studio-ghost.filled {
  opacity: 0; box-shadow: none; border-color: transparent;
  animation: none; transform: scale(0.96);
}

/* ----- palette nodes: grab-draggable. (Base .pal-node look comes from the flow
   block: cursor:grab, hover background.) Add the lift + the hint pulse. ----- */
.pal-node {
  position: relative; user-select: none;
  border: 1px solid transparent; border-radius: 7px;
  transition: background 0.16s, color 0.16s, transform 0.12s, border-color 0.16s, box-shadow 0.16s;
}
.pal-node img { pointer-events: none; }
.pal-node:hover { transform: translateY(-1px); }
/* the "drag me next" state. Specificity (0,2,0) beats the global .hint (0,1,0),
   so position/pointer-events here win over that floating-tooltip rule. We also
   keep position:relative + pointer-events:auto explicitly so the node stays in
   its palette column and stays grabbable. */
.pal-node.hint {
  position: relative; pointer-events: auto;
  border-color: var(--accent-line); color: var(--txt);
  background: var(--accent-soft);
  animation: palHint 1.5s ease-in-out infinite;
}
@keyframes palHint {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255,176,6,0.0); }
  50%      { box-shadow: 0 0 0 2px rgba(255,176,6,0.30), 0 0 16px -4px var(--accent); }
}
/* dim the source node while its clone is being dragged */
.pal-node.dragging { opacity: 0.38; }
/* the clone that follows the cursor: elevated, slightly scaled, shadowed */
.pal-fly {
  display: flex; align-items: center; gap: 9px;
  padding: 6px 9px; border-radius: 9px;
  font-size: 12.5px; color: var(--txt);
  background: linear-gradient(160deg, #2d2b28, #1a1814);
  border: 1px solid var(--accent-line);
  box-shadow: 0 16px 40px -10px #000, 0 0 0 1px rgba(255,176,6,0.20);
  transform: scale(1.06); transform-origin: top left;
  pointer-events: none;
}
.pal-fly img { width: 22px; height: 22px; flex: none; }

/* ----- wiring: the rubber-band path while dragging output -> input ----- */
.studio-edges .wire-drag {
  fill: none; stroke: var(--accent); stroke-width: 2;
  stroke-dasharray: 6 5; stroke-linecap: round;
  filter: drop-shadow(0 0 5px rgba(255,176,6,0.55));
  animation: dash 0.5s linear infinite;
}
/* a valid input port while wiring: enlarge + amber glow so the target is obvious */
.snode .port.compatible {
  background: var(--accent); border-color: rgba(7,7,7,0.85);
  box-shadow: 0 0 0 4px rgba(255,176,6,0.18), 0 0 12px 1px var(--accent);
  transform: scale(1.4);
  animation: portPulse 1.1s ease-in-out infinite;
}
@keyframes portPulse {
  0%, 100% { box-shadow: 0 0 0 4px rgba(255,176,6,0.14), 0 0 10px 0 rgba(255,176,6,0.7); }
  50%      { box-shadow: 0 0 0 6px rgba(255,176,6,0.22), 0 0 16px 2px var(--accent); }
}
/* ports react to the pointer (they start wires); the graph card's port look is
   inherited from the v1 .snode .port rule above. */
.snode .port { transition: transform 0.14s, box-shadow 0.18s, background 0.18s; cursor: crosshair; }
.snode.done .port { background: #4CAF50; }

/* ----- node drop-in: studio.js toggles .snode.placing on placement ----- */
.snode.placing { animation: snode-pop 0.36s cubic-bezier(.2,.8,.2,1) both; }

/* ----- branch panel: the per-industry payoff, a real result card docked at the
   bottom-right of the field so the Confidence Field stays the hero. It holds the
   <canvas class="branch-chart"> (which needs an explicit CSS size so the host can
   read non-zero clientWidth/Height), a title, and a small legend. ----- */
.studio-branch {
  position: absolute; right: 14px; bottom: 14px; z-index: 7;
  width: min(330px, 46%);
  border: 1px solid var(--line-2); border-radius: 14px;
  background: linear-gradient(180deg, rgba(20,20,23,0.96), rgba(14,14,17,0.96));
  box-shadow: var(--shadow); backdrop-filter: blur(6px);
  padding: 12px 13px 13px;
  animation: branchIn 0.34s cubic-bezier(.2,.8,.2,1) both;
}
.studio-branch[hidden] { display: none; }
@keyframes branchIn {
  from { opacity: 0; transform: translateY(12px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}
.branch-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.branch-head::before {
  content: ""; flex: none; width: 7px; height: 7px; border-radius: 2px;
  background: var(--accent); box-shadow: 0 0 8px var(--accent);
}
.branch-title {
  font-size: 12px; font-weight: 600; color: var(--txt); letter-spacing: -0.01em;
}
/* explicit canvas size: clientWidth/Height must be non-zero for studio-charts.js */
.branch-chart {
  display: block; width: 100%; height: 192px;
  border-radius: 9px; background: #050503; border: 1px solid var(--line);
}
.branch-legend {
  margin-top: 9px; display: flex; flex-direction: column; gap: 3px;
  font-family: var(--mono); font-size: 11px; color: var(--muted);
}
.branch-legend .lg-row { line-height: 1.4; }
.branch-legend .lg-row b { color: var(--accent); font-weight: 600; }

/* ----- escape hatches: clearly SECONDARY to the primary amber Next ----- */
/* "Connect this node for me": a ghost pill in the inspector, sits above Next */
.btn-connect-for-me {
  width: 100%; margin-top: 14px; cursor: pointer;
  padding: 10px 16px; border-radius: 999px;
  font-size: 12.5px; font-weight: 600; font-family: var(--sans);
  color: var(--accent); background: transparent;
  border: 1px solid var(--accent-line);
  transition: 0.18s;
}
.btn-connect-for-me:hover {
  background: var(--accent-soft); border-color: var(--accent);
  transform: translateY(-1px);
}
.btn-connect-for-me:disabled { opacity: 0.45; cursor: default; transform: none; }
/* "Auto-build the rest": a small ghost button parked in the story banner */
.btn-autobuild {
  flex: none; cursor: pointer;
  padding: 6px 12px; border-radius: 999px;
  font-size: 11.5px; font-weight: 600; font-family: var(--sans);
  color: var(--muted); background: rgba(255,255,255,0.03);
  border: 1px solid var(--line);
  transition: 0.16s;
}
.btn-autobuild:hover {
  color: var(--accent); border-color: var(--accent-line);
  background: var(--accent-soft);
}
.btn-autobuild.busy { opacity: 0.55; pointer-events: none; }

/* ----- responsive: keep the field visible, let the branch span full width ----- */
@media (max-width: 1100px) {
  .studio-center { grid-template-rows: auto minmax(0, 0.40fr) minmax(0, 0.60fr); }
  .studio-branch { position: static; width: auto; margin: 10px 12px 12px; align-self: stretch; justify-self: stretch; }
  .story-banner { flex-wrap: wrap; }
}

/* ===== studio v3 (Stochos Flow replica) ===== */
/* A literal multi-color skin of the real Stochos Flow desktop app, scoped under
   .sf-app so it wins by specificity over the v1/v2 amber studio blocks above
   (which still serve the kept classes for other code paths). The palette here is
   the Flow look, NOT the PI amber brand: pure-black canvas, dark Qt-grey chrome,
   green built-node borders, white edges, amber category triangles only, and the
   purple/orange/red agent buttons. Local tokens keep the values in one place; the
   ONE carried-over brand cue is the green execution edge. No em or en dashes. */
.sf-app {
  /* Flow look tokens (local; do not touch the global amber brand tokens) */
  --sf-chrome: #2c2c2e;       /* Qt grey app panels */
  --sf-chrome-2: #222;        /* darker strips */
  --sf-chrome-3: #1b1b1b;     /* darkest strips / console-ish */
  --sf-border: #3a3a3c;       /* hairline chrome borders */
  --sf-ink: #e6e6e6;          /* primary text */
  --sf-muted: #9b9b9b;        /* muted text / timestamps */
  --sf-canvas: #000000;       /* pure-black node canvas */
  --sf-green: #39d353;        /* built node border / connected port / check */
  --sf-edge: #e9e9e9;         /* white edge */
  --sf-tri: #e8a33d;          /* category disclosure triangle (amber accent) */
  --sf-blue: #c9890a;         /* active tab underline / save (warm amber replaces blue) */
  --sf-play: #2ecc40;         /* run */
  --sf-stop: #e74c3c;         /* stop */
  --sf-purple: #6b6355;       /* Context button (warm neutral; no blue/indigo per brand) */
  --sf-orange: #e8632a;       /* Send button */
  --sf-red: #b03636;          /* Clear Chat button */
  --sf-tile: #262629;         /* node tile bg */
  --sf-tile-idle: #4a4a4a;    /* idle node border */

  /* full-height grid: titlebar / menubar / toolbar / main(1fr) */
  display: grid;
  grid-template-rows: auto auto auto 1fr;
  grid-template-columns: 100%;
  height: calc(100vh - var(--chrome-h)); min-height: 0;
  background: var(--sf-chrome-3);
  color: var(--sf-ink);
  font-family: var(--sans);
  animation: fade 0.4s ease both;
}
.sf-app * { box-sizing: border-box; }

/* ----------------------------------------------------------- title bar ----- */
.sf-titlebar {
  display: flex; align-items: center; gap: 10px;
  height: 34px; padding: 0 10px;
  background: var(--sf-chrome-2); border-bottom: 1px solid var(--sf-border);
  -webkit-user-select: none; user-select: none;
}
.sf-logo {
  flex: none; width: 22px; height: 22px; border-radius: 6px;
  display: grid; place-items: center;
  font-size: 11px; font-weight: 800; letter-spacing: -0.02em; color: #fff;
  background: linear-gradient(150deg, #2c2c2e, #1b1b1b);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.25);
}
.sf-title { font-size: 12.5px; color: var(--sf-ink); display: flex; align-items: center; gap: 7px; }
.sf-title .sf-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--sf-tri); box-shadow: 0 0 7px rgba(232,163,61,0.7);
}
.sf-winctl { margin-left: auto; display: flex; align-items: center; gap: 9px; }
.sf-winctl i {
  display: inline-block; width: 12px; height: 12px; border-radius: 50%;
  background: #555; transition: background 0.15s; cursor: default;
}
.sf-winctl .sf-min { background: #f4bf4f; }
.sf-winctl .sf-max { background: #5ec45e; }
.sf-winctl .sf-close { background: #e3584f; }

/* Back button: top-left of the titlebar, takes you back to the domain picker */
.sf-back-btn {
  flex: none; display: flex; align-items: center; gap: 5px;
  background: none; border: 1px solid var(--sf-border); border-radius: 6px;
  padding: 3px 9px; cursor: pointer; font-size: 11.5px; font-weight: 600;
  color: var(--sf-muted); font-family: var(--sans);
  transition: color 0.13s, border-color 0.13s, background 0.13s;
  order: -1; /* place before sf-logo */
}
.sf-back-btn:hover { color: var(--sf-ink); border-color: rgba(255,255,255,0.3); background: rgba(255,255,255,0.06); }
.sf-back-btn:active { background: rgba(255,255,255,0.10); }

/* ------------------------------------------------------------- menubar ----- */
.sf-menubar {
  display: flex; align-items: center; gap: 2px;
  height: 28px; padding: 0 6px;
  background: var(--sf-chrome); border-bottom: 1px solid var(--sf-border);
  font-size: 12.5px; color: var(--sf-muted);
  -webkit-user-select: none; user-select: none;
}
.sf-menubar span {
  padding: 3px 9px; border-radius: 5px; cursor: default; transition: 0.14s;
}
.sf-menubar span:hover { background: rgba(255,255,255,0.08); color: var(--sf-ink); }
.sf-menubar .sf-menu-agent { color: #d4b87e; cursor: pointer; }

/* ------------------------------------------------------------- toolbar ----- */
.sf-toolbar {
  display: flex; align-items: center; gap: 8px;
  height: 40px; padding: 0 10px;
  background: var(--sf-chrome); border-bottom: 1px solid var(--sf-border);
}
.sf-drag-handle {
  flex: none; width: 12px; height: 22px; margin-right: 2px;
  background-image: radial-gradient(circle, #6a6a6c 1.1px, transparent 1.3px);
  background-size: 5px 6px; background-position: center; opacity: 0.8;
}
.sf-tool {
  width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  font-size: 12px; line-height: 1;
  border: 1px solid var(--sf-border); background: var(--sf-chrome-2); color: var(--sf-ink);
  transition: transform 0.12s, box-shadow 0.16s, background 0.16s, filter 0.16s;
}
.sf-tool:hover { transform: translateY(-1px); filter: brightness(1.12); }
.sf-tool:active { transform: translateY(0) scale(0.96); }
.sf-tool.sf-play { background: var(--sf-play); border-color: #27a838; color: #062a0c; }
.sf-tool.sf-play:hover { box-shadow: 0 0 16px -3px var(--sf-play); }
.sf-tool.sf-stop { background: var(--sf-stop); border-color: #c1392f; color: #2a0606; }
.sf-tool.sf-stop:hover { box-shadow: 0 0 16px -3px var(--sf-stop); }
.sf-tool.sf-save { background: var(--sf-blue); border-color: #a87009; color: #1a0d00; }
.sf-tool.sf-save:hover { box-shadow: 0 0 16px -3px var(--sf-blue); }
.sf-tool.sf-export { background: var(--sf-chrome-2); color: #e7a44c; border-color: var(--sf-border); }
.sf-tool.sf-export:hover { box-shadow: 0 0 14px -4px #e7a44c; }

/* ---------------------------------------------------------------- main ----- */
/* palette | center | agent. The center carries the canvas + dock as its own
   sub-grid so the black canvas takes all the room above the console. */
.sf-main {
  display: grid; grid-template-columns: 224px 1fr 320px;
  min-height: 0; min-width: 0;
}

/* --------------------------------------------------------- left palette ---- */
.sf-palette {
  border-right: 1px solid var(--sf-border); background: var(--sf-chrome);
  overflow-y: auto; overflow-x: hidden; padding: 10px 9px 18px; min-height: 0;
}
.sf-palette::-webkit-scrollbar { width: 10px; }
.sf-palette::-webkit-scrollbar-thumb { background: #46464a; border-radius: 6px; border: 2px solid var(--sf-chrome); }
.sf-palette::-webkit-scrollbar-track { background: transparent; }
.sf-search { position: relative; margin-bottom: 9px; }
.sf-search-icon {
  position: absolute; left: 9px; top: 50%; transform: translateY(-50%);
  font-size: 12px; opacity: 0.7; pointer-events: none;
}
/* override the .flow-search amber-ish base: a dark Qt field */
.sf-app .sf-search-input {
  width: 100%; margin: 0; padding: 8px 10px 8px 28px;
  border-radius: 7px; border: 1px solid var(--sf-border);
  background: var(--sf-chrome-3); color: var(--sf-ink);
  font-size: 12.5px; font-family: var(--sans); outline: none;
}
.sf-app .sf-search-input::placeholder { color: var(--sf-muted); }
.sf-app .sf-search-input:focus { border-color: var(--sf-blue); }
.sf-palbtns { display: flex; gap: 7px; margin-bottom: 7px; }
.sf-palbtn {
  flex: 1 1 0; min-width: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 7px 8px; border-radius: 7px;
  border: 1px solid var(--sf-border); background: var(--sf-chrome-2); color: var(--sf-ink);
  font-size: 11.5px; font-family: var(--sans); transition: 0.15s;
}
.sf-palbtn:hover { background: #34343a; border-color: #4a4a4f; }
.sf-palbtn-wide { width: 100%; flex: none; margin-bottom: 12px; }
.sf-folder { color: #e7c14c; font-size: 13px; }

/* category groups: amber disclosure triangle + bold name. Override the flow
   .pal-cat (which was an uppercase faint label). */
.sf-app .pal-cat {
  display: flex; align-items: center; gap: 7px;
  margin: 12px 2px 5px; padding: 2px 0;
  font-size: 12.5px; font-weight: 700; letter-spacing: 0; text-transform: none;
  color: var(--sf-ink); cursor: default;
}
.pal-tri { color: var(--sf-tri); font-size: 10px; line-height: 1; flex: none; }

/* node rows: 20px color icon + light label, hover highlight. Override the flow
   .pal-node base look. No transform on hover: nodes only move when actually dragged. */
.sf-app .pal-node {
  display: flex; align-items: center; gap: 9px;
  padding: 6px 8px; margin: 1px 0; border-radius: 7px;
  font-size: 12.5px; color: #d6d6d8; cursor: grab;
  border: 1px solid transparent; background: transparent;
  -webkit-user-select: none; user-select: none;
  /* no transform in the transition: nodes must not shift position on hover */
  transition: background 0.14s, color 0.14s, border-color 0.14s, opacity 0.14s;
  transform: none !important;
}
/* subtle non-moving hover affordance: brighter text + faint background, no translate/scale */
.sf-app .pal-node:hover { background: rgba(255,255,255,0.07); color: #fff; }
.sf-app .pal-node img { width: 20px; height: 20px; flex: none; pointer-events: none; }
.pal-sub { color: var(--sf-muted); font-size: 13px; margin-right: -3px; }
.pal-robot { color: #c0b8a0; font-size: 12px; margin-left: 2px; }
/* the "drag me next" pulse: green, on theme, beats the global .hint + the v2
   amber .pal-node.hint by specificity */
.sf-app .pal-node.hint {
  position: relative; pointer-events: auto;
  background: rgba(57,211,83,0.12); color: #fff;
  border-color: rgba(57,211,83,0.5);
  animation: sfPalHint 1.5s ease-in-out infinite;
}
@keyframes sfPalHint {
  0%, 100% { box-shadow: 0 0 0 0 rgba(57,211,83,0); }
  50%      { box-shadow: 0 0 0 2px rgba(57,211,83,0.3), 0 0 16px -4px var(--sf-green); }
}
.sf-app .pal-node.dragging { opacity: 0.38; }

/* ------------------------------------------------------------ center ------- */
/* canvas grows, dock is a fixed-ish strip below it. min-height:0 lets the canvas
   shrink instead of pushing the dock off-screen. */
.sf-center {
  display: grid; grid-template-rows: 1fr auto;
  min-height: 0; min-width: 0; background: var(--sf-canvas);
}

/* ----------------------------------------------------- canvas (black) ------ */
.sf-canvas {
  position: relative; overflow: hidden; min-height: 0;
  background:
    radial-gradient(circle at center, rgba(255,255,255,0.035) 1px, transparent 1.4px) 0 0 / 26px 26px,
    var(--sf-canvas);
}
svg.sf-edges {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; overflow: visible;
}
/* edges: WHITE bezier, brighten/green on execution. Override .studio-edges. */
.sf-app svg.sf-edges .edge {
  fill: none; stroke: var(--sf-edge); stroke-width: 1.6; transition: stroke 0.2s, filter 0.2s;
}
/* like real Flow: a built edge is WHITE solid; GREEN only flashes during a run. */
.sf-app svg.sf-edges .edge.running {
  stroke: var(--sf-green); stroke-width: 2; stroke-dasharray: 5 6;
  filter: drop-shadow(0 0 5px rgba(57,211,83,0.8));
  animation: dash 0.55s linear infinite;
}
.sf-app svg.sf-edges .edge.hot {
  stroke: #e9e9e9; stroke-width: 1.7; stroke-dasharray: none;
  filter: none; animation: none;
}
.sf-app svg.sf-edges .packet { fill: var(--sf-green); filter: drop-shadow(0 0 6px var(--sf-green)); }
/* the rubber-band wire while dragging output -> input: green, on theme */
.sf-app svg.sf-edges .wire-drag {
  fill: none; stroke: var(--sf-green); stroke-width: 2;
  stroke-dasharray: 6 5; stroke-linecap: round;
  filter: drop-shadow(0 0 5px rgba(57,211,83,0.55));
  animation: dash 0.5s linear infinite;
}

/* node TILES: rounded ~66px square, icon fills it, label below. Override the v1
   .snode-card (linear-gradient + amber-ish look). */
.sf-app .snode { position: absolute; width: 78px; text-align: center; }
.sf-app .snode-card {
  position: relative; width: 66px; height: 66px; margin: 0 auto; border-radius: 12px;
  background: var(--sf-tile);
  border: 2.5px solid var(--sf-tile-idle);
  box-shadow: 0 8px 20px -10px #000, inset 0 1px 0 rgba(255,255,255,0.05);
  display: grid; place-items: center; cursor: grab;
  transition: border-color 0.18s, box-shadow 0.18s, transform 0.12s;
}
.sf-app .snode-card:active { cursor: grabbing; }
.sf-app .snode-card img { width: 44px; height: 44px; pointer-events: none; }
/* the small baked badge (FIT, INIT, NEXT, 70/30, PAM, APP) */
.sf-app .snode-tag {
  position: absolute; left: 4px; bottom: 4px;
  font-size: 8.5px; font-weight: 700; letter-spacing: 0.02em; font-family: var(--mono);
  padding: 1px 4px; border-radius: 4px;
  background: rgba(0,0,0,0.62); color: #eaeaea; border: 1px solid rgba(255,255,255,0.16);
  pointer-events: none;
}
/* white label BELOW the tile */
.sf-app .snode-label {
  margin-top: 7px; font-size: 11.5px; font-weight: 500; color: #f2f2f4;
  white-space: nowrap; font-family: var(--sans); letter-spacing: 0;
}
/* ports: left in / right out; open grey, connected green */
/* PORT CLIP BUG FIX (v4): ports are children of .snode-card and are placed at
   left:0%/left:100% (the card's own left/right edge) with top:N% down the card,
   THEN translate(-50%,-50%) centers the dot exactly ON that edge coordinate
   (half inside the card, half outside), so it renders as a clean full circle
   regardless of the card's pixel size -- no more hardcoded-pixel mismatch
   between this CSS and the card's actual width (that mismatch was the root
   cause: out-ports floated detached past the card's right edge and in-ports
   sat mostly under the card's own left border, reading as half-clipped). The
   matching JS math lives in studio.js's port() helper (CARD_W/CARD_H). z-index
   keeps every port above the card's own background/border, below nothing. */
.sf-app .snode .port {
  position: absolute; width: 9px; height: 9px; border-radius: 50%;
  background: #8a8a8a; border: 2px solid #000;
  transform: translate(-50%, -50%);
  z-index: 4;
  transition: transform 0.14s, box-shadow 0.18s, background 0.18s; cursor: crosshair;
}
.sf-app .snode.done .port { background: var(--sf-green); }
.sf-app .snode .port[data-role="in"] { box-shadow: 0 0 0 1px rgba(0,0,0,0.5); }
/* a compatible input port while wiring: enlarge + green glow (override amber) */
.sf-app .snode .port.compatible {
  background: var(--sf-green); border-color: rgba(0,0,0,0.85);
  box-shadow: 0 0 0 4px rgba(57,211,83,0.18), 0 0 12px 1px var(--sf-green);
  transform: translate(-50%, -50%) scale(1.4); animation: sfPortPulse 1.1s ease-in-out infinite;
}
@keyframes sfPortPulse {
  0%, 100% { box-shadow: 0 0 0 4px rgba(57,211,83,0.14), 0 0 10px 0 rgba(57,211,83,0.7); }
  50%      { box-shadow: 0 0 0 6px rgba(57,211,83,0.22), 0 0 16px 2px var(--sf-green); }
}
/* drop-in + run + done states (override the amber/warm v1 states) */
.sf-app .snode.placing { animation: snode-pop 0.36s cubic-bezier(.2,.8,.2,1) both; }
.sf-app .snode.running .snode-card {
  border-color: #cfcfcf;
  box-shadow: 0 0 0 1px rgba(255,255,255,0.4), 0 0 22px -5px rgba(255,255,255,0.5);
}
.sf-app .snode.running .snode-card::after {
  content: ""; position: absolute; inset: -3px; border-radius: 14px;
  border: 2px solid rgba(255,255,255,0.55); opacity: 0.6; animation: npulse 1s ease-out infinite;
}
/* BUILT/selected node = bright green border + subtle glow */
.sf-app .snode.done .snode-card {
  border-color: var(--sf-green);
  box-shadow: 0 0 0 1px rgba(57,211,83,0.35), 0 0 18px -4px rgba(57,211,83,0.55), 0 8px 20px -10px #000;
}
/* the corner done-check */
.sf-app .snode-badge {
  position: absolute; right: -6px; top: -6px; width: 19px; height: 19px;
  border-radius: 50%; display: grid; place-items: center; font-size: 11px;
  background: var(--sf-green); color: #04200c; opacity: 0; transform: scale(0.4);
  transition: 0.2s; z-index: 3; box-shadow: 0 0 8px -1px var(--sf-green);
}
.sf-app .snode.done .snode-badge { opacity: 1; transform: scale(1); }

/* ghost slots: subtle on black; the wanted slot glows green (not amber) */
.sf-app .studio-ghost {
  position: absolute; z-index: 1;
  border: 1.6px dashed rgba(255,255,255,0.22); border-radius: 12px;
  background: rgba(255,255,255,0.015);
  display: grid; place-items: center; text-align: center;
  opacity: 0.7; transition: opacity 0.25s, border-color 0.25s, box-shadow 0.25s, transform 0.25s;
  pointer-events: none;
}
.sf-app .ghost-label { font-size: 10px; line-height: 1.2; color: var(--sf-muted); padding: 0 4px; }
.sf-app .studio-ghost.want {
  border-color: rgba(57,211,83,0.55); opacity: 0.95;
  animation: sfGhostPulse 1.7s ease-in-out infinite;
}
.sf-app .studio-ghost.want .ghost-label { color: #8ee79c; }
@keyframes sfGhostPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(57,211,83,0); }
  50%      { box-shadow: 0 0 0 4px rgba(57,211,83,0.10), 0 0 18px -4px rgba(57,211,83,0.5); }
}
.sf-app .studio-ghost.target {
  border-color: var(--sf-green); border-style: solid; opacity: 1;
  background: rgba(57,211,83,0.07); transform: scale(1.03);
  box-shadow: 0 0 0 3px rgba(57,211,83,0.16), 0 0 24px -4px var(--sf-green); animation: none;
}
.sf-app .studio-ghost.filled { opacity: 0; box-shadow: none; border-color: transparent; animation: none; transform: scale(0.96); }
/* the palette clone that follows the cursor: dark, green edge (override amber) */
.sf-app .pal-fly, .pal-fly {
  display: flex; align-items: center; gap: 9px; padding: 6px 9px; border-radius: 9px;
  font-size: 12.5px; color: var(--sf-ink);
  background: linear-gradient(160deg, #303034, #1a1a1d);
  border: 1px solid rgba(57,211,83,0.45);
  box-shadow: 0 16px 40px -10px #000, 0 0 0 1px rgba(57,211,83,0.18);
  transform: scale(1.05); transform-origin: top left; pointer-events: none;
}
.pal-fly img { width: 20px; height: 20px; flex: none; }

/* ------------------------------------------------ deploy result card ------- */
/* v4 (2026-07-13, "ungate + chat" rework): the docked Surface/Web-app viewer is
   REMOVED (Jason: "the surface window does not really add value... it can be
   removed"). There is no live Confidence Field canvas anymore; the branch
   analysis chart and the deployed-predictor preview both render as RESULT
   CARDS inside the agent chat stream instead (studio.js's chatResult()), using
   the same real Surrogate math as before. The deploy-* rules below still style
   that predictor content, just hosted in a chat bubble now instead of a
   floating dock pane; `.chat-msg .deploy-body` below narrows it to one column
   to fit the chat column's width. */
.sf-app .deploy-head { padding: 11px 14px 9px; border-bottom: 1px solid var(--sf-border); }
.sf-app .deploy-badge { display: none; }
.sf-app .deploy-head h3 { margin: 0 0 3px; font-size: 15px; letter-spacing: -0.01em; color: var(--sf-ink); }
.sf-app .deploy-blurb { margin: 0; font-size: 11.5px; color: var(--sf-muted); line-height: 1.35; }
.sf-app .deploy-body { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; padding: 11px 14px; }
/* narrow to a single column inside the ~320px chat card so nothing crowds */
.sf-app .chat-msg .deploy-body { grid-template-columns: 1fr; padding: 10px 12px; }
.sf-app .chat-msg .deploy-out-val { font-size: 19px; }
.sf-app .deploy-controls { display: flex; flex-direction: column; gap: 12px; justify-content: center; }
.sf-app .deploy-slider { display: grid; grid-template-columns: 1fr auto; gap: 3px 8px; align-items: center; }
.sf-app .deploy-slider label { grid-column: 1 / -1; font-size: 11.5px; color: var(--sf-muted); }
.sf-app .deploy-slider input[type=range] {
  appearance: none; -webkit-appearance: none; width: 100%; height: 4px; border-radius: 4px;
  background: #4a4a4f; outline: none;
}
.sf-app .deploy-slider input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none; width: 14px; height: 14px; border-radius: 50%;
  background: var(--sf-blue); box-shadow: 0 0 10px -1px var(--sf-blue); cursor: pointer; border: 2px solid #000;
}
.sf-app .deploy-slider input[type=range]::-moz-range-thumb {
  width: 14px; height: 14px; border-radius: 50%; background: var(--sf-blue);
  box-shadow: 0 0 10px -1px var(--sf-blue); cursor: pointer; border: 2px solid #000;
}
.sf-app .deploy-slider-val { font-family: var(--mono); font-size: 12px; color: #c8b98a; }
.sf-app .deploy-out {
  display: flex; flex-direction: column; justify-content: center;
  border: 1px solid var(--sf-border); border-radius: 10px; background: var(--sf-chrome-3); padding: 10px 12px;
}
.sf-app .deploy-out-label { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--sf-muted); }
.sf-app .deploy-out-val { margin-top: 3px; font-family: var(--mono); font-size: 22px; font-weight: 600; color: var(--sf-ink); }
.sf-app .deploy-band { font-size: 12.5px; font-weight: 500; color: #8ee79c; margin-left: 2px; }
.sf-app .deploy-bar { position: relative; height: 6px; border-radius: 4px; margin-top: 9px; background: #1f1f22; overflow: hidden; }
.sf-app .deploy-bar-fill { position: absolute; left: 0; top: 0; height: 100%; background: var(--sf-green); opacity: 0.85; transition: width 0.18s; }
.sf-app .deploy-bar-band { position: absolute; top: 0; height: 100%; background: linear-gradient(90deg, transparent, rgba(57,211,83,0.5), transparent); transition: 0.18s; }
.sf-app .deploy-note { margin-top: 7px; font-size: 10.5px; color: var(--sf-muted); line-height: 1.35; }
.sf-app .deploy-foot {
  display: flex; align-items: center; gap: 10px; padding: 9px 14px;
  border-top: 1px solid var(--sf-border); background: var(--sf-chrome-3);
}
/* PI amber system for the one interactive control the visitor's eye lands on
   in this otherwise-neutral pane, per the rework spec (item 4). */
.sf-app .deploy-download {
  cursor: pointer; padding: 8px 14px; border-radius: 2px; font: 600 12.5px/1.2 var(--sans);
  border: 1px solid var(--accent); background: var(--accent); color: #08070A;
  transition: filter 0.18s, transform 0.18s;
}
.sf-app .deploy-download:hover { filter: brightness(1.08); transform: translateY(-1px); }
.sf-app .deploy-disclaimer { font-size: 10.5px; color: var(--sf-muted); }

/* ----------------------------------------------- bottom dock (console) ----- */
.sf-dock {
  display: flex; flex-direction: column; min-height: 0;
  height: 188px; background: var(--sf-chrome-3);
  border-top: 1px solid var(--sf-border);
  transition: height 0.22s ease;
}
.sf-dock.collapsed { height: 30px; }
.sf-dock.collapsed .sf-console,
.sf-dock.collapsed .sf-prompt,
.sf-dock.collapsed .sf-vars { display: none; }
.sf-dock-head {
  flex: none; display: flex; align-items: center; gap: 12px;
  padding: 7px 12px; background: var(--sf-chrome-2); border-bottom: 1px solid var(--sf-border);
}
.sf-dock-title { font-size: 12px; font-weight: 700; color: var(--sf-ink); flex: none; }
.sf-workdir {
  flex: 1 1 auto; min-width: 0; font-size: 11px; color: var(--sf-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sf-workdir b { color: var(--sf-tri); font-weight: 600; font-family: var(--mono); }
.sf-dock-actions { flex: none; display: flex; gap: 6px; }
.sf-dock-btn {
  cursor: pointer; padding: 4px 11px; border-radius: 6px; font-size: 11.5px;
  border: 1px solid var(--sf-border); background: var(--sf-chrome-3); color: var(--sf-ink); transition: 0.14s;
}
.sf-dock-btn:hover { background: #2e2e32; border-color: #555; }
/* console body: black, mono, timestamped */
.sf-app .sf-console {
  flex: 1 1 auto; min-height: 0; overflow: auto;
  padding: 9px 12px; background: #000;
  font-family: var(--mono); font-size: 11.5px; color: #cfcfd2; line-height: 1.55;
}
.sf-app .sf-console::-webkit-scrollbar { width: 10px; }
.sf-app .sf-console::-webkit-scrollbar-thumb { background: #333; border-radius: 6px; }
/* override the v1 ".console-line::before { content: '› '; color: amber }" */
.sf-app .console-line { padding: 1px 0; animation: fade 0.22s ease both; }
.sf-app .console-line::before { content: none; }
.con-time { color: var(--sf-muted); }
/* the green check mark / [Tools] tokens are baked into the message text; tint the
   common success glyph by leaving the line light and letting the check read green
   via a subtle highlight on lines that start with one. (Plain text; visual only.) */
.sf-prompt {
  flex: none; display: flex; align-items: center; gap: 8px;
  padding: 6px 12px; background: #000; border-top: 1px solid #1b1b1b;
}
.sf-prompt-mark { color: var(--sf-green); font-family: var(--mono); font-size: 12px; font-weight: 700; flex: none; }
.sf-prompt-input {
  flex: 1; min-width: 0; background: transparent; border: none; outline: none;
  color: #cfcfd2; font-family: var(--mono); font-size: 12px;
}
/* Variables table inside the dock (override the .vars-table amber type color) */
.sf-app .sf-vars { width: 100%; border-collapse: collapse; font-size: 11.5px; }
.sf-app .sf-vars[hidden] { display: none; }
.sf-app .sf-vars thead th {
  text-align: left; color: var(--sf-muted); font-weight: 500;
  padding: 6px 12px; background: var(--sf-chrome-2); border-bottom: 1px solid var(--sf-border);
  position: sticky; top: 0; z-index: 1;
}
.sf-app .sf-vars td { padding: 4px 12px; border-bottom: 1px solid rgba(255,255,255,0.05); color: #d0d0d2; }
.sf-app .sf-vars td.type { color: #c8b98a; font-family: var(--mono); }
.sf-app .sf-vars td.val { font-family: var(--mono); color: var(--sf-muted); }
.sf-app .sf-vars td.node { color: var(--sf-ink); }
@keyframes sfVarFlash { from { background: rgba(57,211,83,0.18); } to { background: transparent; } }
.sf-app .sf-vars .vars-row.var-flash { animation: sfVarFlash 0.7s ease-out; }
/* bottom tab strip: Console | Variables | Connections | Notes, active = blue underline */
.sf-app .sf-dock-tabs {
  flex: none; display: flex; align-items: center; gap: 4px;
  padding: 0 10px; height: 30px; background: var(--sf-chrome-2);
  border-top: 1px solid var(--sf-border); font-size: 12px; color: var(--sf-muted);
}
.sf-app .sf-dock-tabs .sf-tab {
  cursor: pointer; padding: 7px 10px 6px; border-bottom: 2px solid transparent;
  transition: color 0.15s, border-color 0.15s;
}
.sf-app .sf-dock-tabs .sf-tab:hover { color: var(--sf-ink); }
.sf-app .sf-dock-tabs .sf-tab.on { color: var(--sf-ink); border-bottom-color: var(--sf-blue); }
.sf-app .sf-dock-tabs .count { margin-left: 4px; color: var(--sf-muted); font-size: 11px; }
.sf-app .sf-dock-tabs .sf-toggle-console { margin-left: auto; cursor: pointer; color: var(--sf-muted); font-size: 11.5px; }
.sf-app .sf-dock-tabs .sf-toggle-console:hover { color: var(--sf-ink); }

/* ------------------------------------------------------- right guide ------- */
/* v4 (2026-07-13, "ungate + chat" rework, Jason's direct feedback): the right
   panel is now the AGENT CHAT, not a step-card inspector. It carries a welcome
   message, short reactions as nodes are placed/wired (reusing the story
   beats), preset chips the visitor clicks (no free-text input -- scripted
   answers must not pretend to be a live LLM), and RESULT cards (the branch
   chart + the deployed-predictor preview) posted as the agent's replies. Still
   PI amber (this is guidance UI, not the product canvas). The old per-phase
   "why"/decision blocks move to small anchored popovers on the canvas itself
   (see .node-popover below); the old Next/Connect-for-me/Auto-build buttons
   are gone (build is ungated) except "Build the workflow for me", now a
   preset chip that triggers the same autobuild. */
.sf-agent {
  display: flex; flex-direction: column; min-height: 0;
  border-left: 1px solid var(--line); background: var(--bg-2); color: var(--txt);
  font-family: var(--sans);
}
.sfg-head {
  flex: none; display: flex; align-items: center; gap: 8px;
  padding: 11px 14px; border-bottom: 1px solid var(--line);
  font-size: 12.5px; font-weight: 700; color: var(--txt);
}
.sfg-agent-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 8px var(--accent); flex: none; }

/* the chat transcript: bubbles, agent left / user echo right, auto-scrolled by JS */
.sfg-chat {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  padding: 14px 14px 8px; display: flex; flex-direction: column; gap: 10px;
}
.sfg-chat::-webkit-scrollbar { width: 10px; }
.sfg-chat::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 6px; border: 2px solid var(--bg-2); }
.chat-msg { max-width: 96%; animation: fade 0.22s ease both; }
.chat-msg.agent { align-self: flex-start; }
.chat-msg.user { align-self: flex-end; }
.chat-msg-title {
  font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 4px; padding: 0 2px;
}
.chat-msg-body { font-size: 12.5px; line-height: 1.5; padding: 9px 12px; border-radius: 12px; }
.chat-msg.agent .chat-msg-body {
  background: var(--accent-soft, rgba(255,176,6,0.07)); border: 1px solid rgba(255,176,6,0.18);
  color: #ece7da; border-bottom-left-radius: 3px;
}
.chat-msg.user .chat-msg-body { background: var(--panel); border: 1px solid var(--line); color: var(--txt); border-bottom-right-radius: 3px; }
/* result cards: same bubble family, but a neutral panel (it is data, not chat prose) */
.chat-msg.result .chat-msg-body {
  background: var(--panel); border: 1px solid var(--line); color: var(--txt);
  border-bottom-left-radius: 12px; display: flex; flex-direction: column; gap: 10px;
}

/* the ONE merged result card content: metrics row + optional compact chart (+
   legend) + a callout, never a stack of separately bordered boxes. */
.result-metrics { display: flex; flex-wrap: wrap; gap: 6px 18px; }
.result-metrics .rm { display: flex; flex-direction: column; gap: 1px; }
.result-metrics .rm b { font-family: var(--mono); font-size: 15px; font-weight: 600; color: var(--accent); line-height: 1.1; }
.result-metrics .rm i { font-style: normal; font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--faint); }
.result-chart { display: block; width: 100%; height: 120px; border-radius: 8px; background: var(--bg); border: 1px solid var(--line); }
.result-legend { display: flex; flex-direction: column; gap: 3px; font-family: var(--mono); font-size: 11px; color: var(--muted); }
.result-legend .lg-row { line-height: 1.4; }
.result-legend .lg-row b { color: var(--accent); font-weight: 600; }
.result-callout { font-size: 12.5px; line-height: 1.5; color: var(--txt); border-left: 2px solid var(--accent); padding: 1px 0 1px 10px; }
/* v4.1: chatResult's content now assembles inside an inner .result-content
   wrapper (revealed as one unit after the typing beat, see below), so a
   callout-only card is a direct child of THAT, not of .chat-msg-body itself. */
.result-content > .result-callout:only-child { border-left: none; padding-left: 0; color: var(--muted); }

/* -------------------------------------------------- chat typing (v4.1) ----- */
/* Product-standard "agent is composing" idiom (Jason: "give the chat writing
   animations"): three small pulsing dots inside the bubble shell, shown
   briefly before every agent reply, then either the text streams in word by
   word (a plain chat line) or the card fades in as ONE unit (a result card --
   a chart/metrics row streaming in piecewise would read as broken, not
   alive). Equal class-specificity to `.chat-msg.result .chat-msg-body` above,
   so this rule is placed AFTER it in the cascade to win while `.typing` is on
   a result bubble too. The sitewide reduced-motion rule (near the top of this
   file) already collapses the dot pulse to a single instant frame as a
   second, CSS-only line of defence; studio.js's own reducedMotion() check
   skips the whole typing/streaming sequence before it starts. */
.chat-msg.typing .chat-msg-body {
  display: inline-flex; flex-direction: row; align-items: center; padding: 11px 14px;
}
.chat-typing { display: inline-flex; align-items: center; gap: 4px; }
.chat-typing i {
  width: 6px; height: 6px; border-radius: 50%; background: var(--accent);
  opacity: 0.35; animation: chatDotPulse 1.1s ease-in-out infinite;
}
.chat-typing i:nth-child(2) { animation-delay: 0.15s; }
.chat-typing i:nth-child(3) { animation-delay: 0.3s; }
@keyframes chatDotPulse {
  0%, 60%, 100% { opacity: 0.32; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-2px); }
}
/* result card content: built up front, revealed as one unit once the typing
   beat elapses (see chatResult in studio.js). A quick fade + settle, not a
   bounce -- matches the site's no-bounce-easing rule. */
.result-content { display: flex; flex-direction: column; gap: 10px; }
.result-content.result-fade-in { animation: resultCardIn 0.22s ease both; }
@keyframes resultCardIn {
  from { opacity: 0; transform: translateY(3px); }
  to { opacity: 1; transform: translateY(0); }
}

/* preset chips: the ONLY input surface (no free-text field -- scripted answers
   must not pretend to be a live LLM). Contextual: studio.js regenerates this
   row after every place/wire/preset action. */
.sfg-presets { flex: none; display: flex; flex-wrap: wrap; gap: 7px; padding: 10px 14px 14px; border-top: 1px solid var(--line); }
.chat-chip {
  cursor: pointer; padding: 7px 12px; border-radius: 999px;
  border: 1px solid rgba(255,176,6,0.35); background: rgba(255,176,6,0.06);
  color: var(--accent); font: 600 11.5px/1.3 var(--sans); text-align: left;
  transition: background 0.15s, border-color 0.15s, transform 0.15s;
}
.chat-chip:hover { background: rgba(255,176,6,0.15); border-color: var(--accent); }
.chat-chip:active { transform: scale(0.97); }

/* ------------------------------------------------ node popovers (v4) ------- */
/* Small, anchored, NON-BLOCKING info popups (Jason: "the info messages and next
   button hold up a natural building process; they should rather be little
   popup highlight boxes"). One at a time (a new one replaces the last).
   pointer-events are scoped to the popover box itself -- everything else on the
   canvas (dragging nodes, wiring, palette) stays fully interactive underneath.
   v4.2 (2026-07-13, direct feedback): the ratio chips and the kappa slider that
   used to live inside this box are REMOVED -- no knobs, no clickable choices
   anywhere on the canvas. Every popover, including the two that used to carry
   those controls, is now plain explainer copy (what the node did, defaults
   included) and always auto-fades; there is no more "decision" variant. */
.node-popover {
  position: absolute; z-index: 20; max-width: 258px; width: max-content;
  background: rgba(9,9,10,0.97); border: 1px solid rgba(255,176,6,0.3);
  border-radius: 8px; padding: 9px 12px 10px; pointer-events: auto;
  box-shadow: 0 16px 36px -12px rgba(0,0,0,0.7), 0 0 0 1px rgba(0,0,0,0.4);
  cursor: default;
  transform: translate(-50%, 0);
  animation: nodePopIn 0.16s ease both;
}
@keyframes nodePopIn { from { opacity: 0; transform: translate(-50%, 4px); } to { opacity: 1; transform: translate(-50%, 0); } }
.popover-title { font-size: 10.5px; font-weight: 700; color: var(--accent); margin-bottom: 4px; letter-spacing: 0.02em; }
.popover-body { font-size: 12px; line-height: 1.5; color: #e6e6e8; }
.popover-body:empty { display: none; }
@media (prefers-reduced-motion: reduce) {
  .node-popover { animation: none; }
}

/* ------------------------------------------------------------ responsive --- */
/* keep it usable on narrow widths: the palette narrows, the agent chat drops
   below the center. v4: with the docked Surface/Web-app viewer removed there
   is nothing left that must never sit in a scrolling ancestor, so narrow
   viewports let the CANVAS scroll horizontally instead of clipping/hiding
   nodes (the previously flagged ~800px clipping) -- the graph itself keeps
   its full 1440px-reference layout and spacing; it just pans. */
@media (max-width: 1100px) {
  .sf-main {
    grid-template-columns: 168px 1fr;
    grid-template-areas: "palette center" "agent agent";
  }
  .sf-palette { grid-area: palette; }
  .sf-center { grid-area: center; }
  .sf-agent {
    grid-area: agent; border-left: none; border-top: 1px solid var(--line);
    max-height: 340px;
  }
  .sf-palette { padding: 9px 7px; }
  .sf-app .pal-node { font-size: 12px; }
  .sf-canvas { overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; }
}
@media (max-width: 720px) {
  .sf-main { grid-template-columns: 0 1fr; }
  .sf-palette { display: none; }
  .sf-menubar { overflow-x: auto; }
}

@media (max-width: 480px) {
  .sf-toolbar { padding: 0 6px; gap: 5px; }
  .sf-drag-handle { display: none; }
  .sf-tool { width: 26px; height: 26px; font-size: 11px; }
  .sf-agent { max-height: 260px; }
  .sf-app .deploy-body { grid-template-columns: 1fr; }
  .pick-cards { grid-template-columns: 1fr; }
  .studio-pick { padding: 32px 14px 48px; }
}

/* studio v3: full-window Flow replica (hide the site top bar on #/studio) */
.sf-fullscreen .topbar { display: none; }
.sf-fullscreen .app { min-height: 100vh; }
.sf-fullscreen .sf-app,
.sf-fullscreen .studio-stage,
.sf-fullscreen .studio,
.sf-fullscreen .studio-pick { height: 100vh; min-height: 100vh; }

/* studio: the "Beat Stochos" challenge return banner (only present in challenge mode) */
.sf-challenge-banner {
  position: fixed; left: 50%; bottom: 16px; transform: translateX(-50%); z-index: 60;
  display: flex; align-items: center; gap: 14px; padding: 9px 9px 9px 18px;
  background: linear-gradient(180deg, #1a1206, #120d05); border: 1px solid var(--accent-line);
  border-radius: 999px; box-shadow: 0 18px 50px -16px rgba(255,176,6,0.4);
  font-size: 13.5px; color: var(--txt);
}
.sf-ch-return {
  padding: 8px 16px; border-radius: 999px; font-size: 13px; font-weight: 700; cursor: pointer;
  border: 1px solid var(--accent); background: var(--accent); color: #1a0d05; transition: 0.16s;
}
.sf-ch-return:hover { transform: translateY(-1px); box-shadow: 0 10px 24px rgba(255,176,6,0.3); }

/* ---------- hub-builder: the "Build it yourself" feature band ----------
   Restored 2026-07-13. Full-bleed alternating band (warm bg + hairline top).
   Two columns on desktop: copy left, a decorative node-chain preview right. */
.hub-builder { border-top: 1px solid var(--hairline); background: var(--bg-warm); padding: 72px 26px; }
.hub-builder-body { max-width: 1120px; margin: 0 auto; display: grid; grid-template-columns: 1.15fr 1fr; align-items: center; gap: 48px; }
.hub-builder-text { min-width: 0; }
.hub-builder-tag { display: block; font: 500 11.5px/1 var(--mono); letter-spacing: 0.16em; text-transform: uppercase; color: var(--amber); margin-bottom: 14px; }
.hub-builder-title { font-family: var(--display); font-weight: 700; font-size: clamp(26px, 3.4vw, 38px); line-height: 1.15; letter-spacing: -0.01em; margin: 0 0 14px; color: var(--text); }
.hub-builder-pitch { font-size: 16px; color: var(--muted); line-height: 1.6; max-width: 46ch; margin: 0 0 26px; }
.hub-builder-cta { display: inline-flex; align-items: center; gap: 8px; padding: 11px 18px; border-radius: 2px; font: 600 14.5px/1 var(--sans); color: var(--amber-lt); border: 1px solid rgba(255, 176, 6, 0.5); transition: border-color 0.18s, background 0.18s, transform 0.18s; }
.hub-builder-cta:hover { border-color: var(--amber); background: rgba(255, 176, 6, 0.08); transform: translateY(-1px); }
.hbv-chain { display: flex; align-items: center; justify-content: center; }
.hbv-node { display: flex; flex-direction: column; align-items: center; gap: 9px; padding: 16px 14px; border: 1px solid var(--hairline); border-radius: 6px; background: var(--surface); min-width: 92px; }
.hbv-node-icon { width: 26px; height: 26px; display: grid; place-items: center; }
.hbv-node-icon img { width: 24px; height: 24px; display: block; }
.hbv-node-label { font: 500 10px/1.2 var(--mono); color: var(--muted); letter-spacing: 0.02em; white-space: nowrap; }
.hbv-link { display: flex; align-items: center; width: 40px; flex: none; }
.hbv-line { flex: 1; height: 1px; background: rgba(255, 176, 6, 0.4); }
.hbv-port { width: 5px; height: 5px; border-radius: 50%; background: var(--amber); flex: none; }
@media (max-width: 880px) {
  .hub-builder-body { grid-template-columns: 1fr; gap: 32px; }
  .hub-builder-visual { order: 2; }
}
/* Phones never see an entry point into the Flow builder (2026-07-29, Jason): it
   is a drag-and-wire node editor, so the hub does not advertise it and the nav
   does not offer it. `.is-phone` is set on <html> by js/phone.js, which holds the
   single definition of "phone" (portrait width or a sideways phone) and also
   guards the #/studio route itself. The band's 480px fit rules further down are
   kept, so restoring it does not mean redoing that sizing work. */
.is-phone .hub-builder,
.is-phone #domnav a[data-domain="studio"] { display: none; }

@media (max-width: 480px) {
  .hub-builder { padding: 44px 18px; }
  /* the chain is 326px wide at 390px, so three 92px nodes and two 20px links
     (316px) fit: the old 64px width shrank the preview below what the column
     allowed and wrapped two of the three labels onto a second line. Nodes keep
     min-width:0 so they still shrink to fit at 360px and below. */
  /* grid items default to min-width:auto, so the chain's min-content width became a
     floor on the whole two-column body and pushed the title and pitch past the
     viewport at 320px. min-width:0 lets the column actually shrink. */
  .hub-builder-body > * { min-width: 0; }
  .hbv-chain { align-items: stretch; }
  /* flexible rather than a fixed 92px: the nodes fill the row up to 92px and shrink
     together below it, so the preview stays whole from 480px down to 320px instead
     of overflowing once three fixed nodes plus two links exceed the column. */
  .hbv-node { flex: 1 1 0; width: auto; max-width: 92px; min-width: 0; padding: 12px 8px; gap: 8px; }
  .hbv-node-icon { width: 26px; height: 26px; }
  .hbv-node-icon img { width: 24px; height: 24px; }
  .hbv-node-label { font-size: 10px; white-space: normal; text-align: center; }
  .hbv-link { width: 20px; }
  /* touch target: the CTA is a link and measured 38.5px tall. Full width is the
     same treatment the site's other mobile CTAs get. */
  .hub-builder-cta { padding: 14px 20px; width: 100%; justify-content: center; }
}
