/* NIX — /echo, the landing for the Mac app
   ══════════════════════════════════════════════════════════════════════════
   THE SHELL IS shell.css's, not this file's. .page gives the panel beside
   the rail its column, its ground and its radius; what is here is the
   panel's contents: a hero, three facts, three steps, three plans and the
   site's foot. The panel is its own scroller, the app's arrangement, and
   min-height:0 is what lets a grid item shrink far enough to scroll.

   Only tokens, no colours of its own. Cards sit on --nix-surface because
   the panel is already --nix-panel, and a card painted the panel's own grey
   is text with no box, which is the mistake the AB01 price cards made once.
   ══════════════════════════════════════════════════════════════════════════ */

.voice {
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* THE AIR BETWEEN SECTIONS, named once. The hero reaches up behind the
     section bar by this same gap (--hero-gap below), so the two must never
     differ; one variable is how they cannot. 96px on a desktop, a step past
     the top of the spacing scale (--space-8 is 64), and 64 on a phone. */
  --section-gap: calc(var(--space-8) * 1.5);
  gap: var(--section-gap);
  padding: 0 var(--space-4);
  /* The sticky section bar's whole block: 50px of pill plus the two margins
     components.css gives it, the same clamps. The full-screen sections (the
     hero and Why) pad by it so their words never sit under the bar. */
  --bar-block: calc(50px + clamp(10px, 1.5svh, 20px) + clamp(24px, 4svh, 44px));
}

/* ── Hero ──────────────────────────────────────────────────────────────── */

.voice__hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 14px;
  max-width: 720px;
  /* ONE SCREEN, CENTRED ON THE SCREEN. The panel is the window less its 8px
     inset top and bottom, and the hero is exactly that tall. Centring it in
     the room left under the sticky section bar put the words a bar's height
     below the middle of the screen, so instead the hero reaches up behind
     the bar (the bar is sticky and sits on top) by the bar's whole block —
     50px of pill plus the two margins components.css gives it, the same
     clamps — and the column's gap after it. The same block as padding at
     both ends keeps the words clear of the bar and keeps the middle of the
     hero the middle of the panel. */
  --hero-gap: var(--section-gap);
  justify-content: center;
  min-height: calc(100svh - 16px);
  /* No section bar above it any more (2026-09-15): the hero is the column's
     first child, so there is nothing to reach up behind. */
  margin-top: 0;
  padding-block: var(--bar-block);
  /* The anchor lands with the pill's height in hand, so the heading is not
     hidden under the sticky bar when the rail or the pill scrolls to it. */
  scroll-margin-top: 72px;
}

/* With the rail out, components.css hides the section bar, and the hero is
   the column's first child: nothing above it to reach behind. */
body.is-nav:has(.nav-rail__drill) .voice__hero { margin-top: 0; }

/* THE FIELD behind the hero (js/hero_field.js): the pointer's ASCII wake and
   the butterflies. Absolute inside the scroller, so it scrolls away with the
   hero; the script sets the canvas's top and height from the hero's own
   box. Its colour is the text token, read by the script, so it follows the
   theme. The hero sits above. */
.voice { position: relative; }

.voice__field {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 0;
  pointer-events: none;
  z-index: 0;
  color: var(--nix-text);
}

.voice__hero {
  position: relative;
  z-index: 1;
}

/* THE HERO'S LIGHT. The feature cards' kind of gradient, in the slate blue
   none of them uses: pale at the top through slate blue, painted on the
   hero's field canvas because
   that is the box as wide as the panel (the hero itself is a 720px
   column). The pale part sits behind the section bar, the headline lands
   in the blue, and the last stop is the panel's own colour so the hero
   runs into Why with no seam.

   Like the cards it has its own light in both themes, so the hero's words,
   its button and the field on top are set light here whatever the theme:
   dark text on slate would not read. */
.voice__field[data-host=".voice__hero"] {
  background: linear-gradient(180deg, #D8D9DC 0%, #A3AAB8 16%, #3C4A63 40%, #1F2533 76%, var(--nix-panel) 100%);
  color: #F4F3F1;
}

.voice__hero {
  --nix-text: #F4F3F1;
  --nix-text-soft: #C3C8D2;
  --nix-ink: #F4F3F1;
  --nix-on-ink: #131312;
  color: var(--nix-text);
}

.voice__mark {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--nix-text-soft);
}

.voice__lead {
  font-size: clamp(36px, 6.5vw, 76px);
  font-weight: 300;
  letter-spacing: -0.03em;
  line-height: 1.02;
  text-wrap: balance;
}

/* The line under the headline sets the scene; a touch larger than body
   text so it reads as part of the hero and not as a caption. */
.voice__hero-sub {
  font-size: clamp(17px, 1.6vw, 20px);
  line-height: 1.45;
}

.voice__text {
  color: var(--nix-text-soft);
  font-size: 18px;
  line-height: 1.5;
  max-width: 600px;
}

/* Download for Mac, centred, with "Soon for Windows" hanging under it
   (.voice__dl is a centred column). The row stays a centred wrapping flex
   row. */
.voice__cta {
  margin-top: 10px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  gap: 12px;
}

.voice__cta .voice__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 230px;
  max-width: 100%;
}

.voice__dl {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

/* OUTLINED, IN THE SECTION'S TEXT COLOUR: white on the hero's slate (it
   sets --nix-text light in both themes), and the theme's ink in the close,
   so the outline never vanishes into a light ground. An inset ring rather
   than a border, so it is exactly as tall as the filled button beside it. */
.voice__btn--outline {
  background: transparent;
  color: var(--nix-text);
  box-shadow: inset 0 0 0 1.5px currentColor;
}

.voice__btn--outline:hover {
  background: color-mix(in srgb, var(--nix-text) 12%, transparent);
  transform: translateY(-1px);
}

.voice__btn-ico {
  width: 16px;
  height: 16px;
  flex: none;
  margin-top: -2px;
}

.voice__soon {
  color: var(--nix-text-soft);
  font-size: var(--text-sm);
}

/* ── Section headings ──────────────────────────────────────────────────── */

.voice__h {
  font-size: clamp(28px, 4vw, 40px);
  font-weight: 300;
  letter-spacing: -0.02em;
  line-height: 1.1;
  text-align: center;
  margin: 0;
}

.voice__why,
.voice__how,
.voice__faq {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  scroll-margin-top: 72px;
}

/* ── Why: one paragraph ───────────────────────────────────────────────────
   Not cards. How it works and the facts below are boxes on --nix-surface;
   a third row of the same boxes would read as more of the same. The reasons
   are one large paragraph instead, so the page changes pace before the
   steps. The body of it is the soft colour; the three reasons are the full
   text colour, so a reader who only skims still reads the three of them. */

.voice__why-text {
  max-width: 760px;
  margin: var(--space-2) 0 0;
  color: var(--nix-text-soft);
  font-size: clamp(19px, 1.9vw, 25px);
  font-weight: 300;
  line-height: 1.5;
  letter-spacing: -0.01em;
  text-align: center;
  text-wrap: pretty;
}

.voice__why-text strong {
  color: var(--nix-text);
  font-weight: 400;
}

/* A SCREEN OF ITS OWN, like the hero: Why, What you get and the close.
   Exactly the panel's height (the window less its 8px inset top and
   bottom), the words in the middle, and the bar's block as padding at both
   ends so the sticky bar never covers them and the middle stays the
   middle. The anchor lands on the section's top edge, not 72px short of
   it, so the rail and the pill open onto the whole screen. */
.voice__screen {
  justify-content: center;
  min-height: calc(100svh - 16px);
  padding-block: var(--bar-block);
  scroll-margin-top: 0;
}

/* ── What it does: three rows in a zigzag ──────────────────────────────
   The words on one side and a square card on the other, the sides
   swapping on every row: text left on the first and third, card left on
   the second. The card is the only boxed thing in the row; the words sit
   on the panel. On a phone each row is one column, words first. */

.voice__facts {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  max-width: 960px;
  width: 100%;
}

.voice__fact {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: var(--space-6);
}

.voice__fact:nth-child(even) .voice__fact-card { order: -1; }

.voice__fact-copy {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.voice__fact-h {
  font-size: clamp(26px, 3.2vw, 36px);
  font-weight: 300;
  letter-spacing: -0.02em;
  line-height: 1.1;
  margin: 0;
}

.voice__fact-p {
  color: var(--nix-text-soft);
  font-size: var(--text-md);
  line-height: 1.55;
  max-width: 34ch;
  margin: 0;
}

/* THE CARD IS A FRAME AND A SCREEN. The frame is a vertical gradient, pale
   at the top through a colour of its own to near black, one colour per row
   (bronze, forest green, plum; the hero has the slate blue), the same in
   both themes: an object
   with its own light, like a photograph. The screen is the page's own card
   surface, rising from the frame's bottom edge with air only above and at
   the sides; its top corners are the frame's radius scaled by the screen's
   width (72%), so the two curves are the same shape at two sizes, and its
   bottom runs off the frame. */
.voice__fact-card {
  aspect-ratio: 1 / 1;
  max-width: 100%;
  display: grid;
  place-items: end center;
  border-radius: var(--radius-2xl);
  background: var(--fact-grad);
  overflow: hidden;
  --fact-grad: linear-gradient(180deg, #DDD9D3 0%, #BDAF9C 22%, #6B5236 52%, #2E231A 76%, #161311 100%);
}

.voice__fact:nth-child(2) .voice__fact-card {
  --fact-grad: linear-gradient(180deg, #D8DCD9 0%, #A6B6AB 22%, #3C5A4A 52%, #1F2B25 76%, #121513 100%);
}

.voice__fact:nth-child(3) .voice__fact-card {
  --fact-grad: linear-gradient(180deg, #DCD8DD 0%, #B4A8BA 22%, #54426B 52%, #271F33 76%, #141217 100%);
}

.voice__fact-screen {
  width: 72%;
  height: 86%;
  display: grid;
  place-items: center;
  padding: 18px;
  border-radius: calc(var(--radius-2xl) * 0.72) calc(var(--radius-2xl) * 0.72) 0 0;
  background: var(--nix-surface);
  box-shadow: 0 -12px 48px rgba(0, 0, 0, 0.28);
  overflow: hidden;
}

/* The drawing: characters on the screen, never wrapped, sized so the
   widest line (37 characters) fits it at every width. */
.voice__fact-art {
  margin: 0;
  font-family: var(--font-mono);
  font-size: clamp(8px, 0.95vw, 11.5px);
  line-height: 1.55;
  color: var(--nix-text-soft);
  white-space: pre;
}

@media (max-width: 720px) {
  .voice__facts { gap: var(--space-6); }
  .voice__fact { grid-template-columns: minmax(0, 1fr); gap: var(--space-3); }
  .voice__fact:nth-child(even) .voice__fact-card { order: 0; }
  .voice__fact-art { font-size: clamp(8px, 2.2vw, 11.5px); }
}

/* ── How it works: one window, played by the scroll (js/how_flow.js) ────
   The section is as tall as the window plus --hw-run of scroll; the pin
   (heading, window, caption) sticks under the section bar for all
   of it, and the script turns the pin's displacement into the three
   screens. Unpinned (reduced motion, no JS) it is an ordinary block: the
   three screens finished, one under the next. */

.hw {
  width: 100%;
  max-width: 1040px;
  --hw-top: 76px;
  --hw-h: min(calc(100svh - 16px - var(--hw-top) - 24px), 860px);
  --hw-run: 540svh;
}

.hw.is-pinned { height: calc(var(--hw-h) + var(--hw-run)); }

.hw__pin {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
}

.hw.is-pinned .hw__pin {
  position: sticky;
  top: var(--hw-top);
  height: var(--hw-h);
}

/* The frame is the feature cards' kind: a gradient with the window rising
   from its bottom edge. Dusk rose, a colour the page does not use yet. */
.hw__frame {
  position: relative;
  width: 100%;
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  border-radius: var(--radius-2xl);
  background: linear-gradient(180deg, #DDD8D9 0%, #BDA7AB 18%, #6B3F48 48%, #2E1C21 76%, #161213 100%);
  overflow: hidden;
}

.hw:not(.is-pinned) .hw__frame { padding-top: 48px; }

.hw__ui {
  position: relative;
  width: 78%;
  height: 88%;
  border-radius: calc(var(--radius-2xl) * 0.78) calc(var(--radius-2xl) * 0.78) 0 0;
  background: var(--nix-surface);
  color: var(--nix-text);
  box-shadow: 0 -12px 48px rgba(0, 0, 0, 0.28);
  overflow: hidden;
  font-size: clamp(10px, 0.95vw, 15px);
}

.hw:not(.is-pinned) .hw__ui { height: auto; }

.hw__screen {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2em;
}

.hw.is-pinned .hw__screen {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: translateY(0.6em);
  transition: opacity 380ms var(--ease-out-expo), transform 380ms var(--ease-out-expo);
}

.hw.is-pinned .hw__screen.is-on { opacity: 1; transform: none; }
.hw:not(.is-pinned) .hw__screen + .hw__screen { border-top: 1px solid var(--nix-rule); }

/* ── The controls the cursor works ── */

.hw-app,
.hw-dlg {
  width: min(30em, 100%);
  display: flex;
  flex-direction: column;
  gap: 1em;
}

.hw-app__bar {
  display: flex;
  align-items: center;
  gap: 0.45em;
  font-size: 0.85em;
  color: var(--nix-text-quiet);
}

.hw-app__bar i { width: 0.7em; height: 0.7em; border-radius: 50%; background: var(--nix-rule); }
.hw-app__bar span { flex: 1; text-align: center; padding-right: 2.4em; }

.hw-f { display: flex; flex-direction: column; gap: 0.4em; }
.hw-f__lab { font-size: 0.85em; color: var(--nix-text-quiet); }

.hw-f__box {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 2.8em;
  padding: 0.7em 1em;
  border-radius: 0.7em;
  background: color-mix(in oklch, var(--nix-text) 7%, transparent);
  transition: background 120ms ease;
}

.hw-f__box.is-press { background: color-mix(in oklch, var(--nix-text) 14%, transparent); }

.hw-caret {
  display: inline-block;
  width: 1px;
  height: 1.1em;
  margin-left: 1px;
  background: var(--nix-text);
  opacity: 0;
}

.is-typing > .hw-caret { opacity: 1; }

.hw-sel svg { width: 1em; height: 1em; margin-left: auto; opacity: 0.6; }

.hw-menu {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% + 0.4em);
  z-index: 3;
  display: flex;
  flex-direction: column;
  padding: 0.3em;
  border-radius: 0.7em;
  background: var(--nix-panel);
  box-shadow: 0 18px 40px -14px rgba(0, 0, 0, 0.45);
  opacity: 0;
  transform: translateY(-0.3em);
  transition: opacity 200ms var(--ease-out-expo), transform 200ms var(--ease-out-expo);
}

.hw-sel.is-open { z-index: 4; }
.hw-sel.is-open .hw-menu { opacity: 1; transform: none; }
.hw-menu span { padding: 0.5em 0.7em; border-radius: 0.45em; font-size: 0.92em; color: var(--nix-text-soft); }
.hw-menu span.on { background: color-mix(in oklch, var(--nix-text) 10%, transparent); color: var(--nix-text); }

.hw-btn {
  align-self: flex-start;
  padding: 0.65em 1.4em;
  border-radius: var(--radius-pill);
  background: var(--nix-ink);
  color: var(--nix-on-ink);
  font-size: 0.95em;
  transition: transform 120ms ease;
}

.hw-btn.is-press { transform: scale(0.94); }

.hw-dlg__h { margin: 0; font-size: 1.5em; font-weight: 300; letter-spacing: -0.01em; }

.hw-consent {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1em;
  padding: 0.8em 1em;
  border-radius: 0.8em;
  border: 1px solid var(--nix-rule);
  font-size: 0.9em;
  color: var(--nix-text-soft);
}

.hw-consent .hw-btn { align-self: auto; }

/* The phone: a composer at the top of it here only because the beats run
   in document order; the chat is drawn under it and reads top to bottom. */
.hw-phone {
  height: min(100%, 32em);
  aspect-ratio: 9 / 16;
  display: flex;
  flex-direction: column;
  gap: 0.7em;
  padding: 0.9em 0.9em 1.1em;
  border-radius: 2.2em;
  border: 1px solid var(--nix-rule);
  background: var(--nix-panel);
}

.hw:not(.is-pinned) .hw-phone { height: 30em; }

.hw-phone__notch {
  align-self: center;
  width: 5em;
  height: 1.1em;
  border-radius: var(--radius-pill);
  background: color-mix(in oklch, var(--nix-text) 18%, transparent);
}

.hw-chat { order: 2; flex: 1; display: flex; flex-direction: column; justify-content: flex-end; gap: 0.6em; }
.hw-compose { order: 3; }

.hw-msg {
  margin: 0;
  max-width: 86%;
  padding: 0.6em 0.85em;
  border-radius: 1em;
  font-size: 0.92em;
  line-height: 1.35;
}

.hw-msg--me {
  align-self: flex-end;
  background: var(--nix-ink);
  color: var(--nix-on-ink);
  opacity: 0;
  transition: opacity 200ms ease;
}

.hw-msg--ai { align-self: flex-start; background: color-mix(in oklch, var(--nix-text) 8%, transparent); }
.hw-msg--ai:not(.is-reached) { visibility: hidden; }

/* Sent: the message leaves the composer and appears as a bubble. */
.hw__screen[data-k="2"] .hw-msg--me { opacity: 1; }
.hw__screen[data-k="2"] .hw-compose__text { visibility: hidden; }

.hw-compose {
  display: flex;
  align-items: center;
  gap: 0.5em;
  min-height: 2.6em;
  padding: 0.35em 0.35em 0.35em 0.9em;
  border-radius: var(--radius-pill);
  border: 1px solid var(--nix-rule);
  font-size: 0.9em;
}

.hw-compose__text { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; }

.hw-send {
  flex: none;
  display: grid;
  place-items: center;
  width: 2em;
  height: 2em;
  border-radius: 50%;
  background: var(--nix-ink);
  color: var(--nix-on-ink);
  transition: transform 120ms ease;
}

.hw-send svg { width: 1em; height: 1em; }
.hw-send.is-press { transform: scale(0.86); }

/* The cursor. Only while pinned and on a screen with something to press. */
.hw__ptr {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 6;
  width: 1.4em;
  height: 1.4em;
  margin: -0.2em 0 0 -0.2em;
  pointer-events: none;
  opacity: 0;
  transition: opacity 200ms ease;
}

.hw.is-pinned .hw__ui.is-acting .hw__ptr { opacity: 1; }
.hw__ptr svg { display: block; width: 100%; height: 100%; }

/* The caption strip: one line per step, sliding as its screen gives way. */
.hw__cap { flex: 0 0 auto; width: min(640px, 100%); overflow: hidden; }
.hw__cap-track { display: flex; will-change: transform; }
.hw__cap-item { flex: 0 0 100%; display: flex; justify-content: center; padding: 0 var(--space-2); }

.hw__cap-item > span {
  padding: 11px 22px;
  border-radius: var(--radius-pill);
  background: color-mix(in oklch, var(--nix-text) 7%, transparent);
  color: var(--nix-text-soft);
  font-size: var(--text-sm);
  text-align: center;
}

.hw:not(.is-pinned) .hw__cap { overflow: visible; }
.hw:not(.is-pinned) .hw__cap-track { flex-direction: column; align-items: center; gap: 8px; }

@media (max-width: 720px) {
  .hw { --hw-top: 70px; }
  .hw__ui { width: 92%; font-size: clamp(9px, 2.6vw, 13px); }
}


/* ── The close: the last line and the one button ─────────────────────── */

.voice__close {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  text-align: center;
  /* Above its field, the canvas of wake and butterflies behind it. */
  position: relative;
  z-index: 1;
}

.voice__close-h {
  font-size: clamp(30px, 4.6vw, 54px);
}

/* ── The foot, inside the scroller ─────────────────────────────────────── */

/* The column centres its children; the foot is the one that has to span it,
   and margin-top:auto keeps it on the floor of a short panel. shell.css
   already takes it off the window's measure (body .index-foot). */
.voice > .index-foot {
  align-self: stretch;
  margin-top: auto;
}

/* ── Focus ─────────────────────────────────────────────────────────────── */

.voice__btn:focus-visible,
.voice__plan-cta:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

/* ── Widths ────────────────────────────────────────────────────────────── */

@media (max-width: 720px) {
  .voice {
    --section-gap: var(--space-8);
    padding: 0 var(--space-2);
  }
  .voice__text { font-size: var(--text-md); }
}
