/* ============================================================
   Lyndran — marketing site, third direction.
   The front page is a door: a near-black page, almost nothing on it, one
   centred statement and one action. Below it, calm bands — a heading and
   one paragraph, generous space, no seams. The hairline grid that used to
   run the whole page survives in exactly two places, where rules make
   things readable rather than decorative: the price table and the FAQ.
   Blue-black ground, one blue, flat, no shadows, no gradients, motion only
   transform and opacity and none of it under reduced-motion.
   This file owns index.html and labs.html, and the shared shell (nav and
   footer) of the document pages, which add doc.css on top.
   ============================================================ */

:root {
  --ground: #0c0e1a;
  --cell: #0f1226;
  --cell-up: #131833;
  --text: #e9ecf8;
  --dim: rgba(233, 236, 248, 0.64);
  --faint: rgba(233, 236, 248, 0.40);
  --hair: rgba(147, 165, 255, 0.16);
  --hair-strong: rgba(147, 165, 255, 0.30);
  --accent: #3d5afe;
  --accent-up: #7d92ff;
  --accent-ink: #ffffff;
  --t1: #4a66ff; --t2: #3d5afe; --t3: #3350f0; --t4: #2a44d6; --t5: #1e33a8; --t6: #16247a;
  --sans: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "Segoe UI", Roboto, Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --navh: 60px;
  --ease: cubic-bezier(0.2, 0.7, 0.3, 1);
  /* the curve things land on as the page scrolls into view. Steeper than
     --ease: nine tenths of the distance in the first 37% of the time, and
     the rest a settle — a landing rather than a drift. The door keeps
     --ease; its beats were tuned to it. */
  --ease-arrive: cubic-bezier(0.32, 0.72, 0, 1);
}
:root[data-theme="light"] {
  --ground: #f6f6f3;
  --cell: #ffffff;
  --cell-up: #fbfbf9;
  --text: #15181f;
  --dim: rgba(21, 24, 31, 0.66);
  --faint: rgba(21, 24, 31, 0.42);
  --hair: rgba(20, 26, 60, 0.14);
  --hair-strong: rgba(20, 26, 60, 0.28);
  --accent: #3d5afe;
  --accent-up: #2a44d6;
}
html.theme-turning body, html.theme-turning .cell, html.theme-turning .nav {
  transition: background-color 0.45s ease, color 0.45s ease, border-color 0.45s ease;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--ground);
  color: var(--text);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
.skip { position: absolute; left: -999px; top: 8px; z-index: 100; padding: 8px 12px; background: var(--accent); color: #fff; }
.skip:focus { left: 8px; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* the letter a dot stands in for, kept for anything that reads aloud */
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* ============ the grid, where it is still used ============
   labs.html and the document pages are still built from cells sharing
   hairlines. The front page is not, and nothing here reaches it. */
.wrap {
  max-width: 1400px;
  margin: 0 auto;
  border-left: 1px solid var(--hair);
  border-right: 1px solid var(--hair);
}
.row {
  display: grid;
  gap: 1px;
  background: var(--hair);
  border-bottom: 1px solid var(--hair);
}
.cell { background: var(--ground); min-width: 0; padding: 32px 34px 36px; }
.cell.up { background: var(--cell); }
.cell.ink { background: var(--accent); color: var(--accent-ink); }
.cell.ink .eyebrow, .cell.ink .dim { color: rgba(255,255,255,0.72); }
.c2 { grid-template-columns: repeat(2, 1fr); }
.c3 { grid-template-columns: repeat(3, 1fr); }
.c4 { grid-template-columns: repeat(4, 1fr); }
.c6 { grid-template-columns: repeat(6, 1fr); }
.span2 { grid-column: span 2; }
.span3 { grid-column: span 3; }
.spanall { grid-column: 1 / -1; }

/* ============ type ============ */
.eyebrow {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--faint);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 18px;
}
.eyebrow::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: var(--accent); }
.eyebrow.plain::before { display: none; }
.mono { font-family: var(--mono); font-size: 12px; letter-spacing: 0.06em; }
.statement {
  font-weight: 600;
  letter-spacing: -0.028em;
  line-height: 1.02;
  font-size: clamp(2.2rem, 4.8vw, 4.6rem);
  margin: 0;
  color: var(--text);
  text-wrap: balance;
}
.statement .k { color: var(--accent-up); }
.cell.ink .statement { color: #fff; }
h3, .h3 { font-size: 20px; font-weight: 600; letter-spacing: -0.015em; line-height: 1.2; margin: 0; }
.dim { color: var(--dim); }
.lede { font-size: 17px; line-height: 1.6; color: var(--dim); max-width: 52ch; margin: 18px 0 0; }
.line { font-size: 15px; line-height: 1.6; color: var(--dim); margin: 12px 0 0; max-width: 40ch; }
.num { font-family: var(--mono); font-size: 12px; letter-spacing: 0.1em; color: var(--accent-up); display: block; margin-bottom: 18px; }
.note { font-size: 13px; color: var(--faint); margin-top: 14px; display: block; line-height: 1.5; }

/* ============ buttons ============ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  height: 42px; padding: 0 18px; border-radius: 8px;
  border: 1px solid var(--hair-strong);
  font-size: 14px; font-weight: 500; color: var(--text);
  white-space: nowrap;
  transition: background-color 0.25s var(--ease), border-color 0.25s var(--ease), color 0.25s var(--ease), transform 0.25s var(--ease);
}
.btn:hover { border-color: var(--accent); }
.btn:active { transform: scale(0.98); }
.btn.accent { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn.accent:hover { background: var(--accent-up); border-color: var(--accent-up); }
.btn svg { width: 16px; height: 16px; flex: 0 0 auto; }
.btn .dlmark { width: 18px; height: 18px; }
.btn .winmark { width: 17px; height: 17px; }
.btnrow { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 30px; }
.arrow { font-family: var(--mono); color: var(--accent-up); }
.btn.edge { border-color: var(--accent); color: var(--accent-up); }
.btn.edge:hover { background: var(--cell-up); border-color: var(--accent); }
/* a full-width slab, still used by the closing section */
.btn.wide { width: 100%; height: 50px; border-radius: 10px; font-size: 15px; font-weight: 600; }
/* the third weight: an outline that does not ask to be pressed. Windows is
   a promise rather than a product, so its door is drawn a shade back from
   the two that open onto something today. */
.btn.quiet { border-color: var(--hair); color: var(--dim); }
.btn.quiet:hover { border-color: var(--hair-strong); color: var(--text); }
/* the platform glyphs. Bigger than the 16px utility icons because they are
   pictures of objects rather than arrows, and the four panes is drawn small
   inside its box, so it is given the two extra pixels back and the negative
   margin that keeps its optical gap equal to the others'. */
.btn .g { width: 18px; height: 18px; }
.btn .gw { width: 20px; height: 20px; margin: 0 -1px; }

/* ============ the bar ============
   What is left of the chrome: the mark, and one pill. No links, no
   dividers, no line under it — the page below is the navigation. */
.nav { position: sticky; top: 0; z-index: 50; background: var(--ground); }
.nav-in {
  max-width: 1200px; margin: 0 auto; padding: 0 24px;
  display: flex; align-items: center; gap: 8px; height: var(--navh);
}
.nav .logo { display: flex; align-items: center; gap: 9px; }
.nav .mark { width: 24px; height: 24px; }
.logotext { font-weight: 700; letter-spacing: -0.04em; font-size: 18px; display: inline-flex; align-items: baseline; }
.adot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); margin: 0 1px; transform: translateY(-1px); }
.nav .grow { flex: 1; }
.skybtn {
  width: 34px; height: 34px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: var(--dim); flex: 0 0 auto;
  transition: color 0.2s var(--ease), background-color 0.2s var(--ease);
}
.skybtn:hover { color: var(--text); background: var(--cell); }
.skybtn svg { width: 17px; height: 17px; stroke: currentColor; }
.skybtn .moon { display: none; }
:root[data-theme="light"] .skybtn .sun { display: none; }
:root[data-theme="light"] .skybtn .moon { display: block; }
.pill {
  display: inline-flex; align-items: center; gap: 8px;
  height: 34px; padding: 0 15px; border-radius: 999px;
  background: var(--cell); border: 1px solid var(--hair);
  font-size: 13.5px; font-weight: 500; white-space: nowrap;
  transition: border-color 0.2s var(--ease);
}
.pill:hover { border-color: var(--hair-strong); }
.pill .dlmark { width: 15px; height: 15px; flex: 0 0 auto; }
/* one label, one gap: the words are a single flex item, so the space
   between "Download" and "for Mac" is a space and not a gap as well */
.pill .also { white-space: pre; }

/* ============ the door ============
   The whole first screen: one sentence, one ask, one action, one honest
   line. Nothing else, and a great deal of nothing around it. */
.door {
  min-height: calc(100vh - var(--navh));
  min-height: calc(100svh - var(--navh));
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center;
  padding: 32px 24px 72px;
  max-width: 1200px; margin: 0 auto;
}
.big {
  font-size: clamp(2.05rem, 4.6vw, 3.9rem);
  font-weight: 600; letter-spacing: -0.026em; line-height: 1.07;
  margin: 0; max-width: 21ch; text-wrap: balance;
}
/* the mark, inside the sentence */
.dot {
  display: inline-block; width: 0.5em; height: 0.5em; border-radius: 50%;
  background: var(--accent); margin: 0 0.045em; vertical-align: 0.03em;
}
.say { font-size: 19px; line-height: 1.6; color: var(--dim); max-width: 52ch; margin: 26px 0 0; }
/* Lyn, once, and small — the drawing she reads best in. Her carrot is held
   above the one button she will not press, which is the last clause of the
   sentence above her. A hand's width of the page, not a poster. */
.lyn { display: block; width: 320px; max-width: 84%; margin: 26px auto 4px; }
.ask {
  font-family: var(--mono); font-size: 13.5px; line-height: 1.5; color: var(--dim);
  margin: 32px 0 0; display: inline-flex; align-items: baseline; gap: 10px;
  text-align: left; max-width: 100%;
}
.ask .pr { color: var(--accent-up); flex: 0 0 auto; }
.acts { width: 100%; max-width: 330px; display: flex; flex-direction: column; gap: 10px; margin-top: 34px; }
.honest { font-size: 13.5px; line-height: 1.5; color: var(--dim); margin: 18px 0 0; max-width: 44ch; }

/* ---- the three ways in ----
   One row, one baseline, three glyphs. The hierarchy is carried by fill and
   not by size: exactly one thing here is filled, because exactly one of the
   three is the thing to do, and a second filled button would only make the
   choice harder. The other two are outlined — the web because it is the
   same product without the download, Windows in the quiet outline because
   it is a promise. All three keep the same height so the glyphs sit on one
   optical line and the row reads as a set rather than a ranking of shapes.
   The honest sentence underneath is the caption of this row, so it is
   pulled close enough to belong to it. */
.door .acts {
  width: auto; max-width: 100%;
  flex-direction: row; flex-wrap: wrap; justify-content: center;
  gap: 10px; margin-top: 32px;
}
.way { display: flex; flex: 0 0 auto; }
.way > .btn { width: 100%; height: 50px; padding: 0 19px; border-radius: 10px; font-size: 15px; }
.way > .btn.accent { padding: 0 24px; font-weight: 600; }
/* a hair more air before the promise, so the row reads as two doors that
   open today and then the one that does not, rather than three of a kind */
.door .acts .way:last-child { margin-left: 6px; }
.door .honest { margin-top: 16px; }
/* the short label the Windows button wears when it is sharing a line. The
   platform word does not disappear when it does — it goes to the readers
   who cannot see the four panes. */
.w-short, .b-short { display: none; }

/* ============ the bands ============
   A heading and one paragraph, centred, with room to breathe. */
.band {
  max-width: 1200px; margin: 0 auto;
  padding: clamp(62px, 7.2vw, 96px) 24px;
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
}
.band h2 {
  font-size: clamp(1.6rem, 2.9vw, 2.5rem);
  font-weight: 600; letter-spacing: -0.022em; line-height: 1.14;
  margin: 0; max-width: 24ch; text-wrap: balance;
}
.band p { font-size: 17px; line-height: 1.65; color: var(--dim); max-width: 58ch; margin: 22px 0 0; }
.band .more { margin-top: 26px; font-size: 15px; }
.band .more a { color: var(--accent-up); }
.band .more a:hover { color: var(--text); }
.band .more .chev { font-family: var(--mono); }
.band .acts { margin-top: 30px; }
.band .honest { color: var(--faint); max-width: 56ch; }

/* ---- custody, as a ledger ----
   The claim that has to be believed, so the one paragraph it used to be is
   three columns: what it touches, what is never copied, and — said out loud
   rather than left to the end of a sentence — what leaves. No rules between
   them: at three items the space is the divider, and a hairline here would
   only make the page look like a spreadsheet again. The label is the
   smallest blue on the page, enough to say "this column has a heading" and
   not enough to be read before the sentence under it. The heading itself is
   not made bigger to announce that this band matters: the page has one
   heading size, and importance is carried by the shape of what sits under
   it rather than by a second size in a system that has one. */
.facts {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 34px; width: 100%; max-width: 940px;
  text-align: left; margin-top: 40px;
}
.fact .k {
  display: block; font-family: var(--mono); font-size: 11px;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent-up);
  margin: 0 0 11px;
}
.fact p { font-size: 16px; line-height: 1.6; margin: 0; max-width: none; }

/* ---- three asks, each with what came back under it ----
   Two voices, and the size is what tells them apart. Hers is the ask, at
   the size a person says a thing out loud. The machine's is the result:
   mono, small, quiet, and marked with the same blue dot the eyebrow uses,
   because it is a report and not a promise. */
.quotes {
  list-style: none; margin: 38px 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 32px; width: 100%; max-width: 960px; text-align: left;
}
/* the asks are one and two lines long, so the results under them would
   land on three different heights. Each ask is a column of its own full
   height and its result is pushed to the foot of it, which puts all three
   reports on one line — the row reads as one answer in three parts. */
.quotes li { margin: 0; max-width: none; display: flex; flex-direction: column; }
.quotes .said {
  display: block;
  font-size: 20px; font-weight: 500; letter-spacing: -0.012em; line-height: 1.35;
  color: var(--text);
}
.quotes .back {
  display: flex; align-items: flex-start; gap: 9px; margin-top: auto; padding-top: 14px;
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.05em;
  line-height: 1.45; color: var(--dim);
}
.quotes .back::before {
  content: ""; flex: 0 0 auto; width: 5px; height: 5px; border-radius: 50%;
  background: var(--accent); margin-top: 6px;
}
/* the fourth beat is a different move from the three above it, so it is
   given the air to be one */
.quotes + p { margin-top: 34px; }

/* ---- the four things the promise holds ----
   One claim per line, in full ink rather than the paragraph grey, because
   these are the promises and not the explanation of them. Left-aligned
   inside a centred block, so four sentences of four different lengths make
   a column and not a shape. */
.holds { list-style: none; margin: 34px 0 0; padding: 0; text-align: left; max-width: 60ch; }
.holds li {
  position: relative; padding-left: 22px; margin: 0 0 13px;
  font-size: 17px; line-height: 1.55; color: var(--text);
}
.holds li:last-child { margin-bottom: 0; }
.holds li::before {
  content: ""; position: absolute; left: 2px; top: 0.62em;
  width: 5px; height: 5px; border-radius: 50%; background: var(--accent);
}

/* the one picture left on the page: her carrot over the button she will not
   press. It is the argument, not decoration, which is why it survived. */
.shown { display: block; width: 320px; max-width: 100%; margin: 44px auto 0; }
.art { display: block; width: 100%; height: auto; }

/* ============ pricing — the grid earns it ============
   A wider measure and taller rows. The rules were never the problem; the
   rows were too close together to be read one at a time. */
/* the top rule lives on the first row rather than on the table, so that it
   travels with the row as it arrives instead of hanging in the air above it */
.ptable { width: 100%; max-width: 480px; text-align: left; margin-top: 38px; }
.prow:first-child { border-top: 1px solid var(--hair); }
.prow { display: flex; justify-content: space-between; align-items: baseline; gap: 20px; padding: 17px 0; border-bottom: 1px solid var(--hair); font-size: 15.5px; }
.prow .mono { color: var(--accent-up); display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.mini-carrot { width: 14px; height: 14px; }
.free { color: var(--accent-up); }

/* ============ faq — and here too ============ */
.faq { width: 100%; max-width: 680px; text-align: left; margin-top: 38px; }
.faq details { border-bottom: 1px solid var(--hair); }
.faq details:first-of-type { border-top: 1px solid var(--hair); }
.faq summary {
  list-style: none; cursor: pointer; font-size: 17px; font-weight: 500;
  display: flex; justify-content: space-between; align-items: center; gap: 16px;
  margin: 0 -14px; padding: 20px 14px; border-radius: 12px;
  transition: background-color 0.25s var(--ease);
}
/* the row state is a swap and not an addition: the fill arrives and the
   rule under it leaves, so no row is ever wearing both at once. The rule
   above a hovered row belongs to the row before it, which is what the :has
   is for; where :has is not understood the fill simply arrives alone. */
.faq summary:hover { background: var(--cell); }
.faq details:hover { border-bottom-color: transparent; }
.faq details:has(+ details:hover) { border-bottom-color: transparent; }
.faq details:first-of-type:hover { border-top-color: transparent; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary .plus { font-family: var(--mono); color: var(--accent-up); font-size: 18px; transition: transform 0.3s var(--ease); }
.faq details[open] summary .plus { transform: rotate(45deg); }
.faq .ans { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.4s var(--ease); }
.faq .ans > div { overflow: hidden; }
.faq details[open] .ans { grid-template-rows: 1fr; }
.faq p { margin: 0 0 20px; color: var(--dim); font-size: 16px; line-height: 1.65; max-width: none; }

/* ============ the waitlist ============ */
.band, #windows { scroll-margin-top: calc(var(--navh) + 8px); }
.wait { display: flex; gap: 10px; margin-top: 30px; width: 100%; max-width: 440px; }
.wait input { flex: 1; min-width: 0; height: 42px; padding: 0 14px; border: 1px solid var(--hair-strong); border-radius: 8px; background: var(--cell); color: var(--text); font: inherit; font-size: 14px; }
.wait input:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: transparent; }
.wfdone { display: block; margin-top: 14px; }
.wait[hidden], .wfdone[hidden] { display: none; }

/* ============ footer ============
   The links nobody needs until they need one: quiet, seamless, under a
   single line that says the page is over. */
.foot {
  grid-template-columns: 1.5fr 1fr 1fr 1fr;
  gap: 0; background: transparent;
  border-bottom: 0; border-top: 1px solid var(--hair);
  max-width: 1200px; margin: 0 auto;
}
.foot .cell { background: transparent; padding: 48px 24px 56px; }
.foot .cell:first-child { padding-left: 24px; }
.foot h4, .foot .foot-label { font-family: var(--mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--faint); margin: 0 0 16px; font-weight: 500; }
.foot ul { list-style: none; margin: 0; padding: 0; }
.foot li { margin: 0 0 10px; font-size: 14px; color: var(--dim); }
.foot li a:hover { color: var(--text); }
.foot .credit { font-family: var(--mono); font-size: 11.5px; letter-spacing: 0.06em; color: var(--faint); margin-top: 26px; }
.foot .line { color: var(--faint); }
.wordlogo svg { height: 24px; width: auto; }

/* ============ labs: the split hero and the wall of tiles ============
   labs.html still uses the older grammar, and keeps it. */
.hero { grid-template-columns: 1.05fr 1fr; min-height: calc(100vh - var(--navh)); }
.hero .copy { display: flex; flex-direction: column; justify-content: center; padding: 64px 56px; }
.hero .statement { font-size: clamp(2.6rem, 5.6vw, 5.4rem); }
.hero .lede { font-size: 18px; max-width: 46ch; }
.hero .stage { padding: 0; position: relative; display: flex; align-items: center; justify-content: center; background: var(--cell-up); overflow: hidden; }
.cell.mosaic { padding: 0; display: grid; grid-template-columns: repeat(6, 1fr); gap: 1px; background: var(--hair); position: relative; }
.mosaic .tl { position: relative; min-height: 0; min-width: 0; display: flex; flex-direction: column; justify-content: space-between; padding: 20px 20px 18px; }
.mosaic .tl.t1 { background: var(--t1); } .mosaic .tl.t2 { background: var(--t2); }
.mosaic .tl.t3 { background: var(--t3); } .mosaic .tl.t4 { background: var(--t4); }
.mosaic .tl.t5 { background: var(--t5); } .mosaic .tl.t6 { background: var(--t6); }
.mosaic .tl.g { background: var(--ground); }
.mosaic .tl.w2 { grid-column: span 2; }
.mosaic .tl.h2 { grid-row: span 2; }
.mosaic .tl.b { grid-column: span 2; grid-row: span 2; }
.mosaic { grid-auto-rows: minmax(0, 1fr); grid-auto-flow: dense; }
.mosaic .tl { display: block; padding: 0; }
.mosaic .cap { position: absolute; left: 14px; bottom: 12px; right: 12px; font-size: 13px; color: rgba(255,255,255,0.92); }
.mosaic .tl.g .cap { color: var(--faint); }
.lstat { font-family: var(--mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--faint); display: inline-flex; align-items: center; gap: 7px; }
.lstat::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.lstat.now { color: var(--accent-up); }
.lstat.next { color: var(--dim); }
.lstat.held { color: var(--faint); }
.lstat.held::before { background: none; box-shadow: inset 0 0 0 1px currentColor; }
.cell .lstat + h3 { margin-top: 16px; }

/* ============ motion ============
   Transform and opacity only. Things arrive; nothing bounces, nothing
   blinks, nothing types itself. Everything below the door lands the same
   way: 24px up, 0.48s, on --ease-arrive, and site.js says when — a tenth
   of the screen in, so the landing happens where the eye is. */
.js .fx, .js .head, .js .cell.ink, .js .cell.up {
  opacity: 0; transform: translateY(24px);
  transition: opacity 0.48s var(--ease-arrive), transform 0.48s var(--ease-arrive);
}
.js .fx.in, .js .head.in, .js .cell.ink.in, .js .cell.up.in { opacity: 1; transform: none; }
/* ---- a band, in order ----
   The band itself never moves. What arrives is its heading, and then the
   rows inside it one after another, 80ms apart, each over the same 0.48s.
   (It used to rise as a block and then rise its rows again on top of that
   — two movements stacked, 28px of travel over a second. One movement of
   24px over half a second is the difference between drifting in and
   landing.) --i is set by site.js on the children of any .stagger;
   anything standing on its own carries its own in the markup, and anything
   with no --i at all lands one beat after the heading. Nothing here
   changes a box, so nothing below moves while it arrives. */
.js .band > h2, .js .band .stagger > *, .js .band .rise {
  opacity: 0; transform: translateY(24px);
  transition: opacity 0.48s var(--ease-arrive), transform 0.48s var(--ease-arrive);
}
.js .band.in > h2 { opacity: 1; transform: none; }
.js .band.in .stagger > *, .js .band.in .rise {
  opacity: 1; transform: none;
  transition-delay: calc(80ms + var(--i, 0) * 80ms);
}
/* ---- the door arrives, in beats ----
   Not a sprinkle: an order. The sentence lands a word at a time, the dot
   lands last and lands differently — it is the mark, so it is the moment —
   and then the line, Lyn, the ask, the buttons and the honest line follow
   at a walking pace. Nothing here touches layout: every box is already the
   size it will end up, so nothing below anything moves while it arrives. */
.js .door .big .w { display: inline-block; opacity: 0; transform: translateY(0.42em); }
.js .door.in .big .w { animation: dword 0.72s var(--ease) forwards; animation-delay: calc(var(--i) * 52ms); }
@keyframes dword { to { opacity: 1; transform: none; } }
/* the dot is left a bare sibling of the words — never wrapped — so its box
   and its baseline are exactly what they were, and no parent's opacity is
   ever multiplied over it. The letters rise; the dot grows into place. */
.js .door .big .dot { opacity: 0; transform: scale(0.2); }
.js .door.in .big .dot { animation: dland 0.52s var(--ease) forwards; animation-delay: 520ms; }
@keyframes dland { to { opacity: 1; transform: scale(1); } }

.js .door .say,
.js .door .lyn,
.js .door .ask,
.js .door .way,
.js .door .honest {
  opacity: 0; transform: translateY(14px);
  transition: opacity 0.8s var(--ease), transform 0.8s var(--ease);
}
.js .door.in .say,
.js .door.in .lyn,
.js .door.in .ask,
.js .door.in .way,
.js .door.in .honest { opacity: 1; transform: none; }
.js .door.in .say    { transition-delay: 0.72s; }
.js .door.in .lyn    { transition-delay: 0.92s; }
.js .door.in .ask    { transition-delay: 1.14s; }
/* the three doors land in the order they are useful, a tenth apart. The
   wrapper moves, never the button, so the press keeps its own quick
   transition and the arrival cannot hold the transform hostage. */
.js .door.in .way    { transition-delay: calc(1.38s + var(--i) * 100ms); }
.js .door.in .honest { transition-delay: 1.72s; }

/* the example ask types itself once and then stays. The finished line is
   kept in the box as a hidden ghost, so the space is reserved to the exact
   pixel and the wrap point never changes as the characters land. */
.ask .typ { position: relative; display: inline-block; }
.ask .typ .ghost { visibility: hidden; }
.ask .typ .cut { position: absolute; left: 0; top: 0; width: 100%; }

/* the one thing that keeps breathing.
   Lyn herself — her body, not the card she sits beside — by one and a half
   percent, anchored at her base, over seven seconds. The stops are a
   cosine, not a triangle — that is the whole difference between breathing
   and a metronome — and it is scale only, so she never bobs. */
.lynart .lynbody { transform-box: fill-box; transform-origin: 50% 100%; }
.js .alive .lynart .lynbody { animation: breathe 7s linear infinite; }
@keyframes breathe {
    0% { transform: scale(1); }
   10% { transform: scale(1.0014); }
   20% { transform: scale(1.0052); }
   30% { transform: scale(1.0098); }
   40% { transform: scale(1.0136); }
   50% { transform: scale(1.015); }
   60% { transform: scale(1.0136); }
   70% { transform: scale(1.0098); }
   80% { transform: scale(1.0052); }
   90% { transform: scale(1.0014); }
  100% { transform: scale(1); }
}
/* labs' statement still rises a word at a time */
.js .statement .w { display: inline-block; opacity: 0; transform: translateY(0.45em); }
.js .statement.in .w { animation: riseword 0.9s var(--ease) forwards; animation-delay: calc(var(--i) * 70ms); }
@keyframes riseword { to { opacity: 1; transform: none; } }
.js .hero .lede, .js .hero .btnrow { opacity: 0; transform: translateY(14px); transition: opacity 0.8s var(--ease), transform 0.8s var(--ease); }
.js .hero.in .lede { opacity: 1; transform: none; transition-delay: 0.5s; }
.js .hero.in .btnrow { opacity: 1; transform: none; transition-delay: 0.7s; }
.js .mosaic .tl { opacity: 0; transform: translateY(14px) scale(0.94); }
.js .mosaic.in .tl { animation: tilein 0.8s var(--ease) forwards; animation-delay: calc(var(--i) * 40ms); }
@keyframes tilein { to { opacity: 1; transform: none; } }
.mosaic .tl { transition: filter 0.4s var(--ease); }
.mosaic .tl:hover { filter: brightness(1.28); }

/* Nothing on this page moves for a reader who has asked for stillness.
   boot.js already withholds the .js class in that case, so none of the
   hidden resting states above can even match; this block is the second
   lock, for a setting changed while the page is open — and the same lock
   for paper, where nothing scrolls and so nothing would ever arrive. */
@media (prefers-reduced-motion: reduce), print {
  .js .fx, .js .band, .js .door, .js .head, .js .cell.ink, .js .cell.up,
  .js .statement .w, .js .hero .lede, .js .hero .btnrow, .js .mosaic .tl,
  .js .door .big .w, .js .door .big .dot, .js .door .say, .js .door .lyn,
  .js .door .ask, .js .door .way, .js .door .honest, .js .alive .lynart .lynbody,
  .js .band > h2, .js .band .stagger > *, .js .band .rise {
    opacity: 1 !important; transform: none !important; animation: none !important; transition: none !important;
  }
  .faq .ans { transition: none; }
  html { scroll-behavior: auto; }
}

/* ============ breakpoints ============ */
/* Below the width where three buttons fit on one line, the row folds the
   way the hierarchy already reads: the slab keeps the full width to itself
   and the two alternatives share the line under it, a shade smaller. That
   is one line shorter than the two stacked slabs it replaces, so the door
   on a phone gets three doors and no extra height. The Windows label drops
   to its short form here — the four panes beside it are already saying
   "Windows", and what the press does is the part that has to stay in
   words. */
@media (max-width: 720px) {
  .door .acts { max-width: 340px; }
  .way { flex: 1 1 0; min-width: 0; }
  .way:first-child { flex: 1 0 100%; }
  .door .acts .way:last-child { margin-left: 0; }
  .way:not(:first-child) > .btn { height: 46px; font-size: 13.5px; padding: 0 10px; gap: 7px; }
  .way:not(:first-child) .g { width: 17px; height: 17px; }
  .way:not(:first-child) .gw { width: 19px; height: 19px; }
  .w-long { display: none; }
  .w-short { display: inline; }
}
/* the narrowest phones still in the world. The verb goes and the place
   stays, because at this width the alternative is a label that eats its own
   padding — and a button whose words touch its edge looks broken, which is
   worse than a shorter true label. */
@media (max-width: 365px) {
  .way:not(:first-child) > .btn { font-size: 13px; padding: 0 8px; gap: 6px; }
  .b-long { display: none; }
  .b-short { display: inline; }
}
/* Three columns stop being three columns while the measure is still
   comfortable, not when it finally breaks: below this the asks would be
   reading at about twenty-five characters a line, which is a poem. */
@media (max-width: 860px) {
  .facts, .quotes { grid-template-columns: 1fr; max-width: 46ch; }
  .facts { gap: 24px; }
  .quotes { gap: 26px; }
}
/* On a phone the column head becomes a run-in head: the same mono label,
   on the same line as the sentence it introduces. Three lines of page
   height back, and it reads the way a margin note reads. */
@media (max-width: 620px) {
  .fact .k { display: inline; margin: 0 9px 0 0; }
  .fact p { display: inline; }
  /* the three facts stop being three blocks and become three sentences
     with their own run-in heads, which is what a stacked ledger is */
  .facts { display: block; margin-top: 28px; }
  .fact { margin: 0 0 13px; }
  .fact:last-child { margin-bottom: 0; }
  .quotes { gap: 20px; margin-top: 30px; }
  .quotes + p { margin-top: 22px; }
  .holds { margin-top: 28px; }
  .holds li { margin-bottom: 10px; }
  /* the extra air the price table and the FAQ were given is a wide-screen
     thing: in a 375px column those rows have room already, and height here
     is paid for out of the same pocket */
  .ptable { margin-top: 30px; }
  .prow { padding: 14px 0; }
  .faq { margin-top: 30px; }
  .faq summary { padding: 18px 12px; margin: 0 -12px; }
}
@media (max-width: 1100px) {
  .foot { grid-template-columns: 1fr 1fr; }
  .hero { grid-template-columns: 1fr; min-height: 0; }
  .hero .copy { padding: 56px 40px; }
  .hero .stage { height: 520px; }
  .mosaic { min-height: 420px; }
}
@media (max-width: 760px) {
  .c2, .c3, .c4, .c6 { grid-template-columns: 1fr; }
  .span2, .span3 { grid-column: auto; }
  .cell { padding: 24px 22px; }
  .hero .copy { padding: 40px 22px 32px; }
  .foot { grid-template-columns: 1fr; }
  .foot .cell { padding: 30px 20px 6px; }
  .foot .cell:last-child { padding-bottom: 44px; }
  .hero .stage { height: 440px; }
  .mosaic { grid-template-columns: repeat(4, 1fr); }
  .mosaic .tl.b { grid-column: span 2; }
  .btnrow .btn { width: 100%; }
  .nav-in { padding: 0 18px; }
  .door { padding: 24px 20px 72px; }
  .band { padding: clamp(56px, 13vw, 84px) 20px; }
  .big { font-size: clamp(1.85rem, 8.6vw, 2.6rem); max-width: none; }
  .say { font-size: 17px; }
  .wait { flex-direction: column; max-width: 330px; }
  .acts { max-width: 330px; }
}
@media (max-width: 400px) {
  .pill .also { display: none; }
}

/* ============ the pages ============
   The site is several pages now, so the bar carries their names: plain
   words, the current one underlined by the accent, nothing else. Below the
   width where they fit, the row scrolls sideways rather than folding into
   a menu — five words are not a menu. */
.nav .links { display: flex; align-items: center; gap: 2px; margin-left: 18px; overflow-x: auto; scrollbar-width: none; }
.nav .links::-webkit-scrollbar { display: none; }
.nav .links a {
  font-size: 13.5px; font-weight: 500; color: var(--dim); white-space: nowrap;
  padding: 6px 9px; border-radius: 6px;
  transition: color 0.2s var(--ease), background-color 0.2s var(--ease);
}
.nav .links a:hover { color: var(--text); background: var(--cell); }
.nav .links a[aria-current="page"] { color: var(--text); box-shadow: inset 0 -2px 0 var(--accent); border-radius: 6px 6px 0 0; }
@media (max-width: 560px) {
  .nav .links { margin-left: 6px; }
  .nav .links a { padding: 6px 7px; font-size: 13px; }
  .pill .also { display: none; }
}

/* the first band of a page is its door: the same heading size the door
   uses, and room above it */
.band.first { padding-top: clamp(48px, 6vw, 84px); }
.band.first h1 {
  font-size: clamp(1.9rem, 3.8vw, 3.2rem);
  font-weight: 600; letter-spacing: -0.024em; line-height: 1.1;
  margin: 0; max-width: 22ch; text-wrap: balance;
}

/* ---- the pictures ----
   Every band carries one now: a prebuilt glyph from art/, the same blues,
   the same hairline ground, never a photograph. In a fact or an ask it
   sits above the words at a hand's width; on its own under a heading it
   is a little larger. It arrives with its cell (the cell moves, it does
   not) and on hover lifts by nothing — the page has no lift. */
.cue { display: block; width: 100%; max-width: 250px; margin: 0 0 14px; }
.cue img { display: block; width: 100%; height: auto; }
.cue.big { max-width: 320px; margin: 30px auto 0; }
.cue.big + p { margin-top: 26px; }
.quotes li .cue { max-width: 220px; }
@media (max-width: 720px) {
  .cue { max-width: 200px; }
}

/* ---- Lyn, alive for real ----
   She is inline in the door, so her parts can move. She has no paw; what
   she holds over the button is her carrot, out of its notch, and the dotted
   line is the way it came. Four things and no more. The carrot drifts
   toward the button on a slow cosine and never lands. When the pointer
   comes near the button she draws the carrot back along that way, into the
   notch (site.js sets --near from the distance; the drawing itself carries
   the way home as --hx/--hy, so nothing here knows the scene). Her pupils
   follow the pointer, and she blinks on her own clock (site.js toggles
   .shut). Press the drawn button and the carrot is home, her eyes become
   two happy arcs, she hops once and says the one line. Her parts are found
   by class, never by colour. All of it is transform and opacity on the one
   easing, and all of it stops under reduced motion. */
.lyn { position: relative; }
.lynart .carrot-pull {
  transform: translate(calc(var(--near, 0) * var(--hx, 0px)), calc(var(--near, 0) * var(--hy, 0px)));
  transition: transform 0.55s var(--ease);
}
.js .alive .lynart .carrot-drift { animation: carrot-drift 4.6s linear infinite; }
@keyframes carrot-drift {
    0% { transform: translateY(0); }
   10% { transform: translateY(0.29px); }
   20% { transform: translateY(1.04px); }
   30% { transform: translateY(1.96px); }
   40% { transform: translateY(2.71px); }
   50% { transform: translateY(3px); }
   60% { transform: translateY(2.71px); }
   70% { transform: translateY(1.96px); }
   80% { transform: translateY(1.04px); }
   90% { transform: translateY(0.29px); }
  100% { transform: translateY(0); }
}
.lynart .trail { opacity: calc(1 - var(--near, 0) * 2.5); transition: opacity 0.4s var(--ease); }
.lynart .eye { transform-box: fill-box; transform-origin: 50% 50%; transition: transform 0.09s var(--ease); }
.lynart.shut .eye { transform: scaleY(0.1); }
.lynart .pupil { transition: transform 0.35s var(--ease); }
.lynart .eyes, .lynart .glad { transition: opacity 0.2s var(--ease); }
.lynart .glad { opacity: 0; }
.lynart .btn { cursor: pointer; outline: none; transform-box: fill-box; transform-origin: 50% 50%; }
.lynart .btn:focus-visible circle { stroke: var(--text); stroke-width: 3; }
.js .pulled .lynart .carrot-pull { transform: translate(var(--hx, 0px), var(--hy, 0px)); transition-duration: 0.26s; }
.js .pulled .lynart .trail { opacity: 0; }
.pulled .lynart .eyes { opacity: 0; }
.pulled .lynart .glad { opacity: 1; }
.js .pulled.alive .lynart .lynbody { animation: lyn-hop 0.46s var(--ease) 1; }
@keyframes lyn-hop { 0%, 100% { transform: translateY(0); } 42% { transform: translateY(-2.2px); } }
.whisper {
  position: absolute; left: 50%; bottom: -22px; transform: translate(-50%, 6px);
  margin: 0; font-family: var(--mono); font-size: 12.5px; letter-spacing: 0.04em;
  color: var(--accent-up); white-space: nowrap; opacity: 0; pointer-events: none;
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
.pulled .whisper { opacity: 1; transform: translate(-50%, 0); }
@media (prefers-reduced-motion: reduce), print {
  .js .alive .lynart .carrot-drift, .js .alive .lynart .lynbody { animation: none !important; }
  .lynart .carrot-pull { transform: none !important; }
  .lynart .trail { opacity: 1 !important; }
  .lynart .carrot-pull, .lynart .trail, .lynart .pupil, .lynart .eye, .lynart .eyes, .lynart .glad, .whisper { transition: none !important; }
}
/* the links take the room between the mark and the sky button, so on a
   phone they are a row that scrolls, not a strip squeezed to nothing */
.nav .links { flex: 1 1 auto; min-width: 0; }
.nav .links + .grow { flex: 0 0 0; }
/* on a phone the bar is two rows: the mark and the doors, then the page
   names on a row of their own that scrolls sideways if it must */
@media (max-width: 560px) {
  .nav-in { flex-wrap: wrap; height: auto; padding-top: 8px; padding-bottom: 6px; row-gap: 2px; }
  .nav .links { order: 9; flex: 1 0 100%; margin-left: -7px; }
}

/* ---- the pictures draw themselves ----
   site.js swaps each cue for its inline SVG. Until the band arrives every
   line is a dash the length of itself, pushed fully off; arrival runs the
   offset to zero, each line 55ms after the last, and the filled shapes
   settle in once the lines have found their places. The cell's own rise
   (the --i stagger) is added, so the picture never draws before its words
   have started to come. Off, whole and still, under reduced motion. */
.cue-svg { display: block; height: auto; }
.js .cue-svg .ln { stroke-dasharray: 1; stroke-dashoffset: 1; }
.js .cue-svg .fl { opacity: 0; }
.js .in .cue-svg .ln {
  animation: draw 0.9s var(--ease-arrive) forwards;
  animation-delay: calc(80ms + var(--i, 0) * 80ms + var(--k, 0) * 55ms);
}
.js .in .cue-svg .fl {
  animation: settle 0.6s var(--ease) forwards;
  animation-delay: calc(420ms + var(--i, 0) * 80ms + var(--k, 0) * 40ms);
}
@keyframes draw { to { stroke-dashoffset: 0; } }
@keyframes settle { to { opacity: 1; } }
@media (prefers-reduced-motion: reduce), print {
  .js .cue-svg .ln { stroke-dasharray: none !important; stroke-dashoffset: 0 !important; animation: none !important; }
  .js .cue-svg .fl { opacity: 1 !important; animation: none !important; }
}
/* Lyn on a document page (the 404): left-aligned like the column she
   stands in, the line under her feet */
.doc-head .lyn { margin: 22px 0 30px; }
