/* Crowd Curve -- FLAT, light by default (2026-09-28 redesign; the previous
 * quant-terminal dark skin is preserved at git tag `pre-flat-redesign` and
 * lives on as the dark theme behind the topbar toggle). EVERY color must go
 * through the tokens below -- a hardcoded hex is a theme bug waiting to
 * happen.
 *
 * Flat rules of the house: white cards on a light ground, solid fills over
 * borders, no gradients, no glow, shadows only where something truly floats
 * (modals), hierarchy from type weight/size and white-space. Color stays
 * RATIONED like before, just bolder: saturated teal for actions/focus/the
 * curve, purple for chart data only, green/red only where money is up or
 * down.
 *
 * Still hand-written, still no build step.
 *
 * TYPOGRAPHY: Inter for UI text, JetBrains Mono for every number, label and
 * data cell -- both flat-canon faces. Self-hosted variable woff2; the CSP
 * allows no external origins. */

@font-face {
  font-family: "Inter";
  src: url("/static/fonts/inter-var.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/static/fonts/jetbrains-mono-var.woff2") format("woff2");
  font-weight: 100 800;
  font-style: normal;
  font-display: swap;
}

:root {
  --bg: #f5f7f9;          /* page ground -- light, airy */
  --surface: #ffffff;     /* cards, flat white */
  --sunken: #eef1f4;      /* inputs: solid light fill, not a bordered box */
  --raised: #e6ebef;      /* hover rows, active seg */
  --ink: #1c2733;         /* body text */
  --bright: #0b141d;      /* headings, big numbers */
  --muted: #52616f;       /* labels */
  --faint: #7e8c99;       /* de-emphasized */
  --line: #e4e9ee;        /* hairline card edges */
  --line-strong: #cbd4dc; /* input borders (focus/ghost only) */
  --accent: #0c8577;      /* bold teal: actions, focus, links, the curve. AA on white. */
  --accent-soft: #c9efe9;
  --good: #15803d;
  --good-soft: #d5f2df;
  --bad: #d1242f;
  --bad-soft: #ffdfdd;
  --warn: #9a6700;
  --warn-soft: #ffefc2;
  --kink: #9a6700;
  --ms: #7c3aed;          /* chart-data purple, saturated for white ground */
  --zebra: #f2f5f7;       /* odd table rows */
  --pill-bg: #e9edf1;
  --btn-primary-ink: #ffffff;  /* text on the solid-teal primary button */
  --btn-primary-hover: #0a6b60;
  --shadow-pop: 0 10px 30px -12px rgba(15, 30, 45, .22);  /* floats only: popovers, modals, tips */
  --card-line: transparent;    /* flat: cards separate by ground contrast, not borders */
  --radius: 10px;
  --radius-s: 6px;
  --tap: 42px;
  --font-ui: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Consolas, monospace;
  --control-h: 36px;      /* one height for buttons/inputs/selects on desktop */
}

/* Dark theme -- the quant-terminal palette, kept behind the topbar toggle
 * and flattened to match the new component rules. Same ration discipline. */
html[data-theme="dark"] {
  --bg: #0f1216;
  --surface: #161b22;
  --sunken: #0d1014;
  --raised: #1b2129;
  --ink: #d8dee9;
  --bright: #e6edf3;
  --muted: #9fb3c8;
  --faint: #7d8998;
  --line: #262c36;
  --line-strong: #30363d;
  --accent: #35c9ba;
  --accent-soft: #0f2e2b;
  --good: #3fb950;
  --good-soft: #103b21;
  --bad: #f85149;
  --bad-soft: #3d1418;
  --warn: #d29922;
  --warn-soft: #2d2308;
  --kink: #d29922;
  --ms: #a371f7;
  --zebra: #12161c;
  --pill-bg: #21262d;
  --btn-primary-ink: #06211e;  /* dark ink on the bright-teal primary */
  --btn-primary-hover: #4fd1c5;
  --shadow-pop: 0 14px 40px -18px rgba(0, 0, 0, .55);
  --card-line: #262c36;        /* dark cards need the hairline to hold shape */
}

* { box-sizing: border-box; }

/* -------- interaction hygiene (Web Interface Guidelines audit, 2026-09-03) */
html { -webkit-tap-highlight-color: transparent; }
button, a, select, input, textarea, summary { touch-action: manipulation; }
button:focus-visible, a:focus-visible, select:focus-visible, summary:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-delay: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}
.skip-link {
  position: absolute; left: -999px; top: 0; z-index: 100;
  background: var(--surface); color: var(--accent);
  padding: .5rem .9rem; border-radius: 0 0 var(--radius-s) 0;
  text-decoration: none; font-size: .8rem;
}
.skip-link:focus { left: 0; }
h1 { text-wrap: balance; }

html { color-scheme: light; }
html[data-theme="dark"] { color-scheme: dark; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 14px/1.55 var(--font-ui);
  letter-spacing: .002em;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
}

/* Flat typography: section titles are real titles -- clean sans, sentence
 * case, hierarchy from size and weight. The mono-microcaps idiom survives
 * ONLY where data density earns it: table headers, stat labels, row labels. */
h1 { font-size: 1.45rem; margin: 0; color: var(--bright); letter-spacing: -.015em; }
h2 {
  font: 650 .95rem/1.35 var(--font-ui); margin: 0; color: var(--bright);
  letter-spacing: -.01em;
}
h3 {
  font: 600 .85rem/1.4 var(--font-ui); margin: 0 0 .5rem; color: var(--ink);
}
a { color: var(--accent); }

/* ---------------------------------------------------------------- shell */

.topbar {
  display: flex; align-items: center; gap: .9rem;
  padding: .55rem 1rem;
  background: var(--surface); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 30;
}
.brand { display: inline-flex; align-items: center; text-decoration: none; }
.brand img { height: 19px; width: auto; display: block; }
/* The wordmark PNG's "CROWD" is white (drawn for the dark ground); on the
 * light default, flip lightness while keeping the CURVE hues (invert +
 * hue-rotate round trip). User call 2026-09-28: the logo stays, text-lockup
 * wordmark rejected. */
.brand img { filter: invert(1) hue-rotate(180deg) saturate(1.35); }
html[data-theme="dark"] .brand img { filter: none; }

.theme-toggle {
  width: 36px; height: 32px; min-height: 32px; padding: 0; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: none;
  border-radius: var(--radius-s); color: var(--muted); cursor: pointer;
}
.theme-toggle:hover { color: var(--bright); background: var(--pill-bg); }
.theme-toggle .moon { display: none; }
html[data-theme="dark"] .theme-toggle .moon { display: block; }
html[data-theme="dark"] .theme-toggle .sun { display: none; }

.signin { font-size: .82rem; color: var(--muted); text-decoration: none; white-space: nowrap; }
.signin:hover { color: var(--accent); }

/* Small screens: the tabs scroll sideways instead of crushing the layout.
   min-width: 0 is the load-bearing line -- without it a flex item's implicit
   min-width:auto equals its full content width, so the 7-link nav refused to
   shrink, pushed the topbar past the viewport, and forced phones to zoom out
   with the header cut off. */
@media (max-width: 720px) {
  .topbar { gap: .7rem; }
  .brand img { height: 15px; }
  .brand { flex: 0 0 auto; }
  .team-tag { display: none; }
  .mainnav {
    gap: .85rem; overflow-x: auto; scrollbar-width: none;
    min-width: 0; flex: 1 1 0;
  }
  .mainnav::-webkit-scrollbar { display: none; }
  .mainnav a { white-space: nowrap; }
}
.crumbs { flex: 1; font-size: .82rem; }
.crumbs a { text-decoration: none; color: var(--muted); }
.crumbs a:hover { color: var(--accent); }

/* Navigation -- same idiom as the trader UI header: quiet links, teal active. */
/* Flat nav: the active link is a filled pill, not an underline. */
.mainnav { display: flex; gap: .35rem; }
.mainnav a {
  color: var(--muted); text-decoration: none; font-size: .82rem; font-weight: 600;
  padding: .3rem .75rem; border-radius: 999px;
}
.mainnav a:hover { color: var(--bright); background: var(--pill-bg); }
.mainnav a.active { color: var(--accent); background: var(--accent-soft); }
.team-tag {
  color: var(--muted); font-size: .78rem;
  background: var(--pill-bg); border: none; border-radius: 999px; padding: .18rem .7rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 180px;
}
.spacer { flex: 1; }

main { padding: 1rem; max-width: 1280px; margin: 0 auto; }
main.builder { padding-bottom: calc(76px + env(safe-area-inset-bottom)); }

.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1rem; margin-bottom: 1rem;
}
.head-fields { flex: 1; min-width: 0; }
.title-input {
  width: 100%; border: none; background: transparent; color: var(--bright);
  font-size: 1.3rem; font-weight: 700; letter-spacing: -.01em;
  padding: .15rem 0; min-height: auto;
}
.title-input:focus { outline: none; border-bottom: 2px solid var(--accent); box-shadow: none; }
.title-input::placeholder { color: var(--faint); }
.head-sub { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .3rem; }
.head-sub input {
  width: auto; flex: 1 1 120px; max-width: 190px;
  font-size: .82rem; min-height: 32px; padding: .3rem .5rem;
}
.head-actions { display: flex; align-items: center; gap: .5rem; }

/* ---------------------------------------------------------------- columns */

.columns { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem; align-items: start; }
/* Grid items default to min-width:auto and can force the track wider than the
 * viewport; zero it so no panel content can ever blow out the phone layout. */
.columns > * { min-width: 0; }
@media (min-width: 980px) {
  .columns { grid-template-columns: minmax(380px, 5fr) minmax(420px, 6fr); }
  .col-stats { position: sticky; top: 60px; }
}

/* ---------------------------------------------------------------- panels */

.panel {
  background: var(--surface);
  border: 1px solid var(--card-line);
  border-radius: var(--radius);
  padding: 1rem 1.15rem;
  margin-bottom: 1.1rem;
}
.col-inputs .panel:last-child { margin-bottom: 0; }
.panel-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: .75rem; margin-bottom: .6rem;
}
.panel-total { font: 500 .76rem/1.5 var(--font-mono); color: var(--faint); }
.panel-note { color: var(--faint); font-size: .75rem; margin: .45rem 0 0; }
.panel-foot {
  display: flex; gap: .5rem; align-items: center;
  margin-top: .7rem; padding-top: .7rem; border-top: 1px solid var(--line);
}
.panel-foot select { flex: 1 1 40%; min-height: 32px; font-size: .78rem; }
.panel-foot input { flex: 1 1 30%; min-height: 32px; font-size: .78rem; }
.panel-foot button { min-height: 32px; font-size: .78rem; padding: .2rem .8rem; }
.panel-foot .tpl-del {
  flex: 0 0 auto; padding: .2rem .5rem; color: var(--faint);
  background: transparent; border-color: transparent; font-size: .85rem;
}
.panel-foot .tpl-del:hover { color: var(--bad); border-color: var(--line-strong); background: transparent; }

/* ---------------------------------------------------------------- inputs */

label { display: block; font-size: .75rem; color: var(--muted); font-weight: 500; }
input, select {
  /* min-width:0 matters: as flex/grid items the default min-width:auto stops
   * these from shrinking below their intrinsic width -- a select's longest
   * option can then push the whole page wider than a phone screen. */
  width: 100%; min-width: 0; min-height: var(--control-h);
  padding: .4rem .6rem; margin-top: .15rem;
  font-family: var(--font-ui); font-size: .9rem;
  color: var(--ink);
  background: var(--sunken);
  /* Flat: the fill IS the field; the border only appears on focus. */
  border: 1px solid transparent; border-radius: var(--radius-s);
  transition: border-color .12s ease, background .12s ease;
}
input:focus, select:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
/* iOS zooms any focused input under 16px -- phones only, where it matters. */
@media (max-width: 720px) {
  input, select { font-size: 16px; min-height: var(--tap); }
}
input::placeholder { color: var(--faint); opacity: .8; }
input[type="number"] { font-family: var(--font-mono); font-size: .84rem; }
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
input[type="date"] { color-scheme: light; }
html[data-theme="dark"] input[type="date"] { color-scheme: dark; }

/* Flat buttons: solid fills, no border texture. The border slot is kept (as
 * transparent) so ghost buttons and sizing stay consistent. */
button {
  min-height: var(--control-h); padding: .4rem 1rem;
  font-size: .82rem; font-family: var(--font-ui); font-weight: 600;
  letter-spacing: .01em;
  border-radius: var(--radius-s); border: 1px solid transparent;
  background: var(--pill-bg); color: var(--ink); cursor: pointer;
  transition: background .12s ease, border-color .12s ease, color .12s ease;
}
@media (max-width: 720px) { button { min-height: var(--tap); } }
button:hover { background: var(--raised); }
/* Primary action: solid bold teal. THE flat statement piece. */
button.primary, .button.primary {
  background: var(--accent); border-color: transparent; color: var(--btn-primary-ink);
  text-decoration: none;
}
button.primary:hover { background: var(--btn-primary-hover); }
button.primary:active { filter: brightness(.9); }
button.ghost { background: transparent; border-color: var(--line-strong); color: var(--muted); }
button.ghost:hover { color: var(--accent); border-color: var(--accent); background: transparent; }
button.link {
  background: none; border: none; color: var(--muted);
  padding: 0; min-height: auto; font-weight: 500;
}
button.link:hover { color: var(--accent); background: none; }
.button { display: inline-flex; align-items: center; padding: .5rem 1rem; border-radius: var(--radius-s); }
form.inline { display: inline; }

/* ---------------------------------------------------------------- rows */

.rows { display: flex; flex-direction: column; gap: .3rem; }
.row { display: flex; align-items: center; gap: .4rem; }
.row .grow { flex: 1 1 auto; min-width: 0; }
.row .amt { flex: 0 0 92px; text-align: right; }
.row .qty { flex: 0 0 64px; text-align: right; }
.row .qty-wide { flex: 0 0 110px; }
.row .allin {
  flex: 0 0 58px; text-align: right;
  font-size: .75rem; color: var(--faint); font-variant-numeric: tabular-nums;
}
.row .x {
  flex: 0 0 30px; min-height: 30px; padding: 0;
  border: none; background: none; color: var(--faint);
  font-size: 1.05rem; line-height: 1; opacity: 0;
  transition: opacity .12s;
}
.row .x:hover { color: var(--bad); background: none; }
.row:hover .x, .row:focus-within .x { opacity: .9; }
@media (hover: none) { .row .x { opacity: .85; } }
.row-labels {
  font: 500 .6rem/1.5 var(--font-mono); color: var(--faint);
  text-transform: uppercase; letter-spacing: .08em; margin-bottom: .1rem;
}
.row-labels span { display: block; }
.row-labels .grow { flex: 1 1 auto; }

.inline-pct { display: flex; align-items: center; gap: .4rem; font-size: .78rem; }
/* Adorned inputs: the box is the WRAPPER, the input inside is naked. The $ and
 * % live inside the field instead of floating next to it -- the formatting the
 * user asked for, done once here for every money and percent field. */
.money-wrap, .pct-wrap {
  display: flex; align-items: center;
  background: var(--sunken); border: 1px solid transparent;
  border-radius: var(--radius-s); margin-top: .15rem; min-height: var(--control-h);
}
@media (max-width: 720px) { .money-wrap, .pct-wrap { min-height: var(--tap); } }
.money-wrap > span, .pct-wrap > span {
  color: var(--faint); font-size: .85rem; user-select: none;
}
.money-wrap > span { padding-left: .55rem; }
.pct-wrap > span { padding-right: .55rem; }
.money-wrap > input, .pct-wrap > input {
  border: none; background: transparent; margin: 0; min-height: auto;
  padding: .4rem .55rem; width: 100%;
  font-family: var(--font-mono); font-size: .84rem;
}
.pct-wrap > input { text-align: right; padding-right: .2rem; }
.money-wrap > input { padding-left: .25rem; }
.money-wrap > input:focus, .pct-wrap > input:focus { outline: none; box-shadow: none; }
.money-wrap:focus-within, .pct-wrap:focus-within {
  border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft);
}
/* Compact variants inside rows and panel headers. */
.row .money-wrap, .row .pct-wrap { margin-top: 0; min-height: auto; }
.row .money-wrap > input { text-align: right; }
.pct-wrap.compact { margin-top: 0; min-height: 30px; width: 92px; }
.pct-wrap.compact > input { padding: .25rem .2rem .25rem .4rem; }
.hint { color: var(--faint); font-size: .75rem; font-weight: 400; }

/* ---------------------------------------------------------------- deal */

.seg {
  display: flex; gap: 4px; padding: 4px;
  background: var(--sunken); border: 1px solid var(--line);
  border-radius: var(--radius-s);
  margin-bottom: .8rem;
}
.seg-btn {
  flex: 1; min-height: 34px; padding: .3rem .4rem;
  font-size: .75rem; font-weight: 600;
  border: 1px solid transparent; border-radius: 5px;
  background: transparent; color: var(--faint);
}
.seg-btn:hover { color: var(--ink); background: transparent; }
form[data-deal="guarantee"]  .seg-btn[data-kind="guarantee"],
form[data-deal="nbor"]       .seg-btn[data-kind="nbor"],
form[data-deal="milestones"] .seg-btn[data-kind="milestones"] {
  background: var(--raised); color: var(--bright);
  border-color: var(--line-strong);
}
.deal-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: .6rem; }

/* Offer sheet panel: fixed two columns so the free-text deal points line up. */
.offer-grid { grid-template-columns: 1fr 1fr; }
.offer-grid .span-2, .deal-grid .span-2 { grid-column: 1 / -1; }
.signatory-box { margin-top: .8rem; border-top: 1px solid var(--line); padding-top: .6rem; }
.signatory-box summary { cursor: pointer; font-size: .78rem; color: var(--muted); font-weight: 600; }
.signatory-box .deal-grid { margin-top: .7rem; }
.sig-remedy { display: block; margin-top: .6rem; }
.sig-remedy textarea {
  width: 100%; margin-top: .15rem; padding: .45rem .55rem;
  font: inherit; font-size: .82rem; color: var(--ink);
  background: var(--sunken); border: 1px solid var(--line-strong);
  border-radius: var(--radius-s); resize: vertical;
}
.sig-remedy textarea:focus { outline: none; border-color: var(--accent); }

.only-nbor, .only-milestones { display: none; }
form[data-deal="nbor"] .only-nbor { display: block; }
form[data-deal="nbor"] p.only-nbor { display: block; }
form[data-deal="milestones"] .only-milestones { display: block; }

/* ---------------------------------------------------------------- stats */

.stat-cards {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: .6rem;
  margin-bottom: .9rem;
}
@media (min-width: 560px) { .stat-cards { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 980px) { .stat-cards { grid-template-columns: repeat(2, 1fr); } }
.stat-card {
  background: var(--sunken); border: none;
  border-radius: var(--radius); padding: .8rem .9rem;
  display: flex; flex-direction: column; gap: .1rem;
}
.stat-label {
  font: 600 .6rem/1.5 var(--font-mono); color: var(--faint);
  text-transform: uppercase; letter-spacing: .09em;
}
.stat-value {
  font: 600 1.35rem/1.25 var(--font-mono); letter-spacing: -.02em;
  color: var(--bright);
}
.stat-sub { font-size: .72rem; color: var(--faint); }

.callout {
  border-radius: var(--radius); padding: .6rem .8rem;
  font-size: .8rem; margin-bottom: .9rem;
  border: 1px solid var(--line);
}
.callout.deal { background: var(--surface); color: var(--muted); }
.callout.warn {
  background: var(--warn-soft); color: var(--warn);
  border-color: rgba(210, 153, 34, .35); font-weight: 550;
}

.totals-row {
  display: flex; flex-wrap: wrap; gap: .4rem 1.1rem;
  color: var(--faint); font-size: .75rem; font-variant-numeric: tabular-nums;
  padding: 0 .2rem;
}

.empty-stats { text-align: center; color: var(--faint); padding: 2.2rem 1rem; }
.empty-stats .button { margin-top: .8rem; }

/* ---------------------------------------------------------------- chart */

.chart-panel { padding-bottom: .6rem; }
.profit-chart { width: 100%; height: auto; display: block; }
.profit-chart .curve { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; }
.profit-chart .zero { stroke: var(--line-strong); stroke-width: 1; stroke-dasharray: 4 4; }
.profit-chart .guide { stroke-width: 1; stroke-dasharray: 2 3; opacity: .5; }
.profit-chart .guide.be { stroke: var(--good); }
.profit-chart .guide.kink { stroke: var(--kink); }
.profit-chart .guide.ms { stroke: var(--ms); }
.profit-chart .dot.be { fill: var(--good); }
.profit-chart .dot.kink { fill: var(--kink); }
.profit-chart .dot.ms { fill: var(--ms); }
.profit-chart .axis { fill: var(--faint); font-size: 11px; }
.profit-chart .axis.end { text-anchor: end; }
.chart-legend {
  display: flex; flex-wrap: wrap; gap: .3rem 1rem;
  font-size: .72rem; color: var(--faint); margin-top: .4rem;
}
.dot-i {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  margin-right: .35rem; vertical-align: baseline;
}
.dot-i.be { background: var(--good); }
.dot-i.kink { background: var(--kink); }
.dot-i.ms { background: var(--ms); }

/* ---------------------------------------------------------------- slide */

.scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table.slide { width: 100%; border-collapse: collapse; font-size: .76rem; }
table.slide th, table.slide td {
  padding: .35rem .45rem; text-align: right; white-space: nowrap;
  border-bottom: 1px solid var(--line); font-variant-numeric: tabular-nums;
}
table.slide th:first-child, table.slide td:first-child { text-align: left; }
table.slide th { color: var(--faint); font: 500 .6rem/1.5 var(--font-mono);
  text-transform: uppercase; letter-spacing: .07em; }
table.slide td { font-family: var(--font-mono); font-size: .74rem; }
table.slide tbody tr:nth-child(odd) { background: var(--zebra); }  /* trader-UI zebra */
table.slide tbody tr:hover { background: var(--raised); }
table.slide tr.binds td { background: var(--warn-soft); }

/* ---------------------------------------------------------------- list */

/* Bookings table -- the trader-dashboard idiom: dense, zebra, tabular figures.
 * Whole row navigates via data-href (app.js); cursor says so. */
.table-panel { padding: .35rem .5rem; }
table.data { width: 100%; border-collapse: collapse; font-size: .82rem; }
table.data th, table.data td {
  padding: .5rem .6rem; text-align: left; white-space: nowrap;
  border-bottom: 1px solid var(--line);
}
table.data th {
  color: var(--faint); font: 500 .62rem/1.5 var(--font-mono);
  text-transform: uppercase; letter-spacing: .08em;
}
table.data th.num, table.data td.num { text-align: right; }
table.data td.num { font-family: var(--font-mono); font-size: .78rem; }
table.data tbody tr:nth-child(odd) { background: var(--zebra); }
table.data tbody tr:last-child td { border-bottom: none; }
table.data tbody tr[data-href] { cursor: pointer; }
table.data tbody tr:hover { background: var(--raised); }
table.data td a { color: var(--bright); font-weight: 600; text-decoration: none; }
table.data td a:hover { color: var(--accent); }

/* Status chips: a dot and a mono label, hairline boxed. Filled candy pills
   were the loudest "AI made this" tell in every table. Variant classes below
   set only the COLOR; the dot picks it up via currentColor. */
.pill {
  display: inline-flex; align-items: center; gap: .38em;
  padding: .2rem .6rem .2rem .5rem; border-radius: 999px;
  /* Flat: a solid tint drawn from the status color itself -- variant
   * classes still set only the COLOR, the fill follows automatically. */
  border: none;
  background: color-mix(in srgb, currentColor 12%, transparent);
  font: 600 .64rem/1.5 var(--font-mono);
  color: var(--faint); text-transform: uppercase; letter-spacing: .06em;
  white-space: nowrap;
}
.pill::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; flex: 0 0 auto;
}
/* Deal pipeline stages. Color earns its place: green = money confirmed,
   red = dead, amber = in motion, purple = public-facing milestones. */
.status-staged { color: var(--muted); }
.status-initiated { color: var(--faint); }
.status-sent { color: var(--accent); }
.status-negotiating { color: var(--warn); }
.status-confirmed, .status-on_sale { color: var(--good); }
.status-rejected { color: var(--bad); }
.status-tba { color: var(--muted); }
.status-announced { color: var(--ms); }

/* A tracked show's own status (Shows tab) -- distinct slugs from the deal
   pipeline above (onsale/cancelled/completed have no underscore and share no
   class with 'on_sale'/'rejected'), same color language. */
.status-onsale { color: var(--accent); }
.status-postponed { color: var(--warn); }
.status-cancelled { color: var(--bad); }
.status-completed { color: var(--muted); }

.stage-chips { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: .8rem; }
.stage-chips .chip {
  min-height: 30px; padding: .2rem .8rem; font-size: .74rem; font-weight: 600;
  background: var(--pill-bg); border: none;
  border-radius: 999px; color: var(--muted);
}
.stage-chips .chip.on { color: var(--accent); background: var(--accent-soft); }

.stage-select {
  width: auto; min-height: 34px; padding: .25rem .5rem;
  font-size: .78rem; font-weight: 600; margin-top: 0;
}
.stage-select.stage-error { border-color: var(--bad); }

.thumb {
  min-height: 32px; padding: .2rem .6rem; font-size: .82rem;
  border-radius: 999px;
}
.thumb.on { border-color: var(--accent); color: var(--accent); background: rgba(79, 209, 197, .08); }

/* ---------------------------------------------------------------- misc */

.discuss { margin-top: 1rem; }
.discuss summary { cursor: pointer; font-weight: 600; font-size: .85rem; color: var(--muted); }
.discuss summary:hover { color: var(--ink); }
.comments { list-style: none; padding: 0; margin: .6rem 0; }
.comments li { padding: .5rem 0; border-bottom: 1px solid var(--line); }
.comments p { margin: .15rem 0 0; }
.comments strong { color: var(--bright); }
.comment-box { display: flex; gap: .5rem; margin-top: .6rem; }
.plain-list { list-style: none; padding: 0; margin: .5rem 0 0; }
.plain-list li { padding: .4rem 0; border-bottom: 1px solid var(--line); font-size: .85rem; }
.plain-list li:last-child { border-bottom: none; }

.card.narrow, .narrow {
  max-width: 420px; margin: 3rem auto;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.6rem;
}
.narrow h1 { margin-bottom: .3rem; }
.narrow .muted, .muted { color: var(--muted); }
.small { font-size: .78rem; }
.block { display: block; width: 100%; text-align: center; margin-top: .8rem; }
.list { list-style: none; padding: 0; margin: .5rem 0; }
.list li { padding: .45rem 0; border-bottom: 1px solid var(--line); }
.stack { display: flex; flex-direction: column; gap: .4rem; margin-top: 1.2rem; }

.good { color: var(--good); }
.bad { color: var(--bad); }
strong.good, .stat-value.good { color: var(--good); }
strong.bad, .stat-value.bad { color: var(--bad); }

.saving { opacity: .5; transition: opacity .15s; }
.flash-saved { animation: flash .9s ease-out; }
@keyframes flash { 0% { box-shadow: 0 0 0 3px var(--good); } 100% { box-shadow: none; } }

/* Bottom tab bar -- the phone's primary navigation. Desktop keeps the
   topbar tabs; below 720px those hide and this takes over. */
.bottomnav { display: none; }
@media (max-width: 720px) {
  .mainnav { display: none; }
  .bottomnav {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
    background: var(--surface); border-top: 1px solid var(--line);
    padding: .3rem 0 calc(.3rem + env(safe-area-inset-bottom));
    transform: translateZ(0);  /* own compositor layer: no repaint lag on scroll */
  }
  /* Mobile browsers shift fixed elements for a frame while their toolbar
     collapses; extend our own background past the viewport edge so that
     shift reveals bar, never page. */
  .bottomnav::after {
    content: ""; position: absolute; top: 100%; left: 0; right: 0;
    height: 80px; background: var(--surface);
  }
  .bottomnav a {
    flex: 1; display: flex; flex-direction: column; align-items: center;
    gap: 2px; padding: .25rem 0; text-decoration: none;
    color: var(--faint); font-size: .62rem; font-weight: 600; letter-spacing: .02em;
  }
  .bottomnav a svg {
    width: 21px; height: 21px; fill: none; stroke: currentColor;
    stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
  }
  .bottomnav a.active { color: var(--accent); }
}

/* Mobile quickbar: the two decision numbers, always visible. */
.quickbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  transform: translateZ(0);
  display: flex; gap: 1.6rem; align-items: center;
  padding: .5rem 1.1rem calc(.5rem + env(safe-area-inset-bottom));
  background: var(--surface); border-top: 1px solid var(--line);
}
.qb-item { display: flex; flex-direction: column; line-height: 1.15; }
.qb-item span {
  font-size: .64rem; color: var(--faint);
  text-transform: uppercase; letter-spacing: .06em;
}
.qb-item strong { font: 600 1rem/1.3 var(--font-mono); color: var(--bright); }
.qb-item strong.good { color: var(--good); }
.qb-item strong.bad { color: var(--bad); }
@media (min-width: 980px) { .quickbar { display: none; } }

/* ---------------------------------------------------------------- shows */

.stat-cards.wide { grid-template-columns: repeat(2, 1fr); }
@media (min-width: 700px) { .stat-cards.wide { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1100px) { .stat-cards.wide { grid-template-columns: repeat(6, 1fr); } }
.stat-sub em { font-style: normal; color: var(--faint); }

.sales-chart { width: 100%; height: auto; display: block; }
.sales-chart .actual { fill: none; stroke: var(--accent); stroke-width: 2.4; stroke-linejoin: round; }
.sales-chart .obs-dot { fill: var(--accent); }
.sales-chart .path { fill: none; stroke-width: 1.4; }
.sales-chart .path.so { stroke: var(--faint); }
.sales-chart .path.safe { stroke: var(--muted); stroke-dasharray: 5 4; }
.sales-chart .path.be { stroke: var(--good); stroke-dasharray: 5 4; }
.sales-chart .path.projection { stroke: var(--accent); stroke-dasharray: 2 4; opacity: .7; }
.sales-chart .today { stroke: var(--line-strong); stroke-width: 1; }
.sales-chart .cap-line { stroke: var(--line); stroke-width: 1; }
.sales-chart .axis { fill: var(--faint); font-size: 11px; }
.sales-chart .axis.end { text-anchor: end; }
.sales-chart .axis.ylab { text-anchor: end; font-variant-numeric: tabular-nums; }
.sales-chart .axis.xlab { text-anchor: middle; }
.sales-chart .tick { stroke: var(--line); stroke-width: 1; opacity: .55; }
.sales-chart .axis-line { stroke: var(--line-strong); stroke-width: 1; }

.line-i {
  display: inline-block; width: 16px; height: 0; border-top: 2px solid var(--faint);
  margin-right: .35rem; vertical-align: middle;
}
.line-i.actual-i { border-color: var(--accent); }
.line-i.be-i { border-top-style: dashed; border-color: var(--good); }
.line-i.safe-i { border-top-style: dashed; border-color: var(--muted); }
.line-i.so-i { border-color: var(--faint); }
.line-i.proj-i { border-top-style: dotted; border-color: var(--accent); }

.obs-entry {
  display: flex; gap: .6rem; align-items: flex-end; flex-wrap: wrap;
  margin-bottom: .6rem;
}
.obs-entry label { flex: 1 1 130px; max-width: 180px; }
.obs-entry button { flex: 0 0 auto; }

.paste-box { margin: .4rem 0 .8rem; }
.paste-box summary { cursor: pointer; font-size: .8rem; color: var(--muted); }
.paste-box textarea {
  width: 100%; margin: .5rem 0; padding: .5rem .6rem;
  font: 13px/1.5 ui-monospace, SFMono-Regular, Consolas, monospace;
  color: var(--ink); background: var(--sunken);
  border: 1px solid var(--line-strong); border-radius: var(--radius-s);
  resize: vertical;
}
.paste-box textarea:focus { outline: none; border-color: var(--accent); }

table.data.compact { font-size: .78rem; }
table.data.compact th, table.data.compact td { padding: .35rem .5rem; }
.x-obs {
  min-height: 26px; min-width: 26px; padding: 0;
  border: none; background: none; color: var(--faint); font-size: 1rem;
}
.x-obs:hover { color: var(--bad); background: none; }

/* Track-sales popover on the booking page */
details.promote { position: relative; }
details.promote > summary {
  list-style: none; cursor: pointer;
  min-height: var(--tap); display: inline-flex; align-items: center;
  padding: .45rem 1rem; font-size: .85rem; font-weight: 600;
  border: 1px solid var(--line-strong); border-radius: var(--radius-s);
  color: var(--ink); background: var(--surface);
}
details.promote > summary:hover { background: var(--raised); }
details.promote[open] > summary { border-color: var(--accent); }
.promote-pop {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 50;
  width: 240px; padding: .8rem;
  background: var(--surface); border: 1px solid var(--card-line);
  border-radius: var(--radius); box-shadow: var(--shadow-pop);
  display: flex; flex-direction: column; gap: .5rem;
}
.ghost-link { color: var(--accent); border: 1px solid var(--line-strong); background: transparent; }

/* ---------------------------------------------------------------- about */

.about { max-width: 1200px; margin: 0 auto; }

.eyebrow {
  display: block; font-size: .7rem; font-weight: 600;
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  text-transform: uppercase; letter-spacing: .3em;
  color: var(--accent); margin-bottom: .9rem;
}

.about-hero { position: relative; padding: 3rem 0 2.6rem; text-align: center; }
/* Engineering-paper dot grid behind the hero, faded at the edges. Painted on a
   pseudo-element so the text sits above it in paint order. */
.about-hero::before {
  content: ""; position: absolute; inset: 0;
  background-image: radial-gradient(var(--line-strong) 1px, transparent 1px);
  background-size: 24px 24px;
  -webkit-mask-image: radial-gradient(ellipse 68% 85% at 50% 42%, #000 15%, transparent 72%);
  mask-image: radial-gradient(ellipse 68% 85% at 50% 42%, #000 15%, transparent 72%);
  opacity: .55;
}
.about-hero > * { position: relative; }
.about-hero .eyebrow { margin-bottom: 1.1rem; }
.about-hero h1 {
  font-size: clamp(2.2rem, 6vw, 3.4rem); font-weight: 800;
  letter-spacing: -.035em; line-height: 1.06; margin-bottom: 1.2rem;
}

/* Split hero (Cymbal idiom): copy left, the live product visual right.
   Big words, ONE line under them, two actions. Nothing else. */
.hero-split {
  display: grid; grid-template-columns: 1fr; gap: 2rem;
  align-items: center; text-align: left; padding: 3.2rem 0 3rem;
}
@media (min-width: 980px) { .hero-split { grid-template-columns: 5fr 4fr; gap: 3rem; } }
.hero-split h1 {
  font-size: clamp(2.6rem, 6.5vw, 4.2rem); margin-bottom: 1.1rem;
}
.hero-split .about-lede {
  margin: 0 0 1.6rem; max-width: 34rem;
  font-size: clamp(1.05rem, 2.6vw, 1.25rem); color: var(--muted);
}
/* (.hero-chip dot-badge removed 2026-09-30 -- "looks AI"; the category
   reads as a plain .eyebrow now.) */
@media (max-width: 720px) {
  /* Phones: the hero starts near the top instead of importing desktop's
     runway -- "empty up top" caught by the user. */
  .hero-split { padding: 1rem 0 2.2rem; gap: 1.6rem; }
  .about-hero .eyebrow { margin-bottom: .7rem; }
}
/* Drift field: the model's curve family flowing behind the hero, layered
   on the engineering dot-grid -- demand curves on graph paper, alive.
   Adapted from the floating-paths concept; ours because the paths ARE the
   product (server-generated Kumaraswamy shapes). Each curve = faint base
   line + a brighter streak whose dash loops along the path. Decorative:
   the global reduced-motion block freezes it. */
.about-hero > .hero-drift {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none;
  -webkit-mask-image: radial-gradient(ellipse 68% 85% at 50% 42%, #000 15%, transparent 72%);
  mask-image: radial-gradient(ellipse 68% 85% at 50% 42%, #000 15%, transparent 72%);
}
.hero-drift polyline { fill: none; stroke: var(--accent); stroke-width: 1; }
.hero-drift .drift-base { opacity: .07; }
.hero-drift .drift-streak {
  opacity: .3; stroke-width: 1.4;
  stroke-dasharray: 10 90;   /* one streak per loop; pathLength=100 */
  animation: drift-flow 30s linear infinite;
}
.hero-drift .alt { stroke: var(--ms); }
.hero-drift .drift-base.alt { opacity: .055; }
.hero-drift .drift-streak.alt { opacity: .22; }
@keyframes drift-flow { to { stroke-dashoffset: -100; } }

.hero-cta { display: flex; gap: .6rem; flex-wrap: wrap; }
.hero-cta .button { min-height: var(--tap); padding: .6rem 1.4rem; font-size: .9rem; }
.hero-visual { margin: 0; padding: 1rem 1.1rem .8rem; }
.hero-visual .eyebrow-mini { display: block; margin-bottom: .6rem; }
.heuristic-strip { scroll-margin-top: 70px; }
/* Emphasis = one solid signal color with an annotation-style underline.
   Deliberately NOT a gradient: gradients read soft, a plotted line reads exact. */
.about-hero h1 em {
  font-style: normal; color: var(--accent);
  text-decoration: underline; text-decoration-thickness: 3px;
  text-underline-offset: .18em;
  text-decoration-color: var(--accent-soft);
}
.about-lede {
  font-size: clamp(.98rem, 2.4vw, 1.12rem); color: var(--muted);
  max-width: 620px; margin: 0 auto; line-height: 1.7;
}
.about-lede strong { color: var(--bright); font-weight: 600; }

.feature-grid {
  display: grid; grid-template-columns: 1fr; gap: .8rem; margin-bottom: 2.4rem;
}
@media (min-width: 700px) { .feature-grid { grid-template-columns: repeat(3, 1fr); } }
.feature-card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.3rem 1.2rem 1.2rem;
  transition: border-color .15s ease;
}
.feature-card:hover { border-color: var(--line-strong); }
.feature-top {
  display: flex; align-items: flex-start; justify-content: space-between;
  margin-bottom: 1.6rem;
}
.feature-glyph { width: 30px; height: 30px; fill: var(--accent); stroke: var(--accent); }
.feature-num {
  font-size: .74rem; font-weight: 600; letter-spacing: .18em;
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  color: var(--faint); font-variant-numeric: tabular-nums;
}
.feature-card h3 {
  font-size: 1.06rem; font-weight: 700; letter-spacing: -.015em;
  color: var(--bright); margin: 0 0 .45rem;
}
.feature-card p { font-size: .86rem; color: var(--muted); line-height: 1.6; margin: 0; }

.about-chart-panel { padding: 1.4rem 1.3rem 1rem; margin-bottom: 1rem; }
.chart-head { margin-bottom: 1.1rem; }
.chart-head .eyebrow { margin-bottom: .4rem; }
.chart-head h2 {
  font-size: clamp(1.2rem, 3.4vw, 1.55rem); font-weight: 800;
  letter-spacing: -.025em; color: var(--bright); margin: 0;
}
.about-chart { width: 100%; height: auto; display: block; }
.about-chart .curve-club { fill: none; stroke: var(--accent); stroke-width: 2.6; }
.about-chart .curve-head { fill: none; stroke: var(--ms); stroke-width: 2.6; }
.about-chart .naive { stroke: var(--faint); stroke-width: 1.2; stroke-dasharray: 6 5; }
.about-chart .gridline { stroke: var(--line); stroke-width: 1; }
.about-chart .axis { fill: var(--faint); font-size: 11px; }
.about-chart .axis.end { text-anchor: end; }
.about-legend { margin-top: .7rem; }
.about-caption { color: var(--faint); font-size: .8rem; margin-top: .55rem; line-height: 1.55; }

.heuristic-strip {
  text-align: center; padding: 2.4rem 1rem;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  margin: 1.6rem 0;
}
/* Dictionary-entry treatment: says "we built a precise instrument" without
   exposing any of the actual (proprietary) math. */
.dict-word {
  display: block;
  font: 700 clamp(1.3rem, 4.2vw, 1.9rem)/1.3 ui-monospace, SFMono-Regular, Consolas, monospace;
  color: var(--bright); letter-spacing: -.01em; margin-bottom: .15rem;
}
.dict-word em { font-style: italic; font-weight: 400; font-size: .55em; color: var(--faint); }
.dict-def {
  display: block; font-size: .92rem; font-style: italic;
  color: var(--muted); margin-bottom: 1rem;
}
.heuristic-copy {
  font-size: .86rem; color: var(--muted);
  max-width: 540px; margin: 0 auto; line-height: 1.7;
}
.heuristic-copy strong { color: var(--bright); }

/* ------------- v2 about (2026-09-28): one story, screens do the explaining.
   Each section: small label, one headline, 1-3 sentences, the product.
   Umpf comes from MOTION (scroll reveals + the hero curve drawing itself),
   never from background noise -- flat keeps its white-space. */
.flow-link {
  display: inline-block; margin-top: .9rem;
  font-weight: 650; font-size: .88rem; color: var(--accent); text-decoration: none;
}
.flow-link:hover { text-decoration: underline; }
.deal-points {
  list-style: none; padding: 0; margin: .9rem 0 .6rem;
  display: flex; flex-direction: column; gap: .45rem;
}
.deal-points li {
  position: relative; padding-left: 1.1rem;
  font-weight: 550; color: var(--ink); font-size: .9rem;
}
.deal-points li::before {
  content: ""; position: absolute; left: 0; top: .42em;
  width: 7px; height: 7px; border-radius: 2px; background: var(--accent);
}
.quiet-note { font-size: .8rem; color: var(--faint); margin: .2rem 0 0; }
.mock-grid { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem .9rem; padding-top: .3rem; }
/* Theater mode: The Curve takes the screen. A full-bleed band that flips to
   the dark terminal palette by re-declaring the tokens locally -- every
   token-driven mini inside re-skins automatically (in dark theme the values
   match the theme, so only the hairline separators read). The band is
   painted by a ::before stretched past the container, so no 100vw scrollbar
   overflow; body overflow-x:clip guards the spill without breaking sticky. */
.curve-sect {
  --bg: #0f1216; --surface: #161b22; --sunken: #0d1014; --raised: #1b2129;
  --ink: #d8dee9; --bright: #e6edf3; --muted: #9fb3c8; --faint: #7d8998;
  --line: #262c36; --line-strong: #30363d;
  --accent: #35c9ba; --accent-soft: #0f2e2b;
  --good: #3fb950; --good-soft: #103b21; --bad: #f85149; --bad-soft: #3d1418;
  --warn: #d29922; --warn-soft: #2d2308; --ms: #a371f7;
  --zebra: #12161c; --pill-bg: #21262d; --card-line: #262c36;
  --shadow-pop: 0 14px 40px -18px rgba(0, 0, 0, .55);
  position: relative; color: var(--ink); text-align: center;
  /* True full-bleed WITHOUT widening the document: the section itself is
     made exactly 100vw (negative margins out, matching padding back in).
     The previous inset:-50vw ::before extended past the viewport, and iOS
     sizes its zoom to the DOCUMENT width regardless of overflow clipping
     -- phones rendered the whole site zoomed out (caught by the user). */
  margin: 0 calc(50% - 50vw) 2.4rem;
  padding: 3rem calc(50vw - 50%) 3.4rem;
  background: var(--bg);
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  min-height: min(80vh, 740px);
  display: flex; flex-direction: column; justify-content: center;
  scroll-margin-top: 70px;
}
/* The hero's engineering dot-grid, echoed once in the theater band -- the
   page's ONE texture, appearing only where the model speaks. Drawn with
   inset:0 INSIDE the section's own box (never past the document edge --
   that is the mobile zoom-out trap). */
.curve-sect::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image: radial-gradient(var(--line-strong) 1px, transparent 1px);
  background-size: 24px 24px;
  -webkit-mask-image: radial-gradient(ellipse 70% 60% at 50% 38%, #000 20%, transparent 70%);
  mask-image: radial-gradient(ellipse 70% 60% at 50% 38%, #000 20%, transparent 70%);
  opacity: .4;
}
.curve-sect > * { position: relative; }
.curve-sect .how-stepper { max-width: 760px; margin-left: auto; margin-right: auto; }
/* Guard the desktop scrollbar sliver (100vw includes it); on html AND body
   so every engine clips the viewport, not just the body box. */
html, body { overflow-x: clip; }
/* Stronger entrance than the generic reveal: the band scales up into place. */
html.js .curve-sect[data-reveal] {
  transform: translateY(44px) scale(.965);
  transition: opacity .7s ease, transform .7s ease;
}
html.js .curve-sect[data-reveal].in { transform: none; }
.curve-title {
  font: 800 clamp(1.5rem, 4vw, 2.1rem)/1.15 var(--font-ui);
  letter-spacing: -.025em; color: var(--bright); margin: .1rem 0 .8rem;
}
.curve-stage { max-width: 720px; margin: 1.6rem auto .4rem; text-align: left; }
.curve-stats {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: .4rem .9rem; padding-top: .5rem;
}
@media (min-width: 640px) { .curve-stats { grid-template-columns: repeat(4, 1fr); } }
.pace-line {
  font: 800 clamp(1.5rem, 4.6vw, 2.3rem)/1.25 var(--font-ui);
  letter-spacing: -.03em; color: var(--bright); margin: 2rem auto 1.4rem;
}
.about-sect { text-align: center; max-width: 620px; margin: 0 auto; padding: 2.2rem 1rem; }
.about-sect p { color: var(--muted); font-size: .95rem; line-height: 1.7; margin: .5rem 0 0; }

/* Scroll reveals -- gated on html.js so a failed script never hides content;
   the global reduced-motion block flattens these to instant. */
html.js [data-reveal] { opacity: 0; transform: translateY(16px); transition: opacity .55s ease, transform .55s ease; }
html.js [data-reveal].in { opacity: 1; transform: none; }
/* The hero curve draws itself: pathLength=100 on the polylines normalizes
   the dash math regardless of pixel length. */
.about-chart.draw .curve-club,
.about-chart.draw .curve-head {
  stroke-dasharray: 100; stroke-dashoffset: 100;
  animation: curve-draw 1.5s ease-out .25s forwards;
}
.about-chart.draw .curve-head { animation-delay: .55s; }
.about-chart.draw polygon,
.about-chart.draw .naive { opacity: 0; animation: curve-fade .7s ease-out 1.4s forwards; }
@keyframes curve-draw { to { stroke-dashoffset: 0; } }
@keyframes curve-fade { to { opacity: 1; } }

/* --------------- story v3 (2026-09-29): THE FOLDER. Each section is a
   full sheet -- copy left + live mini right on ONE card, with its own
   accent (folder-tab color). Sticky does the stacking: sheets pile under
   a fixed point; a small scroll handler sinks and dims the covered sheet
   as the next slides over (scrolling up unpacks). Copy and demo share a
   sheet, so they can never desynchronize. Height caps stay absolute so
   zoomed-out viewports don't gape. */
/* REVERTED to the v2 theaters (2026-09-30, user call: preferred it to the
   folder pile -- the stacking experiment lives on in git history around
   commits f833d3d..bcbbad0). Each section is its own centered unit: copy
   on the page, the mini in a bordered card that rises in as its section
   arrives. The per-section accent palettes and tab edge stay -- the tab
   now sits on the mini card. */
.story { display: block; margin: 0; scroll-margin-top: 70px; }
.sheet-slot { position: relative; }
.sheet {
  position: relative;
  display: grid; grid-template-columns: 1fr; gap: 1.8rem;
  align-items: center; padding: 2.2rem 0;
}
.sheet-copy { min-width: 0; }
.sheet-stage {
  min-width: 0; position: relative;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-pop);
  padding: 1.2rem 1.3rem;
}
@media (min-width: 900px) {
  /* Sections size to their CONTENT -- no screen-filling min-heights, no
     snap. This kills the empty-void bug class at every zoom level for
     good (user hit it three times with viewport-sized sections). */
  .sheet { grid-template-columns: 5fr 6fr; gap: 3.2rem; padding: min(6vh, 56px) 0; }
}
/* The mini card rises in as its section arrives; leaving resets it so it
   replays on the way back. */
html.js .sheet-stage {
  opacity: 0; transform: translateY(32px) scale(.98);
  transition: opacity .6s ease, transform .6s ease;
}
html.js .sheet-stage.on { opacity: 1; transform: none; }
/* (Per-section palettes + tab edge removed 2026-09-30 with the full
   folder revert -- uniform brand teal, as pre-folder.) */
/* Frames grid-stack in ONE cell: the container sizes itself to the card's
   own TALLEST frame and never changes -- no per-frame resize morph (user
   call), and no global fixed height reserving dead space across cards. */
.frames { display: grid; }

/* Particle burst on the About CTAs -- concept adapted from 21st.dev
   (kokonutd's Particle Button), re-cut to tokens + vanilla JS: six accent
   dots scatter from the button's center on click, with a quick squish.
   Decorative, so reduced-motion skips it entirely (see app.js). */
.cc-particle {
  position: fixed; width: 5px; height: 5px; border-radius: 50%;
  background: var(--accent); pointer-events: none; z-index: 90;
  transform: scale(0);
  animation: cc-scatter .6s ease-out forwards;
}
@keyframes cc-scatter {
  0%   { transform: translate(0, 0) scale(0); opacity: 1; }
  40%  { transform: translate(calc(var(--dx) * .6), calc(var(--dy) * .6)) scale(1); }
  100% { transform: translate(var(--dx), var(--dy)) scale(0); opacity: 0; }
}
.hero-cta .primary.button, .closing-tag .primary.button { transition: transform .1s ease; }
.btn-pressed { transform: scale(.94) !important; }
.frame {
  grid-area: 1 / 1;
  display: flex; flex-direction: column; gap: .25rem;
  align-self: start; min-width: 0;
  opacity: 0; transform: translateY(12px);
  transition: opacity .4s ease, transform .4s ease;
}
.frame.on { opacity: 1; transform: none; }
/* (The duplicate mobile .m-mock fallback is gone -- 2026-09-29 v2: the same
   in-flow card serves every viewport, animated everywhere.) */
/* Frame progress: one dot per frame; the active dot stretches into a pill
   that fills with accent over the frame's duration (3.6s -- keep in sync
   with FRAME_MS in app.js). Built by the story engine, so single-frame
   cards never get dots. */
.frame-dots { display: flex; align-items: center; gap: .45rem; margin-top: .7rem; }
.frame-dots i {
  position: relative; width: 7px; height: 7px; border-radius: 999px;
  background: var(--line-strong); cursor: pointer;
  transition: width .25s ease, background .25s ease;
}
/* Invisible hit area: a 7px dot is too small a click target. */
.frame-dots i::before { content: ""; position: absolute; inset: -8px; }
.frame-dots i:hover { background: var(--faint); }
.frame-dots i.on:hover { background: var(--pill-bg); }
.frame-dots i.on {
  width: 30px; background: var(--pill-bg);
  position: relative; overflow: hidden;
}
.frame-dots i.on::after {
  content: ""; position: absolute; inset: 0; border-radius: 999px;
  background: var(--accent); transform-origin: left; transform: scaleX(0);
  animation: dot-fill 5s linear forwards;  /* = FRAME_MS in app.js */
}
@keyframes dot-fill { to { transform: scaleX(1); } }
.frame-tag {
  font: 600 .6rem/1.5 var(--font-mono); letter-spacing: .12em;
  text-transform: uppercase; color: var(--accent); margin-bottom: .5rem;
}
.mock-send {
  align-self: flex-start; margin: .4rem 0;
  background: var(--accent); color: var(--btn-primary-ink);
  font: 600 .8rem/1 var(--font-ui); padding: .55rem 1rem;
  border-radius: var(--radius-s);
}
.mock-stat strong.mock-small { font-size: .9rem; }
.mock-chip.chip-sent { color: var(--accent); }
.mock-chip.chip-good { color: var(--good); }

/* Product-term definitions: sans, foregrounded -- these are features, not
   equations, so no code/mono treatment (user call 2026-09-28). */
.spec-list { margin: 1.1rem 0 .2rem; border-top: 1px solid var(--line); }
.spec {
  display: grid; grid-template-columns: 150px 1fr; gap: 1rem;
  padding: .65rem 0; border-bottom: 1px solid var(--line); align-items: start;
}
.spec strong { font: 650 .9rem/1.45 var(--font-ui); color: var(--bright); }
.spec span { font-size: .88rem; color: var(--muted); line-height: 1.6; }
@media (max-width: 620px) { .spec { grid-template-columns: 1fr; gap: .15rem; } }

/* Section labels sit in the foreground now. */
.feature-num { color: var(--accent); font-weight: 700; }

/* (Promoter Intelligence merged fully into The Curve, 2026-09-29: one
   headline; the dashboard mini's paid-push strip is the payoff.) */

.heuristic-steps {
  display: flex; align-items: center; justify-content: center;
  gap: 1.1rem; flex-wrap: wrap; margin-top: 1.7rem;
}
.hstep { display: flex; flex-direction: column; align-items: center; gap: .32rem; min-width: 150px; }
.hstep-glyph { width: 26px; height: 26px; fill: var(--accent); stroke: var(--accent); margin-bottom: .15rem; }
.hstep-name {
  font: 600 .72rem/1 ui-monospace, SFMono-Regular, Consolas, monospace;
  text-transform: uppercase; letter-spacing: .22em; color: var(--bright);
}
.hstep-sub { font-size: .72rem; color: var(--faint); }
.hstep-arrow { color: var(--faint); font-size: 1.15rem; padding-bottom: 1.4rem; }
@media (max-width: 620px) { .hstep-arrow { display: none; } }

.principles {
  display: flex; flex-wrap: wrap; gap: .5rem; justify-content: center;
  padding-bottom: 2.2rem;
}
.principles .pill {
  font-size: .74rem; font-weight: 600; letter-spacing: .04em;
  color: var(--muted); background: var(--sunken);
  border: 1px solid var(--line-strong); border-radius: 999px;
  padding: .38rem .85rem;
}


/* ------------------------------------------------------------ phone layout
   One breakpoint, real changes: nav moves to the bottom bar (above), the
   builder head stacks with uniform full-width fields, tier rows drop the
   derived all-in column, and dense tables shed their trailing columns. */
@media (max-width: 720px) {
  body { font-size: 15px; }
  main { padding: .75rem .75rem calc(70px + env(safe-area-inset-bottom)); }
  main.builder { padding-bottom: calc(70px + 58px + env(safe-area-inset-bottom)); }
  .quickbar { bottom: calc(53px + env(safe-area-inset-bottom)); padding-bottom: .5rem; }

  .page-head { flex-direction: column; }
  .head-actions { width: 100%; }
  .head-actions [data-action="save"] { flex: 1; }
  .head-sub { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; }
  .head-sub input { max-width: none; width: 100%; }
  .head-sub #f-venue { grid-column: 1 / -1; }

  .row .allin { display: none; }
  /* Keep the desktop 92px: 86px clips the last cent digit of $1,000+ amounts,
   * and hiding the all-in column already freed the space. */

  .panel-foot { flex-wrap: wrap; }
  .panel-foot select { flex: 1 1 100%; }
  .panel-foot input { flex: 1 1 auto; }

  .page-head h1 { font-size: 1.15rem; }
  table.data th:nth-child(n+7), table.data td:nth-child(n+7) { display: none; }
}


/* --------------------------------------------------------------- send offer */
.offer-actions { display: flex; align-items: center; gap: .5rem; }
.send-pop { width: min(340px, 86vw); right: 0; left: auto; }
.send-pop label { margin-top: .5rem; }
.send-pop .send-two { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; }
.send-pop textarea {
  width: 100%; margin-top: .15rem; padding: .45rem .55rem;
  font: inherit; font-size: .82rem; color: var(--ink);
  background: var(--sunken); border: 1px solid var(--line-strong);
  border-radius: var(--radius-s); resize: vertical;
}
.send-pop textarea:focus { outline: none; border-color: var(--accent); }
.send-pop .primary { width: 100%; margin-top: .6rem; }


/* -------------------------------------------------------------- deals tab */
.inbox-search { display: flex; gap: .4rem; }
.inbox-search input { width: 220px; min-height: 32px; font-size: .8rem; margin-top: 0; }
.inbox-search button { min-height: 32px; font-size: .78rem; padding: .2rem .8rem; }
.track-btn { min-height: 28px; font-size: .74rem; padding: .15rem .7rem; }
.track-pick { display: inline-flex; gap: .3rem; align-items: center; }
.track-pick select { width: 200px; min-height: 30px; font-size: .76rem; margin-top: 0; }
.track-pick button { min-height: 30px; font-size: .74rem; padding: .15rem .6rem; }
.pager { display: flex; align-items: center; justify-content: center; gap: 1rem; margin-top: .8rem; }

/* AI status chips reuse the pipeline palette */
.ai-offer_sent { color: var(--accent); }
.ai-negotiating { color: var(--warn); }
.ai-confirmed { color: var(--good); }
.ai-rejected { color: var(--bad); }
.ai-unclear { color: var(--faint); }

.ai-read .ai-summary { color: var(--ink); font-size: .95rem; line-height: 1.55; margin: .5rem 0 .2rem; }

/* Mail: a reading pane, not cards. Hairlines between messages, sender in
   ink, body in comfortable measure -- Gmail idiom, quant-terminal dress. */
.mail-panel { padding: .4rem 1.2rem; }
.mail-list { display: flex; flex-direction: column; }
.mail { padding: .85rem 0; border-bottom: 1px solid var(--line); }
.mail:last-child { border-bottom: none; }
.mail-head { display: flex; align-items: baseline; gap: .7rem; margin-bottom: .35rem; }
.mail-who { font-weight: 600; font-size: .85rem; color: var(--bright); }
.mail.out .mail-who { color: var(--accent); }
.mail-when { font-size: .72rem; color: var(--faint); }
.mail-body {
  white-space: pre-wrap; word-break: break-word;
  font-size: .86rem; line-height: 1.6; color: var(--ink);
  max-width: 72ch;
}
.thread-meta { align-items: center; }
.attach-label { display: flex; align-items: center; gap: .45rem; font-size: .78rem; }
.attach-label .stage-select { margin-top: 0; }

.ai-read { padding: .7rem 1rem; }
.ai-line { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; }
.ai-line .ai-summary { color: var(--ink); font-size: .9rem; line-height: 1.5; flex: 1; min-width: 220px; }
.ai-why { margin: .35rem 0 0; }
.use-suggestion { min-height: 30px; font-size: .74rem; padding: .15rem .7rem; }
@media (max-width: 720px) {
  .inbox-search input { width: 130px; }
  #deals-page table.data th:nth-child(n+7), #deals-page table.data td:nth-child(n+7) { display: revert; }
}


/* ------------------------------------------------------------ connect inbox */
.connect-lede { max-width: 560px; line-height: 1.6; }
.connect-steps { margin: .6rem 0 .9rem; padding-left: 1.2rem; color: var(--muted);
  font-size: .84rem; line-height: 1.7; }
.connect-steps strong, .connect-lede strong { color: var(--ink); }
.connect-form { display: grid; grid-template-columns: 1fr 1fr auto; gap: .6rem;
  align-items: end; max-width: 640px; }
.connect-form .primary { min-height: var(--tap); }
@media (max-width: 720px) { .connect-form { grid-template-columns: 1fr; } }
.danger-link { color: var(--faint); margin-left: .4rem; }
.danger-link:hover { color: var(--bad); }


/* -------------------------------------------------------------- agents tab */
.agent-add, .roster-add { display: flex; gap: .5rem; flex-wrap: wrap; align-items: end; }
.agent-add label { flex: 1 1 160px; }
.agent-add button, .roster-add button { min-height: var(--tap); }
.roster-add { margin-top: .6rem; }
.roster-add input { flex: 1; }
.agent-columns { grid-template-columns: 1fr; }
@media (min-width: 980px) { .agent-columns { grid-template-columns: 2fr 3fr; } }
.plain-list .x-obs { margin-left: .3rem; }
.save-venue-btn { min-height: 32px; font-size: .76rem; padding: .2rem .7rem; flex: 0 0 auto; }
.ai-agent_initiated, .ai-promoter_initiated { color: var(--ms); }
.ai-challenging { color: var(--warn); }
.ai-offer_sent { border-color: var(--accent); color: var(--accent); }


/* Inline icon glyphs sit on the text baseline, tinted by context. */
.tpl-del svg { display: block; }
td svg, .thumb svg { vertical-align: -2px; }


/* --------------------------------------------------------------- ad plan */
.ads-cards { margin-bottom: .7rem; }
.ads-cruise { color: var(--ink); font-size: .9rem; line-height: 1.6; margin: .2rem 0; }
.ads-cruise strong { color: var(--good); }
.ads-phases td { font-family: var(--font-ui); }
.ads-phases td.num { font-family: var(--font-mono); }


/* ------------------------------------------------------ thread page rework */
.thread-seg { margin-bottom: 1rem; flex-wrap: wrap; }
.thread-seg .seg-btn {
  font: 600 .6rem/1.4 var(--font-mono); text-transform: uppercase;
  letter-spacing: .07em; min-height: 30px; padding: .25rem .5rem;
}
.seg-btn.on {
  background: var(--raised); color: var(--accent);
  border-color: var(--line-strong);
}

.thread-layout { display: grid; grid-template-columns: 1fr; gap: 1rem; align-items: start; }
@media (min-width: 980px) {
  .thread-layout { grid-template-columns: minmax(0, 1fr) 280px; }
  .thread-side { position: sticky; top: 60px; }
}

.role-tag {
  font: 600 .56rem/1.4 var(--font-mono); text-transform: uppercase;
  letter-spacing: .07em; color: var(--faint);
  border: 1px solid var(--line-strong); border-radius: 3px;
  padding: .05rem .35rem; white-space: nowrap;
}
.role-tag.me { color: var(--accent); border-color: var(--accent-soft); }

.peep { padding: .65rem 0; border-bottom: 1px solid var(--line); }
.peep:last-child { border-bottom: none; padding-bottom: .2rem; }
.peep-head { display: flex; align-items: center; gap: .5rem; }
.peep-head strong { font-size: .85rem; color: var(--bright); }
.peep-mail { font-size: .72rem; overflow-wrap: anywhere; }
.peep-count { white-space: nowrap; }
.peep-sum { margin: .25rem 0 0; font-size: .78rem; color: var(--muted); line-height: 1.5; }
.peep-link { display: inline-block; margin-top: .25rem; font-size: .75rem; text-decoration: none; }
.peep-sig-box { margin-top: .3rem; }
.peep-sig-box summary { font-size: .7rem; color: var(--faint); cursor: pointer; }
.peep-sig {
  margin: .3rem 0 0; white-space: pre-wrap; word-break: break-word;
  font: 400 .68rem/1.55 var(--font-mono); color: var(--faint);
}

.ai-top { display: flex; align-items: center; gap: .6rem; margin-bottom: .4rem; }
.eyebrow-mini {
  font: 600 .58rem/1.4 var(--font-mono); text-transform: uppercase;
  letter-spacing: .1em; color: var(--faint); white-space: nowrap;
}
.ai-read .ai-summary { margin: 0; }
.ai-details { margin-top: .35rem; }
.ai-details summary { font-size: .7rem; color: var(--faint); cursor: pointer; }
.ai-details p { margin: .25rem 0 0; }
.use-suggestion { min-height: 26px; font-size: .7rem; padding: .1rem .6rem; }


/* ---------------------------------------------------- de-boxing (round 2)
   Fewer boxes, more hairlines. Data surfaces (tables, mail, stat readouts)
   sit on the page ground with rules for structure; boxes stay only where a
   FORM needs grouping. Stat cards become ledger entries: a left tick, not a
   container. */
.panel.flat { background: transparent; border: none; padding: 0; }
.panel.flat table.data thead th { border-bottom: 1px solid var(--line-strong); }

/* Flat stat tiles: solid light fill, no ledger rule. */
.stat-card {
  background: var(--sunken); border: none;
  border-radius: var(--radius-s); padding: .65rem .8rem;
}
.stat-cards { gap: .7rem .7rem; }

.mail-panel { background: transparent; border: none; padding: 0 .15rem; }

/* Chips row breathes without a box around the list below it. */
.stage-chips { margin: 0 0 .6rem; }


/* Dot grid follows the copy column on the split hero. */
.hero-split::before {
  -webkit-mask-image: radial-gradient(ellipse 55% 85% at 26% 45%, #000 15%, transparent 70%);
  mask-image: radial-gradient(ellipse 55% 85% at 26% 45%, #000 15%, transparent 70%);
}


/* ----------------------------------------------------------- integrations */
.integration-row {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; padding: .5rem 0;
}
.integration-id strong { font-size: .92rem; color: var(--bright); display: block; }
.integration-state { display: flex; flex-direction: column; gap: .3rem; align-items: flex-end; }
.hook-url {
  font: 500 .72rem/1.5 var(--font-mono); color: var(--accent);
  background: var(--sunken); border: 1px solid var(--line);
  border-radius: var(--radius-s); padding: .3rem .55rem;
  max-width: 480px; overflow-wrap: anywhere; user-select: all;
}
.integration-actions { display: flex; gap: .6rem; align-items: center; }
@media (max-width: 720px) { .integration-state { align-items: flex-start; } }

/* --------------------------------------------- about: stacked feature rows */
.feature-rows { display: flex; flex-direction: column; gap: 3.2rem; margin: 1rem 0 2.6rem; }
.feature-row {
  display: grid; grid-template-columns: 1fr; gap: 1.4rem; align-items: center;
}
@media (min-width: 860px) {
  .feature-row { grid-template-columns: 5fr 4fr; gap: 3rem; }
  /* Zigzag: rows 1 and 3 flip, so no two consecutive sections (hero
     included) share the same left/right shape. */
  .feature-row:nth-child(odd) { grid-template-columns: 4fr 5fr; }
  .feature-row:nth-child(odd) .feature-text { order: 2; }
  .feature-row:nth-child(odd) .feature-mock { order: 1; }
}
.feature-text .feature-num { display: block; margin-bottom: .5rem; }
.feature-title {
  font: 800 clamp(1.4rem, 3.4vw, 1.9rem)/1.15 var(--font-ui);
  letter-spacing: -.025em; color: var(--bright); text-transform: none;
  margin: 0 0 .7rem;
}
.feature-text p { color: var(--muted); font-size: .92rem; line-height: 1.7; margin: 0 0 .8rem; max-width: 34rem; }
.feature-text strong { color: var(--ink); }

/* Product mocks: tiny fake UI in a window frame. Decorative, aria-hidden. */
.mock {
  background: var(--surface); border: 1px solid var(--card-line);
  border-radius: 10px; padding: .8rem .9rem 1rem;
  box-shadow: var(--shadow-pop);
}
.mock-bar { display: flex; gap: 5px; margin-bottom: .8rem; }
.mock-bar span { width: 8px; height: 8px; border-radius: 50%; background: var(--line-strong); }
.mock-row {
  display: flex; gap: .6rem; align-items: center;
  padding: .3rem .2rem; border-bottom: 1px solid var(--line);
  font: 500 .72rem/1.4 var(--font-mono);
}
.mock-label { flex: 1; color: var(--ink); }
.mock-cell { color: var(--muted); }
.mock-stat { display: flex; flex-direction: column; padding: .55rem .2rem .1rem; }
.mock-stat span { font: 600 .56rem/1.4 var(--font-mono); letter-spacing: .09em; color: var(--faint); }
.mock-stat strong { font: 600 1.15rem/1.3 var(--font-mono); color: var(--bright); }
.mock-stat.good strong { color: var(--good); }
.mock-stat em { font-style: normal; font-size: .68rem; color: var(--faint); }
.mock-chart { width: 100%; height: auto; display: block; }
.mock-legend { font-size: .64rem; color: var(--faint); display: flex; gap: .9rem; margin-top: .4rem; }
.mock-legend i { display: inline-block; width: 12px; border-top: 2px solid; margin-right: .3rem; vertical-align: middle; }
.mock-legend i.a { border-color: var(--accent); }
.mock-legend i.b { border-color: var(--good); border-top-style: dashed; }
.mock-legend i.c { border-color: var(--faint); border-top-style: dashed; }
.mock-chip {
  display: inline-flex; align-items: center; gap: .4em;
  border: 1px solid var(--line-strong); border-radius: 999px;
  padding: .15rem .6rem; font: 600 .6rem/1.4 var(--font-mono);
  letter-spacing: .08em; color: var(--warn); margin-bottom: .5rem;
}
.mock-chip i { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.mock-line { font-size: .78rem; color: var(--ink); line-height: 1.55; margin: 0 0 .3rem; }

/* The math, in public. */
.formula-block { margin: 1.4rem auto .4rem; max-width: 620px; }
.formula-line {
  font: 500 clamp(.9rem, 2.6vw, 1.15rem)/1.6 var(--font-mono);
  color: var(--ink); margin: .9rem 0 .15rem; letter-spacing: .01em;
}
.formula-line sub, .formula-line sup { color: var(--accent); }
.formula-sub { font-size: .74rem; color: var(--faint); margin: 0; line-height: 1.55; }


/* Closing tagline replaces the principles pills. */
.closing-tag { text-align: center; padding: 1.2rem 0 3rem; }
.closing-line {
  font: 800 clamp(1.5rem, 4.5vw, 2.3rem)/1.25 var(--font-ui);
  letter-spacing: -.03em; color: var(--bright); margin: 0 0 1.4rem;
}
.closing-line em {
  font-style: normal; color: var(--accent);
  text-decoration: underline; text-decoration-thickness: 3px;
  text-underline-offset: .18em;
  text-decoration-color: var(--accent-soft);
}


/* Hero curve as ARTWORK, not another product card: no box, no chrome, glow.
   The three feature mocks below keep window frames -- they are screenshots;
   this is the signature graphic. */
.hero-art { background: transparent; border: none; padding: 0; box-shadow: none; }
.hero-art .about-chart { overflow: visible; }
.hero-art .about-chart .curve-club {
  stroke-width: 3;
  filter: drop-shadow(0 0 7px rgba(79, 209, 197, .55));
}
.hero-art .about-chart .curve-head {
  stroke-width: 2.6;
  filter: drop-shadow(0 0 6px rgba(163, 113, 247, .45));
}
.hero-art .about-chart .gridline { opacity: .5; }
.hero-art-legend { justify-content: flex-start; gap: 1.1rem; margin-top: .8rem; }
.hero-art-note {
  margin-left: auto; font: 600 .58rem/1.5 var(--font-mono);
  text-transform: uppercase; letter-spacing: .1em; color: var(--faint);
}


/* Pending-link actions: attach vs create-show inline controls. */
.link-actions { white-space: nowrap; }
.create-show-inline { display: inline-flex; gap: .35rem; align-items: center; }
.create-show-inline input { width: 105px; min-height: 30px; font-size: .74rem; margin-top: 0; }
.create-show-inline input[type="date"] { width: 130px; }
.attach-existing { margin-top: .6rem; border-top: 1px solid var(--line); padding-top: .5rem; }
.attach-existing select { margin: .3rem 0; }

/* The hero sales chart scales with the panel; on a wide screen that made it
   enormous. Cap it and center it -- a chart is a reading, not a mural. */
.chart-panel .sales-chart { max-width: 760px; display: block; margin: 0 auto; }
.chart-panel .chart-legend { justify-content: center; }


/* ------------------------------------------- heuristic walkthrough stepper */
.how-stepper { max-width: 680px; margin: 1.4rem auto .4rem; text-align: center; }
.how-tabs { display: flex; gap: .4rem; flex-wrap: wrap; justify-content: center; }
.how-tab {
  min-height: 30px; padding: .2rem .7rem;
  font: 600 .62rem/1.4 var(--font-mono); text-transform: uppercase;
  letter-spacing: .07em; color: var(--faint);
  background: transparent; border: 1px solid var(--line-strong);
  border-radius: 999px;
}
.how-tab.on { color: var(--accent); border-color: var(--accent); }
.how-panel { display: none; padding-top: 1rem; }
.how-panel.on { display: block; animation: how-in .16s ease; }
@keyframes how-in { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: none; } }
.how-code {
  display: inline-block; margin-bottom: .7rem; padding: .35rem .7rem;
  font: 500 .82rem/1.5 var(--font-mono); color: var(--ink);
  background: var(--sunken); border: 1px solid var(--line);
  border-radius: var(--radius-s);
}
.how-code sub { color: var(--accent); }
.how-panel p { font-size: .88rem; color: var(--muted); line-height: 1.7; margin: 0; }
.how-panel strong { color: var(--ink); }
.how-intro { max-width: 560px; margin: 0 auto !important; }

/* Term-by-term sub-breakdown inside each step. */
.how-parts { margin-top: 1.1rem; text-align: left; display: flex; flex-direction: column; }
.how-part {
  display: grid; grid-template-columns: 150px 1fr; gap: 1rem;
  align-items: start; padding: .7rem 0; border-top: 1px solid var(--line);
}
.how-part > code {
  font: 500 .78rem/1.6 var(--font-mono); color: var(--ink);
  word-break: break-word;
}
.how-part > code sub, .how-part > code sup { color: var(--accent); }
.how-part > p { font-size: .82rem !important; }
@media (max-width: 620px) {
  .how-part { grid-template-columns: 1fr; gap: .25rem; }
}


/* The "and more tiers" row in the booking mock. */
.mock-more { border-bottom: none; padding-top: .1rem; }
.mock-more .mock-label { color: var(--faint); letter-spacing: .2em; }


/* Anchor navigation glides instead of jumping; reduced-motion users get an
   instant jump via the global override above. Targets clear the sticky bar. */
html { scroll-behavior: smooth; }
.feature-rows, .heuristic-strip { scroll-margin-top: 70px; }


/* ---------------------------------------------------------------- setup */
.setup-page { max-width: 760px; margin: 0 auto; }
.setup-step { padding: 1rem 1.2rem 1.1rem; }
.setup-head { display: flex; align-items: center; gap: .7rem; margin-bottom: .5rem; }
.setup-num {
  width: 26px; height: 26px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-strong); border-radius: 50%;
  font: 600 .74rem/1 var(--font-mono); color: var(--muted);
}
.setup-num.done { border-color: var(--good); color: var(--good); }
.setup-title {
  font: 700 1rem/1.3 var(--font-ui); color: var(--bright);
  text-transform: none; letter-spacing: -.01em; margin: 0; flex: 1;
}
.setup-step > p { color: var(--muted); font-size: .88rem; line-height: 1.65; margin: 0 0 .6rem; }
.setup-step strong { color: var(--ink); }
.setup-list { margin: 0; padding-left: 1.25rem; color: var(--muted);
  font-size: .86rem; line-height: 1.7; }
.setup-list li { margin-bottom: .35rem; }
.setup-code {
  font: 500 .8em/1.4 var(--font-mono); color: var(--accent);
  background: var(--sunken); border: 1px solid var(--line);
  border-radius: 4px; padding: .06em .35em;
}
.setup-foot { text-align: center; padding-bottom: 1.5rem; }


/* ------------------------------------------------------------ code sign-in */
.code-input {
  font: 600 1.5rem/1.4 var(--font-mono) !important; letter-spacing: .45em;
  text-align: center;
}
.auth-err { color: var(--bad); font-size: .82rem; }

/* ------------------------------------------------------- notifications bell */
.notif-wrap { position: relative; display: inline-flex; }
.notif-bell { position: relative; }
.notif-dot {
  position: absolute; top: 5px; right: 6px; width: 7px; height: 7px;
  border-radius: 50%; background: var(--accent);
  border: 2px solid var(--surface);
}
.notif-drop {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 60;
  width: 280px; padding: .4rem;
  background: var(--surface); border: 1px solid var(--card-line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-pop);
}
.notif-item {
  display: flex; flex-direction: column; gap: .1rem;
  padding: .55rem .65rem; border-radius: var(--radius-s);
  text-decoration: none;
}
.notif-item:hover { background: var(--raised); }
.notif-item strong { color: var(--bright); font-size: .84rem; }
.notif-empty { padding: .55rem .65rem; }

/* ------------------------------------------------------------ welcome modal */
/* The hidden attribute must always win -- an author display rule otherwise
   overrides its UA display:none, which kept this modal permanently visible. */
.modal-veil[hidden] { display: none !important; }
.modal-veil {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(4, 6, 8, .66);
  display: flex; align-items: center; justify-content: center;
  padding: 1rem; overscroll-behavior: contain;
}
.modal-panel {
  background: var(--surface); border: 1px solid var(--card-line);
  border-radius: 12px; padding: 1.4rem 1.5rem 1.2rem;
  max-width: 460px; width: 100%;
  box-shadow: var(--shadow-pop);
}
.modal-title {
  font: 800 1.25rem/1.3 var(--font-ui); color: var(--bright);
  text-transform: none; letter-spacing: -.02em; margin: 0 0 .4rem;
}
.modal-lede { color: var(--muted); font-size: .9rem; margin: 0 0 .7rem; }
.modal-steps { margin: 0 0 .7rem; padding-left: 1.2rem; color: var(--muted);
  font-size: .86rem; line-height: 1.75; }
.modal-steps strong { color: var(--ink); }
.modal-actions { display: flex; justify-content: flex-end; gap: .6rem; margin-top: 1rem; }


/* --------------------------------------------------------- google sign-in */
.google-btn {
  display: flex; align-items: center; justify-content: center; gap: .6rem;
  width: 100%; min-height: 44px; margin: .4rem 0 .9rem;
  background: #ffffff; color: #1f1f1f;
  border: 1px solid var(--line-strong); border-radius: var(--radius-s);
  font: 600 .9rem/1 var(--font-ui); text-decoration: none;
  transition: filter .12s ease;
}
.google-btn:hover { filter: brightness(.94); }
.auth-divider {
  display: flex; align-items: center; gap: .8rem;
  margin: .2rem 0 .8rem; color: var(--faint); font-size: .72rem;
  text-transform: uppercase; letter-spacing: .08em;
}
.auth-divider::before, .auth-divider::after {
  content: ""; flex: 1; border-top: 1px solid var(--line);
}


/* -------------------------------------------------- show dates, made loud */
.show-date-cell strong { display: block; color: var(--bright); font-weight: 600; }
.show-date-strong { color: var(--bright); font-weight: 600; }
.days-out-chip {
  font: 600 .62rem/1.4 var(--font-mono); letter-spacing: .05em;
  border: 1px solid var(--line-strong); border-radius: 999px;
  padding: .05rem .5rem; color: var(--accent); white-space: nowrap;
}
.days-out-chip.today { color: var(--good); border-color: var(--good); }
.days-out-chip.past { color: var(--faint); }
.days-left { color: var(--ink); font-weight: 600; }

/* -------------------------------------------------- interactive chart tip */
.obs-dot { cursor: pointer; transition: r .1s ease; }
.obs-dot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.chart-tip {
  position: absolute; z-index: 20; pointer-events: none;
  display: flex; flex-direction: column; gap: .05rem;
  background: var(--surface); border: 1px solid var(--card-line);
  border-radius: var(--radius-s); padding: .35rem .6rem;
  box-shadow: var(--shadow-pop);
}
.chart-tip[hidden] { display: none !important; }
.chart-tip strong { font: 600 .82rem/1.3 var(--font-mono); color: var(--bright); }
.chart-tip span { font-size: .68rem; color: var(--faint); white-space: nowrap; }


/* Selected chart point glows so a finger can see what it picked. */
.obs-dot.lit { filter: drop-shadow(0 0 5px rgba(79, 209, 197, .8)); }
.sales-chart { touch-action: pan-y; cursor: crosshair; }

/* ---- Legal pages (privacy / terms) ---------------------------------- */
.legal { max-width: 680px; margin: 0 auto; padding: 24px 4px 60px; }
.legal h1 { font-size: 1.5rem; margin: 0 0 4px; }
.legal h2 { font-size: .95rem; margin: 26px 0 6px; color: var(--bright); }
.legal p { font-size: .86rem; line-height: 1.65; color: var(--ink); margin: 0 0 10px; }
.legal a { color: var(--accent); }
.legal-foot { margin-top: 34px; border-top: 1px solid var(--line); padding-top: 14px; }
.legal-links { margin-top: 14px; font-size: .74rem; }
.legal-links a { color: var(--faint); text-decoration: none; }
.legal-links a:hover { color: var(--accent); }

/* ---- Team management ------------------------------------------------- */
a.team-tag { text-decoration: none; }
a.team-tag:hover { color: var(--accent); border-color: var(--accent); }
.notif-foot {
  display: block; padding: .55rem .65rem; margin-top: .2rem;
  border-top: 1px solid var(--line);
  font-size: .78rem; color: var(--muted); text-decoration: none;
}
.notif-foot:hover { color: var(--accent); }
#team-page .panel { margin-bottom: 1rem; }
.team-rename { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
.team-rename input { max-width: 320px; }
.invite-row { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; margin: .4rem 0; }
.invite-row input[type="email"], .invite-row input { max-width: 300px; }
.role-key { display: flex; flex-direction: column; gap: .15rem; padding: .6rem .2rem 0; font-size: .74rem; }
.role-key strong { color: var(--bright); font-weight: 600; }
.member-role { height: var(--control-h); }
.pill.role-owner { color: var(--accent); }
.pill.role-admin { color: var(--good, #4fd1c5); }
.pill.role-member { color: var(--warn, #e6b455); }
.pill.role-viewer { color: var(--faint); }
.team-switch { list-style: none; margin: 0 0 .8rem; padding: 0;
  display: flex; flex-direction: column; gap: .35rem; }
.team-switch li { display: flex; gap: .6rem; align-items: center; }
.team-switch a { color: var(--bright); text-decoration: none; font-size: .9rem; }
.team-switch a:hover { color: var(--accent); }
.team-switch a.current { font-weight: 600; }

/* ---- Heuristic walkthrough (plain-language default, math behind toggle) -- */
.how-stepper[hidden] { display: none !important; }
.how-walk {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.6rem;
  max-width: 880px; margin: 1.6rem auto .4rem; text-align: left;
}
@media (max-width: 720px) { .how-walk { grid-template-columns: 1fr; gap: 1.1rem; } }
.walk-step .hstep-glyph { width: 28px; height: 28px; }
.walk-name {
  font: 600 .74rem/1 var(--font-mono); text-transform: uppercase;
  letter-spacing: .2em; color: var(--bright); margin: .5rem 0 .4rem;
}
.walk-step p { font-size: .84rem; line-height: 1.6; color: var(--muted); margin: 0; }
.math-toggle {
  display: block; margin: 1.6rem auto 0; padding: .35rem 1rem;
  background: none; border: 1px solid var(--line); border-radius: 999px;
  font: 600 .66rem/1.4 var(--font-mono); text-transform: uppercase;
  letter-spacing: .18em; color: var(--muted); cursor: pointer;
}
.math-toggle:hover { color: var(--accent); border-color: var(--accent); }

/* ---- Offer-sheet branding (team page) -------------------------------- */
.brand-form { display: flex; gap: .8rem; align-items: flex-end; flex-wrap: wrap; margin: .5rem 0 .9rem; }
.brand-form label { display: flex; flex-direction: column; gap: .25rem; font-size: .74rem; color: var(--faint); }
.brand-form input { max-width: 300px; }
.brand-logo-row { display: flex; gap: .8rem; align-items: center; flex-wrap: wrap; margin-bottom: .4rem; }
.brand-logo-preview {
  max-height: 34px; max-width: 240px; padding: 6px 10px;
  background: #0f1216; border: 1px solid var(--line); border-radius: var(--radius-s);
}
.brand-upload { cursor: pointer; }
.brand-upload .button { pointer-events: none; }

/* ---- Mission payoff before the final CTA ------------------------------ */
.mission-line {
  max-width: 32rem; margin: 0 auto 1.5rem;
  font-size: 1.04rem; line-height: 1.7; color: var(--muted);
}

/* ---- Builder header: venue group vs date group ------------------------ */
#f-date[hidden] { display: none !important; }
.venue-load { max-width: 170px; height: var(--control-h); color: var(--muted); }
.date-mode { height: var(--control-h); }
.head-divider {
  display: inline-block; width: 1px; height: 22px;
  background: var(--line); margin: 0 .35rem; align-self: center;
}

/* ---- Deal-grid alignment: inputs pin to the cell bottom so a two-line
   label ("Promoter cut off top / optional") can't knock its field out of
   line with the row. ---------------------------------------------------- */
.deal-grid > label { display: flex; flex-direction: column; }
.deal-grid > label > .money-wrap,
.deal-grid > label > .pct-wrap,
.deal-grid > label > input,
.deal-grid > label > textarea { margin-top: auto; }
.deal-grid > label > .money-wrap, .deal-grid > label > .pct-wrap { width: 100%; }

/* The only-nbor reveal (display:block) out-specifies the deal-grid label
   flex, un-pinning those two inputs. Revealed LABELS must come back as flex. */
form[data-deal="nbor"] .deal-grid > label.only-nbor { display: flex; }

/* ---- Sortable table headers (Shows tab) ------------------------------- */
#shows-table th[data-key] { cursor: pointer; user-select: none; white-space: nowrap; }
#shows-table th[data-key]::after { content: "\2195"; margin-left: .3rem; opacity: .3; font-size: .78em; }
#shows-table th[data-key].asc::after { content: "\2191"; opacity: 1; color: var(--accent); }
#shows-table th[data-key].desc::after { content: "\2193"; opacity: 1; color: var(--accent); }

/* ---- Team page: offer message template -------------------------------- */
#offer-msg-tpl { width: 100%; max-width: 560px; font-size: .84rem; }
#team-page code {
  font: 500 .76rem/1 var(--font-mono); color: var(--accent);
  background: var(--sunken); padding: .1rem .3rem; border-radius: 4px;
}

/* ---- CC checkbox picker (Send to Agent) ------------------------------- */
.cc-pick { margin: .2rem 0; }
.cc-pick > summary {
  cursor: pointer; font-size: .78rem; color: var(--muted); font-weight: 600;
  user-select: none;
}
.cc-pick > summary .hint { font-weight: 400; margin-left: .3rem; }
.cc-pick[open] > summary { color: var(--bright); }
.cc-drop {
  margin-top: .4rem; padding: .45rem;
  background: var(--sunken); border: 1px solid var(--line);
  border-radius: var(--radius-s);
}
#cc-options { max-height: 150px; overflow-y: auto; display: flex; flex-direction: column; gap: .1rem; }
.cc-opt {
  display: flex; align-items: center; gap: .5rem;
  padding: .28rem .3rem; border-radius: 4px;
  font-size: .8rem; color: var(--ink); cursor: pointer;
}
.cc-opt:hover { background: var(--raised); }
.cc-opt input { margin: 0; accent-color: var(--accent); }
.cc-opt em.hint { font-style: normal; margin-left: .3rem; }
.cc-add { display: flex; gap: .4rem; margin-top: .45rem; }
.cc-add input { flex: 1; min-height: 30px; }

.tpl-subject-label { display: block; margin-bottom: .5rem; max-width: 560px;
  font-size: .74rem; color: var(--faint); }
.tpl-subject-label input { width: 100%; margin-top: .25rem; }

/* ---- Greyed Send-to-Agent with hover hint ----------------------------- */
.send-disabled { opacity: .45; cursor: not-allowed; position: relative; }
.send-disabled:hover, .send-disabled:focus-visible { opacity: .7; }
.send-disabled::after {
  content: attr(data-tip);
  position: absolute; right: 0; top: calc(100% + 7px); z-index: 40;
  padding: .42rem .65rem; white-space: nowrap;
  background: var(--surface); border: 1px solid var(--accent);
  color: var(--accent); font-size: .72rem; font-weight: 600;
  border-radius: var(--radius-s);
  box-shadow: var(--shadow-pop);
  opacity: 0; pointer-events: none; transform: translateY(-3px);
  transition: opacity .12s ease, transform .12s ease;
}
.send-disabled:hover::after, .send-disabled:focus-visible::after {
  opacity: 1; transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .send-disabled::after { transition: none; }
}

/* ---- Mobile tables: hide secondary columns instead of forcing a sideways
   scroll -- the columns that answer "how is this going" stay, the rest wait
   for the detail page. Denser cells buy the survivors room. ---------------- */
@media (max-width: 720px) {
  .m-hide { display: none !important; }
  .data th, .data td { padding: .4rem .45rem; font-size: .8rem; }
  .data .pill { font-size: .62rem; }
}

/* ---- CC picker: native checkboxes at chip scale, tight rows ------------ */
.cc-opt input[type="checkbox"] {
  width: 13px; height: 13px; flex: 0 0 13px;
  margin: 0; padding: 0; min-height: 0;
  appearance: auto; -webkit-appearance: checkbox;
}
.cc-opt { padding: .22rem .3rem; gap: .45rem; font-size: .78rem; min-height: 0; }
.cc-drop { max-width: 340px; }

/* Header selects (saved venues, date/MAD) size to content -- the global
   select{width:100%} let "Show date" stretch across the whole line. */
.head-sub select {
  width: auto; flex: 0 1 auto; max-width: 190px;
  font-size: .82rem; min-height: 32px; padding: .3rem .5rem;
}
.head-sub #f-date { flex: 0 1 auto; width: 150px; }

/* Mobile builder header: the desktop divider is meaningless in the 2-col
   grid (it occupied a whole cell as a stray line left of "Show date"), and
   selects go full-width like the inputs so the grid rows align. Saved-venues
   gets its own full row; date-mode + date pair up on one row. */
@media (max-width: 720px) {
  .head-sub .head-divider { display: none; }
  .head-sub select { width: 100%; max-width: none; flex: none; }
  .head-sub .venue-load { grid-column: 1 / -1; }
  .head-sub #f-date { width: 100%; }
}

/* ---- Roster radar under the artist title ------------------------------ */
.roster-hit { margin-top: .25rem; font-size: .76rem; color: var(--accent); }
.roster-hit a { color: var(--accent); font-weight: 600; }
.roster-hit .link { font-size: .74rem; }

/* FIX: the deal-grid label flex rule (alignment) out-specified the
   .only-nbor hide rule, so Guarantee mode leaked the NBOR fields. Hidden
   wins again here; the form[data-deal="nbor"] reveal still out-ranks both. */
.deal-grid > label.only-nbor { display: none; }

/* Mobile builder header: actions wrap instead of forcing the page wider
   than the viewport (which cut off everything above Venue costs). */
@media (max-width: 720px) {
  .page-head, .head-actions { flex-wrap: wrap; }
  .head-actions > * { flex: 0 1 auto; min-width: 0; }
  .promote-pop { max-width: calc(100vw - 32px); }
}


/* ================= about mini: booking builder (agent-built 2026-09-29) ================= */
/* ------------------------------------------- about: mini booking builder
   Animated miniature of the real booking builder for the story rail
   (partials/about_mini_booking.html). Namespace: mb-*. Colors and fonts go
   through the theme tokens only, so both themes come for free.

   Animation contract: the ancestor .frame gains/loses .on as the story
   engine cycles frames, so every entrance is a keyframe animation declared
   under `.frame.on` with `both` fill -- it replays on every reactivation.
   Each frame's choreography finishes well inside the ~5s frame budget.
   The global prefers-reduced-motion override in app.css flattens all of
   these to instant. */

.mini-booking {
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  font: 400 .74rem/1.45 var(--font-ui);
  color: var(--ink);
}

/* The panel idiom, at miniature scale. The surrounding stage card supplies
   the window chrome; this is just the product panel. */
.mb-panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .75rem .85rem .8rem;
}
.mb-phead {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: .6rem; margin-bottom: .55rem;
}
.mb-ptitle {
  font: 650 .8rem/1.3 var(--font-ui); color: var(--bright);
  letter-spacing: -.01em;
}
.mb-ptotal { font: 500 .68rem/1.5 var(--font-mono); color: var(--faint); }

/* Sunken input-look money box: $ (or %) faint at one end, amount at the
   other -- the adorned-input idiom from the real builder. */
.mb-money {
  display: inline-flex; align-items: center; justify-content: space-between;
  gap: .3rem; padding: .22rem .5rem;
  background: var(--sunken); border-radius: var(--radius-s);
  font: 500 .68rem/1.35 var(--font-mono); color: var(--ink);
}
.mb-cur { color: var(--faint); }
.mb-amt { font-variant-numeric: tabular-nums; }

/* ---------------------------------------------- frame 1: venue costs */
.mb-cost-rows { display: flex; flex-direction: column; gap: .34rem; }
.mb-cost {
  display: flex; align-items: center; justify-content: space-between;
  gap: .6rem;
}
.mb-clabel { font-size: .72rem; color: var(--ink); }
.mb-cost .mb-money { flex: 0 0 96px; }

.frame.on .mb-cost { animation: mb-rise .35s ease both; }
.frame.on .mb-cost:nth-child(2) { animation-delay: .12s; }
.frame.on .mb-cost:nth-child(3) { animation-delay: .24s; }
.frame.on .mb-cost:nth-child(4) { animation-delay: .36s; }
.frame.on .mb-cost:nth-child(5) { animation-delay: .48s; }
.frame.on .mb-ptotal { animation: mb-fade .4s ease .95s both; }

/* ---------------------------------------------- frame 2: ticket tiers */
.mb-tax {
  display: inline-flex; align-items: center; gap: .35rem;
  font-size: .64rem; color: var(--muted);
}
.mb-taxbox {
  background: var(--sunken); border-radius: var(--radius-s);
  padding: .12rem .4rem;
  font: 500 .64rem/1.35 var(--font-mono); color: var(--ink);
}
.mb-trow {
  display: grid; grid-template-columns: 1fr 44px 64px 64px;
  gap: .5rem; align-items: center; padding: .26rem .1rem;
  border-bottom: 1px solid var(--line);
  font: 500 .68rem/1.4 var(--font-mono);
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.mb-trow > span { text-align: right; }
.mb-trow > span:first-child { text-align: left; }
.mb-thead {
  font: 500 .56rem/1.5 var(--font-mono); text-transform: uppercase;
  letter-spacing: .08em; color: var(--faint);
  border-bottom: 1px solid var(--line-strong); padding-bottom: .3rem;
}
.mb-dim { color: var(--faint); }
.mb-hint { margin-top: .45rem; font-size: .66rem; color: var(--faint); }

.frame.on .mb-thead { animation: mb-fade .3s ease both; }
.frame.on .mb-tbody .mb-trow { animation: mb-rise .35s ease .1s both; }
.frame.on .mb-tbody .mb-trow:nth-child(2) { animation-delay: .22s; }
.frame.on .mb-tbody .mb-trow:nth-child(3) { animation-delay: .34s; }
.frame.on .mb-tbody .mb-trow:nth-child(4) { animation-delay: .46s; }
.frame.on .mb-hint { animation: mb-fade .4s ease .9s both; }

/* ---------------------------------------------- frame 3: headliner deal */
.mb-seg {
  display: flex; gap: 3px; padding: 3px; margin-bottom: .6rem;
  background: var(--sunken); border: 1px solid var(--line);
  border-radius: var(--radius-s);
}
.mb-seg-opt {
  flex: 1; text-align: center; padding: .24rem .3rem;
  border: 1px solid transparent; border-radius: 4px;
  font: 600 .58rem/1.35 var(--font-ui); color: var(--faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mb-seg-opt.mb-active {
  background: var(--raised); color: var(--bright);
  border-color: var(--line-strong);
}
.mb-fields {
  display: grid; grid-template-columns: 1fr 1fr; gap: .5rem;
  margin-bottom: .6rem;
}
.mb-field { display: flex; flex-direction: column; gap: .22rem; }
.mb-flabel { font-size: .62rem; font-weight: 500; color: var(--muted); }
.mb-field .mb-money { width: 100%; }
.mb-stats { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; }
.mb-stat {
  display: flex; flex-direction: column; gap: .08rem;
  background: var(--sunken); border-radius: var(--radius-s);
  padding: .5rem .6rem .45rem;
}
.mb-slabel {
  font: 600 .54rem/1.5 var(--font-mono); color: var(--faint);
  text-transform: uppercase; letter-spacing: .09em;
}
.mb-svalue {
  font: 600 .98rem/1.25 var(--font-mono); letter-spacing: -.02em;
  color: var(--bright); font-variant-numeric: tabular-nums;
}
.mb-svalue.mb-good { color: var(--good); }
.mb-ssub { font-size: .6rem; color: var(--faint); }

.frame.on .mb-seg { animation: mb-fade .35s ease both; }
.frame.on .mb-field { animation: mb-rise .35s ease .15s both; }
.frame.on .mb-field:nth-child(2) { animation-delay: .28s; }
.frame.on .mb-stat { animation: mb-pop .4s ease .6s both; }
.frame.on .mb-stat:nth-child(2) { animation-delay: .75s; }
.frame.on .mb-stat:nth-child(3) { animation-delay: .9s; }
.frame.on .mb-stat:nth-child(4) { animation-delay: 1.05s; }

/* ---------------------------------------------- frame 4: send to agent */
.mb-actions { display: inline-flex; gap: .4rem; }
.mb-btn {
  display: inline-flex; align-items: center; padding: .26rem .55rem;
  border: 1px solid transparent; border-radius: var(--radius-s);
  font: 600 .62rem/1.35 var(--font-ui); white-space: nowrap;
}
.mb-ghost { border-color: var(--line-strong); color: var(--muted); }
.mb-primary { background: var(--accent); color: var(--btn-primary-ink); }
.mb-pick {
  display: flex; flex-direction: column; gap: .3rem;
  padding: .5rem .55rem; margin-bottom: .55rem;
  background: var(--sunken); border: 1px solid var(--line);
  border-radius: var(--radius-s);
}
.mb-pick-label { font-size: .62rem; font-weight: 600; color: var(--muted); }
.mb-pick-hint { color: var(--faint); font-weight: 400; }
.mb-agent {
  display: flex; align-items: center; gap: .45rem;
  padding: .1rem .05rem; font-size: .68rem;
}
.mb-aname { font-weight: 600; color: var(--bright); }
.mb-agency { color: var(--faint); }
.mb-check {
  position: relative; width: 12px; height: 12px; flex: 0 0 12px;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: 3px;
}
.mb-check::after {
  content: ""; position: absolute; left: 3px; top: 0;
  width: 4px; height: 7px;
  border-right: 2px solid var(--btn-primary-ink);
  border-bottom: 2px solid var(--btn-primary-ink);
  transform: rotate(45deg); opacity: 0;
}
.mb-subject {
  display: flex; flex-direction: column; gap: .22rem;
  margin-bottom: .55rem;
}
.mb-input {
  background: var(--sunken); border-radius: var(--radius-s);
  padding: .3rem .5rem; font-size: .68rem; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mb-sentrow { display: flex; justify-content: flex-end; min-height: 1.2rem; }
.mb-sentwrap { display: inline-flex; }

/* Choreography: sheet in, picker in, rows in, subject in; then the first
   checkbox checks itself, the Send button presses, and SENT pops. */
.frame.on .mb-offer-head { animation: mb-rise .32s ease both; }
.frame.on .mb-pick { animation: mb-rise .35s ease .12s both; }
.frame.on .mb-agent { animation: mb-rise .3s ease .32s both; }
.frame.on .mb-agent + .mb-agent { animation-delay: .44s; }
.frame.on .mb-subject { animation: mb-rise .35s ease .55s both; }
.frame.on .mb-lead .mb-check { animation: mb-checkfill .25s ease 1.45s both; }
.frame.on .mb-lead .mb-check::after { animation: mb-tick .22s ease 1.6s both; }
.frame.on .mb-primary { animation: mb-press .35s ease 2s 1; }
.frame.on .mb-sentwrap { animation: mb-pop .4s ease 2.35s both; }

/* ---------------------------------------------- mb keyframes */
@keyframes mb-rise {
  from { opacity: 0; transform: translateY(7px); }
  to { opacity: 1; transform: none; }
}
@keyframes mb-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes mb-pop {
  0% { opacity: 0; transform: scale(.92); }
  60% { opacity: 1; transform: scale(1.03); }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes mb-checkfill {
  from { background: var(--surface); border-color: var(--line-strong); }
  to { background: var(--accent); border-color: var(--accent); }
}
@keyframes mb-tick {
  from { opacity: 0; transform: rotate(45deg) scale(.3); }
  to { opacity: 1; transform: rotate(45deg) scale(1); }
}
@keyframes mb-press {
  0% { transform: none; }
  40% { transform: scale(.94); }
  100% { transform: none; }
}


/* ================= about mini: deal thread (agent-built 2026-09-29) ================= */
/* Crowd Curve -- About story: miniature DEAL THREAD (about_mini_deals.html).
 * A postcard-scale recreation of the deal-thread screen in pure CSS: the
 * pipeline bar, the mail thread with role tags, the AI read box, the final
 * terms. Every invented class is md- prefixed; every color and font goes
 * through the app.css tokens, so both themes come for free.
 *
 * ANIMATION CONTRACT: the story engine toggles class `on` on the ancestor
 * .frame. Every entrance below is a descendant rule of `.frame.on` using a
 * keyframe animation with `both` fill, so the choreography replays on every
 * activation and elements hold their hidden start state through their delay.
 * Single-`from` keyframes animate TO the element's own computed style, which
 * keeps every landing color a token, never a copy. Each frame's choreography
 * completes within ~3.5s (frames advance every 5s); the global
 * prefers-reduced-motion override in app.css flattens all of it to instant. */

.mini-deals { width: 100%; overflow: hidden; }

/* ------------------------------------------------- pipeline stage bar */

.md-bar {
  display: flex; gap: 4px; padding: 4px;
  background: var(--sunken); border: 1px solid var(--line);
  border-radius: var(--radius-s);
}
.md-step {
  flex: 1; min-width: 0; text-align: center; padding: .32rem .2rem;
  font: 600 .55rem/1.4 var(--font-mono); text-transform: uppercase;
  letter-spacing: .06em; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
  color: var(--faint); border-radius: 5px;
}
.md-step.md-done { color: var(--muted); }
/* The active stage lights in the pipeline's color language:
   sent = accent, in motion = warn, money confirmed = good. */
.md-step.md-on.md-sent { color: var(--accent); background: var(--accent-soft); }
.md-step.md-on.md-neg  { color: var(--warn);   background: var(--warn-soft); }
.md-step.md-on.md-conf { color: var(--good);   background: var(--good-soft); }

/* ------------------------------------------------- mail thread */

.md-thread { margin-top: .55rem; }
.md-msg { padding: .5rem 0 .55rem; border-bottom: 1px solid var(--line); }
.md-msg:last-child { border-bottom: none; }
.md-head { display: flex; align-items: baseline; gap: .45rem; min-width: 0; }
.md-who { font: 600 .76rem/1.4 var(--font-ui); color: var(--bright); }
.md-msg.md-out .md-who { color: var(--accent); }
.md-tag {
  font: 600 .52rem/1.4 var(--font-mono); text-transform: uppercase;
  letter-spacing: .07em; color: var(--faint);
  border: 1px solid var(--line-strong); border-radius: 3px;
  padding: .04rem .32rem; white-space: nowrap;
}
.md-tag.md-me { color: var(--accent); border-color: var(--accent-soft); }
.md-when { font: 500 .6rem/1.4 var(--font-mono); color: var(--faint); white-space: nowrap; }
.md-body { margin: .28rem 0 0; font-size: .74rem; line-height: 1.55; color: var(--ink); }
/* Frame 2's recap of the sent offer: one dimmed line, ellipsized. */
.md-msg.md-dim { opacity: .55; }
.md-body.md-clamp { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.md-attach {
  display: inline-flex; align-items: center; gap: .35rem; margin-top: .45rem;
  padding: .2rem .55rem;
  border: 1px solid var(--line-strong); border-radius: var(--radius-s);
  font: 500 .62rem/1.4 var(--font-mono); color: var(--muted);
}
.md-attach svg { flex: 0 0 auto; }

/* ------------------------------------------------- AI read box */

.md-read {
  margin-top: .6rem; padding: .55rem .75rem .6rem;
  border: 1px solid var(--line); border-radius: var(--radius);
}
.md-read-top { display: flex; align-items: center; gap: .55rem; }
.md-eyebrow {
  font: 600 .55rem/1.4 var(--font-mono); text-transform: uppercase;
  letter-spacing: .1em; color: var(--faint); white-space: nowrap;
}
.md-read-sum { margin: .35rem 0 0; font-size: .76rem; line-height: 1.55; color: var(--ink); font-weight: 550; }

/* Reused .pill/.status-* chips, compacted to postcard scale. */
.mini-deals .pill { font-size: .56rem; padding: .14rem .5rem .14rem .42rem; letter-spacing: .06em; }

/* ------------------------------------------------- final terms */

.md-final {
  margin-top: .8rem; padding: .85rem .95rem .9rem;
  background: var(--sunken); border-radius: var(--radius);
  display: flex; flex-direction: column; gap: .15rem; align-items: flex-start;
}
.md-final-label {
  font: 600 .58rem/1.5 var(--font-mono); text-transform: uppercase;
  letter-spacing: .09em; color: var(--faint);
}
.md-final-value {
  font: 600 1.75rem/1.25 var(--font-mono); letter-spacing: -.02em;
  color: var(--bright); font-variant-numeric: tabular-nums;
}
.md-final-sub { font: 500 .64rem/1.5 var(--font-mono); color: var(--faint); }
.md-confirm { margin-top: .5rem; }

/* ------------------------------------------------- choreography */

/* All frames: the bar surfaces first, then the active stage ignites from
   faint/transparent to its own lit colors (frame 1 accent, 2 warn, 3 good). */
.frame.on .md-bar { animation: md-fade .35s ease both; }
.frame.on .md-step.md-on { animation: md-ignite .45s ease .35s both; }

/* Frames 1-2: messages ride in after the bar; frame 2's dimmed recap of the
   sent offer fades up front so the counter reads as a reply. The attachment
   line trails its message. */
.frame.on .md-msg { animation: md-rise .4s ease .85s both; }
.frame.on .md-msg.md-dim { animation: md-fade .35s ease .1s both; }
.frame.on .md-attach { animation: md-rise .35s ease 1.3s both; }

/* Frame 2's payoff: the AI read lands last with a warn ring that settles. */
.frame.on .md-read {
  animation: md-land .5s ease 1.7s both, md-settle .9s ease 2.2s both;
}

/* Frame 3: the stat block rises, then the confirmed chip pops. */
.frame.on .md-final { animation: md-rise .45s ease .8s both; }
.frame.on .md-confirm { animation: md-pop .35s ease 1.5s both; }

/* Single-`from` keyframes: the end state is the element's own styles. */
@keyframes md-fade { from { opacity: 0; } }
@keyframes md-rise { from { opacity: 0; transform: translateY(10px); } }
@keyframes md-pop { from { opacity: 0; transform: scale(.8); } }
@keyframes md-ignite { from { color: var(--faint); background: transparent; } }
@keyframes md-land { from { opacity: 0; transform: translateY(14px) scale(.98); } }
@keyframes md-settle {
  from { box-shadow: 0 0 0 3px var(--warn-soft); }
  to { box-shadow: 0 0 0 0 transparent; }
}


/* ================= about mini: shows + daily figures (agent-built 2026-09-29) ================= */
/* Crowd Curve -- about-page miniature of the SHOWS table and the Daily
 * figures ledger (companion fragment:
 * web/templates/partials/about_mini_sales.html).
 *
 * Plugs into the story stage's frame system in app.css: the engine adds and
 * removes `on` on an ancestor .frame, so EVERY entrance below is a
 * descendant rule of `.frame.on` with a `both`-fill ms-* keyframe animation.
 * Remove `on` and the animations unmount; re-add it and the whole
 * choreography replays from zero. Each frame completes within ~3.5s
 * (frames advance every 5s). Number count-ups (.count spans) are run by the
 * external story engine -- no JS here.
 *
 * Colors and fonts go through app.css custom properties only, so both
 * themes work for free. The surrounding stage card draws the chrome; the
 * mini draws none. */

.mini-sales {
  width: 100%;
  overflow: hidden;
  font-family: var(--font-ui);
  color: var(--ink);
}

/* -------------------------------------------------- shared table idiom */

.ms-table { width: 100%; border-collapse: collapse; }
.ms-table th, .ms-table td {
  padding: .34rem .45rem;
  text-align: left; white-space: nowrap;
  border-bottom: 1px solid var(--line);
}
.ms-table th:first-child, .ms-table td:first-child { padding-left: .15rem; }
.ms-table th:last-child, .ms-table td:last-child { padding-right: .15rem; }
.ms-table thead th {
  color: var(--faint);
  font: 500 .58rem/1.5 var(--font-mono);
  text-transform: uppercase; letter-spacing: .08em;
}
.ms-table tbody td { font-size: .73rem; }
.ms-table tbody tr:nth-child(odd) td { background: var(--zebra); }  /* subtle zebra */
.ms-table tbody tr:last-child td { border-bottom: none; }

/* Numbers: mono, right-aligned, tabular. */
.ms-table .ms-num { text-align: right; }
.ms-table td.ms-num {
  font-family: var(--font-mono); font-size: .7rem; font-weight: 600;
  color: var(--bright); font-variant-numeric: tabular-nums;
}
.mini-sales .count { display: inline-block; min-width: 3ch; text-align: right; }

.ms-artist { font-weight: 650; color: var(--bright); font-size: .75rem; }
.ms-date { color: var(--muted); font-size: .7rem; }
.ms-cap {
  color: var(--faint); font-weight: 500; font-size: .64rem;
  font-family: var(--font-mono);
}
.ms-table td.ms-delta { color: var(--good); }
.ms-src { font: 500 .6rem/1.6 var(--font-mono); color: var(--faint); }

/* The ledger's date is the row label -- full ink, like the real table. */
.ms-ledger td.ms-date { color: var(--ink); font-size: .72rem; }

/* Compact the reused .pill/.status-onsale chips to mini scale. */
.mini-sales .pill {
  font-size: .54rem; padding: .12rem .48rem .12rem .4rem;
  gap: .32em; letter-spacing: .05em;
}
.mini-sales .pill::before { width: 5px; height: 5px; }

/* Ledger footer: where the counts can come from. */
.ms-foot {
  margin: .55rem 0 0; padding: .5rem .15rem 0;
  border-top: 1px solid var(--line);
  font: 500 .62rem/1.5 var(--font-mono);
  color: var(--faint); letter-spacing: .02em;
}

/* ------------------------- frame 1: shows table -- rows rise in, top to
   bottom, after the header fades up. The 971 count-up runs on activation. */

.frame.on .ms-shows thead th { animation: ms-fade .35s ease .05s both; }
.frame.on .ms-shows tbody td {
  animation: ms-rise .5s cubic-bezier(.2, .7, .3, 1) both;
}
.frame.on .ms-shows tbody tr:nth-child(1) td { animation-delay: .15s; }
.frame.on .ms-shows tbody tr:nth-child(2) td { animation-delay: .33s; }
.frame.on .ms-shows tbody tr:nth-child(3) td { animation-delay: .51s; }
.frame.on .ms-shows tbody tr:nth-child(4) td { animation-delay: .69s; }

/* --------------------- frame 2: daily-figures ledger -- reports land one
   by one (drop in with a brief accent wash, painted as an inset shadow so
   the zebra fill underneath survives the `both` fill), then the sources
   footer fades in. Last landing ends ~2.95s, footer ~3.25s. */

.frame.on .ms-ledger thead th { animation: ms-fade .35s ease .05s both; }
.frame.on .ms-ledger tbody td { animation: ms-land .9s ease-out both; }
.frame.on .ms-ledger tbody tr:nth-child(1) td { animation-delay: .25s; }
.frame.on .ms-ledger tbody tr:nth-child(2) td { animation-delay: .85s; }
.frame.on .ms-ledger tbody tr:nth-child(3) td { animation-delay: 1.45s; }
.frame.on .ms-ledger tbody tr:nth-child(4) td { animation-delay: 2.05s; }
.frame.on .ms-foot { animation: ms-fade .55s ease 2.7s both; }

/* ---------------------------------------------------------- keyframes */

@keyframes ms-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes ms-rise {
  from { opacity: 0; transform: translateY(9px); }
  to   { opacity: 1; transform: none; }
}
@keyframes ms-land {
  0% {
    opacity: 0; transform: translateY(-8px);
    box-shadow: inset 0 0 0 999px transparent;
  }
  30% {
    opacity: 1; transform: none;
    box-shadow: inset 0 0 0 999px
      color-mix(in srgb, var(--accent) 16%, transparent);
  }
  100% {
    opacity: 1; transform: none;
    box-shadow: inset 0 0 0 999px transparent;
  }
}


/* ================= about mini: show dashboard (agent-built 2026-09-29) ================= */
/* Mini SHOW DASHBOARD -- the About page's animated recreation of the real
 * forecast screen (partials/about_mini_dash.html). Every color rides the
 * app.css tokens, so both themes come free; every invented class is mc-*.
 *
 * This mini is NOT in a cycling frame: it animates ONCE, when the
 * surrounding .curve-sect gains .in (the data-reveal observer in app.js).
 * Choreography, t=0 at the .in flip:
 *
 *   0.05-0.65s  the four stat tiles pop in, left to right (ccCount runs
 *               the number count-ups in parallel)
 *   0.30s       the chart frame (grid, cap line, axes) fades up first
 *   0.60s       benchmark paths fade in: gray dashed sellout, green
 *               dashed break-even
 *   0.85s       legend fades
 *   1.0-2.2s    the teal ACTUAL line draws itself left to right --
 *               pathLength="100" + dash trick, same as .about-chart.
 *               Linear timing, so with the normalized dash the pen moves
 *               at constant arc-length speed and each sale-dot's delay
 *               below equals its arc-length share of the path.
 *   2.10s       the "today" rule lands at the line's end
 *   2.40s       the dotted implied trajectory fades in, overshooting to
 *               the cap
 *   2.80s       the Paid-push-plan strip rises in
 *
 * Reduced motion: app.css's global prefers-reduced-motion block flattens
 * every animation to instant; `both` fill then just shows the end state. */

/* ------------------------------------------------------------- layout */

.mini-dash {
  max-width: 720px; margin: 0 auto; text-align: left;
  display: flex; flex-direction: column; gap: .6rem;
  font-family: var(--font-ui);
}

.mc-tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: .45rem; }
@media (max-width: 640px) { .mc-tiles { grid-template-columns: repeat(2, 1fr); } }

.mc-tile {
  background: var(--sunken); border-radius: var(--radius-s);
  padding: .45rem .6rem .5rem;
  display: flex; flex-direction: column; gap: .05rem; min-width: 0;
}
.mc-label {
  font: 600 .52rem/1.5 var(--font-mono); color: var(--faint);
  text-transform: uppercase; letter-spacing: .09em; white-space: nowrap;
}
.mc-value {
  font: 600 1.05rem/1.25 var(--font-mono); letter-spacing: -.02em;
  color: var(--bright);
}
.mc-value.mc-good { color: var(--good); }
.mc-sub {
  font-size: .62rem; color: var(--faint); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}

/* ------------------------------------------------------------- chart */

.mc-chart { width: 100%; height: auto; display: block; }

.mc-axis { fill: var(--faint); font: 500 10px/1 var(--font-ui); text-anchor: middle; }
.mc-axis.mc-end { text-anchor: end; }
.mc-axis.mc-ylab {
  text-anchor: end; font: 500 9.5px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
}
.mc-tick { stroke: var(--line); stroke-width: 1; opacity: .55; }
.mc-cap { stroke: var(--line); stroke-width: 1; }
.mc-axis-line { stroke: var(--line-strong); stroke-width: 1; }
.mc-today { stroke: var(--line-strong); stroke-width: 1; }

.mc-so { fill: none; stroke: var(--faint); stroke-width: 1.4; stroke-dasharray: 5 4; }
.mc-be { fill: none; stroke: var(--good); stroke-width: 1.4; stroke-dasharray: 5 4; }
.mc-implied {
  fill: none; stroke: var(--accent); stroke-width: 1.3;
  stroke-dasharray: 2 4; stroke-linecap: round; opacity: .65;
}
.mc-actual {
  fill: none; stroke: var(--accent); stroke-width: 2.4;
  stroke-linejoin: round; stroke-linecap: round;
}
.mc-dot {
  fill: var(--accent);
  transform-box: fill-box; transform-origin: center;  /* pop scales in place */
}

/* legend */
.mc-legend {
  display: flex; flex-wrap: wrap; gap: .25rem .9rem;
  font-size: .66rem; color: var(--faint);
}
.mc-li {
  display: inline-block; width: 14px; height: 0;
  border-top: 2px solid var(--faint);
  margin-right: .32rem; vertical-align: middle;
}
.mc-li-actual { border-color: var(--accent); }
.mc-li-be { border-top-style: dashed; border-color: var(--good); }
.mc-li-so { border-top-style: dashed; border-color: var(--faint); }
.mc-li-implied { border-top-style: dotted; border-color: var(--accent); }

/* recommendation strip: a quiet callout, the action phrase carries the color */
.mc-reco {
  background: var(--sunken); border-radius: var(--radius-s);
  padding: .5rem .7rem .55rem;
}
.mc-reco-label {
  display: block; font: 600 .52rem/1.5 var(--font-mono); color: var(--faint);
  text-transform: uppercase; letter-spacing: .09em;
}
.mc-reco-text { margin: .1rem 0 0; font-size: .76rem; line-height: 1.5; color: var(--ink); }
.mc-reco-text strong { color: var(--good); font-weight: 650; }

/* ----------------------------------------------------- pre-reveal states
   Gated on html.js like every reveal in app.css: if the script never runs,
   nothing here applies and the finished dashboard simply shows. With JS,
   these hold each piece hidden until .curve-sect gains .in, at which point
   the `both`-filled animations below take over (an animation's fill beats
   these static declarations in the cascade). */

html.js .mini-dash .mc-tile,
html.js .mini-dash .mc-frame,
html.js .mini-dash .mc-so,
html.js .mini-dash .mc-be,
html.js .mini-dash .mc-legend,
html.js .mini-dash .mc-today,
html.js .mini-dash .mc-implied,
html.js .mini-dash .mc-dot,
html.js .mini-dash .mc-reco { opacity: 0; }
html.js .mini-dash .mc-actual { stroke-dasharray: 100; stroke-dashoffset: 100; }

/* ------------------------------------------------------ the choreography
   Fires once: every rule is a descendant of .curve-sect.in, every
   animation fills `both`. */

.curve-sect.in .mini-dash .mc-tile { animation: mc-pop .45s ease-out both; }
.curve-sect.in .mini-dash .mc-tile:nth-child(1) { animation-delay: .05s; }
.curve-sect.in .mini-dash .mc-tile:nth-child(2) { animation-delay: .25s; }
.curve-sect.in .mini-dash .mc-tile:nth-child(3) { animation-delay: .45s; }
.curve-sect.in .mini-dash .mc-tile:nth-child(4) { animation-delay: .65s; }

.curve-sect.in .mini-dash .mc-frame { animation: mc-fade .5s ease-out .3s both; }
.curve-sect.in .mini-dash .mc-so,
.curve-sect.in .mini-dash .mc-be { animation: mc-fade .55s ease-out .6s both; }
.curve-sect.in .mini-dash .mc-legend { animation: mc-fade .5s ease-out .85s both; }

.curve-sect.in .mini-dash .mc-actual {
  stroke-dasharray: 100;
  animation: mc-draw 1.2s linear 1s both;
}

/* Sale-dots pop as the pen passes: delay = 1s + 1.2s x the dot's
   arc-length fraction of the actual path (fractions .19/.39/.64/.77/.92/1). */
.curve-sect.in .mini-dash .mc-dot { animation: mc-dot-pop .25s ease-out both; }
.curve-sect.in .mini-dash .mc-dot:nth-of-type(1) { animation-delay: 1.25s; }
.curve-sect.in .mini-dash .mc-dot:nth-of-type(2) { animation-delay: 1.45s; }
.curve-sect.in .mini-dash .mc-dot:nth-of-type(3) { animation-delay: 1.75s; }
.curve-sect.in .mini-dash .mc-dot:nth-of-type(4) { animation-delay: 1.9s; }
.curve-sect.in .mini-dash .mc-dot:nth-of-type(5) { animation-delay: 2.1s; }
.curve-sect.in .mini-dash .mc-dot:nth-of-type(6) { animation-delay: 2.2s; }

.curve-sect.in .mini-dash .mc-today { animation: mc-fade .35s ease-out 2.1s both; }
.curve-sect.in .mini-dash .mc-implied { animation: mc-fade-dim .6s ease-out 2.4s both; }
.curve-sect.in .mini-dash .mc-reco { animation: mc-rise .5s ease-out 2.8s both; }

/* ---------------------------------------------------------- keyframes */

@keyframes mc-pop {
  from { opacity: 0; transform: translateY(8px) scale(.97); }
  to   { opacity: 1; transform: none; }
}
@keyframes mc-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
/* the implied trajectory rests at .65, not 1 */
@keyframes mc-fade-dim {
  from { opacity: 0; }
  to   { opacity: .65; }
}
@keyframes mc-draw {
  from { stroke-dashoffset: 100; }
  to   { stroke-dashoffset: 0; }
}
@keyframes mc-dot-pop {
  from { opacity: 0; transform: scale(.3); }
  to   { opacity: 1; transform: none; }
}
@keyframes mc-rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}


/* ================= about mini tweaks (2026-09-29) ================= */
/* Saved-preset selects in the booking frames -- the product's "Load saved
   costs/tiers" dropdowns, shown with a chosen value. */
.mb-loadrow { display: flex; align-items: center; gap: .5rem; margin: .05rem 0 .5rem; }
.mb-select {
  display: inline-flex; align-items: center; gap: .5rem;
  background: var(--sunken); border-radius: var(--radius-s);
  padding: .3rem .6rem; font: 550 .66rem/1.2 var(--font-ui); color: var(--ink);
}
.mb-caret { color: var(--faint); font-size: .58rem; }
.mb-loadhint {
  font: 600 .52rem/1.2 var(--font-mono); text-transform: uppercase;
  letter-spacing: .08em; color: var(--faint);
}
.frame.on .mb-loadrow { animation: mb-rise .35s ease both; }


/* ================= about mini: economics (agent-built 2026-09-29) ================= */
/* ================= about mini: booking economics (agent-built 2026-09-29) ================= */
/* Crowd Curve -- About story: miniature BOOKING ECONOMICS
 * (partials/about_mini_econ.html). Two frames for one stage-card: the
 * profit-vs-tickets chart and the ticket slide. Every invented class is
 * me- prefixed; every color and font goes through the app.css tokens,
 * so both themes come for free. The surrounding stage card supplies the
 * window chrome; the mini draws none.
 *
 * ANIMATION CONTRACT: the story engine toggles class `on` on the ancestor
 * .frame, so every entrance below is a descendant rule of `.frame.on`
 * using a me-* keyframe animation with `both` fill -- it holds its hidden
 * start state through its delay and replays on every reactivation.
 * Single-`from` keyframes animate TO the element's own computed style,
 * which keeps every landing color and opacity a token, never a copy.
 * Each frame's choreography completes within ~2.6s (frames advance every
 * 5s). The .count spans are run by the external story engine -- no JS
 * here. The global prefers-reduced-motion override in app.css flattens
 * all of it to instant, and the mobile .m-mock fallback strips the
 * animations, leaving the finished first frame. */

.mini-econ {
  width: 100%;
  overflow: hidden;
  font-family: var(--font-ui);
  color: var(--ink);
}

/* Count-up spans hold their width so the columns never shudder. */
.mini-econ .count { display: inline-block; min-width: 3ch; text-align: right; }

/* ------------------------------------ frame 1: profit vs tickets sold */

.me-chart { width: 100%; height: auto; display: block; }

/* dashed zero line -- the axis the profit line has to climb over */
.me-zero { stroke: var(--line-strong); stroke-width: 1; stroke-dasharray: 5 4; }

/* dotted verticals under the break-even and kink markers */
.me-vline { stroke-width: 1; stroke-dasharray: 2 4; stroke-linecap: round; opacity: .55; }
.me-vbe { stroke: var(--good); }
.me-vkink { stroke: var(--warn); }

/* the profit line itself, drawn by me-draw below */
.me-line {
  fill: none; stroke: var(--accent); stroke-width: 2.6;
  stroke-linejoin: round; stroke-linecap: round;
}

/* marker dots pop in place: fill-box origin, same as the dash mini */
.me-dot { transform-box: fill-box; transform-origin: center; }
.me-dot-be { fill: var(--good); }
.me-dot-kink { fill: var(--warn); }

/* tiny mono corner labels */
.me-lab {
  fill: var(--faint); font: 500 9.5px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
}
.me-lab.me-end { text-anchor: end; }

/* legend row under the chart */
.me-legend {
  display: flex; flex-wrap: wrap; gap: .25rem 1.1rem; margin-top: .3rem;
  font: 500 .62rem/1.5 var(--font-mono); color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.me-key { display: inline-flex; align-items: center; gap: .38rem; white-space: nowrap; }
.me-dotkey { width: 7px; height: 7px; border-radius: 999px; flex: 0 0 auto; }
.me-dk-be { background: var(--good); }
.me-dk-kink { background: var(--warn); }

/* the marginal-dollar explainer */
.me-note { margin: .55rem 0 0; font-size: .68rem; line-height: 1.55; color: var(--muted); }

/* Choreography: zero line and corner labels fade up, the profit line draws
   itself left to right (pathLength=100 dash trick, linear so the pen moves
   at constant arc-length speed), the break-even dot pops as the pen passes
   (~60% of arc at ~1.2s) then the kink dot (~75% at ~1.5s), each pulling
   its dotted vertical up with it; legend fades, explainer rises last. */
.frame.on .me-zero,
.frame.on .me-lab { animation: me-fade .35s ease .2s both; }
.frame.on .me-line {
  stroke-dasharray: 100;
  animation: me-draw 1.2s linear .5s both;
}
.frame.on .me-vbe { animation: me-fade .3s ease 1.2s both; }
.frame.on .me-dot-be { animation: me-pop .3s ease-out 1.2s both; }
.frame.on .me-vkink { animation: me-fade .3s ease 1.5s both; }
.frame.on .me-dot-kink { animation: me-pop .3s ease-out 1.5s both; }
.frame.on .me-legend { animation: me-fade .35s ease 1.8s both; }
.frame.on .me-note { animation: me-rise .4s ease 2.2s both; }

/* ------------------------------------------ frame 2: the ticket slide */

.me-table { width: 100%; border-collapse: collapse; }
.me-table th, .me-table td {
  padding: .28rem .45rem;
  text-align: left; white-space: nowrap;
  border-bottom: 1px solid var(--line);
}
.me-table th:first-child, .me-table td:first-child { padding-left: .15rem; }
.me-table th:last-child, .me-table td:last-child { padding-right: .15rem; }
.me-table thead th {
  color: var(--faint);
  font: 500 .58rem/1.5 var(--font-mono);
  text-transform: uppercase; letter-spacing: .08em;
}
.me-table .me-num { text-align: right; }
.me-table td {
  font: 500 .68rem/1.5 var(--font-mono);
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.me-table td.me-tier { font-weight: 650; color: var(--bright); }
.me-table tbody tr:nth-child(odd) td { background: var(--zebra); }  /* trader-UI zebra */
.me-table tbody tr:last-child td { border-bottom: none; }

/* the red-to-green story of the slide */
.me-table td.me-loss { color: var(--bad); }
.me-table td.me-gain { color: var(--good); }

/* rows where the artist is earning above the bare guarantee -- the same
   warm wash the real ticket slide uses (table.slide tr.binds) */
.me-table tr.me-bind td { background: var(--warn-soft); }

.me-foot { margin: .5rem 0 0; font-size: .66rem; color: var(--faint); }

/* Choreography: header fades, then the seven cumulative rows rise in top
   to bottom -- the red numbers turning green as the scan moves down IS the
   show -- and the footer hint fades last. Rows animate on the tds, not the
   trs, so the zebra and bind fills survive the `both` fill. */
.frame.on .me-table thead th { animation: me-fade .3s ease .05s both; }
.frame.on .me-table tbody td {
  animation: me-rise .4s cubic-bezier(.2, .7, .3, 1) both;
}
.frame.on .me-table tbody tr:nth-child(1) td { animation-delay: .1s; }
.frame.on .me-table tbody tr:nth-child(2) td { animation-delay: .28s; }
.frame.on .me-table tbody tr:nth-child(3) td { animation-delay: .46s; }
.frame.on .me-table tbody tr:nth-child(4) td { animation-delay: .64s; }
.frame.on .me-table tbody tr:nth-child(5) td { animation-delay: .82s; }
.frame.on .me-table tbody tr:nth-child(6) td { animation-delay: 1s; }
.frame.on .me-table tbody tr:nth-child(7) td { animation-delay: 1.18s; }
.frame.on .me-foot { animation: me-fade .45s ease 1.75s both; }

/* ---------------------------------------------------------- keyframes */

/* Single-`from` keyframes: the end state is the element's own styles
   (so the .55-opacity verticals land at .55, not 1). */
@keyframes me-fade { from { opacity: 0; } }
@keyframes me-rise { from { opacity: 0; transform: translateY(8px); } }
@keyframes me-pop { from { opacity: 0; transform: scale(.3); } }
@keyframes me-draw {
  from { stroke-dashoffset: 100; }
  to   { stroke-dashoffset: 0; }
}


/* ================= tactile buttons (2026-09-30) ================= */
/* A small lift on hover, a press on click; primaries bloom a soft accent
   shadow. Transform/shadow only -- colors keep coming from the base
   rules; text-style .link buttons stay still; the global reduced-motion
   block flattens all of it. */
button:not(.link), .button {
  transition: background .12s ease, border-color .12s ease, color .12s ease,
              transform .13s ease, box-shadow .13s ease;
}
button:not(.link):hover, .button:hover { transform: translateY(-1.5px); }
button:not(.link):active, .button:active { transform: translateY(0) scale(.96); }
button.primary:hover, .button.primary:hover {
  transform: translateY(-1.5px) scale(1.02);
  box-shadow: 0 8px 18px -8px color-mix(in srgb, var(--accent) 60%, transparent);
}
button.primary:active, .button.primary:active { box-shadow: none; }

/* ===== venue manager: builder header buttons + modal ===== */
/* Header buttons: same compact sizing as .save-venue-btn. */
.venue-mgr-btn {
  min-height: 32px; font-size: .76rem; padding: .2rem .7rem;
  flex: 0 0 auto; white-space: nowrap;
}

.venue-modal { max-width: 420px; }

.venue-modal-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: .6rem .8rem;
  margin: .6rem 0 .2rem;
}
.venue-modal-grid label {
  display: flex; flex-direction: column; gap: .25rem;
  font-size: .78rem; color: var(--muted);
}
.venue-modal-grid .vm-span-2 { grid-column: 1 / -1; }

.venue-modal-actions { align-items: center; }
.venue-modal-actions .vm-spacer { flex: 1; }

/* The builder header becomes a 2-col grid on phones (app.css @720px); the
   buttons fill their cells like the inputs do. */
@media (max-width: 720px) {
  .head-sub .venue-mgr-btn { width: 100%; }
}

