/* ──────────────────────────────────────────────────────────────────────
   The front page, and nothing else.

   /docs, /blog and /changelog still read app.css, which is
   a different design: lighter, Inter, the clerk-derived palette. This file
   is deliberately not that, and deliberately not shared — so the two can
   be true at once until the rest of the site is brought across.

   Order below follows the page: tokens, the chrome that wraps every band,
   the primitives three or more bands reuse, then one block per band in the
   order they are scrolled through, then the whole responsive layer at the
   end. A band's own exceptions live with the band, not up in the shared
   part — that is what keeps "change the hero" from meaning "read all of it".
   ────────────────────────────────────────────────────────────────────── */

/* ── tokens ───────────────────────────────────────────────────────────
   Two grounds, white and night, and one ink scale that reads on both.
   --periwinkle is the only hue: it marks the thing that is new, and
   nothing else. Buttons on night are white, not periwinkle — the accent
   stays scarce enough to mean something. */
:root {
  color-scheme: light;

  --night:  #07080C;
  --night2: #0E1018;
  --night3: #1A1D2A;
  --periwinkle: #A9B4FF;

  --ink:  #0B0C10;
  --ink2: #52566A;
  --ink3: #8A8F9C;

  --line:  #E4E5EA;
  --line2: #EFF0F3;
  --soft:  #F4F4F6;

  /* the sky where the two grounds meet — see the horizon, band 3 */
  --dusk:  #EFF0F5;

  /* the dark bands keep their own line colours: --line on night is a
     smear, not an edge */
  --dline:  #1A1D2A;
  --dink:   #8C91A0;
  --dink2:  #B4B8C3;
}

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  /* The page makes exactly one height change of its own — the how-it-works
     wrapper giving back the scroll it borrowed for the hold — and corrects
     the scroll position itself, to the pixel. Chrome's scroll anchoring
     would correct it a second time and send the view somewhere else
     entirely; Safari does not implement anchoring at all, so leaving it to
     the browser is not an option either. One correction, ours, everywhere. */
  overflow-anchor: none;
}

body {
  margin: 0;
  background: #fff;
  color: var(--ink);
  font-family: "Schibsted Grotesk", ui-sans-serif, system-ui, -apple-system, sans-serif;
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
  letter-spacing: -.01em;
}

h1, h2, h3, h4 { margin: 0; font-weight: 600; }
p { margin: 0; }
img, svg { max-width: 100%; }
a { color: inherit; text-decoration: none; }
a:hover { text-decoration: none; }

.mono { font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0; }

/* every band is a full-bleed ground; .in is the 1200px column inside it */
.w { position: relative; background: #fff; }
.w.dk { background: var(--night); color: #fff; }
.in {
  width: 100%; max-width: 1200px; margin-inline: auto;
  padding-inline: 24px; position: relative;
}

/* a band's vertical rhythm, named rather than typed per section */
.band   { padding-block: 112px; }

/* ── primitives ───────────────────────────────────────────────────────
   .eb is the mono label over a heading; .h2 the heading; .lede the line
   under it. Three bands or more use each, which is the bar for living up
   here. */
.eb {
  display: inline-flex; align-items: center; gap: 10px; line-height: 1;
  font-family: "JetBrains Mono", monospace; font-size: 12px;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink3); margin-bottom: 20px;
}
.eb .pxm { color: var(--ink); display: block; }
.dk .eb, .eb.dk { color: var(--dink); }
.dk .eb .pxm, .eb.dk .pxm { color: #fff; }

.h2 { font-size: 56px; line-height: 1.04; letter-spacing: -.04em; font-weight: 600; }
.h2-48 { font-size: 48px; line-height: 1.05; }
.dk .h2 { color: #fff; }

.lede { font-size: 17.5px; line-height: 1.6; color: var(--ink2); }
.dk .lede { color: var(--dink2); }
/* the lede is always the line under the heading, and always sits there */
.h2 + .lede { margin-top: 18px; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 44px; padding: 0 18px; border-radius: 999px;
  font-size: 15px; font-weight: 600; white-space: nowrap;
  border: 0; cursor: pointer; font-family: inherit;
  transition: background .15s, box-shadow .15s, color .15s;
}
.b-lime  { background: #fff; color: var(--night); }
.b-lime:hover { background: #E8EAF2; }
.b-dark  { background: var(--ink); color: #fff; }
.b-dark:hover { background: #26293A; }
.b-ghost {
  background: rgba(255, 255, 255, .08); color: #fff;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .14);
}
.b-ghost:hover { background: rgba(255, 255, 255, .14); }

/* the inline "read the docs" link, with its own arrow */
.lnk {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 20px;
  font-size: 14.5px; font-weight: 600; color: var(--ink);
}
.lnk.dk { color: #fff; }
.lnk:hover { text-decoration: underline; text-underline-offset: 4px; }

/* a dotted rule, drawn rather than bordered, so it reads as pixels */
.prule {
  position: absolute; left: 0; right: 0; top: 0; height: 4px;
  background-image: linear-gradient(90deg, #D5D7DE 4px, transparent 4px);
  background-size: 12px 4px;
}

/* the stepped shoulder where a dark band begins or ends. It is a 1440px
   drawing stretched to whatever the window is: only the 20px steps scale,
   and at 24px tall nobody reads the difference. */
.step { display: block; width: 100%; height: 24px; }

/* ── the bar above the nav ────────────────────────────────────────── */
.ann {
  min-height: 38px; display: flex; align-items: center; justify-content: center;
  gap: 10px; padding: 8px 16px; text-align: center;
  font-size: 13.5px; color: #C4C7D0;
  background: var(--night); border-bottom: 1px solid var(--night3);
}
.ann .px {
  font-family: "JetBrains Mono", monospace; font-size: 11px;
  letter-spacing: .04em; text-transform: uppercase; color: var(--periwinkle);
}
.ann a { display: inline-flex; align-items: center; gap: 8px; }
/* the burger panel's own sign-up and log in: the nav has its button
   and its "Log in" at this width, so these two are not here yet */
.nav-cta { display: none; }
.ann a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* ── nav ──────────────────────────────────────────────────────────── */
.navwrap { background: var(--night); position: relative; z-index: 20; }
.nav {
  height: 72px; display: flex; align-items: center; gap: 36px;
  font-size: 14.5px; color: #C4C7D0;
}
.nav .brand {
  display: flex; align-items: center; gap: 9px;
  font-size: 20px; font-weight: 700; color: #fff; letter-spacing: -.03em;
}
.nav .links { display: flex; gap: 28px; margin-left: auto; }
.nav .links a:hover { color: #fff; }
.nav .r { display: flex; gap: 18px; align-items: center; margin-left: 28px; }
.nav .btn { height: 36px; font-size: 14px; padding: 0 15px; }
.nav .sign-in:hover { color: #fff; }

/* below 1000px the links become a panel under the bar */
.nav-burger {
  display: none; width: 38px; height: 38px; margin-left: 4px;
  background: transparent; border: 0; padding: 0; cursor: pointer;
  flex-direction: column; justify-content: center; gap: 4px; align-items: center;
}
.nav-burger i { display: block; width: 18px; height: 1.5px; background: #fff; }

/* ── 1 · hero ─────────────────────────────────────────────────────────
   Night ground, one claim, and the pixel landscape underneath it. The
   image is decoration and says so: masked top and bottom so it has no
   edge of its own, and 1440px wide so it is never upscaled. */
/* The hero is one screen. The headline, the line under it, the two buttons,
   the landscape and the row of logos under that are all meant to be there
   before anything is scrolled — on a 14" laptop as much as on a monitor.

   The landscape is the part that gives: it takes whatever is left once the
   type and the logo row have had theirs, down to a floor it will not go
   below. Nothing here is a guessed pixel height that happens to fit one
   machine. */
@media (min-width: 861px) {
  .hero-band {
    display: flex; flex-direction: column;
    /* what is left of the window once the announcement bar and the nav have
       had theirs. --chrome is their measured height, written by the script;
       the fallback is what the two come to when the bar is one line. */
    min-height: calc(100svh - var(--chrome, 110px));
  }
  .hero-band .art { flex: 1 1 auto; height: auto; min-height: 190px; }
}
.hero { text-align: center; padding-top: clamp(36px, 6vh, 88px); }
.h1 { font-size: 68px; line-height: 1.02; letter-spacing: -.04em; font-weight: 600; }

.hero-sub { margin: clamp(14px, 2.2vh, 20px) auto 0; max-width: 560px; font-size: 18px; }
.hero-cta { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: clamp(20px, 3vh, 28px); }
.hero-cta .mono { font-size: 13px; font-weight: 400; }
/* the narrow half of the lede does not exist until 600 */
.hs-m { display: none; }

.art {
  height: 380px; margin-top: 8px;
  background: url(/landing/night.png) 50% 18% / cover no-repeat;
  image-rendering: pixelated;
  -webkit-mask-image: linear-gradient(transparent, #000 18%, #000 78%, transparent);
          mask-image: linear-gradient(transparent, #000 18%, #000 78%, transparent);
}

.works {
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px 28px; flex-wrap: wrap;
  min-height: 76px; margin-top: -12px; padding-bottom: clamp(8px, 1.6vh, 20px);
  color: #8C91A0; font-size: 15px;
}
/* the five marks are a group only on a phone. `display: contents` lets
   them stay direct children of the row's flex at every other width, so
   the spread across the band is exactly what it was. */
.works-ms { display: contents; }
.works span { display: flex; align-items: center; gap: 9px; color: #D5D8E0; }
.works .works-k { color: #6B7080; }

/* ── 4 · how it works ───────────────────────────────────────
   ONE SCREEN, NOT THREE THUMBNAILS.

   This band was three equal columns, each with a product shot shrunk into a
   360px stage — a modal, a popover and a dashboard, three different objects
   at three different scales, none of them readable, every one of them inside
   two frames. Nothing was first, so nothing was looked at. And it all sat in
   a 240vh wrapper that borrowed the scroll to walk a line between the three
   numerals, which is a great deal of machinery to spend on three thumbnails.

   A selector and one screen instead. It is what /app's own gallery does —
   that sheet puts it plainly, "not a wallpaper with two cards floating on
   it" — and what every site in this category does with a stepped flow: one
   canvas you can read, and a control saying which state it is in. The three
   mocks are unchanged. They were never the problem; their size was. */
.steps-head { text-align: center; display: flex; flex-direction: column; align-items: center; }
.steps-head .h2 { white-space: nowrap; }

/* THE SELECTOR, at /app's measurements: a pill rail on the soft ground with
   the chosen one lifted onto white. The numeral rides inside its own button
   now rather than over a column, which is where it used to be. */
.hwt {
  display: flex; justify-content: center; flex-wrap: wrap; gap: 4px;
  margin: 46px auto 0; padding: 4px;
  background: var(--soft); border-radius: 999px; width: max-content; max-width: 100%;
}
.hwp {
  display: inline-flex; align-items: center; gap: 9px;
  border: 0; background: none; cursor: pointer;
  font: 600 14.5px/1 "Schibsted Grotesk", system-ui, sans-serif;
  letter-spacing: -.015em; color: var(--ink2);
  padding: 11px 18px 12px; border-radius: 999px;
  transition: color .2s ease, background .25s ease, box-shadow .25s ease;
}
.hwp i {
  font: 500 11.5px "JetBrains Mono", monospace; font-style: normal;
  letter-spacing: .08em; color: var(--ink3); transition: color .2s ease;
}
.hwp:hover { color: var(--ink); }
.hwp.is-on {
  color: var(--ink); background: #fff;
  box-shadow: 0 0 0 .5px rgba(16, 17, 25, .09), 0 1px 2px rgba(16, 17, 25, .09),
              0 6px 14px -8px rgba(16, 17, 25, .22);
}
.hwp.is-on i { color: var(--ink); }
.hwp:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
/* While it is advancing on its own the chosen tab says how long is left —
   /app's rule, for /app's reason: a carousel that moves with no warning is
   the thing people hate about carousels.

   ONE SEGMENT PER BEAT, not one rule per tab. Connect is two states and the
   others are one, so a single bar under Connect would fill, reset and fill
   again without the tab ever changing — which reads as a stall rather than
   as progress. Two segments say "two things happen here, this is the
   first". The script builds them from the beat list, so the rule can never
   disagree with what it is counting. */
.hwp { position: relative; }
/* ONE BAR, WHOLE, UNDER EVERY TAB. It held one segment per beat, which gave
   Connect — the only step cut in two — a broken rule with a gap through the
   middle while the other two were unbroken. The beats are how the scene is
   animated, not something a reader is counting, and every step now runs for
   the same DWELL, so there is nothing left for a division to say. */
.hwp-rule {
  position: absolute; left: 16px; right: 16px; bottom: 6px;
  display: flex; height: 2px; pointer-events: none;
}
.hwp-rule i {
  flex: 1; border-radius: 2px; background: var(--ink);
  transform: scaleX(0); transform-origin: left; opacity: 0;
}
.hwt.is-auto .hwp.is-on .hwp-rule i.is-timing {
  opacity: .35; animation: hw-fill var(--hw-dwell, 3400ms) linear forwards;
}
@keyframes hw-fill { from { transform: scaleX(0) } to { transform: scaleX(1) } }

/* THE CANVAS holds the screen, which brings its own 740px and its own
   breakout past this column — see the block at the foot of this file. All
   that is wanted here is the room above it. */
.hwc { margin-top: 36px; }
.hwc > .scene { margin-top: 0; }
.hwc > [hidden] { display: none; }

/* THE DASHBOARD IS A WINDOW ON A DESK, like the two steps before it.

   It used to be the one step with no frame at all: the Runs panel floating
   on the page's own white, which read as a card that had been pasted into
   the band rather than as a screen — and the band visibly lost its edges
   for a third of its cycle, since Connect and Run both fill a 1320×740
   screen and this filled nothing.

   So it takes that screen's exact geometry — the same breakout width, the
   same height, the same radius and the same lift — and the panel sits ON it
   with its own shadow. Nothing about the panel changed; it was already a
   window, it simply had no desk under it.

   A DESKTOP, NOT A BACKDROP. A gradient behind the window was the first
   attempt and it read as a card on a grey card: what makes a screenshot look
   like a screen is the furniture around it — a menu bar along the top, and a
   wallpaper that is plainly a wallpaper. The panel is then a window on it.

   A dock went in beside the bar and came out again. Two pieces of chrome to
   make one point, and the dock was the half with nothing to say: eight blank
   tiles along the foot of a picture whose subject is a table of numbers.

   THE WALLPAPER IS OURS. The reference for this is macOS, and the one thing
   not to do with that reference is reach for Apple's own wallpaper — so it
   is built from the page's own tokens. It stays light for the same reason
   the plain gradient did: the other two steps are white screens, and a dark
   desk would make this the only dark surface in a band showing one product
   three times.

   The width lives HERE and not on the table: centring a grid item shrinks it
   to its content, and a `100%` measured against a shrink-to-fit parent is a
   circle the layout resolves by giving up and using the content width. */
.hwdash {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  /* the same arithmetic as `.scene` — half the column, minus half the
     screen, is how far left to start */
  width: min(1320px, calc(100vw - 48px));
  margin-inline: calc(50% - min(660px, 50vw - 24px));
  /* 44 at the top clears the menu bar, which is absolute and so contributes
     no height of its own; the rest is even, since nothing sits at the foot
     any more for the window to keep clear of. */
  height: 740px; padding: 44px 40px 40px;
  border-radius: 18px; overflow: hidden;
  /* Three washes over a warm base: a periwinkle sky at the top, a dusk band
     through the middle and a warm bloom low and left, which is the page's
     own palette doing what a wallpaper does — give the window something to
     cast a shadow onto that is not flat. */
  background:
    radial-gradient(120% 80% at 18% 92%, rgba(214, 176, 142, .40) 0%, rgba(214, 176, 142, 0) 60%),
    radial-gradient(90% 70% at 82% 10%, rgba(169, 180, 255, .50) 0%, rgba(169, 180, 255, 0) 64%),
    linear-gradient(168deg, #C9CEF2 0%, #E4E2EC 42%, #EFE9E4 72%, #E9E4EA 100%);
  box-shadow: 0 0 0 1px rgba(16, 17, 25, .1),
              0 2px 6px -2px rgba(16, 17, 25, .12),
              0 56px 100px -40px rgba(16, 17, 25, .38),
              0 20px 40px -20px rgba(16, 17, 25, .16);
}

/* the menu bar. Translucent over the wallpaper, like the thing it is of. */
.desk-bar {
  position: absolute; top: 0; left: 0; right: 0; height: 26px; z-index: 2;
  display: flex; align-items: center; gap: 15px; padding: 0 14px;
  background: rgba(255, 255, 255, .42);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  font-size: 12px; color: #2A2B33;
}
.desk-mk { display: grid; place-items: center; width: 15px; color: #16171D; }
.desk-mk svg { width: 14px; height: 12px; display: block; }
.desk-bar b { font-weight: 600; letter-spacing: -.01em; }
.desk-bar i { font-style: normal; color: #44454E; }
/* the status items, which are the half of the bar nobody reads and every
   screenshot has */
.desk-sys { display: flex; align-items: center; gap: 13px; margin-left: auto; color: #3A3B44; }
.desk-sys em { font-style: normal; font-size: 11.5px; letter-spacing: -.01em; }


/* the heading and the line under it, for whichever step is showing */
.hwcaps { margin-top: 28px; text-align: center; }
.hwcap { font-size: 16.5px; line-height: 1.6; color: var(--ink2); }
.hwcap b { color: var(--ink); font-weight: 600; }
.hwcap[hidden] { display: none; }

/* ── THE HAND-OFF ────────────────────────────────────────────────
   Two dialogs of quite different sizes swapping in the middle of a page
   cannot dissolve into each other — for a third of a second there are two
   overlapping rectangles and it reads as a glitch. So it is a hand-off: the
   one leaving is most of the way gone before the one arriving starts, and
   each moves a little as it goes, which is what a dialog does.

   The window beside them needs the same, and it needs one thing more. Both
   frames carry `hidden`, which is display:none, and nothing transitions out
   of display:none — the script below has to let the browser see the element
   before it adds the class, and wait for the fade before it sets `hidden`
   back. That is the whole reason paint() is as long as it is. */
/* THE MOTION GOES ON THE LAYER, NOT THE DIALOG. `.modal` centres itself with
   transform: translate(-50%,-50%), so an entry transform written onto it
   replaces the centring and the dialog walks off toward the bottom right.
   `.modalf` is inset:0 and carries no transform of its own, so moving that
   moves the dialog inside it and leaves the centring alone. */
.hwc .modalf {
  transform: translateY(10px) scale(.986);
  transition: opacity .32s cubic-bezier(.4, 0, 1, 1),
              transform .55s cubic-bezier(.22, .61, .36, 1),
              visibility 0s .32s;
}
.hwc .modalf.is-on {
  transform: none;
  transition: opacity .42s cubic-bezier(0, 0, .2, 1) .24s,
              transform .55s cubic-bezier(.22, .61, .36, 1) .24s,
              visibility 0s;
}

.hwc .scene .shotw > * {
  opacity: 0; transform: translateY(12px) scale(.988);
  transition: opacity .34s cubic-bezier(.4, 0, 1, 1),
              transform .55s cubic-bezier(.22, .61, .36, 1);
}
.hwc .scene .shotw > .is-on {
  opacity: 1; transform: none;
  transition: opacity .44s cubic-bezier(0, 0, .2, 1) .26s,
              transform .66s cubic-bezier(.22, .61, .36, 1) .26s;
}
/* the desk lifts its scrim on the same clock, so the whole beat moves once */
.hwc .scene-scrim { transition: opacity .5s cubic-bezier(.3, 0, .2, 1), visibility 0s; }

/* ── STEP 03: THE RUNS ───────────────────────────────────────────
   What was here was one number on a white card in the middle of a black
   field, and a number by itself is a claim about a number — you take it or
   you do not. The ledger is the runs that made it: who ran, on what, and
   what each one cost. The claim is then not asserted anywhere; it is just
   the Cost column, read downwards, where almost every line is $0.00 and the
   two that are not are worth a cent each.

   It is the product's Runs page — Started, User, Model, Ran on, Cost, with
   the cost summed at the foot, which are its real columns and its real
   total. Light, because the other two steps are product surfaces and the
   dashboard is one. And the rows arrive rather than appearing, on the same
   loop the bento's own log tile has run on all along. */
.hwrun {
  --r-ink: #131316; --r-ink2: #42434d; --r-muted: #747686;
  --r-faint: #9394a1; --r-line: #ececef; --r-line2: #d9d9de;
  width: min(880px, 100%); flex: none;
  border-radius: 14px; background: #fff; overflow: hidden;
  color: var(--r-ink); font-size: 13.5px; letter-spacing: -.005em;
  box-shadow: 0 0 0 1px rgba(16, 17, 25, .08), 0 2px 6px -2px rgba(16, 17, 25, .1),
              0 40px 80px -36px rgba(16, 17, 25, .34);
}
.hwrun-bar {
  display: flex; align-items: baseline; gap: 12px;
  padding: 15px 20px; border-bottom: 1px solid var(--r-line);
}
.hwrun-bar b { font-size: 15px; font-weight: 600; letter-spacing: -.02em; }
.hwrun-bar span { font-size: 12.5px; color: var(--r-muted); }

/* the month, stated once, over the runs that made it */
.hwrun-sum {
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 36px; align-items: end;
  padding: 18px 20px 20px; border-bottom: 1px solid var(--r-line);
}
.hwrun-big p { font-size: 13px; color: var(--r-muted); }
.hwrun-big b { display: block; margin-top: 2px; font-size: 34px; font-weight: 600; letter-spacing: -.04em; line-height: 1.1; }
.hwrun-meter { height: 8px; border-radius: 4px; background: #E7E8EC; overflow: hidden; }
.hwrun-meter i { display: block; width: 71%; height: 100%; background: var(--r-ink); }
.hwrun-sp p { display: flex; justify-content: space-between; margin-top: 8px; font-size: 12.5px; color: var(--r-muted); }

.hwrun-t { width: 100%; border-collapse: collapse; }
.hwrun-t th {
  padding: 9px 20px; text-align: left; font-weight: 500; white-space: nowrap;
  font-size: 11px; letter-spacing: .05em; text-transform: uppercase;
  color: var(--r-faint); border-bottom: 1px solid var(--r-line);
}
.hwrun-t td { padding: 0 20px; height: 38px; border-bottom: 1px solid var(--r-line); white-space: nowrap; }
.hwrun-t tbody tr:last-child td { border-bottom: 0; }
/* time, model and money are columns you compare down, so they are mono */
.hwrun-t0, .hwrun-m, .hwrun-c { font-family: "JetBrains Mono", monospace; font-size: 12px; letter-spacing: 0; }
.hwrun-t0 { color: var(--r-muted); }
.hwrun-m { color: var(--r-ink2); }
.hwrun-c { text-align: right; font-variant-numeric: tabular-nums; }
/* THE ONLY MARK IN THE TABLE, and it is on the rows that cost something.
   Nothing is tinted and nothing is lit: the ones you pay for are simply the
   ones whose number is not zero, and the ink says so. */
.hwrun-t tbody td.hwrun-c { color: var(--r-faint); }
.hwrun-t tbody tr.is-yours td.hwrun-c { color: var(--r-ink); font-weight: 600; }
.hwrun-t tbody tr.is-yours td:nth-child(4) { color: var(--r-ink); }
.hwrun-t tfoot td {
  height: 42px; padding: 0 20px; border-top: 1px solid var(--r-line2);
  background: #fafafb; color: var(--r-muted); font-size: 12.5px;
}
.hwrun-t tfoot td.hwrun-c { color: var(--r-ink); font-weight: 600; }

/* the rows arrive, on the loop the bento's log tile already runs */
.hwdash:not([hidden]) .hwrun-t tbody tr { animation: hwrun-in .5s cubic-bezier(.2,.85,.25,1) both; }
.hwdash:not([hidden]) .hwrun-t tbody tr:nth-child(1) { animation-delay: .06s }
.hwdash:not([hidden]) .hwrun-t tbody tr:nth-child(2) { animation-delay: .14s }
.hwdash:not([hidden]) .hwrun-t tbody tr:nth-child(3) { animation-delay: .22s }
.hwdash:not([hidden]) .hwrun-t tbody tr:nth-child(4) { animation-delay: .30s }
.hwdash:not([hidden]) .hwrun-t tbody tr:nth-child(5) { animation-delay: .38s }
.hwdash:not([hidden]) .hwrun-t tbody tr:nth-child(6) { animation-delay: .46s }
.hwdash:not([hidden]) .hwrun-t tbody tr:nth-child(7) { animation-delay: .54s }
@keyframes hwrun-in { from { opacity: 0; transform: translateY(-5px) } to { opacity: 1; transform: none } }

/* ── STEP 02: THE RUN, RUNNING ───────────────────────────────────
   The desk was a photograph of a run that had already happened: two tool
   rows with their timings printed and a reply half written. A still of
   something finishing is the one thing it cannot show, which is that the
   machine answering is somebody else's.

   So it runs. One clock, --R, and every stage below is a percentage of it:
   the prompt is typed into the composer, it becomes the bubble, each tool
   row arrives in the order it would, the answer streams, and the counter in
   the panel moves the moment the run lands. Nothing is scripted — the band
   is a working loop for anyone whose JavaScript never arrives, which is the
   same contract the pairing band below it is written to.

   EVERY BASE STATE IS THE VISIBLE ONE, and the keyframes bring things in
   from nothing. That is what makes the reduced-motion rule at the foot of
   this file correct for free: `animation: none` leaves the whole thread
   sitting there, read rather than watched. */
.scene.is-live .page-msgs { --R: 11s; }

/* the composer types, then hands the line to the bubble */
.page-typed {
  position: absolute; left: 20px; right: 46px; top: 50%; transform: translateY(-50%);
  overflow: hidden; white-space: nowrap; text-align: left;
  font-size: 13.5px; color: var(--ink); opacity: 0;
}
.page-comp { position: relative; }
.scene.is-live .page-typed {
  animation: run-type var(--R, 11s) steps(48, end) infinite;
  width: 0;
}
.scene.is-live .page-ph { animation: run-ph var(--R, 11s) linear infinite; }
@keyframes run-type {
  0%        { width: 0; opacity: 1 }
  9%        { width: 100%; opacity: 1 }
  11%, 100% { width: 100%; opacity: 0 }
}
@keyframes run-ph { 0%, 11% { opacity: 0 } 12%, 100% { opacity: 1 } }

/* the thread, one arrival at a time */
.scene.is-live .page-msgs > .page-msg { animation: run-in var(--R, 11s) both infinite; }
.scene.is-live .page-msgs > .page-msg.is-you { animation-delay: calc(var(--R, 11s) * .11) }
.scene.is-live .page-msgs > .page-msg:nth-child(2) { animation-delay: calc(var(--R, 11s) * .19) }
.scene.is-live .page-msgs > .page-msg:nth-child(3) { animation-delay: calc(var(--R, 11s) * .58) }
/* …and inside the answer, each tool row as it is called */
.scene.is-live .page-msg:nth-child(2) .page-tool:nth-child(1) { animation: run-in var(--R, 11s) calc(var(--R, 11s) * .22) both infinite }
.scene.is-live .page-msg:nth-child(2) .page-tool:nth-child(2) { animation: run-in var(--R, 11s) calc(var(--R, 11s) * .31) both infinite }
.scene.is-live .page-msg:nth-child(2) > .page-bub > p         { animation: run-in var(--R, 11s) calc(var(--R, 11s) * .41) both infinite }
.scene.is-live .page-msg:nth-child(2) .page-l:nth-of-type(1)  { animation: run-in var(--R, 11s) calc(var(--R, 11s) * .48) both infinite }
.scene.is-live .page-msg:nth-child(2) .page-l:nth-of-type(2)  { animation: run-in var(--R, 11s) calc(var(--R, 11s) * .52) both infinite }
/* a tool row prints its own time only once it has one */
.scene.is-live .page-tool em { animation: run-in var(--R, 11s) calc(var(--R, 11s) * .36) both infinite }
.scene.is-live .page-tool.is-live em { animation-delay: calc(var(--R, 11s) * .62) }

@keyframes run-in {
  0%   { opacity: 0; transform: translateY(5px) }
  4%   { opacity: 1; transform: none }
  92%  { opacity: 1; transform: none }
  97%, 100% { opacity: 0; transform: none }
}

/* the dot on the row that is working, and only that one */
.scene.is-live .page-tool.is-live .page-tdot { animation: run-pulse 1.4s ease-in-out infinite }
@keyframes run-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .25 } }

/* THE COUNTER, which is the only number on the page that moves. It moves
   once, when the run lands, because that is the claim: the run went
   somewhere and it was not your key that paid for it. */
.cnp-tick { position: relative; display: inline-block; }
.cnp-tick i { font-style: normal; display: block; }
/* the second figure is hidden until the run lands, which is also what makes
   it correct when the animation is switched off: `animation: none` leaves
   the base state, and the base state is the number before the run. */
.cnp-tick i + i { position: absolute; inset: 0; opacity: 0; }
.scene.is-live .cnp-tick i:first-child { animation: tick-out var(--R, 11s) infinite }
.scene.is-live .cnp-tick i + i { animation: tick-in var(--R, 11s) infinite }
@keyframes tick-out { 0%, 62% { opacity: 1; transform: none } 66%, 100% { opacity: 0; transform: translateY(-6px) } }
@keyframes tick-in  { 0%, 62% { opacity: 0; transform: translateY(6px) } 66%, 100% { opacity: 1; transform: none } }

/* ── THE CONNECT PANEL ─────────────────────────────────────────
   What stood here was the Soba app's own connect screen, and that is /app's
   story: the thing YOU install. This page's story is what your USER meets
   inside YOUR app, which is packages/connect-ui — the panel you embed, with
   the providers, the key field and the one primary button.

   REDRAWN, NOT IMPORTED. connect-ui.css is 3,700 lines of a product's
   stylesheet and none of it is a landing page's; the same call was made for
   the desktop app's screens in the pairing band, for the same reason. What
   is copied is the measurements, which are the part that has to be right:
   17px heading, 13.5 over a 34ch measure, a 32px tile, 8px between rows,
   11px on the "last used" pill — all of them connect-ui's own. */
.cnp {
  --c-ink: #131316; --c-muted: #6B6B73; --c-line: #E9E9EB; --c-raised: #F6F6F7;
  width: min(352px, 100%); border-radius: 14px; overflow: hidden;
  background: #fff; color: var(--c-ink);
  font-size: 14px; letter-spacing: -.005em; text-align: center;
  box-shadow: 0 0 0 1px rgba(16, 17, 25, .09),
              0 2px 6px -2px rgba(16, 17, 25, .1),
              0 30px 60px -24px rgba(16, 17, 25, .3);
}
.cnp[hidden] { display: none; }
/* the panel names itself and says which step of its own flow it is on */
.cnp-bar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 11px 14px; border-bottom: 1px solid var(--c-line); text-align: left;
}
.cnp-bar b { font-size: 14px; font-weight: 600; letter-spacing: -.01em; }
.cnp-env {
  font-family: "JetBrains Mono", monospace; font-size: 10px; font-weight: 500;
  letter-spacing: .09em; text-transform: uppercase; color: var(--c-muted);
  border: 1px solid var(--c-line); border-radius: 6px; padding: 3px 7px;
}
/* No colour, because it is not a state worth interrupting anyone for — it
   is just where the flow got to. It used to have a CONNECTED twin on the
   summary card, and the pair was the argument for the chip staying one
   object in two words; the card is gone and the connected end of the story
   is told by the pill in the app's own header now. */

.cnp-body { padding: 20px 16px 18px; }
.cnp-art {
  display: grid; place-items: center; width: 44px; height: 44px; margin: 0 auto;
  border-radius: 12px; background: #141210;
}
.cnp-art svg { width: 24px; height: 21px; display: block; }
.cnp-h { margin-top: 13px; font-size: 17px; font-weight: 600; letter-spacing: -.02em; }
/* under 40 characters: two lines of a short measure read as one thought */
.cnp-p { margin: 5px auto 0; max-width: 34ch; font-size: 13.5px; line-height: 1.45; color: var(--c-muted); }

.cnp-opts { display: grid; gap: 8px; margin-top: 16px; text-align: left; }
/* tile, label, and the slot on the right that is usually empty — a real
   column rather than a pill floated over a label that may be long */
.cnp-opt {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 10px;
  padding: 7px 10px; border: 1px solid var(--c-line); border-radius: 10px; background: #fff;
}
.cnp-tile {
  width: 32px; height: 32px; display: grid; place-items: center;
  border: 1px solid var(--c-line); border-radius: 10px; background: var(--c-raised);
}
.cnp-tile svg { width: 15px; height: 15px; }
/* 500, not 600: three rows of semibold in a 350px column is a shouted list */
.cnp-label { font-size: 14px; font-weight: 500; letter-spacing: -.01em; }
.cnp-label em { display: block; font-style: normal; font-size: 12px; color: var(--c-muted); margin-top: 1px; }
.cnp-last {
  font-size: 11px; font-weight: 600; color: var(--c-muted);
  background: var(--c-raised); border-radius: 6px; padding: 3px 7px; white-space: nowrap;
}
/* The row that is in use is not tinted and carries no lamp. It is the only
   row in the panel — there is nothing for it to be distinguished FROM — and
   what sits in the slot on its right is the pill that sits there on the
   screen before: same column, same grey, one different word. */

.cnp-or {
  display: flex; align-items: center; gap: 12px; margin: 16px 0;
  color: var(--c-muted); font-size: 12.5px;
}
.cnp-or::before, .cnp-or::after { content: ""; flex: 1; height: 1px; background: var(--c-line); }

.cnp-k { text-align: left; font-size: 12.5px; color: var(--c-muted); }
.cnp-in {
  display: flex; align-items: center; gap: 1px; margin-top: 6px; height: 38px; padding: 0 11px;
  border: 1px solid var(--c-line); border-radius: 10px; background: #fff;
  font-size: 13px; color: #9394A1; text-align: left;
}
/* the key, as a field shows one: the prefix readable and the rest not */
.cnp-dots {
  flex: 1; height: 5px; margin-left: 2px;
  background-image: radial-gradient(circle, #9394A1 1.6px, transparent 1.7px);
  background-size: 7px 5px; background-repeat: repeat-x;
}
.cnp-note { margin-top: 7px; text-align: left; font-size: 12px; line-height: 1.45; color: #9394A1; }
.cnp-go {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  margin-top: 14px; height: 42px; border-radius: 10px;
  background: var(--c-ink); color: #fff; font-size: 14.5px; font-weight: 600; letter-spacing: -.01em;
}
.cnp-arw { font-size: 9px; }
/* the connected state's two figures, which are the whole point of the band */
.cnp-row {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--c-line);
  font-size: 13px; color: var(--c-muted); text-align: left;
}
.cnp-row b { font-size: 15px; font-weight: 600; color: var(--c-ink); letter-spacing: 0; }

.cnp-foot {
  padding: 11px; border-top: 1px solid var(--c-line); background: var(--c-raised);
  font-size: 12.5px; color: var(--c-muted);
}

/* THE PANEL IS A MODAL. It is a dialog inside the customer's own page — that
   is what connect-ui is — so it sits where that page puts a dialog, which is
   the middle, and not off to one side where the Soba window goes. */
.cnp-modal { display: grid; place-items: center; }
.cnp-modal .cnp { text-align: center; }

/* ── THE SCREEN, shared with /app ─────────────────────────────────
   Everything from here to the end of this block is soba-app.css's screen,
   lifted whole: the customer's own app, the Soba dialog on it, and the Soba
   window beside. It ports without a single edit because not one rule in it
   reads a token — every colour is a literal, which is what a screenshot is.

   THE TWO PAGES SHOW IT FOR DIFFERENT REASONS. On /app it is what you are
   about to install. Here it is what YOUR USER sees in YOUR app, which is the
   one picture this page could not draw for itself: every version of this
   band before it shrank a 290px dialog into a 360px box and called it a
   screenshot.

   IT IS A COPY, AND THAT IS A DEBT. soba-app.css is being renamed and
   rewritten by another session as this lands, and deleting ninety rules out
   from under that is how you lose an afternoon. The moment it settles this
   belongs in one sheet — both pages already load landing.css first, so
   "here" is that sheet, and soba-app.css's copy is the one that goes.
   ────────────────────────────────────────────────────────── */

/* ── the screen ──────────────────────────────────────────────────────── */
/* ONE BROWSER, FILLING THE SCREEN. This is what the screenshots are: the
   app a person already uses, its own page greyed out behind a scrim, Soba
   Connect as a dialog inside it, and the Soba app answering beside it. Not
   a wallpaper with two cards floating on it — the page behind is the whole
   reason the dialog is there. */
.scene {
  position: relative; margin-top: 40px; border-radius: 18px; overflow: hidden;
  /* WIDER THAN THE COLUMN. A dialog centred in the screen and a window
     beside it will not both fit in 1152 without one covering the other, and
     the dialog belongs in the middle — that is where the app it belongs to
     puts it. So the screen breaks out of the text column, up to 1320 and
     never past the window's own edge. The arithmetic: half the column,
     minus half the screen, is how far left to start. */
  width: min(1320px, calc(100vw - 48px));
  margin-inline: calc(50% - min(660px, 50vw - 24px));
  height: 740px;
  background: #fff;
  box-shadow: 0 0 0 1px rgba(16, 17, 25, .1),
              0 2px 6px -2px rgba(16, 17, 25, .12),
              0 56px 100px -40px rgba(16, 17, 25, .38),
              0 20px 40px -20px rgba(16, 17, 25, .16);
}
/* the customer's page. It is read as shape, not as words — the scrim over
   it is doing what a scrim does — so it is written at the size it would be
   and left to blur. */
.page {
  position: absolute; inset: 0; overflow: hidden;
  font-family: "Schibsted Grotesk", system-ui, sans-serif; color: #16171D;
}
/* ── the app's own bar ───────────────────────────────────────────────── */
/* THREE GROUPS, NOT SIX WORDS. It used to be a brand, four sections and an
   account set in one undifferentiated row at one size, which is what made
   it read as a list rather than as the top of an application. Now the mark
   has a tile and a rule after it, the sections are tabs with the open one
   on the same glass as everything else in here, and the account is an
   avatar at the far end. Nothing was added to the page; the same words are
   simply told apart. */
.page-nav {
  display: flex; align-items: center; gap: 2px;
  height: 56px; padding: 0 22px 0 26px;
  border-bottom: 1px solid #ECECF0;
  font-size: 13.5px; color: #74757E;
}
.page-brand {
  display: flex; align-items: center; gap: 10px;
  padding-right: 20px; margin-right: 18px; border-right: 1px solid #ECECF0;
  font-size: 14px; font-weight: 600; color: #16171D; letter-spacing: -.02em;
}
.page-logo {
  flex: none; display: grid; place-items: center; width: 26px; height: 26px;
  border-radius: 8px; color: #16171D;
}
.page-logo svg { width: 15px; height: 15px; }
/* the sections. The open one is the same pane as every other raised thing
   on this page, which is cheaper to read than a weight change and does not
   shift the row by a pixel when it moves. */
.page-tabs { display: flex; align-items: center; gap: 2px; }
.page-tabs b, .page-tabs i {
  display: block; padding: 6px 11px; border-radius: 8px;
  font-style: normal; font-weight: 400;
}
.page-tabs b { font-weight: 500; color: #16171D; }
.page-user {
  display: flex; align-items: center; gap: 9px;
  margin-left: auto; font-size: 13px; color: #8A8B94;
}
.page-ava {
  flex: none; display: grid; place-items: center; width: 26px; height: 26px;
  border-radius: 50%; font-style: normal;
  font-size: 9.5px; font-weight: 600; letter-spacing: .03em; color: #6E6F79;
}

/* ── WHAT A CONNECTED APP ACTUALLY LOOKS LIKE ────────────────────────
   Run used to raise a card in the Soba slot: a bordered panel headed
   "Running on their plan", floating beside the app with nothing holding it
   there. No such panel exists in the product. A connected machine shows up
   as a pill in a header with a popover under it — see
   components/bar/machine-pill.tsx — so the step shows that instead, in the
   customer's own header, which is also the one place it could truthfully be.

   The content is the END USER'S, not the dashboard's. The real panel also
   carries "Your other machines", "Pair another machine" and "Signed in to
   Soba as … / Sign out"; those belong to a vendor looking at their own
   account, and an Acme Desk user would never see them. What stays is what
   is true on both sides: the machine, the agent serving the run, and the
   two figures this band is about.

   ANCHORED TO THE PILL, not to the scene. The popover is a child of the
   wrapper the pill sits in, so it hangs from the pill's own edge whatever
   the header does with its widths — the Soba slot it used to live in is
   56px off the right and would have put it under nothing.
   ────────────────────────────────────────────────────────────────── */
/* LAST IN THE BAR, past the account. It is the only thing in this header
   that is not the customer's, so it sits at the end rather than in among
   their sections — and the popover hanging off the bar's own right edge is
   where a menu from the last control belongs. `.page-user` keeps the auto
   margin that pushes the group right; this just follows it.

   IT TAKES NO SPACE UNTIL IT ARRIVES. Hiding the pill with `visibility`
   alone left 258px of nothing at the end of the header all through Connect,
   with the account stranded short of the edge — a gap that reads as a
   layout fault rather than as a control that has not appeared yet.

   A grid column from `0fr` to `1fr`, which is the one way to animate to a
   width nobody has measured: the pill's width is its text, and the text is
   a machine name. So the account sits at the true right edge while the app
   is unconnected and is pushed left as the pill grows in beside it, which
   is also the honest picture — the header really does make room. */
.page-pw {
  position: relative;
  display: grid; grid-template-columns: 0fr; margin-left: 0;
  transition: grid-template-columns .5s cubic-bezier(.22, .61, .36, 1),
              margin-left .5s cubic-bezier(.22, .61, .36, 1);
}
/* `min-width` and `overflow` are both needed for the column to collapse at
   all: a grid item's floor is its content unless it is told otherwise, and
   the pill sets `nowrap`. The padding is the last 14px — it does not shrink
   with the content, so a "collapsed" track still measured its two sides —
   and it comes back with the width rather than before it. */
.page-pw > .page-pill { min-width: 0; overflow: hidden; padding-inline: 0; }
.js .scene.is-live .page-pw { grid-template-columns: 1fr; margin-left: 14px; }
.js .scene.is-live .page-pw > .page-pill { padding-inline: 3px 11px; }
.page-pill {
  display: flex; align-items: center; gap: 8px;
  height: 30px; padding: 3px 11px 3px 3px; border-radius: 999px;
  background: #fff; font-size: 12.5px; font-weight: 500; color: #16171D;
  white-space: nowrap;
  /* An inset ring and the smallest lift, the same pair the real pill uses: a
     plain border at this shape reads as an input rather than as a thing
     sitting on the bar. */
  box-shadow: inset 0 0 0 1px #E4E4EA, 0 1px 2px rgba(0, 0, 0, .05);
}
.page-pmk {
  flex: none; display: grid; place-items: center; width: 24px; height: 24px;
  border-radius: 50%; background: #16171D; color: #fff;
}
.page-pmk svg { width: 13px; height: 11px; display: block; }
/* The name is the quiet half: "Connected" is what you are checking, which
   machine it is on is what you are confirming. */
.page-pill em { font-style: normal; font-weight: 400; color: #8A8B94; }
/* AFTER THE WORDS, which is what makes it a status light rather than a
   bullet. The real pill makes the same point in the same place. */
.page-pdot {
  flex: none; width: 7px; height: 7px; border-radius: 50%; background: #3FA66A;
}

/* The popover. Hangs from the pill's right edge, below the header rule. */
.sbp {
  /* The screen's palette, declared again because the two rules this borrows
     below — `.cnp-row` and `.cnp-foot`, so the month figures and the footer
     stay the one definition they already were — resolve these tokens, and
     they are set on `.cnp`, which this is not. Without them the foot loses
     its ground and both lose their rule. */
  --c-ink: #131316; --c-muted: #6B6B73; --c-line: #E9E9EB; --c-raised: #F6F6F7;
  position: absolute; top: calc(100% + 9px); right: 0; z-index: 3;
  width: 310px; padding: 12px 12px 0; text-align: left;
  /* CLIPPED, because the footer is full-bleed. Without this its negative
     margins carry its ground out over the border on both sides and its
     corners square off against the panel's radius. */
  overflow: hidden;
  border-radius: 14px; background: #fff; border: 1px solid #E8E8EE;
  box-shadow: 0 1px 2px rgba(16, 17, 24, .04), 0 24px 48px -18px rgba(16, 17, 24, .28);
}
.sbp-k {
  margin: 0 0 7px; padding-left: 2px;
  font-size: 11.5px; font-weight: 500; color: #9A9BA4; letter-spacing: -.005em;
}
.sbp-k + .sbp-card + .sbp-k { margin-top: 14px; }
.sbp-card {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 11px; border-radius: 10px;
  border: 1px solid #ECECF0; background: #FCFCFD;
}
.sbp-tile {
  flex: none; display: grid; place-items: center; width: 26px; height: 26px;
  border-radius: 8px; background: #fff; color: #16171D;
  box-shadow: inset 0 0 0 1px #ECECF0;
}
.sbp-tile svg { width: 15px; height: 15px; }
.sbp-t { min-width: 0; flex: 1; }
.sbp-t b { display: block; font-size: 13px; font-weight: 500; color: #16171D; letter-spacing: -.01em; }
.sbp-t em { display: block; font-style: normal; font-size: 11.5px; color: #9A9BA4; margin-top: 1px; }
.sbp-v { flex: none; font-size: 11.5px; color: #74757E; }
.sbp-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: #3FA66A; }
/* The two figures sit under a rule rather than in cards: they are about the
   month, not about a thing on the machine, and giving them the same box
   would say they were another row of the same list. */
.sbp-rows { margin-top: 13px; border-top: 1px solid #ECECF0; }
/* `margin: 0` is load-bearing: the shared rule carries a 12px top margin for
   the card it was written in, and left on it that stacks with this border. */
.sbp .cnp-row { margin: 0; padding: 9px 2px; border: 0; font-size: 12.5px; color: #74757E; }
.sbp .cnp-row b { font-size: 13.5px; color: #16171D; }
/* Full-bleed to the panel's edges so the ground reaches the corners, but the
   words keep the inset everything above them has — `margin` and `padding`
   cancelling out, rather than the text starting at the border. */
.sbp .cnp-foot {
  margin: 0 -12px; padding: 9px 14px; border-top: 1px solid #ECECF0;
  border-radius: 0 0 14px 14px;
}

/* NEITHER EXISTS UNTIL THE APP IS CONNECTED. During Connect the header is
   the customer's own and nothing of ours is in it, which is the honest
   before-picture and makes the pill's arrival the thing the step is about.
   Visibility, not display: the pill holds its width either way, so the
   header does not reflow when it lands. */
.js .page-pill, .js .sbp { opacity: 0; visibility: hidden; }
.js .page-pill {
  transform: translateY(-3px);
  /* `padding` rides the same curve and duration as the wrapper's column, so
     the pill's sides arrive exactly as its width does. */
  transition: opacity .4s cubic-bezier(.3, 0, .2, 1),
              transform .5s cubic-bezier(.22, .61, .36, 1),
              padding .5s cubic-bezier(.22, .61, .36, 1), visibility 0s .4s;
}
.js .sbp {
  transform: translateY(-6px) scale(.985); transform-origin: 85% 0;
  transition: opacity .34s cubic-bezier(.3, 0, .2, 1),
              transform .46s cubic-bezier(.22, .61, .36, 1), visibility 0s .34s;
}
.js .scene.is-live .page-pill, .js .scene.is-live .sbp {
  opacity: 1; visibility: visible; transform: none;
}
/* The popover follows the pill rather than arriving with it — a menu that
   is already open before the control it belongs to exists reads backwards. */
.js .scene.is-live .page-pill { transition-delay: .06s; }
.js .scene.is-live .sbp { transition-delay: .34s; }

/* ── the app behind the dialog: a chat, and a model using tools ──────── */
/* GLASS, NOT INK. Every control in here used to be a black rectangle, and
   four of them on one screen read as four buttons shouting at each other
   under a scrim that was already flattening the page. They are the same
   surface now: a pane of near-white over the page, a hairline around it and
   a lit top edge, which is what a raised piece of glass looks like from
   straight on. The weight that distinguishes them is the border, not the
   fill, so nothing on this half of the figure competes with the Soba dialog
   standing in front of it. */
.page-new, .page-send, .page-av, .page-msg.is-you p,
.page-logo, .page-ava, .page-tabs b {
  background: linear-gradient(180deg, rgba(255, 255, 255, .92), rgba(246, 246, 250, .74));
  border: 1px solid #DEDEE6;
  box-shadow: 0 1px 2px rgba(16, 17, 25, .05),
              inset 0 1px 0 rgba(255, 255, 255, .9);
  -webkit-backdrop-filter: blur(10px);
          backdrop-filter: blur(10px);
  color: #16171D;
}
/* A support desk with a thread list, a conversation down the middle and a
   composer under it. The one skeleton primitive, .page-l, carries the half
   of it nobody is meant to read; everything above is written out, because
   the point of the figure is that this is an ordinary agentic app and the
   Soba dialog is a visitor in it. */
.page-body { display: grid; grid-template-columns: 252px 1fr; height: calc(100% - 56px); }
.page-brand { font-weight: 600; color: #16171D; letter-spacing: -.02em; margin-right: 6px; }
.page-side { border-right: 1px solid #ECECF0; padding: 16px 14px; }
.page-new {
  display: block; padding: 9px 0; margin-bottom: 14px; border-radius: 10px;
  text-align: center; font-size: 13px; font-weight: 600;
}
.page-thr {
  display: flex; flex-direction: column; gap: 3px;
  padding: 10px 12px; border-radius: 9px;
}
.page-thr.is-on { background: #F3F3F6; }
.page-thr b { font-size: 13.5px; font-weight: 500; letter-spacing: -.01em; }
.page-thr em { font-style: normal; font-size: 12px; color: #9A9BA4; }
.page-thr.is-on em { color: #6E6F79; }
/* the conversation. Bottom-aligned, the way a chat that has been running a
   while sits: the newest turn against the composer, the older ones pushed
   off the top rather than floating in the middle of an empty column. */
/* IT STOPS BEFORE THE SOBA WINDOW STARTS. The window stands at the top
   right of the screen and the dialog in the middle, so a conversation set
   to the full width of the column puts its own newest turn, the one on the
   right, entirely behind them. Capped here it reads as a chat with two
   sides to it, which is the only thing this half of the figure has to say. */
.page-chat {
  display: flex; flex-direction: column; justify-content: flex-end;
  max-width: 620px; padding: 26px 40px 20px;
}
/* the thread the conversation belongs to. It also fills the top of the
   column, which a chat with three turns in it otherwise leaves empty. */
.page-head { padding-bottom: 16px; border-bottom: 1px solid #ECECF0; }
.page-head b { display: block; font-size: 15px; font-weight: 600; letter-spacing: -.02em; }
.page-head em { font-style: normal; font-size: 12.5px; color: #9A9BA4; }
.page-msgs { display: flex; flex-direction: column; gap: 22px; margin-top: auto; padding-top: 26px; }
.page-msg { display: flex; gap: 12px; }
.page-msg.is-you { align-self: flex-end; max-width: 400px; }
/* the person's turn. Without a dark fill the thing that says whose turn it
   is becomes the shape: pulled to the right, and cornered into the side it
   came from. */
.page-msg.is-you p {
  margin: 0; padding: 11px 15px; border-radius: 14px 14px 4px 14px;
  border-color: #D6D7E0; font-size: 14px; line-height: 1.5;
}
.page-av {
  flex: none; display: grid; place-items: center; width: 28px; height: 28px;
  border-radius: 9px; color: #6E6F79;
  font-size: 10.5px; font-weight: 600; letter-spacing: .03em;
}
.page-bub { min-width: 0; flex: 1; }
.page-bub p { margin: 0 0 12px; font-size: 14px; line-height: 1.55; color: #3A3B44; }
/* the tool calls, shown rather than hidden. An agentic app that does not
   say what it just did is a text box with a spinner. */
.page-tool {
  display: flex; align-items: center; gap: 9px; margin-bottom: 8px;
  padding: 7px 12px; border: 1px solid #ECECF0; border-radius: 8px;
  font-size: 12.5px; color: #6E6F79;
}
.page-tool em { font-style: normal; margin-left: auto; color: #A9AAB3; font-size: 11.5px; }
.page-tdot {
  width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: #fff; box-shadow: inset 0 0 0 1.5px #B9BAC4;
}
.page-tool.is-live { border-color: #D7D7E0; color: #16171D; }
/* THE ONE DIVERGENCE FROM /app's COPY OF THIS SCREEN. There the live dot is
   green, which is fine on a page where nothing else is; here it was the
   fourth green thing in one picture. Hollow ring means done and filled means
   running, which the shape already said — the colour was only repeating it,
   and the pulse above says the rest. */
.page-tool.is-live .page-tdot { background: #16171D; box-shadow: none; }
.page-l { display: block; height: 8px; border-radius: 4px; background: #ECECF0; margin-bottom: 10px; }
/* the composer is the one pane the glass sits ON rather than beside: a
   wider, flatter sheet with the send button raised out of it */
.page-comp {
  display: flex; align-items: center; margin-top: 24px;
  padding: 13px 14px 13px 18px; border: 1px solid #E4E4EA; border-radius: 13px;
  background: linear-gradient(180deg, rgba(252, 252, 254, .9), rgba(246, 246, 250, .66));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .9);
}
.page-ph { font-size: 14px; color: #A9AAB3; }
.page-send {
  margin-left: auto; display: grid; place-items: center; width: 30px; height: 30px;
  border-radius: 9px; font-size: 9px; color: #6E6F79;
}
/* ONCE THE PAIRING IS ANSWERED THE PAGE COMES BACK, which is what actually
   happens: the dialog closes and the app the person was using carries on.
   From the third screen on, the scrim and the dialog fade and the window is
   alone on a page that is no longer waiting on it. */
.scene-scrim { transition: opacity .5s cubic-bezier(.3, 0, .2, 1), visibility 0s; }
.scene.is-live .scene-scrim { opacity: 0; visibility: hidden; transition: opacity .5s cubic-bezier(.3,0,.2,1), visibility 0s .5s; }
/* the dialog's own states, stacked in one place and cross-faded the way
   the window beside them is */
.modalf { position: absolute; inset: 0; opacity: 0; visibility: hidden; transition: opacity .4s cubic-bezier(.3,0,.2,1), visibility 0s .4s; }
.modalf.is-on { opacity: 1; visibility: visible; transition: opacity .4s cubic-bezier(.3,0,.2,1) .05s, visibility 0s; }
/* what makes the two windows legible, and what makes the grey in the
   screenshots grey */
.scene-scrim {
  position: absolute; inset: 0; background: rgba(22, 23, 29, .52);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
}
/* ── the connect modal, as the customer's own page draws it ──────────── */
/* The web half of the pairing, on the desk beside the app that answers it.
   Behind the popover and a shade softer: it is the thing being answered. */
.modal {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 342px;
  border-radius: 14px; overflow: hidden; background: #fff;
  font-family: "Schibsted Grotesk", system-ui, sans-serif; color: #16171D;
  box-shadow: 0 0 0 .5px rgba(0, 0, 0, .28), 0 1px 3px rgba(0, 0, 0, .2),
              0 34px 64px -26px rgba(0, 0, 0, .5);
}
.modal-bar { display: flex; align-items: center; justify-content: space-between; padding: 13px 15px; color: #9A9BA4; }
.modal-bar b { font-size: 13.5px; font-weight: 600; color: #16171D; letter-spacing: -.015em; }
.modal-body { display: flex; flex-direction: column; align-items: center; padding: 4px 22px 20px; text-align: center; }
/* THE ORBIT. Soba's arc going round whichever provider is being opened —
   the one thing on the dialog that says it is waiting rather than done.
   A dash of the circumference, rotating; the stroke is Soba's, the mark
   underneath is the provider's. */
.modal-art { position: relative; display: grid; place-items: center; width: 86px; height: 86px; }
.modal-mark { color: #16171D; }
.modal-orbit { position: absolute; inset: 0; width: 100%; height: 100%; }
.modal-orbit circle {
  fill: none; stroke: #16171D; stroke-width: 5; stroke-linecap: round;
  /* r=44 → a circumference of about 276, so 62 is a 80° arc */
  stroke-dasharray: 62 214;
  transform-origin: 50% 50%;
  animation: modal-spin 1.15s cubic-bezier(.45, 0, .55, 1) infinite;
}
@keyframes modal-spin { to { transform: rotate(360deg) }
}
.modal-h { margin: 13px 0 0; font-size: 17px; font-weight: 600; letter-spacing: -.028em; }
.modal-s { margin: 6px 0 0; font-size: 12.5px; color: #6E6F79; }
.modal-n { display: flex; align-items: center; gap: 6px; margin: 13px 0 0; font-size: 11.5px; color: #6E6F79; }
.modal-n svg { width: 13px; height: 13px; color: #16171D; flex-shrink: 0; }
.modal-btn {
  display: block; width: 100%; margin-top: 17px; padding: 11px 0; border-radius: 10px;
  font-size: 13px; font-weight: 600; text-align: center; box-shadow: inset 0 0 0 1px #E3E3E8;
}
.modal-s2 { margin-top: 13px; }
.modal-get {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; margin-top: 17px; padding: 9px 9px 9px 13px;
  border-radius: 10px; background: #F5F5F7; font-size: 12px; color: #45464F;
}
.modal-get i {
  font-style: normal; font-size: 12.5px; font-weight: 600; color: #16171D;
  background: #fff; border-radius: 8px; padding: 6px 11px;
  box-shadow: 0 0 0 1px #E3E3E8, 0 1px 2px rgba(0, 0, 0, .06);
}
.modal-foot {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  padding: 12px 0 14px; border-top: 1px solid #EFEFF2; font-size: 11.5px; color: #6E6F79;
}
.modal-foot svg { width: 15px; height: 13px; color: #16171D; }
/* ── the app, as it ships ────────────────────────────────────────────── */
/* Real macOS chrome, because the thing has real macOS chrome: a titled bar
   with traffic lights in it, not the bare panel the canvas drew. */
.shotw { position: absolute; top: 52px; right: 56px; width: 400px; height: 624px; }
/* the frames are stacked in one box and cross-fade: a hard cut between two
   screens of the same app reads as a glitch, not as a choice */
.shotf { position: absolute; inset: 0; }
.js .shotf {
  opacity: 0; visibility: hidden;
  transform: translateY(12px) scale(.986);
  transition: opacity .44s cubic-bezier(.3, 0, .2, 1),
              transform .66s cubic-bezier(.22, .61, .36, 1),
              visibility 0s .44s;
}
.js .shotf.is-on {
  opacity: 1; visibility: visible; transform: none;
  transition: opacity .44s cubic-bezier(.3, 0, .2, 1) .06s,
              transform .66s cubic-bezier(.22, .61, .36, 1) .06s;
}
.app {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  border-radius: 12px; overflow: hidden; background: #F2F2F4;
  font-family: "Schibsted Grotesk", system-ui, sans-serif;
  color: #16171D; font-size: 13px; letter-spacing: -.008em;
  box-shadow: 0 0 0 .5px rgba(0, 0, 0, .35), 0 2px 6px -1px rgba(0, 0, 0, .3),
              0 46px 84px -24px rgba(0, 0, 0, .55), 0 18px 32px -16px rgba(0, 0, 0, .35);
}
.app-bar {
  position: relative; display: flex; align-items: center; flex-shrink: 0;
  height: 38px; padding: 0 13px; background: #F7F7F9;
  border-bottom: 1px solid rgba(16, 17, 25, .08);
}
/* SCOPED TO THE BAR, because `.app-dots` is two different things.

   The window's traffic lights are these; the handshake dots in the pairing
   graphic below are also `.app-dots`, and that block redefined the bare
   class. Later and same specificity, so it won — the traffic lights were
   being drawn at the handshake's 4px instead of 11px — while THESE two
   `:nth-child` rules outrank it on specificity and went on painting red and
   yellow onto the handshake. Each had half of the other's appearance.

   Both sides are scoped now. Nothing else in the sheet shares the name. */
.app-bar .app-dots { display: flex; gap: 7px; z-index: 1; }
.app-bar .app-dots i { width: 11px; height: 11px; border-radius: 50%; background: #DCDCE1; }
.app-bar .app-dots i:first-child { background: #FF5F57; }
.app-bar .app-dots i:nth-child(2) { background: #FEBC2E; }
.app-bar b { position: absolute; left: 0; right: 0; text-align: center; font-size: 13.5px; font-weight: 600; letter-spacing: -.015em; }
.app-body { flex: 1; min-height: 0; display: flex; flex-direction: column; padding: 12px 12px 0; }
.app-card { background: #fff; border-radius: 11px; box-shadow: 0 0 0 .5px rgba(16, 17, 25, .09), 0 1px 2px rgba(16, 17, 25, .05); }
.app-row { position: relative; display: flex; align-items: center; gap: 11px; padding: 9px 12px; min-height: 42px; }
.app-row + .app-row::before { content: ""; position: absolute; top: 0; left: 50px; right: 0; height: 1px; background: rgba(16, 17, 25, .07); }
.app-gi { width: 29px; height: 29px; border-radius: 8px; flex-shrink: 0; display: grid; place-items: center; background: #F1F1F4; color: #45464F; }
.app-rt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.app-rt b { font-size: 13px; font-weight: 600; letter-spacing: -.012em; }
.app-rt em { font-style: normal; font-size: 11.5px; color: #9A9BA4; }
/* ── the six screens, from the v2 canvas ─────────────────────────────── */
/* Board "02 · Pairing" end to end, then "03 · Home". Every screen below is
   the canvas's; the window around them is the one the app ships with. */

/* a screen that is all one thing: Soba facing the app, or a code, or the
   moment it is done */
.app-ask { display: flex; flex-direction: column; padding: 26px 20px 20px; flex: 1; min-height: 0; }
.app-mid { justify-content: center; }
.app-pair { display: flex; align-items: center; justify-content: center; gap: 13px; }
/* THE HANDSHAKE, AND IT IS GREY. These are not window controls — they are
   the line travelling between the Soba tile and the app's, and a red and an
   amber one in the middle of it read as two warnings about a connection that
   is going fine. They were borrowing the traffic lights' colours by accident;
   see the note on `.app-bar .app-dots` above. */
.app-pair .app-dots { display: flex; gap: 4px; }
.app-pair .app-dots i { width: 4px; height: 4px; border-radius: 50%; background: #C9C9D1; }
.app-pair .app-dots i:first-child, .app-pair .app-dots i:nth-child(2) { background: #C9C9D1; }
/* once it is connected the line between them fills in */
.app-pair .app-dots.is-on i { background: #16171D; }
.app-gi-lg { width: 52px; height: 52px; border-radius: 14px; font-size: 21px; }
/* the front page's placeholder app: a dashed tile, because the app in the
   picture is whichever one the reader is building */
.app-gi-ph { background: none; overflow: hidden; }
.app-ph { width: 100%; height: 100%; display: block; border-radius: inherit; }
.app-gi-s { position: relative; background: #141210; color: #fff; }
.app-gi-s svg { width: 26px; height: 22px; }
/* the green light on the Soba tile, once the pairing is answered */
.app-lamp {
  position: absolute; right: -3px; bottom: -3px; width: 15px; height: 15px;
  border-radius: 50%; background: #22A06B; box-shadow: 0 0 0 3px #F2F2F4;
}
.app-solo { margin: 0 auto; }
/* mid-handshake the dots travel; connected, they are simply filled */
.app-pair .app-dots.is-go i { animation: app-go 1.1s ease-in-out infinite; }
.app-pair .app-dots.is-go i:nth-child(2) { animation-delay: .16s; }
.app-pair .app-dots.is-go i:nth-child(3) { animation-delay: .32s; }
@keyframes app-go { 0%, 100% { opacity: .35 }
50% { opacity: 1; background: #16171D }
}
/* ── connect: the AI you already have ────────────────────────────────── */
/* The front page's own first pairing screen. Three ways in that cost
   nothing, then a key as the "or" — in that order, because the order is
   the argument. */
/* WITH THE KEY FIELD GONE the screen is shorter than the window, and a
   column that starts at the top leaves 130px of nothing under "Secured by
   Soba". Centred, the same content reads as a screen that fits. */
.app-tight { justify-content: center; padding-top: 22px; }
/* ── Connect: the choices ────────────────────────────────────────────── */
/* EACH ONE IS ITS OWN ROW. They used to be three rows inside one card,
   divided by hairlines, which is the shape a list of settings takes — and
   it made "Continue with Claude" and "Sign in with ChatGPT" look like two
   of a kind when the whole point is that one runs here and the other runs
   in a browser tab. Separated, with the fine print and a rule between the
   two groups, the screen says which is which before a word is read. */
.app-pick {
  position: relative; display: flex; align-items: center; justify-content: center;
  margin-top: 10px; padding: 12px; min-height: 48px;
  border: 1px solid #E4E4EA; border-radius: 11px; background: #fff;
}
.app-pick b { font-size: 13.5px; font-weight: 600; letter-spacing: -.012em; }
/* the mark sits at the left edge and the label stays in the middle of the
   whole row — see the note on _pick in windows.py */
.app-pg {
  position: absolute; left: 14px; width: 26px; height: 26px;
  display: grid; place-items: center; color: #16171D;
}
.app-pg svg { width: 19px; height: 19px; }
/* the one piece of state on the screen: this machine already has Claude */
.app-rdy {
  position: absolute; right: 12px; font-style: normal;
  padding: 3px 8px 2px; border-radius: 999px;
  /* grey, not green. This pill came back when the app's own screens did, and
     it is the same call as the dot beside it: nothing on this page signals a
     good state with a second hue — the word is already the signal. */
  background: #F1F1F3; color: #6B6B73;
  font-size: 10.5px; font-weight: 600; letter-spacing: -.01em;
}
/* .app-or marked where this machine stopped and a browser tab began. Every
   route on this screen runs on the machine now, so there is no border left
   for a rule to draw. */
/* the way in for somebody the three choices above do not fit */
.app-code { margin: 22px 0 0; text-align: center; font-size: 13px; color: #45464F; }
/* the footer is the window's, not the column's: a rule across the whole
   panel with the mark and the one promise under it */
.app-foot {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  margin: 0 -12px; padding: 13px 0 14px; border-top: 1px solid #ECECF0;
  font-size: 11.5px; color: #9A9BA4;
}
.app-foot svg { width: 13px; height: 13px; }
.app-ask h3 {
  margin: 22px 0 0; text-align: center;
  font-size: 18px; font-weight: 600; letter-spacing: -.028em; line-height: 1.28;
  text-wrap: balance;
}
.app-ask > p { margin: 9px 0 0; text-align: center; font-size: 12.5px; line-height: 1.5; color: #7B7C85; }
/* what it gets: three rows, no ticks. A tick would read as a choice that
   has already been made. */
.app-grants { margin-top: 22px; }
.app-grants .app-row { padding: 11px 13px; min-height: 48px; }
.app-grants .app-gi { background: #F4F4F7; color: #5B5C66; }
.app-fine { margin: 14px 2px 0; text-align: center; font-size: 11.5px; line-height: 1.5; color: #9A9BA4; }
.app-fine b { font-weight: 600; color: #45464F; }
/* Authorize leads; the second answer is a real one and stays legible
   rather than greyed into a hint */
.app-btn { display: flex; align-items: center; justify-content: center; gap: 7px; margin-top: 16px; padding: 13px 0; border-radius: 11px; text-align: center; font-size: 14.5px; font-weight: 600; background: #16171D; color: #fff; }
.app-ask .app-btn:first-of-type { margin-top: auto; }
.app-mid .app-btn:first-of-type { margin-top: 26px; }
.app-tight .app-btn:first-of-type { margin-top: 16px; }
.app-btn-g { margin-top: 9px; background: transparent; color: #45464F; box-shadow: inset 0 0 0 1px #E5E5EA; }

/* THE MOCKS THEMSELVES, unchanged. They were drawn for the three stages and
   they were never what was wrong with the band — only the 360px box each one
   was shrunk into. The one screen shows them at the size the product is. */
/* step 1 — the connect sheet */
.sheet { width: 290px; border-radius: 14px; background: #fff; padding: 20px; text-align: center; }
.sheet-h { font-size: 16px; font-weight: 600; margin-top: 8px; }
.sheet-s { font-size: 12.5px; color: var(--ink3); margin: 4px 0 10px; line-height: 1.45; }
.opt {
  height: 40px; border-radius: 9px; display: flex; align-items: center; justify-content: center;
  gap: 9px; font-size: 13.5px; font-weight: 600; border: 1px solid var(--line); margin-top: 8px;
}
.opt.on { background: var(--ink); color: #fff; border-color: var(--ink); }
.opt.mute { color: var(--ink3); font-weight: 500; }

/* step 2 — the menu-bar popover, as the desktop app actually draws it */
.scx-st { align-items: flex-start; padding: 22px 0 0; }
.scx {
  zoom: .63; width: 340px; display: flex; flex-direction: column; align-items: center;
  font-family: "Schibsted Grotesk", system-ui, sans-serif; color: #0A0A0A; letter-spacing: -.005em;
}
.scx-pill {
  display: flex; align-items: center; gap: 9px; height: 36px; padding: 0 14px 0 5px;
  border-radius: 999px; background: #fff; box-shadow: 0 0 0 1px #E4E4E7;
  font-size: 14px; color: #71717A; white-space: nowrap;
}
.scx-pill b { font-weight: 600; color: #0A0A0A; }
.scx-pm { width: 27px; height: 27px; border-radius: 50%; background: #F4F4F5; box-shadow: 0 0 0 1px #E4E4E7; display: grid; place-items: center; color: #0A0A0A; }
.scx-g { width: 8px; height: 8px; border-radius: 50%; background: #3FA34D; display: block; flex-shrink: 0; }
.scx-pop {
  margin-top: 8px; width: 320px; border-radius: 22px; background: #F4F4F5; padding: 12px;
  box-shadow: 0 0 0 1px rgba(10, 10, 10, .06), 0 24px 48px -20px rgba(10, 10, 10, .3);
}
.scx-hd { display: flex; justify-content: space-between; align-items: center; padding: 0 0 8px 12px; font-size: 13px; color: #8A8A92; }
.scx-btns { display: flex; gap: 6px; }
.scx-btns i { width: 30px; height: 30px; border-radius: 50%; background: #fff; display: grid; place-items: center; color: #52525B; box-shadow: 0 0 0 1px rgba(10, 10, 10, .04); }
.scx-c { background: #fff; border-radius: 16px; box-shadow: 0 1px 2px rgba(10, 10, 10, .04); }
.scx-m { display: flex; align-items: center; gap: 12px; padding: 12px; }
.scx-m b { display: block; font-size: 15px; font-weight: 500; }
.scx-m span { font-size: 13px; color: #71717A; }
.scx-tile { position: relative; width: 46px; height: 46px; border-radius: 12px; background: #F4F4F5; box-shadow: inset 0 0 0 1px #E4E4E7; display: grid; place-items: center; flex-shrink: 0; }
.scx-b { position: absolute; right: -3px; bottom: -3px; width: 11px; height: 11px; border: 2px solid #fff; }
.scx-l { font-size: 13px; color: #8A8A92; padding: 14px 12px 8px; }
.scx-list { padding: 6px 12px; }
.scx-list > div { display: flex; align-items: center; gap: 12px; height: 34px; }
.scx-n { flex: 1; font-size: 14.5px; }
.scx-n em { font-style: normal; color: #A1A1AA; }
.scx-cc { width: 18px; height: 18px; border-radius: 4px; background: #D97757; display: grid; place-items: center; }
.scx-ol { width: 18px; height: 18px; display: grid; place-items: center; }
/* the dot on the agent that is mid-run */
.scx-p { animation: scxp 1.6s ease-in-out infinite; }
@keyframes scxp {
  0%, 100% { box-shadow: 0 0 0 0 rgba(63, 163, 77, .35); }
  50%      { box-shadow: 0 0 0 4px rgba(63, 163, 77, 0); }
}

/* step 3 — the number you did not pay */
.dash { width: 100%; border-radius: 10px; background: #fff; padding: 20px; }
.dash-k { font-size: 13px; color: var(--ink3); }
.dash-l { font-size: 14px; color: var(--ink2); margin-top: 14px; }
.dash-v { font-size: 52px; font-weight: 600; letter-spacing: -.045em; line-height: 1.05; }
.dash .bar { display: flex; height: 10px; border-radius: 3px; overflow: hidden; margin-top: 16px; }
.dash-split { display: flex; justify-content: space-between; font-size: 12.5px; color: var(--ink2); margin-top: 8px; }
.dash-split span + span { color: var(--ink3); }
.kp { display: grid; grid-template-columns: 1fr 1fr; border-top: 1px solid var(--line2); margin-top: 18px; }
.kp div { padding-top: 12px; }
.kp div + div { padding-left: 16px; border-left: 1px solid var(--line2); }
.kp span { font-size: 12px; color: var(--ink3); }
.kp b { display: block; font-size: 22px; letter-spacing: -.03em; font-weight: 600; margin-top: 2px; }



/* ── 5 · where a run lands ────────────────────────────────────────────
   Carried over from the page this one replaces: the diagram was already
   right, and only its type needed to move to this family. The two ends
   are the same app, before and after — you type, their machine works,
   the text appears. */
.lands {
  --dark: var(--night); --dark-2: var(--night2); --dark-text: var(--ink3);
  background: var(--night); color: #fff; padding-block: 104px 96px;
}
.lands .in > .sec-head { text-align: center; display: flex; flex-direction: column; align-items: center; }
.lands .lede { max-width: 520px; color: var(--ink3); font-size: 17.5px; }

.opt-tabs { display: flex; justify-content: center; gap: 8px; margin-top: 26px; flex-wrap: wrap; }
.opt-tab {
  display: flex; align-items: center; gap: 9px; font: inherit; font-size: 14px; font-weight: 500;
  letter-spacing: -.014em; color: rgba(255, 255, 255, .82);
  background: transparent; border: 1px solid rgba(255, 255, 255, .16);
  border-radius: 999px; padding: 7px 16px;
}

/* the path of one run: your call → their machine → your surfaces */
.nvf {
  margin: 26px auto 0; display: grid; grid-template-columns: 1fr auto 2.1fr auto 1fr;
  align-items: stretch; gap: 4px;
}
.nvf-node {
  background: rgba(255, 255, 255, .028); border: 1px solid rgba(255, 255, 255, .09);
  border-radius: 12px; padding: 18px 16px; align-self: center;
}
.nvf-mid { display: flex; flex-direction: column; justify-content: center; gap: 8px; }
.nvf-k { font-size: 13px; font-weight: 600; color: #fff; letter-spacing: -.015em; margin-bottom: 7px; }
.nvf-s { font-size: 12.5px; line-height: 1.5; color: var(--ink3); }
.nvf-cap {
  margin-top: 12px; padding: 0 4px; text-align: center; min-height: 39px;
  font-size: 12.5px; line-height: 1.55; color: var(--ink3);
}
.nvf-cap b { font-weight: 600; color: rgba(255, 255, 255, .92); }
.nvf-link { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 7px; padding: 0 2px; }
.nvf-lab { font-size: 10px; text-transform: uppercase; letter-spacing: .08em; color: var(--ink3); }
.nvf-chevs { width: 72px; height: 26px; flex: none; }
.nvf-chev {
  fill: none; stroke: #fff; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
  animation: drift 2.2s ease-in-out infinite;
}
.nvf-c2 { animation-delay: .13s; }
.nvf-c3 { animation-delay: .26s; }
@keyframes drift {
  0%, 100% { opacity: .25; transform: translateX(-2px); }
  50%      { opacity: 1;   transform: translateX(2px); }
}

/* your app, at both ends of the flow */
.appm { margin: 12px 0; border: 1px solid rgba(255, 255, 255, .12); border-radius: 9px; background: var(--night2); overflow: hidden; }
.appm-bar {
  display: flex; align-items: center; gap: 7px; padding: 7px 10px;
  border-bottom: 1px solid rgba(255, 255, 255, .08);
  font-size: 11px; font-weight: 500; color: rgba(255, 255, 255, .62);
}
.appm-dot { width: 9px; height: 9px; border-radius: 3px; background: rgba(255, 255, 255, .22); }
.appm-body { padding: 11px 10px 12px; }
.appm-you {
  background: rgba(255, 255, 255, .07); border-radius: 7px; padding: 8px 9px;
  font-size: 11.5px; line-height: 1.4; color: rgba(255, 255, 255, .86);
}
.appm-cur { display: inline-block; width: 1.5px; height: 11px; margin-left: 2px; vertical-align: -1px; background: rgba(255, 255, 255, .7); }
.appm-go {
  margin: 9px 0 0 auto; width: fit-content;
  border: 1px solid rgba(255, 255, 255, .16); border-radius: 6px; padding: 4px 9px;
  font-size: 11px; font-weight: 500; color: rgba(255, 255, 255, .82);
}
.appm-body-out { display: flex; flex-direction: column; gap: 7px; padding-block: 14px 16px; }
.appm-ln { height: 6px; border-radius: 3px; background: rgba(255, 255, 255, .16); }
.appm-ln-m { width: 78%; }
.appm-ln-s { width: 46%; background: rgba(255, 255, 255, .1); }

/* the app as it arrives: a volume you drag it out of */
.dmg {
  border-radius: 10px; overflow: hidden; border: 1px solid rgba(255, 255, 255, .1);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .08) inset, 0 36px 74px -26px rgba(0, 0, 0, .9);
}
.dmg-bar {
  position: relative; display: flex; align-items: center; height: 32px; padding: 0 10px;
  border-bottom: 1px solid rgba(255, 255, 255, .06);
  background: linear-gradient(180deg, rgba(60, 60, 64, .96), rgba(46, 46, 50, .96));
}
.dmg-lights { display: flex; gap: 8px; }
.dmg-lights i { width: 9px; height: 9px; border-radius: 50%; display: block; box-shadow: 0 0 0 .5px rgba(0, 0, 0, .35) inset; }
.dmg-lights i:nth-child(1) { background: #ff5f57; }
.dmg-lights i:nth-child(2) { background: #febc2e; }
.dmg-lights i:nth-child(3) { background: #28c840; }
.dmg-title {
  position: absolute; left: 0; right: 0; text-align: center; pointer-events: none;
  font-size: 11.5px; font-weight: 600; letter-spacing: -.012em; color: rgba(255, 255, 255, .78);
}
.dmg-body {
  display: flex; align-items: center; justify-content: center; gap: 14px; padding: 62px 14px 56px;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(255, 255, 255, .05), rgba(255, 255, 255, 0) 62%),
    linear-gradient(180deg, #202023, #171719);
}
.dmg-slot { display: flex; flex-direction: column; align-items: center; gap: 9px; width: 96px; }
.dmg-art { width: 72px; height: 72px; display: block; filter: drop-shadow(0 12px 18px rgba(0, 0, 0, .45)); }
.dmg-name { font-size: 11.5px; letter-spacing: -.01em; color: rgba(255, 255, 255, .82); padding: 2px 8px; border-radius: 5px; white-space: nowrap; }
.dmg-name.is-sel { background: rgba(255, 255, 255, .14); color: #fff; }
.dmg-arrow { width: 58px; height: 18px; flex: none; margin-bottom: 20px; }
.dmg-chev { fill: none; stroke: rgba(255, 255, 255, .5); stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; animation: drift 2.2s ease-in-out infinite; }

/* ── DRAG IT ONTO APPLICATIONS ───────────────────────────────────────
   The window was a picture of the install; now it is the install. The one
   gesture everybody already knows for "put this on my Mac" is the one this
   image has always been of, and doing it takes you to Get it.

   `touch-action: none` is what makes it work with a finger: without it the
   browser claims the gesture as a scroll the moment it moves, and the icon
   is dropped mid-drag. The anchor still answers a tap, because a tap never
   becomes a drag — see the note in the markup. */
.dmg-drag {
  text-decoration: none; color: inherit;
  cursor: -webkit-grab; cursor: grab;
  touch-action: none; -webkit-user-select: none; user-select: none;
  border-radius: 10px;
}
.dmg-drag:focus-visible { outline: 2px solid #fff; outline-offset: 4px; }
/* no transition while it is under the pointer — it has to BE where the
   pointer is, and an eased follow is a lag */
.dmg-drag .dmg-art { transition: transform .42s cubic-bezier(.22, .61, .36, 1); }
.dmg.is-dragging { cursor: -webkit-grabbing; cursor: grabbing; }
.dmg.is-dragging .dmg-drag { cursor: -webkit-grabbing; cursor: grabbing; }
.dmg.is-dragging .dmg-art { transition: none; }
/* the icon rides above the folder it is being taken to */
.dmg.is-dragging .dmg-drag { position: relative; z-index: 2; }
/* Finder lights the folder when the drop would land. Scale and a lift, not a
   colour: the folder is the one blue thing on a dark window and tinting it
   further says less than moving it does. */
.dmg-target .dmg-art { transition: transform .22s cubic-bezier(.22, .61, .36, 1), filter .22s; }
.dmg-target.is-over .dmg-art {
  transform: scale(1.09);
  filter: drop-shadow(0 12px 18px rgba(0, 0, 0, .45)) brightness(1.12);
}
/* dropped: into the folder and gone, which is the whole of what a drop
   looks like in Finder before the copy sheet appears */
.dmg-drag.is-in .dmg-art { opacity: 0; transform: var(--dmg-to) scale(.34); }
.dmg-drag.is-in .dmg-name { opacity: 0; transition: opacity .2s; }

@media (prefers-reduced-motion: reduce) {
  /* the drop still works and still goes to Get it; it simply does not fly */
  .dmg-drag .dmg-art, .dmg-target .dmg-art { transition: none; }
  .dmg-chev { animation: none; }
}
.dmg-c2 { animation-delay: .11s; }
.dmg-c3 { animation-delay: .22s; }
.dmg-c4 { animation-delay: .33s; }
.dmg-c5 { animation-delay: .44s; }

.lands-more { text-align: center; }
.link-more { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; font-weight: 500; margin-top: 26px; color: #fff; }
.link-more:hover { text-decoration: underline; text-underline-offset: 4px; }
.arrow { font-size: 9px; opacity: .7; }

/* ── 6a · plans ───────────────────────────────────────────────────────
   The pairing band's slot, and its ground.

   THE LADDER, AND THE SWITCH ON EACH ROUTE. Ten drawings have stood here.
   The last two were both answering the right question with the wrong
   picture: one fanned a single plan out into its fields, which explains
   what a plan IS to somebody who already knows they have them; the other
   drew the catalogue — cards with names and prices — and on a landing page
   a catalogue reads as the price of the product you are reading about.

   The heading promises the DECIDING, so the figure draws it. On the left,
   the route a run walks: four routes in ladder order, each with the switch
   the vendor threw on it, and the one that spends the vendor's own money
   turned off. On the right, the other half of the same object. plans.md
   says pricing and routing are the same decision in those words, which is
   why they are one figure under one caption rather than two.

   Everything here is drawn from the page's own tokens — the dashed edge
   on a switch that is off is the same grammar the rest of the page uses
   for a route that cannot be taken. */
/* wide enough for the longer of the heading's two written lines to stay
   on one — "You decide what your users run on," needs it at .h2-48 */
.bcopy { max-width: 790px; }
.bcopy .h2 { max-width: none; }
.bcopy .lede { max-width: 56ch; }

.bld {
  margin-top: 52px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 404px);
  column-gap: 72px; row-gap: 40px;
  align-items: start;
}
/* each half is one column — its heading and the thing under it — so a
   narrow screen stacks them in the order they are read rather than
   putting both headings above both halves */
.bld-h { min-width: 0; }
/* each side named once, above, so no row has to carry a label saying which
   half of the argument it belongs to */
.bld-k {
  font-family: "JetBrains Mono", monospace; font-size: 11px; font-weight: 500;
  letter-spacing: .09em; text-transform: uppercase; color: var(--ink3);
  padding-bottom: 18px;
}

/* THE LADDER. One hairline behind the numbers, from the first route to the
   last, which is what makes four rows read as an order rather than a set.
   The numbers sit on the page's own white and cover it. */
/* capped rather than fluid: a route is a short noun phrase and a switch,
   and at the full width of this column the two ended up at opposite ends
   of an empty card */
.brg { position: relative; max-width: 616px; list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.brg::before {
  content: ""; position: absolute; left: 10px; top: 20px; bottom: 20px; width: 1px;
  background: var(--line); z-index: 0;
}
.brg-r { position: relative; display: grid; grid-template-columns: 21px minmax(0, 1fr); column-gap: 17px; align-items: center; }
.brg-n {
  position: relative; z-index: 1; text-align: center; padding: 4px 0; background: #fff;
  font-family: "JetBrains Mono", monospace; font-size: 11px; font-weight: 500;
  letter-spacing: .02em; color: var(--ink3);
}
.brg-c {
  display: flex; align-items: center; gap: 14px; min-width: 0;
  border: 1px solid var(--line); border-radius: 13px; background: #fff;
  padding: 14px 16px;
  box-shadow: 0 10px 26px -20px rgba(11, 12, 16, .38);
}
/* THE ONE THE VENDOR TURNED OFF. The same grammar the rest of the page
   uses for a route that cannot be taken: dashed, with no ground under it.
   A row of equal-weight cards reads as "all of these", however one of them
   is annotated. */
.brg-r.is-off .brg-c { background: transparent; border-style: dashed; border-color: #D3D6E1; box-shadow: none; }
.brg-r.is-off .brg-t b { color: var(--ink3); font-weight: 500; }
.brg-r.is-off .brg-mk { color: #BABEC9; opacity: .9; }
.brg-r.is-off .brg-n { color: #BABEC9; }

.brg-mk { flex: none; display: flex; align-items: center; gap: 5px; color: var(--ink); }
.brg-t { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.brg-t b { font-size: 14.5px; font-weight: 600; letter-spacing: -.02em; color: var(--ink); }
.brg-t em { font-style: normal; font-size: 12.5px; line-height: 1.35; color: var(--ink3); }

/* THE SWITCH, DRAWN AND NOT WIRED. It is the vendor's control, on a page
   the vendor cannot act from — so it is a picture of a setting rather than
   a setting, and it carries its state to a screen reader as a label rather
   than pretending to be a checkbox somebody could toggle. */
/* OUTLINED, NOT FILLED. Three solid black lozenges down the right-hand edge
   of the ladder were the heaviest thing in the band — darker than the
   headings, and the eye went to them before the rows they belong to. They
   are not the subject; the four things a run may reach are, and the switch
   is only there to say which of them are open.

   THIS IS THE PRODUCT'S OWN SWITCH, COPIED, and it got here after three
   inventions that were all worse: a filled black lozenge, then the same
   lozenge outlined in black, then the whole thing greyed until it looked
   switched off. Drawing a control from scratch for a picture of a control
   was the mistake each time. The dashboard has a real one — the "give the
   fallback a share" switch in components/plan-routes.tsx — and this is its
   geometry and its four colours, converted to this sheet's palette:

     18x32, hairline --line-2 ring, 12px knob, 2px inset
     on    track --raised (#f6f6f7)  knob --text   (#1a1a1c)
     off   track --line   (#e9e9eb)  knob --panel  (#ffffff)

   The ring is the same quiet grey in both states; the weight lives in the
   knob, and only the knob. */
.brg-sw {
  flex: none; position: relative; width: 32px; height: 18px; border-radius: 999px;
  background: #E9E9EB; border: 1px solid #DCDCE0;
  transition: background-color .2s;
}
.brg-sw i {
  position: absolute; top: 2px; left: 2px; width: 12px; height: 12px;
  border-radius: 50%; background: #fff;
  transition: left .2s, background-color .2s;
}
.brg-sw.is-on { background: #F6F6F7; }
.brg-sw.is-on i { left: 16px; background: #1A1A1C; }

/* THE OTHER HALF OF THE SAME OBJECT, as a panel rather than a card: it is
   not one of a set, it is the second thing this one plan says. */
.bpay {
  border: 1px solid var(--line); border-radius: 16px;
  background: linear-gradient(180deg, #fff 0%, #F8F9FD 100%);
  box-shadow: 0 22px 44px -28px rgba(11, 12, 16, .42);
  padding: 22px 22px 18px;
}
.bpay-k {
  font-family: "JetBrains Mono", monospace; font-size: 10.5px; font-weight: 500;
  letter-spacing: .09em; text-transform: uppercase; color: var(--ink3);
}
.bpay-n { margin-top: 7px; font-size: 22px; font-weight: 600; letter-spacing: -.03em; color: var(--ink); }
.bpay-p { margin-top: 14px; font-size: 34px; font-weight: 600; letter-spacing: -.04em; line-height: 1; color: var(--ink); }
/* 28, not the 34 a real figure gets: "your price" is six times the width of
   "19", and at 34 the line ran to the card's edge. */
.bpay-p:has(.bpay-ph) { font-size: 28px; }
.bpay-p span { margin-left: 8px; font-size: 12.5px; font-weight: 500; letter-spacing: 0; color: var(--ink3); }
/* THE CARD IS A TEMPLATE, NOT AN OFFER.

   It used to read "Pro / $19 a month". On a band about what YOUR users pay YOU
   that is still the one figure a reader takes for OUR price — a named tier
   beside a round monthly number is the shape of a thing being sold, and the
   heading above it does not undo the shape. Reported as "the 19 dollars seems
   like the plan they pay".

   So the values become slots. Nothing on the card is a number any more, which
   leaves nothing to mistake.

   THE `$` KEEPS THE INK, NOT THE SIZE, and that correction matters. It held
   the price's full 34px while the words beside it ran at 24px, and two sizes
   on one baseline is exactly how a currency symbol comes to look dropped: the
   bigger glyph straddles the line, rising above the words and reaching below
   them, so the pair reads as misaligned however carefully the baselines
   agree. One size settles it. The `$` is still the dark, semibold half — the
   slot beside it is unmistakably money rather than a stray line of grey text —
   and the words are still lighter: present, clearly unfilled, not shouting.

   The name takes ink2 rather than ink3 for the opposite reason — at ink3 the
   bullets below would be the darkest thing in the card and the hierarchy would
   invert. Size and weight are untouched throughout. The tight tracking goes:
   it is set for numerals and crowds words. */
.bpay-n.is-ph { color: var(--ink2); letter-spacing: -.02em; }
.bpay-ph {
  font-style: normal; margin-left: 5px;
  /* inherits the line's size on purpose — see the note above */
  font-weight: 500; letter-spacing: -.02em; color: var(--ink3);
}

.bpay-l { list-style: none; margin: 18px 0 0; padding: 16px 0 0; border-top: 1px solid var(--line2); display: flex; flex-direction: column; gap: 10px; }
.bpay-l li { display: flex; align-items: flex-start; gap: 10px; font-size: 13px; line-height: 1.35; color: var(--ink2); }
.bpay-l svg { flex: none; margin-top: 1px; }

.bpay-f { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line2); font-size: 11.5px; line-height: 1.35; color: var(--ink3); }

/* the consequence, under the whole figure rather than inside either half */
.bld-s { margin-top: 40px; max-width: 78ch; font-size: 14px; line-height: 1.65; color: var(--ink3); }
.bld-a { margin-top: 16px; }

.bmk { display: block; }
.bmk-ln { fill: none; stroke: var(--ink3); stroke-width: 1.35; stroke-linecap: round; stroke-linejoin: round; }

/* ── 6b · platform ────────────────────────────────────────────────────
   The night band with the bento in it. Four cards: routing, billing,
   payments, and the log. The three white panels are drawn once in white
   and inverted into the dark — one source of truth for a card that has
   to exist on both grounds. */
.dsec { position: relative; display: flex; flex-direction: column; }
.dsec > svg { flex: none; }
.dbody { flex: 1; background: var(--night); color: #fff; position: relative; padding: 96px 0 110px; }
.dst i { position: absolute; background: #fff; display: block; }
.dh { text-align: center; display: flex; flex-direction: column; align-items: center; }
.dh .h2 { font-size: 48px; line-height: 1.05; }
.dh .lede { font-size: 16.5px; margin-top: 16px; max-width: 520px; color: var(--dink2); }

.bento {
  margin-top: 64px; display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-rows: 430px 520px; gap: 8px;
}
.bc {
  display: flex; flex-direction: column; overflow: hidden; position: relative;
  border-radius: 16px;
  background: radial-gradient(110% 70% at 50% 0%, #252528 0%, #1B1B1E 55%, #161618 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .07), inset 0 0 0 1px rgba(255, 255, 255, .04);
}
.bc.run  { grid-column: 1 / 3; grid-row: 1; flex-direction: row-reverse; align-items: stretch; }
.bc.bill  { grid-column: 3; grid-row: 1 / 3; flex-direction: column-reverse; justify-content: flex-end; }
.bc.pay   { grid-column: 1; grid-row: 2; flex-direction: column-reverse; justify-content: flex-end; }
.bc.c3    { grid-column: 2; grid-row: 2; flex-direction: column-reverse; justify-content: flex-end; }

.bv { flex: 1; display: flex; align-items: center; justify-content: center; position: relative; min-height: 0; }
/* the light each card is lit from, so the panel inside has somewhere to be */
.bv::before {
  content: ""; position: absolute; left: 0; right: 0; top: -10%; height: 75%; pointer-events: none;
  background: radial-gradient(50% 65% at 50% 0%, rgba(255, 255, 255, .075), transparent 70%);
}
.bt { width: auto; padding: 28px 28px 0; position: relative; z-index: 3; }
.bt b { display: block; font-size: 15px; font-weight: 600; letter-spacing: -.01em; line-height: 1.3; color: #F4F4F5; }
.bt p { font-size: 14px; line-height: 1.5; color: #8E8E94; margin-top: 6px; max-width: 330px; }

.bc.run .bt { width: 290px; flex: none; padding: 32px 0 0 32px; }
.bc.run .bv { flex: 1; padding: 44px 40px 0 0; justify-content: flex-end; align-items: flex-start; }
.bc.run .bv::before { left: auto; width: 560px; right: -60px; }
.bc.bill .bv { flex: 1; padding: 28px 0 0; align-items: center; justify-content: center; }
.bc.bill .bv::before { top: 0; }
.bc.pay .bv { flex: 1; padding: 24px 0 0; align-items: center; }
.bc.c3 .bv {
  flex: 1; display: block; padding: 28px 28px 0;
  -webkit-mask-image: linear-gradient(#000 55%, transparent 98%);
          mask-image: linear-gradient(#000 55%, transparent 98%);
}
.bc.c3 .bv::before { display: none; }

/* the white panels, inverted into the dark */
.bento .rw, .bento .pfc {
  background: #292929; border-color: rgba(255, 255, 255, .06);
  box-shadow: 0 30px 60px -24px rgba(0, 0, 0, .85), 0 0 0 1px rgba(255, 255, 255, .05);
}
.bento .rw > *, .bento .pfc > * { filter: invert(.84) grayscale(1); }

/* ── routing: one run, finding the first AI the user has ───────────── */
.rwstack { position: relative; }
.rw {
  --R: 6s;
  width: 340px; border-radius: 14px; background: #fff; color: var(--ink);
  box-shadow: 0 30px 60px -24px rgba(0, 0, 0, .9); overflow: hidden; font-size: 12.5px;
}
.bc.run .rw { zoom: 1.1; }
.bc.run .rw:not(.rwg) { position: relative; z-index: 2; }
.rwh, .rwy { display: flex; gap: 10px; align-items: flex-start; padding: 14px 16px; }
.rwa { width: 26px; height: 26px; border-radius: 50%; background: #E8C9A8; color: #5A3A1C; display: grid; place-items: center; font-size: 11px; font-weight: 700; flex-shrink: 0; }
.rwm { font-size: 11px; color: #9A9EAA; }
.rwm b { color: var(--ink); font-weight: 600; }
.rwq { margin-top: 5px; display: inline-block; padding: 7px 11px; border-radius: 4px 11px 11px 11px; background: #F2F2F5; font-size: 12.5px; animation: rwq var(--R) infinite; }
.rwl { position: relative; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.rwl::before { content: ""; position: absolute; left: 28px; top: 0; height: 60px; border-left: 2px solid var(--ink); transform-origin: top; animation: rwline var(--R) infinite; }
.rwl::after { content: ""; position: absolute; left: 28px; top: 64px; bottom: 22px; border-left: 1.5px dashed #C9CBD3; animation: rwdim var(--R) infinite; }
.rwr { position: relative; display: flex; align-items: center; gap: 9px; height: 42px; padding: 0 16px 0 20px; border-top: 1px solid #F0F0F3; color: var(--ink3); }
.rwr:first-child { border-top: 0; }
.rwn { position: relative; z-index: 1; width: 18px; display: flex; justify-content: center; }
.rwn::before { content: ""; width: 9px; height: 9px; border-radius: 50%; background: #fff; box-shadow: inset 0 0 0 1.5px #C9CBD3; display: block; }
.rwr.off { animation: rwoff var(--R) infinite; }
.rwr.off .rwn::before { width: 11px; height: 11px; box-shadow: inset 0 0 0 1.5px #B9BCC6; }
.rwr.off em { animation: rwe1 var(--R) infinite; }
.rwr.on { background: #F6F6F8; color: var(--ink); font-weight: 600; animation: rwon var(--R) infinite; }
.rwr.on .rwn::before { width: 12px; height: 12px; background: var(--ink); box-shadow: none; animation: rwdot var(--R) infinite; }
.rwr.on .rwn::after { content: ""; position: absolute; left: 15px; top: 50%; width: 14px; height: 2px; background: var(--ink); transform-origin: left; animation: rwarr var(--R) infinite; }
.rwr.on em { color: var(--ink); font-weight: 500; animation: rwe2 var(--R) infinite; }
.rwr.dim { opacity: .75; animation: rwdim var(--R) infinite; }
.rwi { width: 22px; height: 22px; border-radius: 6px; border: 1px solid var(--line); display: grid; place-items: center; background: #fff; flex-shrink: 0; color: inherit; }
.rwt { flex: 1; }
.rwr em { font: 400 10.5px "JetBrains Mono", monospace; font-style: normal; color: #9A9EAA; }
.rwy { position: relative; }
.rwy > span, .rwy .rwm { animation: rwy var(--R) infinite; }
.rwyi { width: 26px; height: 26px; display: block; flex-shrink: 0; }
.rwtyp {
  position: absolute; left: 52px; top: 30px; display: flex; gap: 4px; align-items: center;
  height: 30px; padding: 0 12px; border-radius: 4px 11px 11px 11px; background: #F2F2F5;
  opacity: 0; animation: rwtyp var(--R) infinite;
}
.rwtyp i { width: 5px; height: 5px; border-radius: 50%; background: #A6AAB5; animation: rwdt .9s infinite; }
.rwtyp i:nth-child(2) { animation-delay: .15s; }
.rwtyp i:nth-child(3) { animation-delay: .3s; }
.rwb { margin-top: 5px; padding: 8px 11px; border-radius: 4px 11px 11px 11px; box-shadow: inset 0 0 0 1px var(--line); font-size: 12px; line-height: 1.45; animation: rwb var(--R) infinite; }
.rwv { margin-top: 7px; font: 400 10.5px "JetBrains Mono", monospace; color: var(--ink3); animation: rwv var(--R) infinite; }

/* the two runs either side are stills, not a second clock */
.bc.run .rwg {
  position: absolute; margin: 0; padding-bottom: 20px; pointer-events: none; z-index: 1;
  zoom: 1.15; box-shadow: 0 30px 60px -24px rgba(0, 0, 0, .9);
}
.rwg, .rwg * { animation: none !important; }
.rwg1 {
  left: -90px; top: 50px; opacity: .5; transform: scale(.94); transform-origin: right top;
  -webkit-mask-image: linear-gradient(to left, #000 45%, transparent 100%);
          mask-image: linear-gradient(to left, #000 45%, transparent 100%);
}
.rwg2 {
  right: -90px; top: 78px; opacity: .35; transform: scale(.88); transform-origin: left top;
  -webkit-mask-image: linear-gradient(to right, #000 40%, transparent 100%);
          mask-image: linear-gradient(to right, #000 40%, transparent 100%);
}

@keyframes rwq    { 0% { opacity: 0; transform: translateY(6px) } 4%, 94% { opacity: 1; transform: none } 100% { opacity: 0 } }
@keyframes rwoff  { 0%, 5% { color: #C9CBD3 } 7%, 11% { color: #0B0C10; background: #F6F6F8 } 14%, 100% { color: #8A8F9C; background: transparent } }
@keyframes rwe1   { 0%, 12% { opacity: 0 } 14%, 94% { opacity: 1 } 100% { opacity: 0 } }
@keyframes rwline { 0%, 13% { transform: scaleY(0) } 18%, 94% { transform: scaleY(1) } 100% { transform: scaleY(1); opacity: 0 } }
@keyframes rwdot  { 0%, 17% { transform: scale(0) } 19% { transform: scale(1.35) } 21%, 94% { transform: scale(1) } 100% { transform: scale(0) } }
@keyframes rwarr  { 0%, 19% { transform: scaleX(0) } 23%, 94% { transform: scaleX(1) } 100% { transform: scaleX(0) } }
@keyframes rwon   { 0%, 20% { background: transparent; color: #C9CBD3; font-weight: 400 } 23%, 100% { background: #F6F6F8; color: #0B0C10; font-weight: 600 } }
@keyframes rwe2   { 0%, 23% { opacity: 0 } 25%, 94% { opacity: 1 } 100% { opacity: 0 } }
@keyframes rwdim  { 0%, 24% { opacity: .25 } 28%, 100% { opacity: .75 } }
@keyframes rwy    { 0%, 29% { opacity: 0 } 31%, 94% { opacity: 1 } 100% { opacity: 0 } }
@keyframes rwtyp  { 0%, 30% { opacity: 0 } 32%, 40% { opacity: 1 } 41%, 100% { opacity: 0 } }
@keyframes rwdt   { 0%, 60%, 100% { transform: none; opacity: .5 } 30% { transform: translateY(-2px); opacity: 1 } }
@keyframes rwb    { 0%, 40% { opacity: 0; transform: translateY(4px) } 43%, 94% { opacity: 1; transform: none } 100% { opacity: 0 } }
@keyframes rwv    { 0%, 44% { opacity: 0 } 47%, 94% { opacity: 1 } 100% { opacity: 0 } }

/* ── billing: the plan you set, and the card they see ───────────────── */
/* Set a plan and the card your users see follows. The sweep walks the six
   settings, and when it reaches the end the card below re-renders — which
   is the whole claim of this tile, drawn rather than written. --B is one
   pass; every step below is a percentage of it. */
.bk2 { --B: 7.2s; width: 330px; display: flex; flex-direction: column; gap: 12px; position: relative; z-index: 2; }
.bkc { background: #292929; border-radius: 12px; box-shadow: 0 30px 60px -24px rgba(0, 0, 0, .85), 0 0 0 1px rgba(255, 255, 255, .06); color: #D6D6D6; font-size: 13px; overflow: hidden; }
.bkh { display: flex; justify-content: space-between; align-items: center; height: 40px; padding: 0 16px; }
.bkh b { font-weight: 600; color: #F2F2F2; }
.bkh span { font: 10.5px "JetBrains Mono", monospace; color: #77777D; }
.bkr { display: flex; justify-content: space-between; align-items: center; gap: 12px; height: 38px; padding: 0 16px; border-top: 1px solid #363636; }
.bkr { position: relative; }
.bkr::after {
  content: ""; position: absolute; inset: 0; opacity: 0;
  background: rgba(255, 255, 255, .055); pointer-events: none;
  animation: bkrow var(--B) linear infinite;
}
.bkc .bkr:nth-child(2)::after { animation-delay: 0s; }
.bkc .bkr:nth-child(3)::after { animation-delay: .72s; }
.bkc .bkr:nth-child(4)::after { animation-delay: 1.44s; }
.bkc .bkr:nth-child(5)::after { animation-delay: 2.16s; }
.bkc .bkr:nth-child(6)::after { animation-delay: 2.88s; }
.bkc .bkr:nth-child(7)::after { animation-delay: 3.6s; }
@keyframes bkrow { 0% { opacity: 0 } 2%, 9% { opacity: 1 } 12%, 100% { opacity: 0 } }
.bkp { animation: bkcard var(--B) ease-in-out infinite; }
@keyframes bkcard {
  0%, 62%   { opacity: 1; transform: none; }
  66%       { opacity: .4; transform: translateY(5px); }
  75%, 100% { opacity: 1; transform: none; }
}
.bkprice b { display: inline-block; animation: bkprice var(--B) ease-out infinite; }
@keyframes bkprice { 0%, 68% { transform: none } 72% { transform: scale(1.07) } 80%, 100% { transform: none } }
.bkr em { font-style: normal; color: #8B8B91; }
.bkr span { display: flex; align-items: center; gap: 8px; color: #E4E4E4; }
.bkr i { width: 5px; height: 5px; border-right: 1.3px solid #77777D; border-bottom: 1.3px solid #77777D; transform: rotate(45deg) translateY(-2px); }
.bkp { padding: 16px; }
.bkph { display: flex; justify-content: space-between; font-size: 13px; }
.bkph b { color: #F2F2F2; font-weight: 600; }
.bkph span { color: #8B8B91; }
.bkprice { margin-top: 10px; font-size: 13px; color: #8B8B91; }
.bkprice b { font-size: 34px; letter-spacing: -.04em; color: #F4F4F4; font-weight: 600; }
.bkpl { margin-top: 6px; font-size: 12.5px; line-height: 1.8; color: #9A9AA0; }
.bkbtn { margin-top: 14px; height: 34px; border-radius: 8px; background: #D9D9DB; color: #141414; font-size: 13px; font-weight: 500; display: grid; place-items: center; }
.bknote { margin-top: 10px; text-align: center; font-size: 11.5px; color: #77777D; }

/* ── payments: four charges, one Stripe ────────────────────────────── */
.pfc { width: 340px; border-radius: 14px; background: #fff; color: var(--ink); box-shadow: 0 30px 60px -24px rgba(0, 0, 0, .9); overflow: hidden; }
.bc.pay .pfc { zoom: .95; }
.pfh { display: flex; justify-content: space-between; align-items: center; height: 36px; padding: 0 14px; border-bottom: 1px solid var(--line); }
.pfh b { font: 600 12px "JetBrains Mono", monospace; }
.pfh em { font-style: normal; font-size: 11.5px; color: var(--ink3); }
.pflow { position: relative; width: 330px; height: 304px; margin: 14px 15px 12px; zoom: .9; }
.pf { position: absolute; left: 0; width: 168px; height: 44px; border-radius: 10px; background: #F6F6F8; box-shadow: inset 0 0 0 1px var(--line); display: flex; align-items: center; padding: 0 12px; }
.pf b { display: block; font: 600 13px "JetBrains Mono", monospace; }
.pf small { font-size: 10.5px; color: var(--ink3); }
.pfst { position: absolute; left: 262px; top: 96px; width: 64px; height: 54px; border-radius: 12px; background: var(--ink); display: grid; place-items: center; box-shadow: 0 0 0 4px #EEEFF2; }
.pfst span { font-size: 16px; font-weight: 700; letter-spacing: -.04em; color: #fff; }
.pfsum { position: absolute; left: 0; right: 0; top: 262px; display: flex; justify-content: space-between; align-items: baseline; padding: 10px 4px 0; border-top: 1px solid var(--line); color: var(--ink3); font-size: 12px; }
.pfsum b { font-size: 20px; color: var(--ink); letter-spacing: -.02em; }
/* four charges, on their way to one Stripe account */
.pfl { stroke-dasharray: 26 128; animation: pfl 1.6s linear infinite; }
@keyframes pfl { from { stroke-dashoffset: 154 } to { stroke-dashoffset: 0 } }
/* and each chip leaning after the one that just left it */
.pf { animation: pfgo 1.6s linear infinite; }
.pflow > div:nth-of-type(1) { animation-delay: 0s; }
.pflow > div:nth-of-type(2) { animation-delay: -.45s; }
.pflow > div:nth-of-type(3) { animation-delay: -.9s; }
.pflow > div:nth-of-type(4) { animation-delay: -1.35s; }
@keyframes pfgo { 0%, 100% { transform: none } 7% { transform: translateX(4px) } 20% { transform: none } }

/* ── the log ───────────────────────────────────────────────────────── */
/* a log fills; it does not simply exist. Rows land in order once a cycle
   and then stand for most of it, so this is a table nearly all the time and
   a live feed just often enough to say so. */
.rlog { --L: 8s; width: 100%; font: 12.5px/1 "JetBrains Mono", monospace; color: #B5B5B8; display: flex; flex-direction: column; justify-content: center; }
.rlog div:not(.lh) { animation: logrow var(--L) linear infinite; }
.rlog div:nth-child(2) { animation-delay: 0s; }
.rlog div:nth-child(3) { animation-delay: .22s; }
.rlog div:nth-child(4) { animation-delay: .44s; }
.rlog div:nth-child(5) { animation-delay: .66s; }
.rlog div:nth-child(6) { animation-delay: .88s; }
.rlog div:nth-child(7) { animation-delay: 1.1s; }
.rlog div:nth-child(8) { animation-delay: 1.32s; }
@keyframes logrow {
  0%        { opacity: 0; transform: translateY(-6px); }
  5%, 94%   { opacity: 1; transform: none; }
  100%      { opacity: 0; transform: translateY(-6px); }
}
.rlog div { display: grid; grid-template-columns: .8fr 1.5fr .8fr; align-items: center; height: 32px; font-size: 11.5px; border-top: 1px solid #262629; padding: 0 4px; }
.rlog div:first-child { border-top: 0; }
.rlog .lh { height: 28px; }
.rlog .lh span { font: 400 10px "JetBrains Mono", monospace; letter-spacing: .06em; text-transform: uppercase; color: #B5B5B8; }
.rlog .wh { color: #fff; }
.rlog span:last-child { text-align: right; }
.rlog .c { color: #EEF0F4; }
.rlog .c.hi { color: #fff; }

/* ── 7 · for your customers ───────────────────────────────────────────
   The settings panel on the left and the sentence it writes on the
   right. Every lifted word on the right is a control on the left, which
   is the only claim this band makes. */
.cvhead { text-align: center; display: flex; flex-direction: column; align-items: center; }
/* the band said what the settings do but never who sets them, and a sentence
   about free users with no subject reads as Soba's policy rather than yours */
.cvlede { max-width: 60ch; }
/* The panel is sized to its longest row and nothing more; everything else
   goes to the sentence, which is the half that has to hold a line together. */
.cvg { display: grid; grid-template-columns: 404px minmax(0, 1fr); gap: 56px; align-items: center; margin-top: 56px; }

.cvpn {
  background: #fff; border-radius: 20px; overflow: hidden;
  box-shadow: 0 0 0 1px var(--line), 0 40px 70px -44px rgba(11, 12, 16, .35);
}
.cvhd { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid var(--line2); }
.cvhd b { font-size: 16px; font-weight: 600; letter-spacing: -.02em; }
/* whose screen this is, on the screen itself */
.cvhd span { font: 500 11.5px "JetBrains Mono", monospace; letter-spacing: .06em; text-transform: uppercase; color: #A3A7B3; }
.cvbd { padding: 4px 20px 16px; }
.cvgt { font: 500 11.5px "JetBrains Mono", monospace; letter-spacing: .06em; text-transform: uppercase; color: #A3A7B3; padding: 20px 0 6px; }
.cvr { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 6px 0; min-height: 44px; }
.cvr em { font-style: normal; font-size: 14px; color: var(--ink2); }

/* the controls. Buttons, because they do something — the panel on the left
   and the pills on the right are two faces of the same six settings, and
   either one can be the thing you reach for. */
.sgm { display: flex; gap: 2px; padding: 3px; border-radius: 10px; background: #F1F2F5; }
.sgm button {
  font: inherit; font-size: 13px; border: 0; cursor: pointer;
  padding: 6px 11px; border-radius: 7px; color: var(--ink3);
  background: transparent; white-space: nowrap;
  transition: background .12s, color .12s, box-shadow .12s;
}
.sgm button:hover { color: var(--ink2); }
.sgm button[aria-pressed="true"] {
  background: #fff; color: var(--ink); font-weight: 500;
  box-shadow: 0 1px 2px rgba(11, 12, 16, .08), 0 0 0 1px rgba(11, 12, 16, .05);
}
/* a value the rest of the plan has ruled out: shown, not removed, so the
   dependency is something you can see rather than something you discover */
.sgm button:disabled { cursor: not-allowed; opacity: .4; }
.sgm button:disabled:hover { color: var(--ink3); }

.cvs {
  position: relative;
  font-size: 24px; line-height: 1.45; letter-spacing: -.03em; font-weight: 500; color: var(--ink);
}
.cvs p + p { margin-top: 10px; }
.cvs .m { color: #9A9EAB; }
.cvp {
  display: inline-flex; align-items: center; gap: 7px; height: 40px; padding: 0 12px 0 14px;
  border: 0; border-radius: 10px; background: #fff; vertical-align: middle; margin: 0 3px;
  font: inherit; font-weight: 600; color: inherit; cursor: pointer;
  box-shadow: 0 0 0 1px var(--line), 0 14px 28px -20px rgba(11, 12, 16, .35);
  transition: box-shadow .12s, transform .12s;
}
.cvp:hover { box-shadow: 0 0 0 1px #CFD2DA, 0 16px 30px -20px rgba(11, 12, 16, .45); }
.cvp[aria-expanded="true"] { box-shadow: 0 0 0 1px #B9BCC6, 0 16px 30px -20px rgba(11, 12, 16, .45); }
.cvp svg { color: #9A9EAB; flex: none; transition: transform .15s; }
.cvp[aria-expanded="true"] svg { transform: rotate(180deg); }
/* with no trial there is no deadline to name, so the clause goes */
.cvwhen[hidden] { display: none; }

/* the list a pill opens. One element, moved to whichever pill asked for it. */
.cvmenu {
  position: absolute; z-index: 5; min-width: 168px; padding: 6px;
  border-radius: 12px; background: #fff; list-style: none; margin: 0;
  font-size: 15px; font-weight: 500; letter-spacing: -.01em;
  box-shadow: 0 0 0 1px var(--line), 0 18px 40px -16px rgba(11, 12, 16, .35);
}
.cvmenu[hidden] { display: none; }
.cvmenu button {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; font: inherit; text-align: left; cursor: pointer;
  padding: 9px 10px; border: 0; border-radius: 8px; background: transparent; color: var(--ink2);
}
.cvmenu button:hover { background: var(--soft); }
.cvmenu button[aria-selected="true"] { color: var(--ink); }
.cvmenu button[aria-selected="true"]::after { content: "\2713"; font-size: 13px; }
.cvmenu button:disabled { opacity: .4; cursor: not-allowed; }
.cvmenu button:disabled:hover { background: transparent; }

.sgm button:focus-visible, .cvp:focus-visible, .cvmenu button:focus-visible {
  outline: 2px solid var(--ink); outline-offset: 2px;
}
/* the open pill's own edge gets out of the way of the focus ring */
.cvp:focus-visible { box-shadow: 0 0 0 1px var(--line), 0 14px 28px -20px rgba(11, 12, 16, .35); }

.cvn { margin-top: 36px; font-size: 16px; color: var(--ink3); }

/* ── 8 · for trust ────────────────────────────────────────────────── */
.tc { text-align: center; display: flex; flex-direction: column; align-items: center; }
.plk { margin-bottom: 26px; line-height: 0; }
.tr3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 48px; margin-top: 64px; }
.tr3 > div { border-top: 1px solid #26282F; padding-top: 26px; }
.tr3 b { display: block; font-size: 20px; font-weight: 600; letter-spacing: -.025em; color: #fff; }
.tr3 p { margin-top: 12px; font-size: 16px; line-height: 1.6; color: var(--dink2); max-width: 340px; }

/* ── 9 · for developers ───────────────────────────────────────────── */
.devg { display: grid; grid-template-columns: 400px 1fr; gap: 72px; align-items: center; }
.devg .lede { margin-top: 18px; }
.devg .lede .mono { color: var(--ink); font-size: 15px; }
.devg .btn { margin-top: 28px; }
.ed { border-radius: 12px; background: #0D0E13; overflow: hidden; }
.tabs { display: flex; gap: 2px; padding: 10px 12px; border-bottom: 1px solid #1C1E26; }
.tabs span { font: 12.5px "JetBrains Mono", monospace; color: #6B7080; padding: 6px 12px; border-radius: 7px; }
.tabs span.on { background: #1C1E26; color: #fff; }
.src { padding: 20px 24px; font: 14px/1.85 "JetBrains Mono", monospace; color: #D5D8E0; white-space: nowrap; overflow-x: auto; }
.src .k { color: #E7C27D; }
.src .s { color: #9FD8B8; }
.src .t { color: #8FC3F2; }
.src .c { color: #5E6272; }
.src .n { color: #474B58; display: inline-block; width: 28px; }

/* ── 10 · questions ───────────────────────────────────────────────── */
.faq { padding-top: 96px; padding-bottom: 132px; position: relative; }
.faq .in { display: grid; grid-template-columns: 400px 1fr; gap: 80px; }
.faq .prule { left: 120px; right: 120px; }
.faq-k { margin-top: 14px; font-size: 16px; color: var(--dink); }
.faq-k a { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
.qa { border-top: 1px solid var(--line); padding: 22px 0; }
.qa:last-child { border-bottom: 1px solid var(--line); }
.qa h3 { font-size: 18.5px; letter-spacing: -.02em; }
.qa p { margin-top: 8px; font-size: 15.5px; line-height: 1.6; color: #5A5F6B; max-width: 640px; }
/* the answers carry links now, and an answer whose link is the same ink
   as the sentence around it is an answer nobody clicks */
.qa p a { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--line); }
.qa p a:hover { text-decoration-color: var(--ink3); }

/* ── 11 · close, and the footer under it ──────────────────────────── */
.close { text-align: center; padding-top: 120px; }
.close .h1 { font-size: 88px; }
.close-cta { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 34px; }
.art-wide {
  height: 360px; margin-top: 40px;
  background: url(/landing/night-wide.png) 50% 50% / cover no-repeat;
  image-rendering: pixelated;
  -webkit-mask-image: linear-gradient(transparent, #000 30%);
          mask-image: linear-gradient(transparent, #000 30%);
}

.ft {
  display: grid; grid-template-columns: 1.5fr 1fr 1fr 1fr; gap: 40px;
  padding: 48px 0 50px; font-size: 14px; color: var(--dink); border-top: 1px solid var(--dline);
}
.ft-brand { display: flex; gap: 9px; align-items: center; color: #fff; font-size: 20px; font-weight: 700; letter-spacing: -.03em; }
.ft p { margin-top: 14px; line-height: 1.6; max-width: 300px; }
.ft b { display: block; color: #fff; margin-bottom: 12px; font-weight: 600; }
/* every footer link is a row of its own — except the brand, which is a
   logo beside a word */
.ft a:not(.ft-brand) { display: block; margin-top: 9px; }
.ft a:hover { color: #fff; }
.fine {
  border-top: 1px solid var(--dline); padding: 22px 0 38px; gap: 40px;
  font-size: 12.5px; line-height: 1.6; color: #5C6170;
  display: flex; justify-content: space-between;
}
.fine span { max-width: 780px; }
/* the changelog separates what works end to end from what does not,
   and a date in the footer is the cheapest way to say the page is
   describing something that is still moving */
.fine-r { flex: none; text-align: right; }
.fine a { text-decoration: underline; text-underline-offset: 3px; }

/* ── the responsive layer ─────────────────────────────────────────────
   The page was drawn at 1440. Everything above is that drawing; this is
   what it does on the way down to a phone. Three widths, and the rule is
   the same at each: columns become rows, and anything that was only ever
   decoration stops being drawn. */

/* laptop and small desktop: the headline sizes come down, and the pairs
   that need a full 1200px to read stop being pairs */
@media (max-width: 1100px) {
  /* the screen's own responsive layer, from the same sheet */
  .page-chat { padding-inline: 26px; }
  .page-nav { padding-inline: 18px; }
  .page-body { grid-template-columns: 200px 1fr; }
  .modal { width: 318px; }
  .shotw { right: 32px; }

  .h1 { font-size: 56px; }
  .h2 { font-size: 46px; }
  .h2-48, .pl .h2, .dh .h2 { font-size: 40px; }
  .close .h1 { font-size: 64px; }
  .steps-head .h2 { white-space: normal; }

  /* the billing band's two halves stop being halves with the rest of the
     side-by-sides; the panel keeps its own width and centres */
  /* the hub stacks: the wires are drawn across, so below this width they
     are dropped rather than turned — a curve that has to bend twice to
     reach a tile under it reads as a knot, not as a path. The two column
     headers go with them: stacked, each group follows the core it belongs
     to, and a header pinned to the top would name the wrong half. */

  /* the panel gives its width up first: four route rows set the height of
     this figure, and the panel has room to spare at every width above
     where the two stack */
  .bld { column-gap: 44px; grid-template-columns: minmax(0, 1fr) minmax(0, 330px); }

  .pgrid { grid-template-columns: 1fr; gap: 56px; }
  .devg { grid-template-columns: 1fr; gap: 40px; }
  .faq .in { grid-template-columns: 1fr; gap: 40px; }
  .faq .prule { left: 24px; right: 24px; }

  .hwdash { min-height: 0; padding-block: 8px; }
  .steps-head .h2 { white-space: normal; }

  /* the bento stops being a bento: four cards, one column each */
  .bento { grid-template-columns: 1fr; grid-template-rows: none; grid-auto-rows: auto; gap: 12px; }
  .bc, .bc.run, .bc.bill, .bc.pay, .bc.c3 {
    grid-column: auto; grid-row: auto;
    flex-direction: column-reverse; justify-content: flex-end; align-items: stretch;
  }
  .bc.run .bt { width: auto; padding: 28px 28px 0; }
  .bc.run .bv, .bc.bill .bv, .bc.pay .bv { padding: 32px 24px 40px; justify-content: center; align-items: center; }
  .bc.run .bv::before { left: 0; right: 0; width: auto; }
  /* the two stills either side of the live run have nowhere to be */
  .bc.run .rwg { display: none; }
  .bc.c3 .bv {
    padding: 24px 28px 28px;
    -webkit-mask-image: none; mask-image: none;
  }

  /* the run's path, read downwards */
  .nvf { grid-template-columns: 1fr; gap: 16px; max-width: 420px; }
  .nvf-link { flex-direction: row; gap: 10px; }
  .nvf-chevs { width: 48px; transform: rotate(90deg); }
}

/* The sentence stacks under the panel earlier than everything else: its
   longest clause — "Then they see an upgrade prompt when the trial ends" —
   needs about 630px to stay on one line, and below this width the column
   it would sit in is narrower than that. */
@media (max-width: 1180px) {
  .cvg { grid-template-columns: 1fr; gap: 48px; }
}

/* tablet: the last of the side-by-sides give up */
@media (max-width: 860px) {


  /* the two halves stop being side by side, and the headings go with
     them: stacked, each half is the only thing under its own label */
  .bld { grid-template-columns: minmax(0, 1fr); margin-top: 40px; row-gap: 34px; }
  .bld-s { margin-top: 30px; }
  /* Started and Model go first: who ran it, where it ran, and what it cost
     are the three that carry the claim, and five columns do not fit the card
     once it is narrower than its own content. */
  .hwrun-t th:nth-child(1), .hwrun-t td:nth-child(1),
  .hwrun-t th:nth-child(3), .hwrun-t td:nth-child(3) { display: none; }
  /* the desk and the dialog go; the app is the half that still reads */
  .page, .scene-scrim, .modal { display: none; }
  .shotw { position: relative; top: 0; right: auto; margin-inline: auto; }
  .scene { height: auto; width: 100%; margin-inline: 0; box-shadow: none; background: none; }
  .hwc .shotw { position: relative; top: 0; right: auto; left: auto;
                transform: none; margin-inline: auto; width: min(352px, 100%); }

  .h1 { font-size: 46px; }
  .h2 { font-size: 38px; }
  .h2-48, .pl .h2, .dh .h2 { font-size: 34px; }
  .close .h1 { font-size: 50px; }
  .band { padding-block: 80px; }
  .psec { padding: 80px 0; }
  .dbody { padding: 72px 0 80px; }
  .lands { padding-block: 80px 72px; }
  .faq { padding-top: 72px; padding-bottom: 88px; }
  .close { padding-top: 80px; }

  .tr3 { grid-template-columns: 1fr; gap: 28px; }
  .tr3 p { max-width: none; }
  .ft { grid-template-columns: 1fr 1fr; gap: 32px; }
  .cvs { font-size: 22px; }
  .cvp { height: 40px; padding: 0 11px 0 13px; }
  .bc.run .rw { zoom: 1; }
  .art { height: 300px; }
  .art-wide { height: 260px; }

  /* THE BAR FOLLOWS, THE NOTICE DOES NOT. Sticky from the width the links
     go behind the burger: on a phone the only way back to them is the top
     of a 13,000px page, which is a scroll nobody makes.

     `.navwrap` keeps its space in flow, so the chrome measurement the hero
     subtracts from 100svh is unchanged. The announcement bar above it is
     left to scroll away — it is a one-line notice, and pinning both would
     cost a fifth of a small screen. The border is new: over a white band
     the bar needs an edge it never needed against its own ground. */
  .navwrap {
    position: sticky; top: 0;
    border-bottom: 1px solid var(--night3);
  }

  /* the nav links become a panel the burger opens */
  .nav { gap: 16px; height: 62px; }
  .nav .links {
    display: none; position: absolute; left: 0; right: 0; top: 100%;
    flex-direction: column; gap: 0; padding: 8px 24px 16px;
    background: var(--night); border-bottom: 1px solid var(--night3);
  }
  .nav.is-open .links { display: flex; }
  .nav .links a { padding: 10px 0; }
  .nav .r { margin-left: auto; }
  .nav .sign-in { display: none; }
  .nav-burger { display: flex; }
  /* THE SECOND "START BUILDING" GOES. The nav's button and the hero's sat
     one above the other on a phone, two taps apart, saying the same thing
     — and the nav one had to be 44px tall to be hittable, which made it
     the loudest thing on a screen whose job is the headline under it. The
     action moves into the panel the burger opens, where the links are. */
  .nav .r .btn { display: none; }
  .nav-cta {
    display: flex; align-items: center; justify-content: center;
    margin-top: 10px; min-height: 44px; border-radius: 999px;
    background: #fff; color: var(--night); font-weight: 600;
  }
  .nav-cta-2 {
    margin-top: 8px; background: transparent; color: var(--dink2); font-weight: 500;
    box-shadow: inset 0 0 0 1px var(--night3);
  }
}

/* phone */
@media (max-width: 600px) {


  /* A LINE OF PROSE IS LEFT-ALIGNED HERE. Centred copy reads as a caption
     for as long as it is one line; at this width the same sentence is four
     or five, and a ragged left edge on every one of them is the slowest
     thing on the page to read. Headings keep their centring — they are
     still one or two lines, and they are the thing being looked at. */
  .lede, .act-p, .hwcap, .cwhub-s { text-align: left; }
  /* … except where the section is a centred axis. A hero is an icon, a
     label, a headline, a sentence and a button on one centre line; taking
     the sentence off it leaves the composition looking broken rather than
     looking readable. */
  .hero .lede, .ph .lede, .close .lede, .steps-head .lede { text-align: center; }
  /* except the hero's, which sits under a centred headline and over a
     centred button: left-setting it put one ragged edge between two
     centred ones */
  .hero-sub { text-align: center; }
  .in { padding-inline: 20px; }
  /* 36 put the headline within a few pixels of both gutters on a 390
     screen, which reads as a crop rather than as a hero. Three points
     down is the same two lines with air around them. */
  .h1 { font-size: 33px; }
  .h2 { font-size: 30px; }
  .h2-48, .pl .h2, .dh .h2 { font-size: 28px; }
  .close .h1 { font-size: 38px; }
  .hero { padding-top: 44px; }
  .hero-sub, .lede { font-size: 16px; }
  /* the line breaks in the headings were set for 1440px; at this width
     they only make the ragging worse */
  .h1 br, .h2 br { display: none; }
  .hero-cta .mono { font-size: 11px; }
  .ann { font-size: 12.5px; }
  /* THE SHORT LEDE, AND ONE SLAB INSTEAD OF TWO. The hero was a headline,
     three lines of lede and two full-width pills stacked on each other
     before the artwork — four blocks of near-equal weight, which is what
     makes a phone hero feel like a form.

     The lede swaps to its narrow half, and the second button stops being
     a slab: it is the same copy-to-clipboard control, drawn as the mono
     line it always was rather than as a second thing to press. */
  .hs-w { display: none; }
  .hs-m { display: inline; }
  .hero-sub { font-size: 15.5px; }

  .hero-cta { gap: 4px; }
  .hero-cta .btn { width: 100%; }
  .close-cta .btn { width: 100%; }
  .hero-cta .b-ghost {
    width: auto; height: auto; min-height: 40px; margin-inline: auto;
    padding: 8px 10px; background: none; box-shadow: none; color: var(--dink2);
  }
  .hero-cta .b-ghost:hover { background: none; }

  /* the landscape was butting straight into the buttons */
  .art { margin-top: 22px; }
  /* THE LOGO ROW, SET RATHER THAN WRAPPED. As a wrapping flex it put the
     label on the first line with two of the five marks beside it and the
     other three in ragged rows under it — five items in an order nobody
     chose, each starting at a different x. The label takes its own line
     and the marks take a two-column grid, so every one of them begins at
     one of two places. */
  /* A CENTRED CLOUD, NOT A TABLE. Two columns put a gutter down the
     middle and left a hole where the fifth mark would have been; the
     wrapping flex it replaced started every row at a different x. Centred
     and wrapping, the five read as one group under their label, which is
     all a logo row has ever been. */
  .works {
    flex-direction: column; justify-content: center;
    gap: 12px 16px; font-size: 13.5px; min-height: 0;
    padding-top: 4px; padding-bottom: 18px;
  }
  .works-ms { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px 18px; }
  .works span { gap: 7px; }

  /* the rail wraps to two lines before it would scroll sideways */
  .hwt { width: auto; border-radius: 22px; }
  .hwp { font-size: 13.5px; padding: 10px 14px 11px; }
  /* three columns in 345px will not size themselves: auto layout gives the
     emails what they ask for and the money falls off the end of the card.
     Fixed, and the two text columns give up their tails instead. */
  .hwrun-t { table-layout: fixed; }
  .hwrun-t th, .hwrun-t td { padding-inline: 12px; font-size: 13px; }
  .hwrun-t0, .hwrun-m, .hwrun-c { font-size: 11.5px; }
  .hwrun-t th:nth-child(2), .hwrun-t td:nth-child(2) { width: 42%; }
  .hwrun-t th:nth-child(4), .hwrun-t td:nth-child(4) { width: 33%; }
  .hwrun-t th:nth-child(5), .hwrun-t td:nth-child(5) { width: 25%; }
  .hwrun-t tbody td:nth-child(2), .hwrun-t tbody td:nth-child(4) { overflow: hidden; text-overflow: ellipsis; }
  /* the total's label has an empty cell beside it to run into, so it is the
     one row that does not need trimming */
  .hwrun-t tfoot td { overflow: visible; }
  .hwrun-bar, .hwrun-sum { padding-inline: 13px; }
  .hwrun-sum { grid-template-columns: 1fr; gap: 16px; }
  .hwrun-big b { font-size: 28px; }
  .scx { zoom: .52; }
  .dash-v { font-size: 40px; }

  .pz2 { zoom: .82; }
  .pz2s { padding: 28px 16px; }
  .rw, .pfc, .bk2 { width: 100%; max-width: 340px; }
  .bc.run .bv, .bc.bill .bv, .bc.pay .bv { padding: 28px 16px 32px; }
  .bc .bt, .bc.run .bt, .bc.c3 .bv { padding-inline: 20px; }
  .rlog { font-size: 11px; }


  .brg-r { grid-template-columns: 19px minmax(0, 1fr); column-gap: 12px; }
  .brg-c { padding: 12px 14px; gap: 11px; }
  .brg-t b { font-size: 14px; }
  .bpay { padding: 20px 18px 16px; }
  .bpay-p { font-size: 30px; }
  /* The placeholder line is longer than a figure, so it gives up more here.
     The slot inherits it — `$` and words stay one size, which is what keeps
     them looking aligned. */
  .bpay-p:has(.bpay-ph) { font-size: 25px; }

  /* THE SETTINGS PANEL GOES, AND THE SENTENCE STAYS. The band is one
     state read two ways, and on a phone the two cost a screen and a half
     each: six labelled rows of segmented buttons above the sentence they
     write, saying the same thing twice to somebody who has to scroll
     between them to notice.

     The sentence is the half that survives the trip — every value in it
     is a pill that opens the same options, so nothing is lost but the
     duplicate. Hidden rather than removed: the script still renders it,
     and at 601px it is simply there again. */
  .cvpn { display: none; }
  .cvs { font-size: 19px; line-height: 1.6; }
  .cvp { height: 34px; border-radius: 9px; }

  .ft { grid-template-columns: 1fr; gap: 28px; }
  .fine { flex-direction: column; gap: 14px; }
  .faq .prule { left: 20px; right: 20px; }

  /* ── THE REST OF THE 44px FLOOR ──────────────────────────────────
     The pass that sized the footer, the burger and the buttons stopped
     short of the plan builder, which is where the only things on the
     page you are actually invited to press live: fourteen segmented
     buttons at 28px, in groups of two and three, inside a panel that
     already scrolls sideways. A miss there lands on the value next to
     the one you wanted and silently changes the sentence. */
  .sgm button { min-height: 40px; padding: 0 14px; }
  .cvp { height: 40px; }
  .brand { min-height: 44px; display: inline-flex; align-items: center; }
  .hwp { padding: 12px 15px 13px; }

  /* the standalone links — the ones that end a band rather than sit in a
     sentence — get their padding as hit area. .lnk carries a margin-top
     of its own, so the padding comes off that rather than adding to it. */
  .lnk, .link-more {
    display: inline-flex; align-items: center; min-height: 44px;
    margin-top: 6px; padding-block: 0;
  }
  /* an inline-flex anchor ignores min-height unless it is a box, which it
     already is here; the announcement bar's one is the exception */
  .ann a { min-height: 32px; }

  /* THE CODE PANEL STOPS LYING ABOUT ITS WIDTH. It scrolls sideways and
     always did, but a line cut flush at the panel's edge reads as a line
     that ends there. Smaller type fits more of each line, and the mask
     feathers the cut so it reads as "there is more". */
  .src { padding: 18px 16px; font-size: 12px; }
  .src .n { width: 22px; }
  .src {
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 26px), transparent);
    mask-image: linear-gradient(90deg, #000 calc(100% - 26px), transparent);
  }
}

/* ── motion ───────────────────────────────────────────────────────────
   Four loops run on this page: the pairing screens, the routing run, the
   chevrons and the payment line. Asked to stop, they stop on the frame
   that says the most — the last screen, the chosen row — rather than the
   first, which says nothing. */
/* ── the narrow end ───────────────────────────────────────────────────
   320px is where two figures stop fitting, and both for the same reason:
   they are drawings with intrinsic widths rather than text that reflows.
   The DMG window is two 96px slots and an arrow between them, and the
   payments flow is a 330px canvas. Neither can wrap, so both are scaled
   — `zoom` rather than `transform`, because the layout has to shrink with
   them or the band keeps the width they gave up. */
@media (max-width: 360px) {
  .dmg { zoom: .86; }
  .pflow { zoom: .7; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  .pz2x3, .pz2g3 { opacity: 1; }
  .pz2d3 { width: 18px; background: var(--ink); }
  .ps0, .ps1, .ps2, .ps3 { opacity: 1; }
  .rwtyp { display: none; }
  .rwr.dim { opacity: .75; }
  /* the flow lines are dashed only so the dashes can travel */
  .pfl { stroke-dasharray: none; }
  /* the reveal is not taken, so nothing may depend on it having run */
  .js .st { opacity: 1; transform: none; }
  .st .num { color: var(--ink); }
  .st::after { width: 100%; }
}

/* ── EVERY TOUCH DEVICE, NOT EVERY NARROW WINDOW ──────────────────────
   These were written under `max-width: 600px` first, which caught phones and
   missed tablets: measured on an 834-wide iPad the page still had twenty-two
   targets under 44px, because 834 is not a narrow window — it is a wide one
   being poked with a finger. Width was never the question; `pointer: coarse`
   is, and it leaves every mouse-driven layout exactly as it was.

   THE WIDTH IS KEPT ALONGSIDE IT, as an or. Not because a narrow window
   needs big targets — it does no harm — but because `pointer` cannot be
   simulated in a desktop browser, and a rule that can only be checked on
   real hardware is a rule that silently rots. The width arm is the one
   under test; the pointer arm is the one that is correct. */
@media (pointer: coarse), (max-width: 600px) {
/* ── SIZED FOR A THUMB ─────────────────────────────────────
   Measured at 390: the footer's links were 17px tall and 9px apart, the
   burger 38, the buttons 36, the gallery's tabs 35. The floor is 44.

   The footer is the one that mattered. A link that is 17px of text in a
   column of other 17px links is not a small target, it is an ambiguous
   one — the miss lands on its neighbour and takes you somewhere else. The
   height here is padding rather than font-size, so nothing looks bigger;
   it is just hittable. */
.ft a:not(.ft-brand) {
  margin-top: 0; padding-block: 11px;
  min-height: 44px; display: flex; align-items: center;
}
.ft b { margin-bottom: 4px; }
.ft-brand { min-height: 44px; }
.nav-burger { width: 44px; height: 44px; }
.btn { height: 44px; }
/* the bar above the nav is one line of text and a chevron; it needs the
   room around it rather than a bigger type size */
.ann { padding-block: 12px; }
.ann a { min-height: 24px; }
.fine a { display: inline-block; padding-block: 6px; }
}
