/* The palette comes off the logo. The crab sits on a gradient that runs
   from cyan on the left to green on the right, so those two are the brand
   and everything else is a near-black tinted very slightly towards them —
   a neutral grey background next to a saturated logo makes the logo look
   like it was pasted on. */
:root {
  --bg: #0B100F;
  --panel: #141A19;
  --line: #253230;
  --text: #E4EDEA;
  --dim: #8FA19D;

  --brand-cyan: #0098B2;
  --brand-green: #7BD65C;

  /* One flat colour for text, borders and small marks, because the
     gradient is unreadable at 13px. It sits at the midpoint of the two
     brand colours and is light enough to pass on the dark background. */
  --accent: #43C58F;

  /* The gradient itself is reserved for the things meant to be pressed. */
  --brand-gradient: linear-gradient(100deg, var(--brand-cyan), var(--brand-green));

  /* Text that sits on top of the accent or the gradient. */
  --on-accent: #04150F;

  /* Two real typefaces instead of whatever the operating system hands
     over. The system stack is the single loudest tell that nobody chose
     anything: every untouched site in the world wears it.

     Archivo is slightly narrow and industrial — headings hold together
     at large sizes without going soft. IBM Plex Sans has a humanist
     lowercase with actual quirks in the a and g, so paragraphs read as
     written rather than rendered. Both carry fallbacks, so the site still
     looks deliberate if the fonts never arrive. */
  --display: Archivo, "Arial Narrow", "Helvetica Neue", system-ui, sans-serif;
  --body: "IBM Plex Sans", "Segoe UI", system-ui, sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  padding: 32px 20px 80px;
  background: var(--bg);
  color: var(--text);
  font-family: var(--body);
  line-height: 1.6;
  font-size: 16px;
}

main { max-width: 760px; margin: 0 auto; }
main.narrow { max-width: 420px; margin: 8vh auto 0; }

/* account bar */
.topbar {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 28px;
}

.topbar .tagline { margin-bottom: 0; }

.account {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  font-size: 13px;
  padding-top: 6px;
}

.account-email { color: var(--text); }
.account-quota { color: var(--dim); }
.account-quota.empty { color: #FF7A7A; }

.link-button {
  background: none;
  border: 0;
  color: var(--dim);
  padding: 0;
  margin: 0;
  font-size: 13px;
  font-weight: 400;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.link-button:hover { color: var(--accent); filter: none; }

/* sign in page */
.tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 22px;
  border-bottom: 1px solid var(--line);
}

.tab {
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  color: var(--dim);
  padding: 8px 4px;
  margin: 0 14px 0 0;
  font-size: 15px;
  font-weight: 500;
}

.tab:hover { filter: none; color: var(--text); }
.tab.active { color: var(--accent); border-bottom-color: var(--accent); }

.stacked {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 16px;
}

.stacked span { color: var(--dim); font-size: 13px; }
.stacked input { width: 100%; }

.captcha { margin: 4px 0 18px; min-height: 65px; }

#auth-form button[type="submit"] { width: 100%; margin-top: 0; }

.message {
  font-size: 14px;
  margin: 16px 0 0;
  padding: 11px 14px;
  border-radius: 8px;
  border: 1px solid var(--line);
}

.message.good { color: #7BD88F; border-color: #2F5A3A; background: #14231A; }
.message.bad  { color: #FF7A7A; border-color: #5A2F2F; background: #231414; }

.footnote { margin-top: 20px; margin-bottom: 0; }

h1, h2, h3, .step-badge, .tile-value, .chart-title b,
.section-title, .eyebrow, .sub, .nav-brand, button, .cta, .ghost,
.button-link, .package-price, .balance-number, .plan h3, .package h3 {
  font-family: var(--display);
}

h1 {
  font-size: 28px;
  margin: 0 0 4px;
  font-weight: 800;
  letter-spacing: -.025em;
}

h2 {
  font-size: 17px;
  margin: 0 0 12px;
  font-weight: 700;
  letter-spacing: -.015em;
}

.tagline { color: var(--dim); margin: 0 0 32px; }
.hint { color: var(--dim); font-size: 14px; margin: 0 0 12px; }

section {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 20px;
  margin-bottom: 20px;
}

textarea, select, input {
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 10px 12px;
  font-family: inherit;
  font-size: 15px;
  line-height: 1.6;
}

#story-input { width: 100%; resize: vertical; }

textarea:focus, select:focus, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

button {
  background: var(--accent);
  color: var(--on-accent);
  border: 0;
  border-radius: 8px;
  padding: 11px 22px;
  font-size: 15px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  margin-top: 12px;
}

button:hover { filter: brightness(1.08); }
button:disabled { opacity: .5; cursor: default; }

/* one editable story line */
.row {
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr) 34px;
  gap: 10px;
  padding: 8px 0;
  align-items: start;
}

.speaker-select { width: 100%; }

.line-text {
  width: 100%;
  resize: none;
  overflow: hidden;
}

.icon-button {
  background: none;
  color: var(--dim);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0;
  height: 40px;
  font-size: 20px;
  line-height: 1;
  margin: 0;
}

.icon-button:hover { color: #FF7A7A; border-color: #FF7A7A; filter: none; }

/* one speaker and their voice */
.cast-row {
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr) 36px 36px;
  gap: 10px;
  padding: 8px 0;
  align-items: center;
}

.cast-row .icon-button {
  height: 36px;
  font-size: 15px;
  margin: 0;
}

.cast-name {
  color: var(--accent);
  font-size: 14px;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.colour-code {
  color: var(--dim);
  font-size: 11px;
  font-family: Consolas, ui-monospace, monospace;
  letter-spacing: .03em;
}

input[type="color"] {
  width: 36px;
  height: 36px;
  padding: 2px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--bg);
  cursor: pointer;
}

.field {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
}

.field span { color: var(--dim); font-size: 14px; }

/* music track picker */
.track-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 14px;
}

.track {
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 7px 14px;
  font-size: 14px;
  font-weight: 400;
  margin: 0;
  display: flex;
  align-items: center;
  gap: 7px;
}

.track:hover { filter: none; border-color: var(--accent); }

.track.selected {
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}

.track .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--line);
  flex: none;
}

.track.playing .dot { background: var(--accent); }

input[type="range"] {
  padding: 0;
  border: 0;
  background: none;
  accent-color: var(--accent);
  width: 160px;
}

#music-volume-label {
  font-variant-numeric: tabular-nums;
  min-width: 40px;
}

/* progress and result */
.bar {
  height: 8px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 999px;
  overflow: hidden;
  margin: 18px 0 8px;
}

.bar-fill {
  height: 100%;
  width: 0;
  background: var(--accent);
  transition: width .3s ease;
}

video {
  width: 100%;
  max-width: 300px;
  border-radius: 10px;
  background: #000;
  display: block;
}

.button-link {
  display: inline-block;
  background: var(--accent);
  color: var(--on-accent);
  text-decoration: none;
  border-radius: 8px;
  padding: 11px 22px;
  font-weight: 600;
  margin-top: 16px;
}

.error { color: #FF7A7A; font-size: 14px; margin: 12px 0 0; }
/* A utility class that means "this element is not here" has to beat
   whatever display the element sets for itself. Without the !important,
   any later rule with the same specificity — .feedback-email { display:
   flex } sitting further down the file — silently wins, and the element
   stays on screen while the code believes it is hidden. That is what
   happened: the feedback panel asked signed-in people for their email
   address it already had. */
.hidden { display: none !important; }
.status { color: var(--dim); font-size: 14px; }

/* ---------------------------------------------------------------- brand */

.brand {
  display: flex;
  align-items: center;
  gap: 14px;
  text-decoration: none;
  color: inherit;
}

.brand img { border-radius: 9px; display: block; }

/* ---------------------------------------------------------------- tokens */

.tokens {
  display: inline-block;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 13px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
}

.tokens:hover { border-color: var(--accent); color: var(--accent); }
.tokens.empty { border-color: #FF7A7A; color: #FF7A7A; }

.account-links { display: flex; gap: 12px; align-items: center; }
.account-links a { color: var(--dim); font-size: 13px; text-decoration: underline;
                   text-underline-offset: 3px; }
.account-links a:hover { color: var(--accent); }

.button-row {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

.button-row .hint { margin: 12px 0 0; }
.hint.warn { color: #FF7A7A; }

.two-up {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 14px;
}

/* ---------------------------------------------------------------- preview */

/* A rough impression of the subtitle look, not a real render — enough to
   choose by without waiting a minute to find out. */
.preview-frame {
  margin-top: 16px;
  height: 190px;
  border-radius: 10px;
  border: 1px solid var(--line);
  /* Stands in for footage. The old diagonal stripes were left over from
     the grey theme and read as "missing image"; a soft brand-lit dark
     reads as a frame of video, which is what is actually behind these
     captions. */
  background:
    radial-gradient(closest-side, rgba(0, 152, 178, .3), transparent 72%) 28% 38% / 70% 90% no-repeat,
    radial-gradient(closest-side, rgba(123, 214, 92, .22), transparent 72%) 76% 66% / 66% 86% no-repeat,
    #071410;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 0 16px;
}

.preview-caption {
  text-align: center;
  line-height: 1.15;
  color: #fff;
}

.style-pop {
  font-family: "Arial Black", Impact, sans-serif;
  font-size: 34px;
  text-shadow:
    -2px -2px 0 #000,
    2px -2px 0 #000,
    -2px 2px 0 #000,
    2px 2px 0 #000,
    0px -2px 0 #000,
    0px 2px 0 #000,
    -2px 0px 0 #000,
    2px 0px 0 #000,
    0 3px 8px rgba(0,0,0,.85);
}

.style-mega {
  font-family: Impact, "Arial Black", sans-serif;
  font-size: 52px;
  letter-spacing: .01em;
  text-shadow:
    -3px -3px 0 #000,
    3px -3px 0 #000,
    -3px 3px 0 #000,
    3px 3px 0 #000,
    0px -3px 0 #000,
    0px 3px 0 #000,
    -3px 0px 0 #000,
    3px 0px 0 #000,
    0 3px 8px rgba(0,0,0,.85);
}

.style-karaoke {
  font-family: "Arial Black", Impact, sans-serif;
  font-size: 26px;
  color: #909090;
  text-shadow:
    -2px -2px 0 #000,
    2px -2px 0 #000,
    -2px 2px 0 #000,
    2px 2px 0 #000,
    0px -2px 0 #000,
    0px 2px 0 #000,
    -2px 0px 0 #000,
    2px 0px 0 #000,
    0 3px 8px rgba(0,0,0,.85);
}

/* the highlight travelling across the phrase */
.style-karaoke::first-line { color: #fff; }

.style-boxed {
  font-family: Verdana, "Segoe UI", sans-serif;
  font-weight: 700;
  font-size: 26px;
  background: rgba(0, 0, 0, .62);
  padding: 12px 18px;
  border-radius: 6px;
}

/* entrance animations, mirroring what the renderer does */
@keyframes previewPop {
  0%   { transform: scale(.7); opacity: 0; }
  70%  { transform: scale(1.04); opacity: 1; }
  100% { transform: scale(1); }
}

@keyframes previewFade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes previewRise {
  from { transform: translateY(26px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

.anim-pop  { animation: previewPop 1.6s ease-out infinite; }
.anim-fade { animation: previewFade 1.8s ease-in-out infinite alternate; }
.anim-rise { animation: previewRise 1.8s ease-out infinite; }
.anim-none { animation: none; }

@media (prefers-reduced-motion: reduce) {
  .preview-caption { animation: none !important; }
}

/* ---------------------------------------------------------------- pricing */

.notice {
  background: #1E1710;
  border: 1px solid #4A3520;
  color: #E8B37A;
  border-radius: 10px;
  padding: 14px 18px;
  font-size: 14px;
  margin-bottom: 20px;
}

.packages {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 16px;
  margin-bottom: 20px;
}

.package {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 22px 20px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.package.featured { border-color: var(--accent); }

.package h3 { margin: 0; font-size: 15px; color: var(--dim); font-weight: 600; }

.package-tokens { font-size: 22px; font-weight: 700; }

.package-price {
  font-size: 30px;
  font-weight: 700;
  color: var(--accent);
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 4px 0 2px;
}

.alt-price { font-size: 14px; font-weight: 500; color: var(--dim); }

.per-token {
  color: var(--dim);
  font-size: 12px;
  margin: 0 0 6px;
  border-top: 1px solid var(--line);
  padding-top: 10px;
}

.package button { width: 100%; margin-top: auto; }

/* Plans sit in the same grid language as the packs so the page reads as
   one thing, but they are taller and carry a list, so they get their own
   minimum width rather than being squeezed into the pack size. */
.plans {
  display: grid;
  /* Narrow enough that four tiers fit on one row. Plans are compared
     against each other, and a tier that wraps onto its own line below
     stops being part of the comparison. */
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 14px;
  margin-bottom: 8px;
}

.plan {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 20px 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.plan .package-price { font-size: 26px; }

.plan h3 { margin: 0; font-size: 15px; color: var(--dim); font-weight: 600; }

.plan.featured { border-color: var(--accent); }

/* The plan the reader is already on is marked by a filled edge rather
   than a colour change, so it stays legible next to the featured card
   without the two fighting over the same signal. */
.plan.current {
  border-color: #4A6A55;
  background: #16201A;
}

.plan-period {
  color: var(--dim);
  font-size: 12px;
  margin: -6px 0 4px;
}

.plan-perks {
  list-style: none;
  margin: 4px 0 14px;
  padding: 0;
  border-top: 1px solid var(--line);
  padding-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  font-size: 13px;
  color: var(--dim);
}

.plan-perks li { position: relative; padding-left: 16px; }

.plan-perks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 6px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
}

.plan.current .plan-perks li::before { background: #7BD88F; }

.plan button { width: 100%; margin-top: auto; }

.plan button.quiet {
  background: transparent;
  border: 1px solid var(--line);
  color: var(--dim);
}

.current-badge { background: #7BD88F; }

.plan-line {
  color: var(--dim);
  font-size: 13px;
  margin: 6px 0 0;
}

.badge {
  position: absolute;
  top: -10px;
  right: 16px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 11px;
  font-weight: 700;
  padding: 3px 10px;
  border-radius: 999px;
  letter-spacing: .03em;
}

/* ---------------------------------------------------------------- account */

.balance-card {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}

.balance-number {
  font-size: 44px;
  font-weight: 700;
  line-height: 1;
  color: var(--accent);
}

.balance-card .button-link { margin-top: 0; }

.history-row {
  display: grid;
  grid-template-columns: 52px 1fr 60px 128px;
  gap: 12px;
  align-items: baseline;
  padding: 9px 0;
  border-bottom: 1px solid var(--line);
  font-size: 14px;
}

.history-row:last-child { border-bottom: 0; }

.history-delta {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.history-delta.up { color: #7BD88F; }
.history-delta.down { color: #FF9E7A; }

.history-balance {
  color: var(--dim);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.history-when { color: var(--dim); font-size: 12px; text-align: right; }

@media (max-width: 540px) {
  .history-row { grid-template-columns: 46px 1fr 52px; }
  .history-when { display: none; }
}

.signin-brand {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 4px;
}

.signin-brand img { border-radius: 12px; display: block; }
.signin-brand h1 { margin: 0; }

/* ================================================================
   LANDING PAGE
   Everything below only applies to index.html, which sets
   <body class="landing">. The app pages are unaffected.
   ================================================================ */

body.landing {
  padding: 0;
  /* Two very wide, very faint brand-coloured washes. They give the near
     black some depth without ever being bright enough to read as colour. */
  background:
    radial-gradient(900px 520px at 12% -8%, rgba(0, 152, 178, .16), transparent 60%),
    radial-gradient(760px 460px at 92% 6%, rgba(123, 214, 92, .12), transparent 62%),
    var(--bg);
  background-attachment: fixed;
}

/* The app pages give every <section> a panel look. On the landing page a
   section is just a band of the page, so the panel is stripped off.
   Only the panel: this selector is more specific than the .band and
   .strip rules below, so anything it sets they cannot set back. It got
   the padding and the centring wrong twice before it was narrowed to
   exactly the four properties that make a panel. */
body.landing section {
  background: none;
  border: 0;
  border-radius: 0;
  margin-bottom: 0;
}

/* ---------------------------------------------------------------- nav */

.navbar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 14px 26px;
  background: rgba(11, 16, 15, .72);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid rgba(255, 255, 255, .05);
}

.nav-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text);
  text-decoration: none;
  font-weight: 700;
  font-size: 17px;
  letter-spacing: -.01em;
  flex: none;
}

.nav-brand img { border-radius: 8px; display: block; }

/* The pill is one rounded container holding the items, so the active
   item is a shape inside a shape rather than a lone highlighted word. */
.nav-pill {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 4px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, .07);
  background: rgba(255, 255, 255, .03);
}

.nav-item {
  color: var(--dim);
  text-decoration: none;
  font-size: 14px;
  font-weight: 500;
  padding: 7px 16px;
  border-radius: 999px;
  white-space: nowrap;
  transition: color .18s ease, background .18s ease;
}

.nav-item:hover { color: var(--text); }

.nav-item.active {
  color: var(--text);
  background: rgba(255, 255, 255, .09);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .06);
}

.nav-right {
  display: flex;
  align-items: center;
  gap: 20px;
  flex: none;
}

.nav-link {
  color: var(--text);
  text-decoration: none;
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
}

.nav-link:hover { color: var(--accent); }

/* ---------------------------------------------------------------- buttons */

/* inline-flex, not inline-block. An inline-block button sits on the text
   baseline of its line box, which leaves a few pixels of descender space
   under it and makes the button look nudged down next to anything else.
   inline-flex with centred items removes both the gap and the wobble. */
.cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  vertical-align: middle;
  background: var(--brand-gradient);
  color: var(--on-accent);
  text-decoration: none;
  font-weight: 700;
  font-size: 14px;
  padding: 10px 22px;
  border-radius: 999px;
  white-space: nowrap;
  box-shadow: 0 4px 18px rgba(67, 197, 143, .22);
  transition: transform .16s ease, box-shadow .16s ease;
}

.cta:hover {
  transform: translateY(-1px);
  box-shadow: 0 7px 24px rgba(67, 197, 143, .34);
}

.cta.big { font-size: 16px; padding: 14px 32px; }

.ghost {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  vertical-align: middle;
  color: var(--text);
  text-decoration: none;
  font-weight: 600;
  font-size: 14px;
  padding: 10px 22px;
  border-radius: 999px;
  border: 1px solid var(--line);
  white-space: nowrap;
}

.ghost:hover { border-color: var(--accent); color: var(--accent); }
.ghost.big { font-size: 16px; padding: 13px 30px; }

/* ---------------------------------------------------------------- hero */

.hero {
  max-width: 1180px;
  margin: 0 auto;
  padding: 78px 26px 60px;
  display: grid;
  grid-template-columns: 1.05fr .95fr;
  gap: 56px;
  align-items: center;
}

.eyebrow {
  color: var(--accent);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  margin: 0 0 16px;
}

.hero-title {
  font-size: clamp(38px, 5vw, 60px);
  line-height: 1.06;
  letter-spacing: -.025em;
  margin: 0 0 20px;
  font-weight: 800;
}

.grad-text {
  background: var(--brand-gradient);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.hero-sub {
  color: var(--dim);
  font-size: 17px;
  line-height: 1.65;
  margin: 0 0 30px;
  max-width: 46ch;
}

.hero-buttons { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
.hero-buttons.centred { justify-content: center; }

.hero-note { color: var(--dim); font-size: 13px; margin: 20px 0 0; }

/* ---------------------------------------------------------------- phone */

.phone-wrap { position: relative; display: flex; justify-content: center; }

.phone {
  position: relative;
  width: 268px;
  aspect-ratio: 9 / 16;
  border-radius: 26px;
  border: 1px solid rgba(255, 255, 255, .1);
  background: #05100C;
  overflow: hidden;
  box-shadow: 0 30px 70px rgba(0, 0, 0, .6);
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Stands in for the gameplay footage. It only has to move enough to read
   as video rather than as a placeholder rectangle. */
.phone-scene {
  position: absolute;
  inset: -30%;
  background:
    radial-gradient(closest-side, rgba(0, 152, 178, .5), transparent 70%) 20% 30% / 62% 52% no-repeat,
    radial-gradient(closest-side, rgba(123, 214, 92, .38), transparent 70%) 78% 66% / 66% 56% no-repeat,
    #071410;
  filter: blur(14px);
  animation: drift 13s ease-in-out infinite alternate;
}

@keyframes drift {
  from { transform: translate3d(-4%, -3%, 0) scale(1.05); }
  to   { transform: translate3d(5%, 4%, 0) scale(1.16); }
}

.phone-caption {
  position: relative;
  z-index: 2;
  width: 84%;
  text-align: center;
  line-height: 1.15;
  color: #fff;
}

.phone-caption.show { animation: capIn .34s cubic-bezier(.2, 1.5, .5, 1) both; }

@keyframes capIn {
  from { opacity: 0; transform: scale(.72); }
  to   { opacity: 1; transform: scale(1); }
}

/* The four looks, matched to what the renderer burns in. */
.phone-caption.pop {
  font-family: "Arial Black", Impact, sans-serif;
  font-size: 30px;
  text-transform: uppercase;
  letter-spacing: -.01em;
  text-shadow: -3px -3px 0 #000,
    3px -3px 0 #000,
    -3px 3px 0 #000,
    3px 3px 0 #000,
    0px -3px 0 #000,
    0px 3px 0 #000,
    -3px 0px 0 #000,
    3px 0px 0 #000,
    0 2px 8px rgba(0,0,0,.85);
}

.phone-caption.mega {
  font-family: Impact, "Arial Black", sans-serif;
  font-size: 52px;
  letter-spacing: .01em;
  text-shadow: -3px -3px 0 #000,
    3px -3px 0 #000,
    -3px 3px 0 #000,
    3px 3px 0 #000,
    0px -3px 0 #000,
    0px 3px 0 #000,
    -3px 0px 0 #000,
    3px 0px 0 #000,
    0 2px 8px rgba(0,0,0,.85);
}

.phone-caption.karaoke {
  font-family: "Arial Black", sans-serif;
  font-size: 21px;
  color: #9FB2AC;
  text-shadow: -2px -2px 0 #000,
    2px -2px 0 #000,
    -2px 2px 0 #000,
    2px 2px 0 #000,
    0px -2px 0 #000,
    0px 2px 0 #000,
    -2px 0px 0 #000,
    2px 0px 0 #000,
    0 2px 8px rgba(0,0,0,.85);
}

.phone-caption.karaoke em { font-style: normal; color: var(--brand-green); }

.phone-caption.boxed {
  font-family: Verdana, sans-serif;
  font-size: 21px;
  font-weight: 700;
}

.phone-caption.boxed::before {
  content: "";
  position: absolute;
  inset: -12px -14px;
  background: rgba(0, 0, 0, .62);
  border-radius: 6px;
  z-index: -1;
}

.phone-label {
  position: absolute;
  z-index: 3;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--dim);
  background: rgba(0, 0, 0, .5);
  border: 1px solid rgba(255, 255, 255, .1);
  padding: 5px 13px;
  border-radius: 999px;
}

.phone-glow {
  position: absolute;
  width: 300px;
  height: 300px;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(67, 197, 143, .3), transparent);
  filter: blur(50px);
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 0;
}

/* ---------------------------------------------------------------- strip */

.strip {
  max-width: 1180px;
  margin: 0 auto;
  padding: 26px;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  text-align: center;
}

.strip b {
  display: block;
  font-family: var(--display);
  font-size: 30px;
  font-weight: 800;
  letter-spacing: -.02em;
  color: var(--accent);
}

.strip span { color: var(--dim); font-size: 13px; }

/* ---------------------------------------------------------------- bands */

.band {
  max-width: 1180px;
  margin: 0 auto;
  padding: 74px 26px;
}

.band-title {
  font-size: clamp(26px, 3.2vw, 36px);
  font-weight: 800;
  letter-spacing: -.02em;
  margin: 0 0 14px;
}

.band-sub {
  color: var(--dim);
  font-size: 16px;
  line-height: 1.7;
  margin: 0 0 18px;
  max-width: 62ch;
}

.band-sub.centred { margin-left: auto; margin-right: auto; text-align: center; }

.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
  margin-top: 34px;
}

.step {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 24px 22px;
}

.step-number {
  width: 30px;
  height: 30px;
  border-radius: 9px;
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 800;
  font-size: 15px;
  display: grid;
  place-items: center;
  margin-bottom: 14px;
}

.step h3 { margin: 0 0 8px; font-size: 16px; font-weight: 700; }
.step p { margin: 0; color: var(--dim); font-size: 14px; line-height: 1.6; }
.step code {
  font-family: Consolas, ui-monospace, monospace;
  font-size: 13px;
  color: var(--accent);
}

/* ---------------------------------------------------------------- styles */

.style-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 16px;
  margin-top: 34px;
}

.style-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  overflow: hidden;
}

.style-demo {
  height: 132px;
  display: grid;
  place-items: center;
  padding: 12px;
  text-align: center;
  background:
    radial-gradient(closest-side, rgba(0, 152, 178, .32), transparent 72%) 30% 40% / 80% 80% no-repeat,
    radial-gradient(closest-side, rgba(123, 214, 92, .26), transparent 72%) 74% 64% / 78% 78% no-repeat,
    #071410;
  border-bottom: 1px solid var(--line);
}

.style-card h3 { margin: 16px 20px 6px; font-size: 15px; font-weight: 700; }
.style-card p { margin: 0 20px 20px; color: var(--dim); font-size: 13px; line-height: 1.55; }

.cap-pop, .cap-mega, .cap-karaoke, .cap-boxed { color: #fff; line-height: 1.15; }

.cap-pop {
  font-family: "Arial Black", sans-serif;
  font-size: 21px;
  text-transform: uppercase;
  text-shadow: -2px -2px 0 #000,
    2px -2px 0 #000,
    -2px 2px 0 #000,
    2px 2px 0 #000,
    0px -2px 0 #000,
    0px 2px 0 #000,
    -2px 0px 0 #000,
    2px 0px 0 #000,
    0 2px 8px rgba(0,0,0,.85);
}

.cap-mega {
  font-family: Impact, "Arial Black", sans-serif;
  font-size: 40px;
  text-shadow: -3px -3px 0 #000,
    3px -3px 0 #000,
    -3px 3px 0 #000,
    3px 3px 0 #000,
    0px -3px 0 #000,
    0px 3px 0 #000,
    -3px 0px 0 #000,
    3px 0px 0 #000,
    0 2px 8px rgba(0,0,0,.85);
}

.cap-karaoke {
  font-family: "Arial Black", sans-serif;
  font-size: 17px;
  color: #9FB2AC;
  text-shadow: -2px -2px 0 #000,
    2px -2px 0 #000,
    -2px 2px 0 #000,
    2px 2px 0 #000,
    0px -2px 0 #000,
    0px 2px 0 #000,
    -2px 0px 0 #000,
    2px 0px 0 #000,
    0 2px 8px rgba(0,0,0,.85);
}

.cap-karaoke em { font-style: normal; color: var(--brand-green); }

.cap-boxed {
  font-family: Verdana, sans-serif;
  font-size: 16px;
  font-weight: 700;
  background: rgba(0, 0, 0, .62);
  padding: 9px 13px;
  border-radius: 6px;
}

/* ---------------------------------------------------------------- voices */

.split {
  display: grid;
  grid-template-columns: 1.15fr .85fr;
  gap: 50px;
  align-items: center;
}

.voice-panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 8px;
  /* Fourteen rows would run far past the paragraph beside them. Capping
     the height and scrolling keeps the two columns roughly level, and the
     cut-off row at the bottom is itself the hint that there are more. */
  max-height: 440px;
  overflow-y: auto;
}

.voice-list { list-style: none; margin: 0; padding: 0; }

.voice-list li { border-bottom: 1px solid var(--line); }
.voice-list li:last-child { border-bottom: 0; }

/* Each row is a real button: it does something, so it should be reachable
   by keyboard and announce itself as pressable. */
.voice-row {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 12px;
  margin: 0;
  background: none;
  border: 0;
  border-radius: 8px;
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.voice-row:hover { filter: none; background: rgba(255, 255, 255, .04); }

.voice-row.playing { background: rgba(67, 197, 143, .12); color: var(--accent); }
.voice-row.loading { opacity: .75; }

.voice-name { flex: none; }

.voice-list em {
  font-style: normal;
  color: var(--dim);
  font-weight: 400;
  margin-left: auto;
  font-size: 13px;
  text-align: right;
}

.voice-icon {
  flex: none;
  width: 22px;
  text-align: center;
  color: var(--dim);
  font-size: 11px;
}

.voice-row:hover .voice-icon,
.voice-row.playing .voice-icon { color: var(--accent); }

.swatch { width: 11px; height: 11px; border-radius: 3px; flex: none; }

/* ---------------------------------------------------------------- closing */

.closing { text-align: center; padding-bottom: 90px; }
.closing .band-title { margin-bottom: 12px; }

.foot {
  max-width: 1180px;
  margin: 0 auto;
  padding: 26px;
  border-top: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}

.foot-links { display: flex; gap: 22px; }
.foot-links a { color: var(--dim); text-decoration: none; font-size: 14px; }
.foot-links a:hover { color: var(--accent); }

/* ---------------------------------------------------------------- narrow */

@media (max-width: 940px) {
  .hero { grid-template-columns: 1fr; gap: 44px; padding-top: 52px; }
  .split { grid-template-columns: 1fr; gap: 32px; }
  .nav-pill { display: none; }
  .strip { grid-template-columns: repeat(2, 1fr); gap: 24px 16px; }
}

@media (max-width: 560px) {
  /* On a phone the bar has to hold a logo, an account link and a call to
     action inside 390px. The wordmark is the piece that carries the least
     information next to the logo it sits beside, so it is the piece that
     goes; the button keeps its full label because that label is the only
     thing on the bar telling a visitor what to do next. */
  .navbar { padding: 12px 14px; gap: 8px; }
  .nav-brand span { display: none; }
  .nav-right { gap: 12px; min-width: 0; }
  .nav-link { font-size: 13px; }
  .cta { font-size: 13px; padding: 9px 16px; }

  /* Pricing survives in the footer and on the plans page. */
  .nav-right .nav-link[href="/pricing.html"] { display: none; }
  .hero, .band, .strip, .foot { padding-left: 18px; padding-right: 18px; }
  .band { padding-top: 54px; padding-bottom: 54px; }
  .hero-buttons { flex-direction: column; align-items: stretch; text-align: center; }
}

@media (prefers-reduced-motion: reduce) {
  .phone-scene, .phone-caption.show { animation: none !important; }
  .cta:hover { transform: none; }
}

/* ================================================================
   APP PAGES
   The studio shares the landing page's shell — same bar, same card
   language — so moving from one to the other does not feel like
   moving between two different products.
   ================================================================ */

body.app {
  padding: 0 0 90px;
  background:
    radial-gradient(900px 480px at 15% -10%, rgba(0, 152, 178, .13), transparent 62%),
    radial-gradient(700px 420px at 88% 2%, rgba(123, 214, 92, .09), transparent 64%),
    var(--bg);
  background-attachment: fixed;
}

body.app main { max-width: 860px; padding: 0 26px; }

.page-head { padding: 46px 0 26px; }
.page-head h1 { font-size: 34px; font-weight: 800; letter-spacing: -.025em; margin: 0 0 8px; }
.page-head .tagline { margin: 0; max-width: 60ch; }

.account-email { color: var(--dim); display: block; font-size: 13px; margin-top: 6px; }

body.app section {
  border-radius: 14px;
  padding: 24px;
  margin-bottom: 18px;
}

body.app h2 {
  display: flex;
  align-items: center;
  gap: 11px;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -.01em;
  margin-bottom: 14px;
}

.step-badge {
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 8px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 13px;
  font-weight: 800;
  display: grid;
  place-items: center;
}

/* The token chip lives in the nav bar now, so it has to sit on the bar's
   baseline rather than in a stacked account block. */
body.app .tokens, .navbar .tokens {
  display: inline-flex;
  align-items: center;
  /* A flex container drops the whitespace between the count and the word,
     so "5 tokens" arrives as "5tokens" without this. */
  gap: 4px;
  border-radius: 999px;
  padding: 7px 15px;
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  color: var(--text);
  border: 1px solid var(--line);
  white-space: nowrap;
}

.navbar .tokens.unlimited {
  border-color: var(--accent);
  color: var(--accent);
}

.navbar .link-button { margin: 0; }

/* One speaker's row gains a play button, so a voice can be heard before
   it is assigned rather than after the video comes out. */
.cast-row { grid-template-columns: 150px minmax(0, 1fr) 36px 36px 36px; }

.icon-button.playing { color: var(--accent); border-color: var(--accent); }
.icon-button.playing:hover { color: var(--accent); border-color: var(--accent); }

@media (max-width: 640px) {
  body.app main { padding: 0 16px; }
  .page-head { padding: 32px 0 20px; }
  .page-head h1 { font-size: 27px; }
  body.app section { padding: 18px; }
  .cast-row { grid-template-columns: minmax(0, 1fr) 36px 36px 36px; }
  .cast-row .cast-name { grid-column: 1 / -1; }
}

/* ================================================================
   THE WORKBENCH  (studio.html)

   Two columns. Left: everything you change. Right: the thing you are
   making, pinned so it never scrolls away. The old layout stacked six
   identical panels and put the video at the bottom of all of them, so
   the one output the site exists to produce was the one thing you
   could not see while producing it.
   ================================================================ */

body.studio main.workbench {
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 26px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 30px;
  align-items: start;
}

body.studio .controls { min-width: 0; padding-bottom: 60px; }

/* Sticky, offset by the height of the navbar so it never hides under it. */
body.studio .rail {
  position: sticky;
  top: 84px;
  padding-top: 46px;
}

.rail-inner {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* The preview and the finished video share one slot and one size, so the
   swap between them reads as the same object changing state rather than
   a new panel appearing somewhere else. */
body.studio .preview-frame,
body.studio #result-video {
  width: 100%;
  max-width: none;
  height: auto;
  aspect-ratio: 9 / 16;
  margin: 0;
  border-radius: 10px;
}

body.studio .preview-frame { padding: 0 14px; }

body.studio #result-step { display: flex; flex-direction: column; gap: 10px; }
body.studio #result-step.hidden { display: none; }
body.studio #download-link { text-align: center; margin: 0; }

.rail-actions { display: flex; flex-direction: column; gap: 6px; }
.rail-actions button { width: 100%; margin: 0; }
.rail-actions .hint { margin: 0; text-align: center; font-size: 13px; }

.rail .error { margin: 0; font-size: 13px; }

.hint.quiet { color: var(--line-2, var(--dim)); font-size: 13px; opacity: .75; }

/* ---------------------------------------------------------------- crab */

/*
   A loop, not a measurement.

   The old bar showed a percentage the server was guessing at, so it sat
   at 66% for half a minute and looked broken. This says one thing —
   "still working" — and says it honestly, while the line underneath
   names the stage, which we do know.

   Drawn in SVG rather than with CSS boxes: a crab needs curves, an oval
   shell and two hinged pincers, and stacking rounded div corners to fake
   those produced something closer to a beetle.
*/
#progress-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 4px 0 2px;
}

#progress-box.hidden { display: none; }

.crab-cutter { width: 128px; height: 56px; display: block; overflow: visible; }

/* film ------------------------------------------------------------- */

/* A frame is a lit image with a dark sprocket bar above and below it.
   The holes need to beat the frame rule on specificity — matched only by
   class, they lost, and the whole strip came out as green buttons. */
.crab-cutter .frame rect { fill: url(#crabShell); opacity: .38; }
.crab-cutter .frame rect.hole { fill: var(--bg); opacity: .9; }

/* One frame plus one gap is 22 units. Sliding exactly that distance and
   snapping back is invisible, because the strip is identical every 22. */
.crab-cutter .film { animation: film-run 1.15s linear infinite; }

@keyframes film-run {
  from { transform: translateX(0); }
  to   { transform: translateX(-22px); }
}

/* crab -------------------------------------------------------------- */

.crab-cutter .shell { fill: url(#crabShell); }
.crab-cutter .eye { fill: var(--brand-green); }

.crab-cutter .stalk,
.crab-cutter .legs path {
  stroke: var(--brand-cyan);
  stroke-width: 1.8;
  stroke-linecap: round;
  fill: none;
}

.crab-cutter .arm {
  stroke: var(--brand-cyan);
  stroke-width: 3.2;
  stroke-linecap: round;
  fill: none;
}

.crab-cutter .jaw {
  stroke: var(--brand-cyan);
  stroke-width: 3;
  stroke-linecap: round;
  fill: none;
}

/* Only the upper half of each pincer turns, hinged at the wrist. Rotating
   the whole claw would swing the arm with it and read as waving.

   transform-box is set explicitly: without it the browser can measure the
   origin against the element's own bounding box instead of the viewBox,
   and the claws fly off across the drawing. */
.crab-cutter .upper {
  transform-box: view-box;
  animation: snip 1.15s cubic-bezier(.3, 0, .2, 1) infinite;
}

.crab-cutter .claw-left .upper { transform-origin: 47px 26px; }
.crab-cutter .claw-right .upper { transform-origin: 85px 26px; }

/* Open on the way in, snap shut on the beat. A pincer that only closes
   never looks like it cut anything — it has to gape first. */
@keyframes snip {
  0%, 32%   { transform: rotate(0deg); }
  50%, 60%  { transform: rotate(26deg); }
  78%, 100% { transform: rotate(0deg); }
}

.crab-cutter .claw-right .upper { animation-name: snip-right; }

@keyframes snip-right {
  0%, 32%   { transform: rotate(0deg); }
  50%, 60%  { transform: rotate(-26deg); }
  78%, 100% { transform: rotate(0deg); }
}

/* The whole crab dips slightly into the cut. Small enough to feel like
   effort rather than bouncing. */
.crab-cutter .crab {
  transform-box: view-box;
  animation: crab-dip 1.15s ease-in-out infinite;
}

@keyframes crab-dip {
  0%, 30%, 100% { transform: translateY(0); }
  56%           { transform: translateY(1.5px); }
}

#progress-text { margin: 0; text-align: center; font-size: 13px; }

@media (prefers-reduced-motion: reduce) {
  .crab-cutter .film,
  .crab-cutter .crab,
  .crab-cutter .upper { animation: none; }
}

/* ---------------------------------------------------------------- narrow */

@media (max-width: 940px) {
  .hero { grid-template-columns: 1fr; gap: 44px; padding-top: 52px; }
  .split { grid-template-columns: 1fr; gap: 32px; }
  .nav-pill { display: none; }
  .strip { grid-template-columns: repeat(2, 1fr); gap: 24px 16px; }
}

@media (max-width: 560px) {
  /* On a phone the bar has to hold a logo, an account link and a call to
     action inside 390px. The wordmark is the piece that carries the least
     information next to the logo it sits beside, so it is the piece that
     goes; the button keeps its full label because that label is the only
     thing on the bar telling a visitor what to do next. */
  .navbar { padding: 12px 14px; gap: 8px; }
  .nav-brand span { display: none; }
  .nav-right { gap: 12px; min-width: 0; }
  .nav-link { font-size: 13px; }
  .cta { font-size: 13px; padding: 9px 16px; }

  /* Pricing survives in the footer and on the plans page. */
  .nav-right .nav-link[href="/pricing.html"] { display: none; }
  .hero, .band, .strip, .foot { padding-left: 18px; padding-right: 18px; }
  .band { padding-top: 54px; padding-bottom: 54px; }
  .hero-buttons { flex-direction: column; align-items: stretch; text-align: center; }
}

@media (prefers-reduced-motion: reduce) {
  .phone-scene, .phone-caption.show { animation: none !important; }
  .cta:hover { transform: none; }
}

/* ================================================================
   APP PAGES
   The studio shares the landing page's shell — same bar, same card
   language — so moving from one to the other does not feel like
   moving between two different products.
   ================================================================ */

body.app {
  padding: 0 0 90px;
  background:
    radial-gradient(900px 480px at 15% -10%, rgba(0, 152, 178, .13), transparent 62%),
    radial-gradient(700px 420px at 88% 2%, rgba(123, 214, 92, .09), transparent 64%),
    var(--bg);
  background-attachment: fixed;
}

body.app main { max-width: 860px; padding: 0 26px; }

.page-head { padding: 46px 0 26px; }
.page-head h1 { font-size: 34px; font-weight: 800; letter-spacing: -.025em; margin: 0 0 8px; }
.page-head .tagline { margin: 0; max-width: 60ch; }

.account-email { color: var(--dim); display: block; font-size: 13px; margin-top: 6px; }

body.app section {
  border-radius: 14px;
  padding: 24px;
  margin-bottom: 18px;
}

body.app h2 {
  display: flex;
  align-items: center;
  gap: 11px;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -.01em;
  margin-bottom: 14px;
}

.step-badge {
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 8px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 13px;
  font-weight: 800;
  display: grid;
  place-items: center;
}

/* The token chip lives in the nav bar now, so it has to sit on the bar's
   baseline rather than in a stacked account block. */
body.app .tokens, .navbar .tokens {
  display: inline-flex;
  align-items: center;
  /* A flex container drops the whitespace between the count and the word,
     so "5 tokens" arrives as "5tokens" without this. */
  gap: 4px;
  border-radius: 999px;
  padding: 7px 15px;
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  color: var(--text);
  border: 1px solid var(--line);
  white-space: nowrap;
}

.navbar .tokens.unlimited {
  border-color: var(--accent);
  color: var(--accent);
}

.navbar .link-button { margin: 0; }

/* One speaker's row gains a play button, so a voice can be heard before
   it is assigned rather than after the video comes out. */
.cast-row { grid-template-columns: 150px minmax(0, 1fr) 36px 36px 36px; }

.icon-button.playing { color: var(--accent); border-color: var(--accent); }
.icon-button.playing:hover { color: var(--accent); border-color: var(--accent); }

@media (max-width: 640px) {
  body.app main { padding: 0 16px; }
  .page-head { padding: 32px 0 20px; }
  .page-head h1 { font-size: 27px; }
  body.app section { padding: 18px; }
  .cast-row { grid-template-columns: minmax(0, 1fr) 36px 36px 36px; }
  .cast-row .cast-name { grid-column: 1 / -1; }
}

/* ================================================================
   THE WORKBENCH  (studio.html)

   Two columns. Left: everything you change. Right: the thing you are
   making, pinned so it never scrolls away. The old layout stacked six
   identical panels and put the video at the bottom of all of them, so
   the one output the site exists to produce was the one thing you
   could not see while producing it.
   ================================================================ */

body.studio main.workbench {
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 26px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 30px;
  align-items: start;
}

body.studio .controls { min-width: 0; padding-bottom: 60px; }

/* Sticky, offset by the height of the navbar so it never hides under it. */
body.studio .rail {
  position: sticky;
  top: 84px;
  padding-top: 46px;
}

.rail-inner {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* The preview and the finished video share one slot and one size, so the
   swap between them reads as the same object changing state rather than
   a new panel appearing somewhere else. */
body.studio .preview-frame,
body.studio #result-video {
  width: 100%;
  max-width: none;
  height: auto;
  aspect-ratio: 9 / 16;
  margin: 0;
  border-radius: 10px;
}

body.studio .preview-frame { padding: 0 14px; }

body.studio #result-step { display: flex; flex-direction: column; gap: 10px; }
body.studio #result-step.hidden { display: none; }
body.studio #download-link { text-align: center; margin: 0; }

.rail-actions { display: flex; flex-direction: column; gap: 6px; }
.rail-actions button { width: 100%; margin: 0; }
.rail-actions .hint { margin: 0; text-align: center; font-size: 13px; }

.rail .error { margin: 0; font-size: 13px; }

.hint.quiet { color: var(--line-2, var(--dim)); font-size: 13px; opacity: .75; }

/* ---------------------------------------------------------------- narrow */

@media (max-width: 900px) {
  /* The rail moves to the top and stops being sticky. A preview pinned
     over the controls on a phone would cover the very thing you are
     trying to press.

     It also stays a COLUMN. Laying it out as a row put the preview and
     the crab side by side, two fixed-width items in a 390px window, and
     the page scrolled sideways. A narrow centred preview costs nothing
     and cannot overflow. */
  body.studio main.workbench {
    grid-template-columns: 1fr;
    gap: 0;
  }

  body.studio .rail { position: static; padding-top: 22px; order: -1; }

  .rail-inner { align-items: center; }

  body.studio .preview-frame,
  body.studio #result-video,
  body.studio #result-step { width: 100%; max-width: 190px; }

  .rail-actions { width: 100%; max-width: 320px; }
}

/* A grid item defaults to min-width:auto, so a `1fr` column refuses to
   shrink past the textarea's intrinsic width (~20 characters). One story
   row then set the width of the entire left column and the page scrolled
   sideways on a phone. Stacking the dropdown above the line fixes the
   cause rather than hiding the symptom. */
@media (max-width: 560px) {
  .row {
    grid-template-columns: minmax(0, 1fr) 34px;
    grid-template-areas: "speaker remove" "text text";
    row-gap: 6px;
  }

  .row .speaker-select { grid-area: speaker; }
  .row .icon-button { grid-area: remove; }
  .row .line-text { grid-area: text; }
}

/* ================================================================
   CONTROL PANEL  (admin.html)
   ================================================================ */

body.panel main { max-width: 1000px; }

/* ---------------------------------------------------------------- tiles */

.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 14px;
  background: none !important;
  border: 0 !important;
  padding: 0 !important;
}

.tile {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 18px 20px;
}

.tile-value {
  font-size: 38px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.tile-label { font-size: 14px; font-weight: 600; margin-top: 7px; }
.tile-note { font-size: 12.5px; color: var(--dim); margin-top: 3px; line-height: 1.45; }

/* ---------------------------------------------------------------- charts */

.chart-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 16px;
  margin-top: 16px;
}

.chart {
  margin: 0;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 14px 15px 10px;
}

.chart-title {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
  font-size: 13px;
  color: var(--dim);
  margin-bottom: 12px;
}

.chart-title b {
  color: var(--text);
  font-size: 17px;
  font-variant-numeric: tabular-nums;
}

.plot {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 62px;
}

/* The slot is the hit target, not the bar. A one-pixel bar on a quiet
   day would otherwise be impossible to hover or tab to. */
.slot {
  flex: 1;
  height: 100%;
  display: flex;
  align-items: flex-end;
  border-radius: 3px;
}

.slot:hover, .slot:focus-visible {
  background: rgba(255, 255, 255, .05);
  outline: none;
}

.bar {
  width: 100%;
  min-height: 2px;
  /* Rounded only at the top: the bottom is anchored to the baseline, and
     rounding it there would lift the bar off its own axis. */
  border-radius: 4px 4px 0 0;
}

/* A day with nothing in it still gets a mark, so it reads as a measured
   zero rather than a gap in the data. */
.bar.empty { background: var(--line) !important; min-height: 2px; }

.axis {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  color: var(--dim);
  margin-top: 7px;
}

/* ---------------------------------------------------------------- bars */

.two-col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 26px;
  margin-top: 14px;
}

.two-col.three { grid-template-columns: repeat(3, 1fr); }

.sub {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--dim);
  margin: 0 0 10px;
}

.hbar {
  display: grid;
  grid-template-columns: minmax(0, 104px) minmax(0, 1fr) auto;
  gap: 10px;
  align-items: center;
  margin-bottom: 7px;
  font-size: 13px;
}

.hbar-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.hbar-track {
  height: 9px;
  background: var(--line);
  border-radius: 999px;
  overflow: hidden;
}

.hbar-fill { display: block; height: 100%; border-radius: 999px; }

.hbar-value {
  color: var(--dim);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
}

/* ---------------------------------------------------------------- table */

body.panel table { border-collapse: collapse; width: 100%; font-size: 14px; }

body.panel th {
  text-align: left;
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--dim);
  font-weight: 600;
  padding: 0 12px 8px 0;
  border-bottom: 1px solid var(--line);
}

body.panel td {
  padding: 10px 12px 10px 0;
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}

body.panel tr:last-child td { border-bottom: 0; }
body.panel td.num { font-variant-numeric: tabular-nums; }
body.panel td.muted { color: var(--dim); }

/* ---------------------------------------------------------------- chips */

.chips { display: flex; flex-wrap: wrap; gap: 8px; }

/* State is a word plus a mark, never a colour on its own — the sentence
   still reads correctly in black and white. */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 13px;
  font-size: 12.5px;
  white-space: nowrap;
}

.chip.good    { border-color: #2F5A3A; color: #7BD88F; }
.chip.busy    { border-color: #2A4E58; color: #6FC7DA; }
.chip.waiting { border-color: #4A3A20; color: #E0AC63; }
.chip.bad     { border-color: #5A2F2F; color: #FF9E8E; }
.chip.quiet   { color: var(--dim); }

@media (max-width: 720px) {
  .two-col, .two-col.three { grid-template-columns: 1fr; gap: 22px; }
}

/* ================================================================
   ERRORS THAT EXPLAIN THEMSELVES

   Three parts, three weights. The heading says what did not happen, the
   line under it says why, the last line says what to do. Ranking them
   visually is the whole point: a reader who only takes in the bold line
   still learns something true, and a reader in a hurry can jump to the
   last line, which is the only one that asks them to act.
   ================================================================ */

.error strong,
.message strong {
  display: block;
  font-size: 15px;
  font-weight: 700;
  margin-bottom: 5px;
}

.error .why,
.message .why {
  display: block;
  font-weight: 400;
  opacity: .82;
  margin-bottom: 7px;
}

/* The instruction gets a rule beside it, so it reads as the next step
   rather than a third sentence of explanation. */
.error .fix,
.message .fix {
  display: block;
  font-weight: 600;
  padding-left: 11px;
  border-left: 2px solid currentColor;
  opacity: .95;
}

.error {
  line-height: 1.5;
  background: #231414;
  border: 1px solid #5A2F2F;
  border-radius: 10px;
  padding: 13px 15px;
}

.message { line-height: 1.5; }

/* In the studio the error sits in the narrow rail, so it has to wrap
   rather than push the column wider. */
.rail .error { font-size: 13px; }
.rail .error strong { font-size: 14px; }

/* ================================================================
   TOKENS PAGE
   Same shell as the rest of the app, and a centred opening so the page
   reads as a choice being offered rather than a form to fill in.
   ================================================================ */

body.tokens-page main { max-width: 1000px; }

.page-head.centred { text-align: center; padding-bottom: 8px; }
.page-head.centred .tagline.wide { max-width: 58ch; margin: 0 auto; }

.page-head .eyebrow {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 12px;
}

body.tokens-page .page-head h1 { font-size: clamp(28px, 4.4vw, 40px); }

/* Sections here are groupings, not panels — the cards inside them are
   already boxes, and a box inside a box just adds a second border. */
section.bare {
  background: none;
  border: 0;
  padding: 0;
  margin-bottom: 34px;
}

.section-title {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim);
  margin: 0 0 14px;
  display: block;
}

body.tokens-page .notice {
  max-width: 620px;
  margin: 0 auto 34px;
  text-align: center;
  line-height: 1.55;
}

body.tokens-page .notice b { color: #F0C48A; }

.explainer { margin-top: 8px; }
.explainer h2 { margin-bottom: 14px; }

.two-up-wide {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 26px;
}

.two-up-wide .hint { margin: 0; }

@media (max-width: 720px) {
  .two-up-wide { grid-template-columns: 1fr; gap: 14px; }
}

/* A wide table scrolls inside its own box. Letting it push the page
   sideways instead would move the whole layout every time somebody had a
   long email address. */
.table-scroll { overflow-x: auto; }
.table-scroll table { min-width: 460px; }

/* ================================================================
   THE PERFORATION

   A strip of film sprocket holes, used where a page needs a break.

   The site had nothing in it that belonged to this product rather than
   to any product: the same rounded cards, the same accent, the same
   gradient every tool has worn since 2023. This is small, quiet, and
   could not sit on a banking app — which is the whole point of it.

   Drawn with a repeating gradient rather than an image, so it costs
   nothing and takes the theme's colours with it.
   ================================================================ */

.perf {
  height: 13px;
  margin: 6px 0;
  background-image:
    linear-gradient(var(--line), var(--line)),
    repeating-linear-gradient(
      to right,
      transparent 0 9px,
      var(--line) 9px 19px
    );
  background-size: 100% 1px, 100% 5px;
  background-position: 0 6px, 0 0;
  background-repeat: no-repeat, repeat-x;
  opacity: .75;
}

/* Above the rail in the studio: the panel now reads as the head of a
   strip of film, which is what the thing inside it is. */
body.studio .rail-inner::before {
  content: "";
  display: block;
  height: 5px;
  margin: -4px -4px 2px;
  background: repeating-linear-gradient(
    to right,
    transparent 0 9px,
    var(--line) 9px 19px
  );
  border-radius: 2px;
}

/* Section headings on the tokens page get the same mark, so the two
   pages feel cut from one roll. */
body.tokens-page .section-title::after {
  content: "";
  display: block;
  height: 5px;
  margin-top: 9px;
  background: repeating-linear-gradient(
    to right,
    var(--line) 0 10px,
    transparent 10px 20px
  );
  opacity: .8;
}

/* ================================================================
   FEEDBACK

   Bottom corner, out of the way, present on every page. The reason it
   floats rather than living on a "Contact" page is timing: people want
   to report a thing while they are looking at it, and a link that takes
   them somewhere else to say it loses most of them on the way.
   ================================================================ */

.feedback {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
}

.feedback-open {
  margin: 0;
  background: var(--panel);
  color: var(--dim);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 9px 18px;
  font-size: 13px;
  font-weight: 600;
  box-shadow: 0 6px 20px -8px rgba(0, 0, 0, .8);
}

.feedback-open:hover { color: var(--accent); border-color: var(--accent); filter: none; }

.feedback-panel {
  order: -1;
  width: min(330px, calc(100vw - 36px));
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 16px;
  box-shadow: 0 18px 50px -14px rgba(0, 0, 0, .9);
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.feedback-panel.hidden { display: none; }

.feedback-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-family: var(--display);
  font-size: 15px;
}

.feedback-close {
  background: none;
  border: 0;
  color: var(--dim);
  font-size: 21px;
  line-height: 1;
  padding: 0 2px;
  margin: 0;
}

.feedback-close:hover { color: var(--text); filter: none; }

.feedback-kinds { display: flex; flex-wrap: wrap; gap: 6px; }

.feedback-kind {
  margin: 0;
  background: var(--bg);
  color: var(--dim);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 12px;
  font-size: 12px;
  font-weight: 500;
  font-family: var(--body);
}

.feedback-kind:hover { filter: none; color: var(--text); }

.feedback-kind.on {
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}

.feedback-panel textarea {
  width: 100%;
  resize: vertical;
  font-size: 14px;
}

.feedback-email { display: flex; flex-direction: column; gap: 5px; }
.feedback-email span { color: var(--dim); font-size: 12px; }
.feedback-email input { width: 100%; font-size: 14px; }

.feedback-panel > button#fb-send { margin: 0; width: 100%; }

.feedback-note { margin: 0; font-size: 12.5px; color: var(--dim); min-height: 1em; }
.feedback-note.good { color: #7BD88F; }
.feedback-note.bad { color: #FF9E8E; }

/* On a phone the button sits where a thumb already rests, and the panel
   uses the full width rather than a floating card with nowhere to go. */
@media (max-width: 560px) {
  .feedback { right: 12px; bottom: 12px; left: 12px; align-items: stretch; }
  .feedback-open { align-self: flex-end; }
  .feedback-panel { width: auto; }
}

/* One chart on its own should not stretch to the full width of a row
   built for three — a 900px-wide bar chart of fourteen days reads as a
   fence rather than a measurement. */
.chart-row.single { grid-template-columns: minmax(0, 380px); }

.history-line { grid-template-columns: 46px minmax(0, 1fr) auto; }


/* Links inside body text. Without this they wear the browser's 1994 blue,
   which on a dark green-black page is both ugly and hard to read. */
.hint a, p a, td a {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid rgba(67, 197, 143, .35);
}

.hint a:hover, p a:hover, td a:hover { border-bottom-color: var(--accent); }
