/* Accord landing page.
   The film is the product onboarding, unchanged: turn-based rather than
   scrolled, one idea on screen at a time, each with a single animation that
   finishes before the next begins. Animations are bound to .is-live so they
   replay on revisit instead of firing once at load.

   The only structural difference from the in-app version is that the page
   scrolls - the film occupies the first viewport and the waitlist sits below
   it - so the film's own chrome is positioned against .film rather than the
   viewport, and scrolls away with it. */

:root {
  --bg: #fbfaf8;
  --panel: #ffffff;
  --ink: #1b1a17;
  --ink-2: #55524b;
  --ink-3: #8a867d;
  --line: #e3dfd7;
  --line-2: #d2cdc2;
  --accent: #2f6f4f;
  --accent-soft: #e7f1ea;
  --focus: #1f5fd0;
  --focus-soft: #e6eefb;
  --warn: #a9601a;
  --bad: #a33a2c;
  --serif: "Iowan Old Style", Georgia, "Times New Roman", serif;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, monospace;

  /* The page is light-only. Declaring it stops the browser from rendering the
     form controls in its own dark UA styles for a visitor whose system is set
     to dark - which is what would otherwise leave dark inputs sitting in a
     light page. */
  color-scheme: light;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font-family: var(--sans);
}

/* ------------------------------------------------------------- scaffold */

/* svh, not vh: on mobile the address bar would otherwise push the chapter
   dots below the fold on first paint. */
.film {
  position: relative; width: 100%;
  height: 100svh; min-height: 560px;
  display: grid; place-items: center;
}

.scene {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; padding: 40px 28px 120px; text-align: center;
  opacity: 0; visibility: hidden;
  transform: translateY(14px);
  transition: opacity .55s ease, transform .55s ease, visibility .55s;
}
.scene.is-live { opacity: 1; visibility: visible; transform: none; }
/* Leaving scenes drift the opposite way, so forward and back read directionally. */
.scene.is-past { transform: translateY(-14px); }

.scene h2 {
  font-family: var(--serif); font-size: 34px; font-weight: 600;
  letter-spacing: -.015em; margin: 26px 0 0; line-height: 1.15;
  /* Wide enough that a full-sentence headline lands in two or three lines.
     At 18ch the longest ones broke into four, which reads as a stack of
     fragments rather than a sentence. */
  max-width: 24ch;
}
.scene p {
  margin: 12px 0 0; font-size: 15px; line-height: 1.6;
  color: var(--ink-2); max-width: 48ch;
}
.scene.is-live h2 { animation: rise .6s .18s ease both; }
.scene.is-live p  { animation: rise .6s .30s ease both; }

/* An explicit `to` is required: elements that start at opacity 0 would
   otherwise animate straight back to their own hidden base value. */
@keyframes rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

.stage {
  position: relative; width: min(560px, 90vw); height: 300px;
  display: grid; place-items: center;
}

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

.hero-title {
  font-family: var(--serif); font-weight: 600; letter-spacing: -.02em;
  font-size: clamp(38px, 7vw, 76px); line-height: 1.05;
  margin: 0; max-width: 16ch; position: relative; z-index: 2;
}
.hero-title .w { display: inline-block; }
.hero.is-live .hero-title .w,
.end.is-live .hero-title .w { animation: wordIn .8s cubic-bezier(.2,.8,.2,1) both; }
.hero.is-live .w:nth-child(1), .end.is-live .w:nth-child(1) { animation-delay: .10s; }
.hero.is-live .w:nth-child(2), .end.is-live .w:nth-child(2) { animation-delay: .20s; }
.hero.is-live .w:nth-child(3), .end.is-live .w:nth-child(3) { animation-delay: .30s; }
.hero.is-live .w:nth-child(4), .end.is-live .w:nth-child(4) { animation-delay: .40s; }

@keyframes wordIn {
  from { opacity: 0; transform: translateY(26px) rotate(-2deg); filter: blur(6px); }
}

.hero-sub {
  position: relative; z-index: 2; margin-top: 22px !important;
  font-family: var(--mono); font-size: 12px; letter-spacing: .28em;
  text-transform: uppercase; color: var(--ink-3);
}
.hero.is-live .hero-sub { animation: rise .8s .7s ease both; }

.cta {
  position: relative; z-index: 2; margin-top: 30px;
  padding: 12px 22px; border-radius: 999px; text-decoration: none;
  font-size: 15px; background: var(--accent); color: var(--bg);
  transition: transform .2s ease, filter .2s ease;
}
.cta:hover { transform: translateY(-2px); filter: brightness(1.08); }

/* The quieter of the two: it goes one section down rather than to the ask, so
   it should read as the lesser commitment it is. */
.cta-ghost {
  background: transparent; color: var(--ink);
  border: 1px solid var(--line-2);
  /* The filled button's border is its background; matching the padding here
     keeps the two the same height. */
  padding: 11px 21px;
}
.cta-ghost:hover { filter: none; border-color: var(--ink-3); }

.cta-row {
  position: relative; z-index: 2; margin-top: 30px;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px;
}
.cta-row .cta { margin-top: 0; }
.end.is-live .cta { animation: rise .7s .6s ease both; }
.end.is-live .cta-ghost { animation-delay: .5s; }

/* drifting orbs behind the hero */
.orbs { position: absolute; inset: 0; overflow: hidden; z-index: 0; }
.orb {
  position: absolute; border-radius: 50%; filter: blur(58px); opacity: .5;
  animation: drift 18s ease-in-out infinite alternate;
}
.o1 { width: 340px; height: 340px; left: 12%; top: 16%; background: var(--accent-soft); }
.o2 { width: 300px; height: 300px; right: 14%; top: 30%; background: var(--focus-soft);
      animation-duration: 22s; animation-delay: -6s; }
.o3 { width: 260px; height: 260px; left: 42%; bottom: 8%; background: #f6e6cf;
      animation-duration: 26s; animation-delay: -12s; }

@keyframes drift {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(46px, -38px) scale(1.14); }
}

/* constellation that draws itself in */
.constellation { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; }
.constellation .lines line {
  stroke: var(--line-2); stroke-width: 1.2; stroke-dasharray: 300;
  stroke-dashoffset: 300; opacity: .8;
}
.constellation .stars circle { fill: var(--accent); opacity: 0; }
.hero.is-live .lines line { animation: drawLine 1.6s ease forwards; }
.hero.is-live .lines line:nth-child(2) { animation-delay: .18s; }
.hero.is-live .lines line:nth-child(3) { animation-delay: .34s; }
.hero.is-live .lines line:nth-child(4) { animation-delay: .12s; }
.hero.is-live .lines line:nth-child(5) { animation-delay: .40s; }
.hero.is-live .lines line:nth-child(6) { animation-delay: .52s; }
.hero.is-live .stars circle { animation: pop .5s ease forwards; }
.hero.is-live .stars circle:nth-child(2) { animation-delay: .16s; }
.hero.is-live .stars circle:nth-child(3) { animation-delay: .30s; }
.hero.is-live .stars circle:nth-child(4) { animation-delay: .44s; }
.hero.is-live .stars circle:nth-child(5) { animation-delay: .24s; }
.hero.is-live .stars circle:nth-child(6) { animation-delay: .56s; }

@keyframes drawLine { to { stroke-dashoffset: 0; } }
@keyframes pop {
  0%   { opacity: 0; transform: scale(.2); transform-origin: center; }
  70%  { opacity: 1; transform: scale(1.5); }
  100% { opacity: .9; transform: scale(1); }
}

/* ------------------------------------------------- 1. upload ground truth */

.tray {
  position: absolute; bottom: 42px; width: 260px; height: 12px;
  border-radius: 8px; background: var(--line);
}
.doc {
  position: absolute; width: 108px; height: 138px; border-radius: 10px;
  background: var(--panel); border: 1px solid var(--line-2);
  box-shadow: 0 10px 26px rgba(0,0,0,.08);
  padding: 16px 12px; display: flex; flex-direction: column; gap: 8px;
  opacity: 0;
}
.doc span { height: 6px; border-radius: 3px; background: var(--line); }
.doc span:nth-child(2) { width: 78%; }
.doc span:nth-child(3) { width: 54%; }
.doc-label {
  position: absolute; font-size: 11px; font-family: var(--mono);
  color: var(--ink-3); opacity: 0;
}
/* These are bare, globally-scoped class names on positioned elements, which
   makes them a trap: any later element that happens to use .l1 and is not
   statically positioned inherits this offset. Keep new classes prefixed. */
.l1 { left: 4%;  top: 20%; } .l2 { right: 2%; top: 14%; } .l3 { right: 6%; bottom: 26%; }

[data-scene="1"].is-live .d1 { animation: dropIn .9s .15s cubic-bezier(.2,.8,.2,1) both; --rot: -8deg; --tx: -74px; }
[data-scene="1"].is-live .d2 { animation: dropIn .9s .40s cubic-bezier(.2,.8,.2,1) both; --rot: 3deg;  --tx: 0px; }
[data-scene="1"].is-live .d3 { animation: dropIn .9s .65s cubic-bezier(.2,.8,.2,1) both; --rot: 9deg;  --tx: 74px; }
[data-scene="1"].is-live .doc-label { animation: rise .6s ease both; }
[data-scene="1"].is-live .l1 { animation-delay: .5s; }
[data-scene="1"].is-live .l2 { animation-delay: .75s; }
[data-scene="1"].is-live .l3 { animation-delay: 1s; }

@keyframes dropIn {
  from { opacity: 0; transform: translate(var(--tx), -180px) rotate(calc(var(--rot) * 3)) scale(.86); }
  to   { opacity: 1; transform: translate(var(--tx), 0) rotate(var(--rot)) scale(1); }
}

/* ------------------------------------------------------ 2. label examples */

.grid {
  display: grid; grid-template-columns: repeat(3, 78px);
  grid-auto-rows: 78px; gap: 14px;
}
.cell {
  border-radius: 10px; background: var(--panel);
  border: 1px solid var(--line-2); opacity: 0;
  position: relative;
}
.cell.miss::after {
  content: ''; position: absolute; inset: -1px; border-radius: 10px;
  border: 2px solid var(--bad); opacity: 0;
}
[data-scene="2"].is-live .cell { animation: cellIn .5s ease both; }
[data-scene="2"].is-live .c0 { animation-delay: .05s } [data-scene="2"].is-live .c1 { animation-delay: .11s }
[data-scene="2"].is-live .c2 { animation-delay: .17s } [data-scene="2"].is-live .c3 { animation-delay: .23s }
[data-scene="2"].is-live .c4 { animation-delay: .29s } [data-scene="2"].is-live .c5 { animation-delay: .35s }
[data-scene="2"].is-live .c6 { animation-delay: .41s } [data-scene="2"].is-live .c7 { animation-delay: .47s }
[data-scene="2"].is-live .c8 { animation-delay: .53s }
[data-scene="2"].is-live .cell.miss::after { animation: flag .6s 1.1s ease both; }
[data-scene="2"].is-live .c5::after { animation-delay: 1.35s; }

@keyframes cellIn { from { opacity: 0; transform: scale(.8); } to { opacity: 1; transform: none; } }
@keyframes flag {
  from { opacity: 0; transform: scale(1.25); }
  to   { opacity: 1; transform: none; }
}

/* ------------------------------------------------------- 3. find patterns */

.scatter { width: 100%; height: 100%; }
.scatter .dot { fill: var(--ink-3); opacity: 0; }
[data-scene="3"].is-live .dot { animation: gather 1.5s cubic-bezier(.3,.9,.3,1) both; }
[data-scene="3"].is-live .g2 { animation-delay: .3s; }
.scatter .g1 { fill: var(--warn); } .scatter .g2 { fill: var(--focus); }

@keyframes gather {
  0%   { opacity: 0; transform: translate(var(--dx), var(--dy)); }
  25%  { opacity: 1; }
  100% { opacity: 1; transform: translate(0, 0); }
}

.lasso {
  fill: none; stroke-width: 1.6; stroke-dasharray: 5 6;
  opacity: 0; transform-origin: center;
}
.lasso-a { stroke: var(--warn); } .lasso-b { stroke: var(--focus); }
[data-scene="3"].is-live .lasso { animation: lassoIn .8s 1.15s ease both, spin 26s 1.9s linear infinite; }
[data-scene="3"].is-live .lasso-b { animation-delay: 1.4s, 2.2s; }

@keyframes lassoIn { from { opacity: 0; transform: scale(.7); } to { opacity: .85; transform: scale(1); } }
@keyframes spin { to { stroke-dashoffset: 220; } }

.pattern-chip {
  position: absolute; font-size: 11.5px; padding: 4px 10px; border-radius: 999px;
  background: var(--panel); border: 1px solid var(--line-2); opacity: 0;
  white-space: nowrap;
}
.p1 { left: 6%; top: 12%; color: var(--warn); border-color: var(--warn); }
.p2 { right: 4%; bottom: 16%; color: var(--focus); border-color: var(--focus); }
[data-scene="3"].is-live .p1 { animation: rise .6s 1.7s ease both; }
[data-scene="3"].is-live .p2 { animation: rise .6s 1.95s ease both; }

/* ---------------------------------------------------- 4. models improve */

.chart { width: 100%; height: 100%; }
.chart .axis line { stroke: var(--line-2); stroke-width: 1.5; }
.curve {
  fill: none; stroke: var(--accent); stroke-width: 3; stroke-linecap: round;
  stroke-dasharray: 620; stroke-dashoffset: 620;
}
.sc-improve.is-live .curve { animation: drawCurve 1.9s .2s cubic-bezier(.4,0,.2,1) both; }
@keyframes drawCurve { to { stroke-dashoffset: 0; } }

.chart .head { fill: var(--accent); opacity: 0; }
.sc-improve.is-live .head { animation: pop .5s 1.9s ease both; }

.chart .rule { fill: var(--accent-soft); opacity: 0; }
.sc-improve.is-live .rule { animation: rise .5s ease both; }
.sc-improve.is-live .r1 { animation-delay: .7s; }
.sc-improve.is-live .r2 { animation-delay: 1.05s; }
.sc-improve.is-live .r3 { animation-delay: 1.4s; }

.score-tick {
  position: absolute; right: 8%; top: 12%;
  font-family: var(--mono); font-size: 26px; color: var(--accent); opacity: 0;
}
.sc-improve.is-live .score-tick { animation: rise .6s 2.05s ease both; }

/* --------------------------------------------------------- 5. share */

.hub {
  position: absolute; width: 74px; height: 74px; border-radius: 20px;
  background: var(--accent); opacity: 0; z-index: 2;
  display: grid; place-items: center;
}
/* The mark, so the thing every source is feeding is legibly the product
   rather than an anonymous green square. Set from CSS because the hub appears
   twice - once in the deck, once in the section below the form - and a letter
   in the markup would have to be kept in step in both. */
.hub::after {
  content: "A";
  font-family: var(--serif); font-weight: 600; font-size: 38px;
  line-height: 1; color: var(--bg);
  /* The serif A sits optically low in its box; nudging it up centres it by
     eye rather than by metrics. */
  transform: translateY(-2px);
}
/* Its own keyframes rather than the shared `pop`, for two reasons: `pop`
   settles at .9, and a hub carrying a letter needs to be fully opaque to stay
   crisp; and `pop` overshoots to 1.5x on the way in, which is a lot of motion
   for the one element on screen that is meant to sit still while everything
   else arrives at it. This just settles into place. */
.sc-share.is-live .hub { animation: hubIn .5s .1s ease both; }
@keyframes hubIn {
  from { opacity: 0; transform: scale(.94); }
  to   { opacity: 1; transform: none; }
}

.peer {
  position: absolute; display: flex; align-items: center; gap: 7px;
  padding: 6px 14px 6px 10px; border-radius: 999px; font-size: 12.5px;
  background: var(--panel); border: 1px solid var(--line-2);
  box-shadow: 0 2px 8px rgba(0,0,0,.05);
  white-space: nowrap; opacity: 0; z-index: 2;
}
.peer-icon { width: 16px; height: 16px; flex: none; display: block; }

/* API and upload are not brands, so they are drawn as strokes that take the
   page's ink colour rather than a colour of their own. Keeping them quiet is
   deliberate: the two real logos should be what the eye lands on. */
.peer-icon.glyph {
  fill: none; stroke: var(--ink-2); stroke-width: 1.9;
  stroke-linecap: round; stroke-linejoin: round;
}

/* The spokes feed the hub, so the marks lean very slightly toward it as they
   arrive - the same gesture as the dotted lines, one step subtler. */
.sc-share.is-live .peer-icon { animation: markIn .5s ease both; }
.sc-share.is-live .pr1 .peer-icon { animation-delay: .55s }
.sc-share.is-live .pr2 .peer-icon { animation-delay: .7s }
.sc-share.is-live .pr3 .peer-icon { animation-delay: .85s }
.sc-share.is-live .pr4 .peer-icon { animation-delay: 1s }

@keyframes markIn {
  from { opacity: 0; transform: scale(.5) rotate(-25deg); }
  to   { opacity: 1; transform: none; }
}
.pr1 { left: 8%;  top: 14%; } .pr2 { right: 8%; top: 14%; }
.pr3 { left: 10%; bottom: 18%; } .pr4 { right: 10%; bottom: 18%; }
.sc-share.is-live .peer { animation: rise .55s ease both; }
.sc-share.is-live .pr1 { animation-delay: .45s } .sc-share.is-live .pr2 { animation-delay: .6s }
.sc-share.is-live .pr3 { animation-delay: .75s } .sc-share.is-live .pr4 { animation-delay: .9s }

.spokes { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; }
.spokes path {
  fill: none; stroke: var(--accent); stroke-width: 2; opacity: .5;
  stroke-dasharray: 6 8; stroke-dashoffset: 200;
}
.sc-share.is-live .spokes path { animation: flow 1.6s linear .8s infinite; }
@keyframes flow { to { stroke-dashoffset: 0; } }

/* ------------------------------------------------------ 6. keep improving */

.monitor { width: 100%; height: 100%; }
.trace {
  fill: none; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 520; stroke-dashoffset: 520;
}
.trace.base { stroke: var(--line-2); }
.trace.new  { stroke: var(--accent); }
.trace.drop { stroke: var(--bad); }
[data-scene="6"].is-live .base { animation: drawCurve 1.1s .1s ease both; }
[data-scene="6"].is-live .new  { animation: drawCurve 1.3s .5s ease both; }
[data-scene="6"].is-live .drop { animation: drawCurve 1.3s .9s ease both; }
.monitor .alert { fill: none; stroke: var(--bad); stroke-width: 2.5; opacity: 0; }
[data-scene="6"].is-live .alert { animation: ping 1.4s 1.9s ease-out infinite; }

@keyframes ping {
  0%   { opacity: .9; transform-origin: 280px 178px; transform: scale(.6); }
  70%  { opacity: 0;  transform-origin: 280px 178px; transform: scale(2.1); }
  100% { opacity: 0; }
}

.tag {
  position: absolute; font-size: 11px; font-family: var(--mono);
  padding: 3px 9px; border-radius: 999px; opacity: 0;
}
.t-new  { right: 6%; top: 22%; color: var(--accent); background: var(--accent-soft); }
.t-drop { right: 4%; bottom: 22%; color: var(--bad); background: #ffe2de; }
[data-scene="6"].is-live .t-new  { animation: rise .6s 1.5s ease both; }
[data-scene="6"].is-live .t-drop { animation: rise .6s 2.1s ease both; }

/* -------------------------------------------------------------- controls */

.controls {
  position: absolute; bottom: 34px; left: 50%; transform: translateX(-50%);
  display: flex; align-items: center; gap: 14px; z-index: 10;
}
.chapter-dots { display: flex; gap: 8px; }
.chapter-dots i {
  width: 7px; height: 7px; border-radius: 50%; background: var(--line-2);
  cursor: pointer; transition: all .3s ease; flex: none;
}
.chapter-dots i.on { background: var(--accent); width: 26px; border-radius: 4px; }
.nav-btn {
  width: 30px; height: 30px; border-radius: 50%; cursor: pointer;
  background: transparent; border: 1px solid var(--line-2);
  color: var(--ink-3); font-size: 15px; line-height: 1;
  display: grid; place-items: center; transition: all .2s ease;
}
.nav-btn:hover { color: var(--ink); border-color: var(--ink-3); }
.nav-btn[disabled] { opacity: .3; cursor: default; }


/* A film that moves on its own is hostile to anyone who needs it still. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001s !important; animation-iteration-count: 1 !important;
    transition-duration: .001s !important;
  }
}

/* -------------------------------------------------------------- waitlist */

.waitlist {
  border-top: 1px solid var(--line);
  /* The fineprint is the last thing in the form and needs room to end on;
     without it the sentence butts straight into the next section's rule. */
  padding: 92px 28px 104px;
  display: flex; flex-direction: column; align-items: center;
}
.waitlist-inner { width: 100%; max-width: 520px; }

.waitlist h2 {
  font-family: var(--serif); font-weight: 600; letter-spacing: -.015em;
  font-size: 34px; line-height: 1.15; margin: 0; text-align: center;
}
.waitlist-sub {
  margin: 12px auto 34px; text-align: center;
  font-size: 15px; line-height: 1.6; color: var(--ink-2); max-width: 44ch;
}

.field { display: block; margin-bottom: 16px; }
.field .label {
  display: block; margin-bottom: 6px;
  font-size: 12px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-3);
}
/* "optional" has to be legible enough to be believed, or people fill in
   everything anyway and the form feels longer than it is. */
.field .label em {
  font-style: normal; text-transform: none; letter-spacing: 0;
  color: var(--ink-3); opacity: .75; margin-left: 5px;
}
.field input {
  width: 100%; padding: 12px 14px; font: inherit; font-size: 15px;
  color: var(--ink); background: var(--panel);
  border: 1px solid var(--line-2); border-radius: 9px;
  transition: border-color .18s ease, box-shadow .18s ease;
}
.field input::placeholder { color: var(--ink-3); opacity: .6; }
.field input:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.field input:disabled { opacity: .5; }
.field.invalid input { border-color: var(--bad); }

.field-row { display: flex; gap: 14px; }
.field-row .field { flex: 1; min-width: 0; }
@media (max-width: 480px) { .field-row { display: block; } }

/* Bots fill in every input they find. Hidden from people, not from them. */
.trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.submit {
  width: 100%; margin-top: 8px; padding: 13px 22px;
  font: inherit; font-size: 15px; cursor: pointer;
  border: none; border-radius: 999px;
  background: var(--accent); color: var(--bg);
  transition: transform .2s ease, filter .2s ease;
}
.submit:hover:not(:disabled) { transform: translateY(-2px); filter: brightness(1.08); }
.submit:disabled { cursor: default; opacity: .6; }

.form-msg { margin: 14px 0 0; font-size: 13.5px; line-height: 1.5; min-height: 1.2em; }
.form-msg.ok { color: var(--accent); }
.form-msg.err { color: var(--bad); }

.fineprint {
  margin: 10px 0 0; font-size: 12px; line-height: 1.5; color: var(--ink-3);
}

.site-foot {
  margin-top: 90px; padding: 26px 0 30px; width: 100%; text-align: center;
  font-family: var(--mono); font-size: 11px; letter-spacing: .28em;
  text-transform: uppercase; color: var(--ink-3);
}

/* ------------------------------------------------- how it works (2nd deck) */


/* ------------------------------------------------------- onboard (badge) */

.badge {
  position: absolute; width: 168px; height: 214px; border-radius: 16px;
  background: var(--panel); border: 1px solid var(--line-2);
  box-shadow: 0 14px 34px rgba(0,0,0,.10);
  display: flex; flex-direction: column; align-items: center;
  padding-top: 34px; gap: 11px; opacity: 0; z-index: 2; overflow: hidden;
}
/* The lanyard slot is what makes a rounded rectangle read as a badge rather
   than as one more card - it is doing most of the work in this illustration. */
.badge-hole {
  position: absolute; top: 13px; left: 50%; transform: translateX(-50%);
  width: 40px; height: 7px; border-radius: 4px;
  background: var(--bg); border: 1px solid var(--line-2);
}
.badge-face {
  width: 58px; height: 58px; border-radius: 50%; background: var(--accent-soft);
  border: 2px solid var(--accent);
}
.badge-line {
  height: 8px; border-radius: 4px; background: var(--line);
  position: relative; overflow: hidden;
}
.badge-line.badge-name { width: 88px; }
.badge-line.badge-role { width: 58px; }
/* Each trait that docks fills a little more of the badge. Without this the
   scene empties out the moment the traits land, and the last thing on screen
   is a blank card - the opposite of what the slide is claiming. */
.badge-line::after {
  content: ''; position: absolute; inset: 0; width: 0;
  background: var(--accent); border-radius: 4px;
}
.badge-foot {
  position: absolute; left: 0; right: 0; bottom: 0; height: 26px;
  background: var(--accent-soft); border-top: 1px solid var(--accent);
  opacity: 0;
}
[data-scene="onboard"].is-live .badge { animation: rise .6s .1s ease both; }
[data-scene="onboard"].is-live .badge-line.badge-name::after { animation: learn 2.6s 1.0s ease both; }
[data-scene="onboard"].is-live .badge-line.badge-role::after { animation: learn 2.6s 1.6s ease both; }
[data-scene="onboard"].is-live .badge-foot { animation: rise .7s 4.2s ease both; }
@keyframes learn { to { width: 100%; } }

/* Traits fly in and dock into the badge: tacit knowledge being loaded. The
   pace is deliberate - this is the one scene where the animation *is* the
   argument, and a chip that flicks past cannot be read. */
.trait {
  position: absolute; font-size: 13px; font-family: var(--mono);
  padding: 7px 14px; border-radius: 999px; white-space: nowrap;
  background: var(--panel); border: 1px solid var(--line-2); color: var(--ink-2);
  box-shadow: 0 2px 10px rgba(0,0,0,.05);
  opacity: 0; z-index: 3;
}
.t1 { left: 0%;  top: 16%; } .t2 { right: 0%; top: 26%; }
.t3 { left: 3%;  bottom: 20%; } .t4 { right: 0%; bottom: 13%; }
[data-scene="onboard"].is-live .trait { animation: dock 2.5s cubic-bezier(.5,0,.5,1) both; }
[data-scene="onboard"].is-live .t1 { animation-delay: .6s }
[data-scene="onboard"].is-live .t2 { animation-delay: 1.2s }
[data-scene="onboard"].is-live .t3 { animation-delay: 1.8s }
[data-scene="onboard"].is-live .t4 { animation-delay: 2.4s }

@keyframes dock {
  0%   { opacity: 0; transform: none; }
  14%  { opacity: 1; transform: none; }
  58%  { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translate(var(--to-x, 0), var(--to-y, 0)) scale(.5); }
}
.t1 { --to-x: 210px;  --to-y: 74px; }
.t2 { --to-x: -210px; --to-y: 44px; }
.t3 { --to-x: 180px;  --to-y: -74px; }
.t4 { --to-x: -220px; --to-y: -44px; }

/* ---------------------------------------------------- structure (pile→table) */

.pile, .table { position: absolute; width: 150px; height: 168px; }
.pile { left: 6%; } .table { right: 6%; }

.sheet {
  position: absolute; left: 50%; top: 50%; width: 74px; height: 92px;
  margin: -46px 0 0 -37px; border-radius: 6px;
  background: var(--panel); border: 1px solid var(--line-2);
  box-shadow: 0 4px 12px rgba(0,0,0,.07); opacity: 0;
}
/* Scattered: the pile is meant to look like nobody filed it. */
.pile .s1 { --r: -17deg; --x: -30px; --y: -34px; }
.pile .s2 { --r: 11deg;  --x: 26px;  --y: -22px; }
.pile .s3 { --r: -6deg;  --x: -12px; --y: 4px; }
.pile .s4 { --r: 21deg;  --x: 22px;  --y: 26px; }
.pile .s5 { --r: -13deg; --x: -28px; --y: 34px; }
.pile .s6 { --r: 5deg;   --x: 6px;   --y: -6px; }
[data-scene="structure"].is-live .sheet { animation: scatterIn .7s ease both; }
[data-scene="structure"].is-live .s1 { animation-delay: 0.15s }
[data-scene="structure"].is-live .s2 { animation-delay: 0.28s }
[data-scene="structure"].is-live .s3 { animation-delay: 0.41s }
[data-scene="structure"].is-live .s4 { animation-delay: 0.54s }
[data-scene="structure"].is-live .s5 { animation-delay: 0.67s }
[data-scene="structure"].is-live .s6 { animation-delay: 0.8s }
@keyframes scatterIn {
  from { opacity: 0; transform: translate(var(--x), var(--y)) rotate(var(--r)) scale(.8); }
  to   { opacity: 1; transform: translate(var(--x), var(--y)) rotate(var(--r)); }
}

.sort-arrow {
  position: absolute; width: 60px; height: 24px; opacity: 0;
  fill: none; stroke: var(--accent); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
[data-scene="structure"].is-live .sort-arrow { animation: rise .7s 1.4s ease both; }

/* The same six documents, now in register. */
.row {
  position: absolute; left: 0; width: 100%; height: 18px; border-radius: 5px;
  background: var(--accent-soft); border: 1px solid var(--accent);
  opacity: 0; transform-origin: left center;
}
.table .r1 { top: 6px; } .table .r2 { top: 34px; } .table .r3 { top: 62px; }
.table .r4 { top: 90px; } .table .r5 { top: 118px; } .table .r6 { top: 146px; }
[data-scene="structure"].is-live .row { animation: snapIn .7s cubic-bezier(.2,.8,.2,1) both; }
[data-scene="structure"].is-live .r1 { animation-delay: 1.85s }
[data-scene="structure"].is-live .r2 { animation-delay: 2.02s }
[data-scene="structure"].is-live .r3 { animation-delay: 2.19s }
[data-scene="structure"].is-live .r4 { animation-delay: 2.36s }
[data-scene="structure"].is-live .r5 { animation-delay: 2.53s }
[data-scene="structure"].is-live .r6 { animation-delay: 2.7s }
@keyframes snapIn {
  from { opacity: 0; transform: scaleX(.15) translateX(-30px); }
  to   { opacity: 1; transform: none; }
}



/* ------------------------------------------------- scale (graded in clusters) */

/* Forty cells rather than a handful: the claim is about volume, and a grid you
   can count at a glance undercuts the phrase "100s of tasks". */
.swarm {
  /* Cell metrics are variables because the cluster rings are placed by
     arithmetic over them - changing the size in one place has to move the
     rings too, or they stop enclosing what they name. */
  --cell: 30px; --gap: 8px;
  position: relative;
  display: grid; grid-template-columns: repeat(10, var(--cell));
  grid-auto-rows: var(--cell); gap: var(--gap);
}
.unit {
  border-radius: 5px; background: var(--panel);
  border: 1px solid var(--line-2); opacity: 0;
}

/* The sequence is the argument, so it runs in four beats:
     1. every task lands, ungraded and grey
     2. a ring closes around a group that fails the same way
     3. that whole group takes one verdict at once - this is the point
     4. a second group, then the stragglers judged individually
   Grey to green or red, never grey to grey: an unlabeled cell has to look
   unlabeled or the "graded" state means nothing. */
[data-scene="scale"].is-live .unit { animation: appear .45s ease both; }
[data-scene="scale"].is-live .unit:nth-child(10n+1) { animation-delay: .10s }
[data-scene="scale"].is-live .unit:nth-child(10n+2) { animation-delay: .16s }
[data-scene="scale"].is-live .unit:nth-child(10n+3) { animation-delay: .22s }
[data-scene="scale"].is-live .unit:nth-child(10n+4) { animation-delay: .28s }
[data-scene="scale"].is-live .unit:nth-child(10n+5) { animation-delay: .34s }
[data-scene="scale"].is-live .unit:nth-child(10n+6) { animation-delay: .40s }
[data-scene="scale"].is-live .unit:nth-child(10n+7) { animation-delay: .46s }
[data-scene="scale"].is-live .unit:nth-child(10n+8) { animation-delay: .52s }
[data-scene="scale"].is-live .unit:nth-child(10n+9) { animation-delay: .58s }
[data-scene="scale"].is-live .unit:nth-child(10n)   { animation-delay: .64s }

@keyframes appear { from { opacity: 0; transform: scale(.75); } to { opacity: 1; transform: none; } }

/* A second animation carries the verdict, so it can start long after the cell
   has landed. Both keyframes begin at the grey base, so `both` holds the cell
   grey until its moment rather than colouring it early. */
@keyframes toPass {
  from { background: var(--panel); border-color: var(--line-2); }
  to   { background: var(--accent-soft); border-color: var(--accent); }
}
@keyframes toFail {
  from { background: var(--panel); border-color: var(--line-2); }
  to   { background: #ffe2de; border-color: var(--bad); }
}
/* Whole cluster, one verdict, no stagger - a stagger here would say they were
   labeled one by one, which is the thing this slide exists to deny. */
[data-scene="scale"].is-live .unit.ca {
  animation: appear .45s ease both, toPass .5s 2.3s ease both;
}
[data-scene="scale"].is-live .unit.cb {
  animation: appear .45s ease both, toFail .5s 4.3s ease both;
}
/* The stragglers, judged individually afterwards. */
[data-scene="scale"].is-live .unit.rest {
  animation: appear .45s ease both, toPass .4s ease both;
}
[data-scene="scale"].is-live .unit.rest.miss {
  animation: appear .45s ease both, toFail .4s ease both;
}
[data-scene="scale"].is-live .unit.rest:nth-child(4n+1) { animation-delay: .2s, 5.3s }
[data-scene="scale"].is-live .unit.rest:nth-child(4n+2) { animation-delay: .3s, 5.5s }
[data-scene="scale"].is-live .unit.rest:nth-child(4n+3) { animation-delay: .4s, 5.7s }
[data-scene="scale"].is-live .unit.rest:nth-child(4n)   { animation-delay: .5s, 5.9s }

/* The ring that names a cluster. Sized and placed against the grid: 30px cells
   with 8px gaps, so a span of n columns is 30n + 8(n-1). */
.ring {
  position: absolute; border-radius: 11px; opacity: 0;
  border: 2px dashed var(--ink-3); pointer-events: none;
}
.ring-a {
  left: -7px; top: -7px;
  width: calc(4 * var(--cell) + 3 * var(--gap) + 14px);
  height: calc(2 * var(--cell) + var(--gap) + 14px);
  border-color: var(--accent);
}
.ring-b {
  left: calc(6 * (var(--cell) + var(--gap)) - 7px);
  top: calc(2 * (var(--cell) + var(--gap)) - 7px);
  width: calc(3 * var(--cell) + 2 * var(--gap) + 14px);
  height: calc(2 * var(--cell) + var(--gap) + 14px);
  border-color: var(--bad);
}
[data-scene="scale"].is-live .ring-a { animation: ringIn .5s 1.5s ease both, ringOut .5s 3.4s ease both; }
[data-scene="scale"].is-live .ring-b { animation: ringIn .5s 3.5s ease both, ringOut .5s 5.4s ease both; }
@keyframes ringIn  { from { opacity: 0; transform: scale(1.12); } to { opacity: .95; transform: none; } }
@keyframes ringOut { to { opacity: 0; } }

/* Narrow screens. Kept at the end of the file on purpose: a media query adds
   no specificity, so an override placed above the rule it overrides simply
   loses the cascade. */
@media (max-width: 560px) {
  .pile, .table { width: 112px; }
  .sort-arrow { width: 38px; }
  /* Shrink the cells, never the column count: the clusters are contiguous
     blocks of the 10-wide grid, and reflowing to 8 columns would scatter them
     into shapes the rings no longer enclose. */
  .swarm { --cell: 19px; --gap: 5px; }
}

/* ---------------------------------------------------------------- pitch */

/* Below the form the argument stops being a film and becomes a page. The
   illustrations are the same elements with the same data-scene values, so
   every keyframe written for the deck applies unchanged; only the trigger
   differs - an IntersectionObserver rather than the deck's sequencer. */
.pitch {
  border-top: 1px solid var(--line);
  /* Bottom padding lives here now that the closing callout is gone; the form
     below brings its own top padding and its own rule. */
  padding: 96px 28px 104px;
  max-width: 1120px; margin: 0 auto;
}

.pitch-head { max-width: 720px; margin: 0 auto; text-align: center; }
.pitch-head + .pitch-head,
.feature + .pitch-head { margin-top: 88px; }
.pitch-head h2 {
  font-family: var(--serif); font-weight: 600; letter-spacing: -.015em;
  font-size: clamp(28px, 3.4vw, 38px); line-height: 1.18; margin: 0;
  /* Two lines, not three: at 20ch these headlines broke after the first two
     words and read as a stack of fragments. */
  max-width: 26ch; margin-inline: auto;
}
.pitch-head > p {
  margin: 14px auto 0; font-size: 16px; line-height: 1.6;
  color: var(--ink-2); max-width: 52ch;
}

/* The example workloads. Small enough to scan in one pass, and coloured so
   they read as a set rather than a list - this is the one place on the page
   where the whole palette shows up at once. */
.use-cases {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 9px;
  margin-top: 26px;
}
.use-case {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 15px 7px 12px; border-radius: 999px;
  background: var(--panel); border: 1px solid var(--line-2);
  font-size: 13px; color: var(--ink-2); white-space: nowrap;
  box-shadow: 0 2px 8px rgba(0,0,0,.04);
}
.use-case .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.dot.a { background: var(--accent); }
.dot.b { background: var(--focus); }
.dot.c { background: var(--warn); }
.dot.d { background: var(--bad); }

/* ------------------------------------------------------------- features */

.feature {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center; gap: 56px;
  padding: 60px 0;
}
/* Alternating sides so a run of five sections has a rhythm rather than
   reading as one long left-hand column. */
.feature:nth-of-type(even) .stage { order: 2; }
.feature .stage { width: 100%; max-width: 520px; margin: 0 auto; }

.feature-copy h3 {
  font-family: var(--serif); font-weight: 600; letter-spacing: -.012em;
  font-size: 27px; line-height: 1.2; margin: 0; max-width: 20ch;
}
.feature-copy p {
  margin: 14px 0 0; font-size: 15.5px; line-height: 1.65;
  color: var(--ink-2); max-width: 46ch;
}
/* The copy waits for its illustration rather than being there first. */
.feature-copy h3, .feature-copy p { opacity: 0; }
.feature.is-live .feature-copy h3 { animation: rise .6s .12s ease both; }
.feature.is-live .feature-copy p  { animation: rise .6s .24s ease both; }


@media (max-width: 820px) {
  .feature {
    /* minmax(0,1fr), not 1fr: a grid item's automatic minimum is its content,
       so a plain 1fr lets a wide illustration push the column past the
       viewport instead of being constrained by it. */
    grid-template-columns: minmax(0, 1fr); gap: 24px; padding: 56px 0;
    text-align: center;
  }
  /* Illustration first on every row: alternating sides means nothing once the
     columns are stacked, and `order` would put copy above its own picture. */
  .feature:nth-of-type(even) .stage { order: 0; }
  .feature-copy h3, .feature-copy p { margin-inline: auto; }
  .pitch-head + .pitch-head, .feature + .pitch-head { margin-top: 68px; }
}
