/* Docksmith site design system — shared by the landing page and every
   feature page. One file so N pages can never drift apart. */

:root {
  --bg: #0a0c12;
  --surface: rgba(255,255,255,.055);
  --surface-strong: rgba(255,255,255,.09);
  --stroke: rgba(255,255,255,.11);
  --text: #eef1f7;
  --text-dim: #9aa3b5;
  --accent: #4f8bff;
  --accent-2: #ff8a3d;
  --radius: 18px;
  color-scheme: dark;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, sans-serif;
  background: var(--bg); color: var(--text);
  line-height: 1.6; -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
/* The macOS-wallpaper backdrop the whole page sits on */
.wallpaper {
  position: fixed; inset: 0; z-index: -1;
  background:
    radial-gradient(1100px 700px at 78% -10%, rgba(79,139,255,.28), transparent 60%),
    radial-gradient(900px 600px at 8% 28%, rgba(122,82,255,.20), transparent 55%),
    radial-gradient(700px 500px at 55% 105%, rgba(255,138,61,.13), transparent 55%),
    var(--bg);
}
.container { max-width: 1080px; margin: 0 auto; padding: 0 24px; }

/* ---------- glass primitives ---------- */
.glass {
  background: var(--surface);
  -webkit-backdrop-filter: blur(28px) saturate(1.35);
  backdrop-filter: blur(28px) saturate(1.35);
  border: 1px solid var(--stroke);
  border-radius: var(--radius);
}

/* ---------- nav ---------- */
nav {
  position: sticky; top: 14px; z-index: 50;
  margin: 14px auto 0; max-width: 1080px; padding: 10px 18px;
  display: flex; align-items: center; gap: 22px;
}
/* The wordmark. It was 17px at weight 700 — the same size as body copy, so the
   product's own name carried no more authority than a paragraph. It is now
   20.5px at 750 with the tracking pulled in: on this typeface, weight plus
   NEGATIVE letter-spacing is what separates a logotype from a bold sentence.
   The system font stays, deliberately — a page arguing that the app is native
   should not open by loading a web font. */
nav .brand { display: flex; align-items: center; }
/* margin, NOT gap: the wordmark is split into two spans for its two weights,
   so a flex gap would apply BETWEEN THEM as well and print "Dock smith". */
nav .brand img { width: 36px; height: 36px; margin-right: 11px; }
nav .brand a {
  color: var(--text); text-decoration: none; display: flex; align-items: center;
  font-size: 20.5px; font-weight: 750; letter-spacing: -.024em;
}
/* "smith" one shade back: the eye reads DOCK first, which is the word that
   says what this is, and the whole reads as one mark rather than two. */
nav .brand .mk2 { color: var(--text-dim); font-weight: 650; }
nav .links { margin-left: auto; display: flex; gap: 20px; align-items: center; }
nav a { color: var(--text-dim); text-decoration: none; font-size: 14.5px; transition: color .15s; }
nav a:hover { color: var(--text); }
/* THE GRADIENT RULE, and everything below obeys it: the blue-violet gradient
   means "this is the purchase". It belongs to .btn-primary alone. Four elements
   used to wear it — this nav pill, the assistant launcher, the stepper's +/-,
   and the Most-popular badge — and when four things shout in the same voice,
   none of them is heard. Each one below steps down to a rank that matches what
   it actually is: a standing invitation, a helper, a quantity control, a label.

   Scope: the PAGE surface. Inside the assistant panel the gradient still marks
   the visitor's own messages and the send button — that is a separate surface,
   only ever on screen when the panel covers the corner, and nothing there
   competes with a Buy button. Do not "fix" those two to match this rule. */
nav .cta {
  color: color-mix(in srgb, var(--accent) 82%, #fff);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 38%, transparent);
  padding: 7px 15px; border-radius: 999px; font-weight: 620;
  transition: background .16s ease, border-color .16s ease, color .16s ease;
}
nav .cta:hover {
  color: #fff; background: color-mix(in srgb, var(--accent) 26%, transparent);
  border-color: color-mix(in srgb, var(--accent) 60%, transparent);
}

/* ---------- hero ---------- */
/* header.container, not header: every hero on this site also carries .container,
   whose `padding: 0 24px` shorthand zeroes the vertical padding and outranks a
   bare type selector. The plain `header { padding: 84px 0 40px }` that lived
   here never applied — the landing page's h1 began at exactly the sticky nav's
   bottom edge, with no air at all, and had done since it was written. Feature
   pages override this further down with header.page-hero. */
header.container { padding: 92px 0 40px; text-align: center; }
h1 {
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", sans-serif;
  font-size: clamp(2.6rem, 6.4vw, 4.4rem);
  line-height: 1.05; letter-spacing: -0.025em; font-weight: 800;
}
h1 .grad {
  background: linear-gradient(100deg, var(--accent), #9d7bff 55%, var(--accent-2));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.sub {
  max-width: 620px; margin: 22px auto 0;
  color: var(--text-dim); font-size: 19px;
}
.hero-ctas { margin-top: 34px; display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }
.btn {
  display: inline-block; text-decoration: none; font-weight: 650; font-size: 16px;
  padding: 13px 26px; border-radius: 999px; transition: transform .15s, box-shadow .15s;
}
.btn:hover { transform: translateY(-1px); }
.btn-primary {
  color: #fff; background: linear-gradient(135deg, var(--accent), #6f5bff);
  box-shadow: 0 8px 30px rgba(79,139,255,.35);
}
.btn-ghost { color: var(--text); background: var(--surface-strong); border: 1px solid var(--stroke); }
.fineprint { margin-top: 14px; font-size: 13px; color: var(--text-dim); }

/* ---------- hero scene (landing page: the product video) ---------- */
.scene {
  position: relative; margin: 64px auto 0; max-width: 880px;
  aspect-ratio: 16 / 9;
  border-radius: 22px; overflow: hidden;
  border: 1px solid var(--stroke);
  background: #10131c;
  box-shadow: 0 30px 80px rgba(0,0,0,.5);
}
.scene video { width: 100%; height: 100%; object-fit: cover; display: block; }
.menubar {
  position: absolute; top: 0; left: 0; right: 0; height: 26px;
  background: rgba(255,255,255,.06);
  -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px);
  display: flex; align-items: center; gap: 14px; padding: 0 12px;
  font-size: 11px; color: var(--text-dim);
}
.menubar b { color: var(--text); }
.win {
  position: absolute; border-radius: 12px;
  background: linear-gradient(180deg, #1d2230, #171b26);
  border: 1px solid rgba(255,255,255,.10);
  box-shadow: 0 18px 50px rgba(0,0,0,.45);
}
.win .titlebar { height: 26px; display: flex; align-items: center; gap: 6px; padding: 0 10px; border-bottom: 1px solid rgba(255,255,255,.06); }
.dotr, .doty, .dotg { width: 10px; height: 10px; border-radius: 50%; }
.dotr { background: #ff5f57; } .doty { background: #febc2e; } .dotg { background: #28c840; }
.win .lines { padding: 12px; display: grid; gap: 8px; }
.win .lines i { display: block; height: 8px; border-radius: 4px; background: rgba(255,255,255,.10); }
.win .lines i:nth-child(2) { width: 72%; } .win .lines i:nth-child(3) { width: 46%; }
.dock {
  position: absolute; left: 50%; transform: translateX(-50%); bottom: 16px; z-index: 5;
  display: flex; align-items: flex-end; gap: 10px; padding: 10px 14px;
  border-radius: 20px;
  background: rgba(255,255,255,.08);
  -webkit-backdrop-filter: blur(24px) saturate(1.4); backdrop-filter: blur(24px) saturate(1.4);
  border: 1px solid rgba(255,255,255,.14);
}
.tile {
  width: 44px; height: 44px; border-radius: 11px;
  transition: transform .18s cubic-bezier(.34,1.56,.64,1);
  position: relative;
}
.tile:hover { transform: translateY(-8px) scale(1.12); }
.tile::after {
  content: ""; position: absolute; left: 50%; bottom: -7px; transform: translateX(-50%);
  width: 4px; height: 4px; border-radius: 50%; background: rgba(255,255,255,.65);
}
.tile:nth-child(4)::after { background: var(--accent); box-shadow: 0 0 6px var(--accent); }
.t1 { background: linear-gradient(135deg,#4f8bff,#2857c9); }
.t2 { background: linear-gradient(135deg,#ff8a3d,#e05612); }
.t3 { background: linear-gradient(135deg,#3fd68b,#159957); }
.t4 { background: linear-gradient(135deg,#e8ecf5,#b9c2d4); }
.t5 { background: linear-gradient(135deg,#a56bff,#6d2fd6); }
.t6 { background: linear-gradient(135deg,#ff5d7b,#d61f45); }
.dock .sep { width: 1px; align-self: stretch; margin: 4px 2px; background: rgba(255,255,255,.16); }
.kbd-hint {
  position: absolute; right: 18px; top: 42px; z-index: 6;
  font-size: 12px; color: var(--text-dim);
  background: rgba(255,255,255,.07); border: 1px solid var(--stroke);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  padding: 6px 10px; border-radius: 9px;
}
.kbd-hint kbd {
  font-family: inherit; background: rgba(255,255,255,.12); border-radius: 5px;
  padding: 1px 6px; margin-right: 2px; color: var(--text);
}

/* ---------- sections ---------- */
/* A landing page is scrolled, not studied. A reader who slides past a heading
   without registering it has lost the whole section beneath it — so each one
   announces itself three times over: a seam that ends what came before, a badge
   that names what follows, and a title big enough to stop the eye. Vertical room
   is the cheap ingredient here; Fred's call is that length is allowed and
   ambiguity is not. */
/* section.container, not section: every landing section also carries .container,
   whose `padding: 0 24px` outranks a bare type selector no matter which is
   written last. The plain `section { padding: 88px 0 0 }` that lived here was
   dead from the day it was typed — which is exactly why the sections ran into
   each other with nothing between them. Raising the specificity is the fix; the
   horizontal 24px still comes from .container. */
section.container { padding-top: 116px; scroll-margin-top: 84px; }

/* The seam. A hairline that fades out at both ends separates without enclosing —
   a full-width rule would read as a table border and box each section in. */
section::before {
  content: ""; display: block; width: 210px; height: 1px; margin: 0 auto 38px;
  background: linear-gradient(90deg, transparent, var(--stroke), transparent);
}

.kicker { text-align: center; font-size: 13.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--accent); }
h2 { text-align: center; font-size: clamp(1.7rem, 3.6vw, 2.4rem); letter-spacing: -0.02em; margin-top: 10px; }
.lead { text-align: center; max-width: 560px; margin: 14px auto 0; color: var(--text-dim); }

/* The three rules below are scoped to a direct section child on purpose. The
   same three classes do a second, quieter job in every feature page's hero,
   where .kicker carries key caps and must stay a plain line — and no feature
   page has a <section>, so that job is untouched. */
section > .kicker {
  width: fit-content; margin-inline: auto;
  padding: 7px 16px 6px; font-size: 12.5px; letter-spacing: .16em;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 13%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
}
section > h2 {
  font-size: clamp(2.15rem, 5vw, 3.05rem); line-height: 1.14; margin-top: 20px;
}
section > .lead { max-width: 620px; font-size: 17px; margin-top: 18px; }

.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-top: 44px; }
.card { padding: 26px 24px; }
.card h3 { font-size: 17px; margin-bottom: 6px; letter-spacing: -0.01em; }
.card p { font-size: 14.5px; color: var(--text-dim); }
/* The keyboard chords used to sit here on the landing page. They are gone by
   Fred's call: a landing page's job is to make you want the thing, not to teach
   you its shortcuts. Every one of them still lives on its own feature page,
   where someone who has decided to care will actually read it. */

/* "Learn more" is the only invitation on a card, so it should read as a control
   rather than a footnote — but it must not compete with the download button
   above it. Hence a quiet pill that only picks up the accent on hover, with the
   arrow travelling instead of the whole thing moving. */
.more {
  display: inline-flex; align-items: center; gap: 7px;
  margin-top: 16px; padding: 9px 16px;
  font-size: 14px; font-weight: 620; text-decoration: none;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--stroke);
  border-radius: 999px;
  transition: background .16s ease, border-color .16s ease, color .16s ease;
}
.more i {
  font-style: normal; font-size: 15px; line-height: 1;
  color: var(--accent);
  transition: transform .18s cubic-bezier(.34,1.4,.64,1);
}
.more:hover {
  color: #fff;
  background: var(--surface-strong);
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
}
.more:hover i { transform: translateX(4px); }
.more:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
  .more, .more i { transition: none; }
  .more:hover i { transform: none; }
}

/* wide card: the differentiators get double width */
.card.wide { grid-column: span 2; }

/* ---------- feature vignettes: each card plays its function on loop ---------- */
.vig {
  position: relative; height: 150px; margin-bottom: 16px;
  border-radius: 12px; overflow: hidden;
  border: 1px solid var(--stroke);
  background:
    radial-gradient(220px 140px at 72% 8%, rgba(79,139,255,.30), transparent 60%),
    radial-gradient(200px 130px at 12% 92%, rgba(122,82,255,.22), transparent 55%),
    #0f1219;
}
.vbadge {
  position: absolute; top: 10px; right: 10px; z-index: 8;
  font-size: 10.5px; color: var(--text);
  background: rgba(255,255,255,.10); border: 1px solid var(--stroke);
  border-radius: 6px; padding: 2px 7px;
}
.vtile { display: inline-block; width: 18px; height: 18px; border-radius: 5px; flex: none; }
.vtile.b { background: linear-gradient(135deg,#4f8bff,#2857c9); }
.vtile.o { background: linear-gradient(135deg,#ff8a3d,#e05612); }
.vtile.g { background: linear-gradient(135deg,#3fd68b,#159957); }
.vtile.w { background: linear-gradient(135deg,#e8ecf5,#b9c2d4); }
.vtile.p { background: linear-gradient(135deg,#a56bff,#6d2fd6); }
.vtile.r { background: linear-gradient(135deg,#ff5d7b,#d61f45); }
.vdock {
  position: absolute; display: flex; gap: 5px; padding: 5px 7px;
  background: rgba(255,255,255,.10);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  border: 1px solid rgba(255,255,255,.15); border-radius: 10px;
}
.vdock.v { flex-direction: column; }
.vw {
  position: absolute; border-radius: 8px;
  background: linear-gradient(180deg, #1e2433, #171b26);
  border: 1px solid rgba(255,255,255,.12);
  box-shadow: 0 10px 26px rgba(0,0,0,.45);
  padding: 20px 9px 9px;
}
.vw .vtb { position: absolute; top: 6px; left: 8px; width: 22px; height: 5px; border-radius: 3px;
  background: linear-gradient(90deg,#ff5f57 0 5px, transparent 5px 8.5px, #febc2e 8.5px 13.5px, transparent 13.5px 17px, #28c840 17px); }
.vw .vl { display: block; height: 5px; border-radius: 3px; background: rgba(255,255,255,.12); margin-top: 5px; }
.vw .vl.short { width: 60%; }
.vcursor {
  position: absolute; z-index: 6; width: 11px; height: 15px;
  background: #fff; clip-path: polygon(0 0, 100% 62%, 58% 66%, 76% 100%, 60% 100%, 44% 72%, 0 88%);
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.6));
}

/* 1 — docks everywhere: the side screen's dock toggles with its chord */
.vscreen {
  position: absolute; top: 18%; width: 40%; height: 64%;
  border-radius: 8px; border: 1px solid rgba(255,255,255,.13);
  background: radial-gradient(120px 80px at 60% 20%, rgba(79,139,255,.25), transparent 65%), #141926;
  overflow: hidden;
}
.vscreen.s1 { left: 7%; }
.vscreen.s2 { right: 7%; }
.vscreen .vdock.h { bottom: 6px; left: 50%; transform: translateX(-50%); }
.vscreen .vdock.v { right: 6px; top: 50%; transform: translateY(-50%); animation: vdocktoggle 6s infinite; }
@keyframes vdocktoggle {
  0%, 12% { transform: translateY(-50%) translateX(220%); opacity: 0; }
  22%, 68% { transform: translateY(-50%) translateX(0); opacity: 1; }
  78%, 100% { transform: translateY(-50%) translateX(220%); opacity: 0; }
}
.vig-docks .vbadge { animation: vbadgepulse 6s infinite; }
@keyframes vbadgepulse {
  0%, 10% { opacity: .45; } 14%, 20% { opacity: 1; transform: scale(1.08); }
  24%, 64% { opacity: .45; transform: scale(1); }
  68%, 74% { opacity: 1; transform: scale(1.08); } 78%, 100% { opacity: .45; transform: scale(1); }
}

/* 2 — switcher: the selection ring walks the MRU row */
.vrow {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  display: flex; gap: 10px; padding: 10px 12px;
  background: rgba(255,255,255,.09);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  border: 1px solid rgba(255,255,255,.15); border-radius: 12px;
}
.vring {
  position: absolute; top: 6px; left: 8px; width: 26px; height: 26px;
  border: 2px solid var(--accent); border-radius: 8px;
  box-shadow: 0 0 10px rgba(79,139,255,.5);
  animation: vringwalk 6.5s infinite;
}
@keyframes vringwalk {
  0%, 12% { transform: translateX(0); }
  18%, 32% { transform: translateX(28px); }
  38%, 52% { transform: translateX(56px); }
  58%, 72% { transform: translateX(84px); }
  78%, 88% { transform: translateX(112px); }
  94%, 100% { transform: translateX(0); }
}

/* 3 — focus veil: the blur hole GLIDES from window to window */
/* The focused window sits ABOVE the veil — exactly how the app does it
   (a panel below the focused window's level). A clip-path keyhole was the
   wrong tool: browsers parse polygon(evenodd, …) yet still blurred every
   window, active one included (Fred saw it; a screenshot proved it). */
.vig-focus .vw { width: 38%; height: 55%; }
.vig-focus .wa { left: 8%; top: 20%; z-index: 3; animation: vfocusa 7s steps(1) infinite; }
.vig-focus .wb { right: 8%; top: 26%; z-index: 1; animation: vfocusb 7s steps(1) infinite; }
@keyframes vfocusa { 0%, 44% { z-index: 3; } 45%, 100% { z-index: 1; } }
@keyframes vfocusb { 0%, 44% { z-index: 1; } 45%, 100% { z-index: 3; } }
.vveil {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  -webkit-backdrop-filter: blur(4px) brightness(.6);
  backdrop-filter: blur(4px) brightness(.6);
}

.vfring {
  position: absolute; z-index: 4; border: 1px solid rgba(255,255,255,.30); border-radius: 9px;
  /* base geometry, so Reduce Motion still rings the sharp window */
  left: calc(8% - 2px); top: calc(20% - 2px); width: calc(38% + 4px); height: calc(55% + 4px);
  animation: vfringglide 7s infinite;
}
/* The ring hands over WITH the sharpness (44/45%), and stays put after —
   it used to walk back to the blurred window for the last 12% (audit). */
@keyframes vfringglide {
  0%, 44% { left: calc(8% - 2px); top: calc(20% - 2px); width: calc(38% + 4px); height: calc(55% + 4px); }
  45%, 100% { left: calc(54% - 2px); top: calc(26% - 2px); width: calc(38% + 4px); height: calc(55% + 4px); }
}

/* 4 — workspace: a tile rides to the desktop, the ghost grid answers,
       the windows tile themselves */
.vghost {
  position: absolute; left: 8%; top: 10%; right: 8%; height: 60%;
  display: grid; grid-template-columns: 1fr 1fr; gap: 6px; z-index: 2;
  opacity: 0; animation: vghostshow 8s infinite;
}
.vghost i {
  border: 1.5px dashed rgba(79,139,255,.6); border-radius: 8px;
  background: rgba(79,139,255,.10);
}
@keyframes vghostshow {
  0%, 12% { opacity: 0; }
  20%, 46% { opacity: 1; }
  54%, 100% { opacity: 0; }
}
.vdragw { position: absolute; left: 47%; bottom: 10px; z-index: 5; animation: vworkdrag 8s infinite; }
@keyframes vworkdrag {
  0%, 10% { transform: translate(0, 0) scale(1); opacity: 1; }
  36%, 44% { transform: translate(26px, -66px) scale(1.15); opacity: 1; }
  52%, 100% { transform: translate(26px, -66px) scale(.5); opacity: 0; }
}
.vig-work .vwa, .vig-work .vwb { top: 12%; height: 54%; width: 37%; opacity: 0; z-index: 3; }
.vig-work .vwa { left: 9.5%; animation: vworkland 8s infinite; }
.vig-work .vwb { right: 9.5%; animation: vworkland2 8s infinite; }
@keyframes vworkland {
  0%, 46% { opacity: 0; transform: translateY(10px) scale(.92); }
  54%, 86% { opacity: 1; transform: translateY(0) scale(1); }
  94%, 100% { opacity: 0; transform: translateY(10px) scale(.92); }
}
@keyframes vworkland2 {
  0%, 52% { opacity: 0; transform: translateY(10px) scale(.92); }
  60%, 86% { opacity: 1; transform: translateY(0) scale(1); }
  94%, 100% { opacity: 0; transform: translateY(10px) scale(.92); }
}
.vdock.h.center { bottom: 8px; left: 50%; transform: translateX(-50%); }
.vdock.h.center.low { bottom: 10px; }

/* 5 — hover previews: the cursor glides in, a live card pops above the tile */
.vprev {
  position: absolute; left: 50%; bottom: 66px; transform: translateX(-58%);
  width: 78px; height: 54px; border-radius: 7px; z-index: 5;
  background: linear-gradient(180deg, #202637, #181c28);
  border: 1px solid rgba(255,255,255,.14);
  box-shadow: 0 10px 26px rgba(0,0,0,.5);
  padding: 16px 7px 7px; opacity: 0;
  animation: vprevpop 6s infinite;
}
.vprev .vtb { position: absolute; top: 5px; left: 6px; width: 18px; height: 4px; border-radius: 2px;
  background: linear-gradient(90deg,#ff5f57 0 4px, transparent 4px 7px, #febc2e 7px 11px, transparent 11px 14px, #28c840 14px); }
.vprev .vl { display: block; height: 4px; border-radius: 2px; background: rgba(255,255,255,.13); margin-top: 4px; }
.vprev .vl.short { width: 55%; }
@keyframes vprevpop {
  0%, 30% { opacity: 0; transform: translateX(-58%) translateY(6px); }
  40%, 78% { opacity: 1; transform: translateX(-58%) translateY(0); }
  88%, 100% { opacity: 0; transform: translateX(-58%) translateY(6px); }
}
.vig-preview .vtile.pop { animation: vtilepop 6s infinite; }
@keyframes vtilepop {
  0%, 28% { transform: translateY(0) scale(1); }
  38%, 78% { transform: translateY(-5px) scale(1.18); }
  88%, 100% { transform: translateY(0) scale(1); }
}
.vig-preview .vcursor { animation: vcursorglide 6s infinite; }
@keyframes vcursorglide {
  0% { left: 82%; top: 30%; }
  28%, 80% { left: 47%; top: 74%; }
  100% { left: 82%; top: 30%; }
}

/* 6 — automation: the URL types itself, the veil answers */
.vterm {
  position: absolute; top: 14px; left: 14px; z-index: 5;
  display: flex; align-items: center;
  background: rgba(10,12,18,.85); border: 1px solid var(--stroke);
  border-radius: 8px; padding: 7px 10px;
  font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 10.5px; color: #b7e6c4;
}
.vtype {
  display: inline-block; overflow: hidden; white-space: nowrap; width: 20ch;
  animation: vtypeit 7.5s steps(20, end) infinite;
}
@keyframes vtypeit {
  0%, 6% { width: 0; }
  40%, 92% { width: 20ch; }
  97%, 100% { width: 0; }
}
.vcaret { width: 5px; height: 11px; background: #b7e6c4; margin-left: 2px; animation: vblink 1s steps(1) infinite; }
@keyframes vblink { 50% { opacity: 0; } }
.vig-auto .wauto { left: 26%; top: 38%; width: 44%; height: 48%; z-index: 3; }
.vveil.dim {
  -webkit-backdrop-filter: blur(3px) brightness(.55);
  backdrop-filter: blur(3px) brightness(.55);
  opacity: 0; animation: vveilanswer 7.5s infinite;
}
@keyframes vveilanswer {
  0%, 42% { opacity: 0; }
  50%, 90% { opacity: 1; }
  96%, 100% { opacity: 0; }
}

/* 7 — the double bar: two stacked docks reveal and hide as one */
.vig-stack .vdock { left: 50%; transform: translateX(-50%); }
.vig-stack .stack-top { bottom: 52px; animation: vstackride 6.5s infinite; }
.vig-stack .stack-bot { bottom: 14px; animation: vstackride 6.5s infinite; }
@keyframes vstackride {
  0%, 10% { transform: translateX(-50%) translateY(90px); opacity: 0; }
  22%, 72% { transform: translateX(-50%) translateY(0); opacity: 1; }
  84%, 100% { transform: translateX(-50%) translateY(90px); opacity: 0; }
}

/* 8 — folders: the tile opens a live grid of latest files */
.vtile.folder { background: linear-gradient(135deg,#6aa9ff,#3c7ae0); position: relative; }
.vtile.folder::before {
  content: ""; position: absolute; top: -3px; left: 2px; width: 8px; height: 4px;
  border-radius: 2px 2px 0 0; background: #3c7ae0;
}
.vpop {
  position: absolute; left: 50%; bottom: 64px; transform: translateX(-50%);
  display: grid; grid-template-columns: repeat(3, 16px); gap: 6px;
  padding: 9px; border-radius: 9px; z-index: 5;
  background: rgba(255,255,255,.10);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  border: 1px solid rgba(255,255,255,.16);
  opacity: 0; transform-origin: bottom center;
  animation: vpopopen 6.8s infinite;
}
@keyframes vpopopen {
  0%, 24% { opacity: 0; transform: translateX(-50%) translateY(8px) scale(.85); }
  34%, 80% { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
  90%, 100% { opacity: 0; transform: translateX(-50%) translateY(8px) scale(.85); }
}
.vfile { width: 16px; height: 20px; border-radius: 3px; display: block; }
.vfile.a { background: linear-gradient(160deg,#8ab6ff,#5480d6); }
.vfile.b { background: linear-gradient(160deg,#ffd08a,#e09a3c); }
.vfile.c { background: linear-gradient(160deg,#a9f0c9,#57bf8a); }
.vfile.d { background: linear-gradient(160deg,#e6e9f2,#b9c2d4); }
.vfile.e { background: linear-gradient(160deg,#d9b7ff,#9d6de0); }
.vfile.f { background: linear-gradient(160deg,#ffb7c4,#e06d86); }

/* 9 — trash: a file drops in, the tile answers */
.vtile.tr {
  background: linear-gradient(135deg,#aeb6c6,#7d8698); position: relative;
  animation: vtrashpop 6s infinite;
}
.vtile.tr::before {
  content: ""; position: absolute; top: -3px; left: 3px; right: 3px; height: 3px;
  border-radius: 2px; background: #cdd4e0;
}
@keyframes vtrashpop {
  0%, 44% { transform: scale(1); }
  50%, 56% { transform: scale(1.22); }
  62%, 100% { transform: scale(1); }
}
.vfiledrop {
  position: absolute; z-index: 6; width: 14px; height: 18px; border-radius: 3px;
  background: linear-gradient(160deg,#f2f5fb,#c6cedd);
  clip-path: polygon(0 0, 70% 0, 100% 26%, 100% 100%, 0 100%);
  left: 30%; top: 12%;
  animation: vfilefall 6s infinite;
}
@keyframes vfilefall {
  0%, 14% { transform: translate(0,0) rotate(-8deg); opacity: 0; }
  20% { opacity: 1; }
  46% { transform: translate(86px, 88px) rotate(10deg); opacity: 1; }
  52%, 100% { transform: translate(92px, 100px) scale(.3); opacity: 0; }
}
.vglow {
  position: absolute; z-index: 4; width: 34px; height: 34px; border-radius: 50%;
  left: calc(50% + 26px); bottom: 12px;
  background: radial-gradient(circle, rgba(79,139,255,.55), transparent 70%);
  opacity: 0; animation: vglowpulse 6s infinite;
}
@keyframes vglowpulse {
  0%, 48% { opacity: 0; } 54%, 66% { opacity: 1; } 76%, 100% { opacity: 0; }
}

/* 10 — appearance: the slider moves, the dock obeys */
.vdock.appear { left: 50%; top: 38%; transform: translateX(-50%); }
.vdock.appear .vtile { animation: vgrow 7s infinite; }
.vdock.appear .vtile:nth-child(2) { animation-name: vgrow2; }
@keyframes vgrow {
  0%, 18% { transform: scale(1); border-radius: 5px; }
  45%, 72% { transform: scale(1.35); border-radius: 8px; }
  92%, 100% { transform: scale(1); border-radius: 5px; }
}
@keyframes vgrow2 {
  0%, 18% { transform: scale(1); border-radius: 5px; }
  45%, 72% { transform: scale(1.35); border-radius: 8px; }
  92%, 100% { transform: scale(1); border-radius: 5px; }
}
.vslider {
  position: absolute; left: 50%; bottom: 26px; transform: translateX(-50%);
  width: 110px; height: 5px; border-radius: 3px; background: rgba(255,255,255,.16);
}
.vslider .vknob {
  position: absolute; top: 50%; left: 0; width: 14px; height: 14px;
  border-radius: 50%; background: #fff; box-shadow: 0 1px 4px rgba(0,0,0,.5);
  transform: translateY(-50%);
  animation: vknobride 7s infinite;
}
@keyframes vknobride {
  0%, 18% { left: 8px; }
  45%, 72% { left: 88px; }
  92%, 100% { left: 8px; }
}

/* 11 — reliability: a display goes away, the dock doesn't flinch */
.vscreen.blink { animation: vunplug 7.5s infinite; }
@keyframes vunplug {
  0%, 30% { opacity: 1; }
  38%, 62% { opacity: .12; }
  70%, 100% { opacity: 1; }
}
.vcheck {
  position: absolute; left: 22%; top: 26%; z-index: 7;
  width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center;
  font-size: 12px; font-weight: 800; color: #0a0c12;
  background: #3fd68b; box-shadow: 0 0 14px rgba(63,214,139,.6);
  opacity: 0; animation: vcheckpulse 7.5s infinite;
}
@keyframes vcheckpulse {
  0%, 40% { opacity: 0; transform: scale(.6); }
  48%, 60% { opacity: 1; transform: scale(1); }
  68%, 100% { opacity: 0; transform: scale(.6); }
}

/* 12 — the knock: park at the edge (nothing), tap, pull back — the glint
       answers — tap again, the dock rises */
.vig-knock .vcursor { animation: vknockcursor 8s infinite; }
@keyframes vknockcursor {
  0%, 4% { left: 74%; top: 14%; }
  16%, 26% { left: 48%; top: 86%; }   /* park: hover never reveals */
  32%, 36% { left: 48%; top: 62%; }   /* pull back — the glint lights */
  42%, 58% { left: 48%; top: 86%; }   /* the second tap */
  74%, 100% { left: 74%; top: 14%; }
}
.vglint {
  position: absolute; left: 50%; bottom: 5px; transform: translateX(-50%);
  width: 92px; height: 3px; border-radius: 2px; z-index: 4;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent), 0 0 20px rgba(79,139,255,.75);
  opacity: 0; animation: vglintshow 8s infinite;
}
@keyframes vglintshow {
  0%, 30% { opacity: 0; }
  35%, 44% { opacity: 1; }
  48%, 100% { opacity: 0; }
}
.vig-knock .vdock { left: 50%; bottom: 10px; z-index: 3; transform: translateX(-50%); animation: vknockreveal 8s infinite; }
@keyframes vknockreveal {
  0%, 44% { transform: translateX(-50%) translateY(70px); opacity: 0; }
  52%, 82% { transform: translateX(-50%) translateY(0); opacity: 1; }
  90%, 100% { transform: translateX(-50%) translateY(70px); opacity: 0; }
}
.vig-knock .vbadge { animation: vbadgepulse 8s infinite; }

/* 13 — courteous auto-hide: a click anywhere dismisses NOW; the busy edge
       waits a beat before answering a park */
.vig-hide .vcursor { animation: vhidecursor 8s infinite; }
@keyframes vhidecursor {
  0%, 6% { left: 26%; top: 30%; }
  12%, 34% { left: 28%; top: 40%; }   /* the click that dismisses */
  52%, 84% { left: 52%; top: 86%; }   /* park at the edge: a beat, then the dock */
  100% { left: 26%; top: 30%; }
}
.vripple {
  position: absolute; left: calc(28% - 7px); top: calc(40% + 2px); z-index: 5;
  width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid rgba(255,255,255,.75);
  opacity: 0; animation: vripple 8s infinite;
}
@keyframes vripple {
  0%, 12% { opacity: 0; transform: scale(.4); }
  15% { opacity: .9; transform: scale(.55); }
  24%, 100% { opacity: 0; transform: scale(1.6); }
}
.vdots {
  position: absolute; left: 50%; bottom: 36px; transform: translateX(-50%);
  display: flex; gap: 4px; z-index: 4;
  opacity: 0; animation: vdotswait 8s infinite;
}
.vdots i { width: 4px; height: 4px; border-radius: 50%; background: var(--text-dim); display: block; }
@keyframes vdotswait {
  0%, 56% { opacity: 0; }
  60%, 66% { opacity: 1; }
  70%, 100% { opacity: 0; }
}
.vig-hide .vdock { left: 50%; bottom: 10px; z-index: 3; transform: translateX(-50%); animation: vhidedock 8s infinite; }
@keyframes vhidedock {
  0%, 12% { transform: translateX(-50%) translateY(0); opacity: 1; }     /* visible */
  17%, 66% { transform: translateX(-50%) translateY(70px); opacity: 0; } /* click-away: gone NOW */
  72%, 90% { transform: translateX(-50%) translateY(0); opacity: 1; }    /* the park earns it back */
  96%, 100% { transform: translateX(-50%) translateY(70px); opacity: 0; }
}

/* 14 — the palettes' shared miniature: a glass field, letters, answer rows.
      Used by the search-apps card (typing + ring walk) and, with a picker
      menu beside it, by the open-a-file card. */
.vpal {
  position: absolute; left: 50%; top: 12%; transform: translateX(-50%);
  width: 156px; border-radius: 11px; overflow: hidden; z-index: 4;
  background: rgba(255,255,255,.10);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  border: 1px solid rgba(255,255,255,.16);
}
.vpal .vpf {
  display: flex; align-items: center; gap: 6px; height: 24px; padding: 0 8px;
  border-bottom: 1px solid rgba(255,255,255,.10);
  font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 10.5px; color: var(--text);
}
.vpal .vpmag { width: 7px; height: 7px; flex: none; position: relative;
  border: 1.4px solid var(--text-dim); border-radius: 50%; }
.vpal .vpmag::after { content: ""; position: absolute; right: -3px; bottom: -2px;
  width: 4px; height: 1.4px; border-radius: 1px; background: var(--text-dim);
  transform: rotate(45deg); }
.vpal .vpq i { font-style: normal; opacity: 0; }
.vpal .vpq .l1 { animation: vpq1 6.5s steps(1) infinite; }
.vpal .vpq .l2 { animation: vpq2 6.5s steps(1) infinite; }
@keyframes vpq1 { 0%, 11% { opacity: 0; } 12%, 88% { opacity: 1; } 89%, 100% { opacity: 0; } }
@keyframes vpq2 { 0%, 17% { opacity: 0; } 18%, 88% { opacity: 1; } 89%, 100% { opacity: 0; } }
.vpal .vpcaret { width: 1.5px; height: 11px; background: var(--accent);
  animation: vpcaret 1s steps(1) infinite; }
@keyframes vpcaret { 50% { opacity: 0; } }
.vpal .vprows { position: relative; padding: 5px; display: grid; gap: 3px; }
.vpal .vpr { display: flex; align-items: center; gap: 6px; height: 21px; padding: 0 6px;
  position: relative; z-index: 2; opacity: 0; }
.vpal .vpr .vtile { width: 13px; height: 13px; border-radius: 4px; }
.vpal .vpn { height: 5px; border-radius: 3px; background: rgba(255,255,255,.32); }
.vpal .p1 .vpn { width: 52px; }
.vpal .p2 .vpn { width: 38px; }
.vpal .p3 .vpn { width: 60px; }
.vpal .p1 { animation: vprin1 6.5s infinite; }
.vpal .p2 { animation: vprin2 6.5s infinite; }
.vpal .p3 { animation: vprin3 6.5s infinite; }
@keyframes vprin1 { 0%, 13% { opacity: 0; transform: translateY(4px); }
  16%, 86% { opacity: 1; transform: translateY(0); } 92%, 100% { opacity: 0; } }
@keyframes vprin2 { 0%, 15% { opacity: 0; transform: translateY(4px); }
  18%, 86% { opacity: 1; transform: translateY(0); } 92%, 100% { opacity: 0; } }
@keyframes vprin3 { 0%, 17% { opacity: 0; transform: translateY(4px); }
  20%, 86% { opacity: 1; transform: translateY(0); } 92%, 100% { opacity: 0; } }
.vpal .vpring { position: absolute; left: 5px; right: 5px; top: 5px; height: 21px;
  border-radius: 6px; z-index: 1;
  background: rgba(79,139,255,.18); border: 1px solid var(--accent);
  opacity: 0; animation: vpring 6.5s infinite; }
@keyframes vpring {
  0%, 22% { opacity: 0; transform: translateY(0); }
  26%, 46% { opacity: 1; transform: translateY(0); }
  54%, 86% { opacity: 1; transform: translateY(24px); }
  92%, 100% { opacity: 0; transform: translateY(24px); }
}

/* 15 — open a file: the hit carries its folder, the picker offers the apps.
      The panel parks left, its query and rows stand still (the menu is the
      story here); the picker pops beside it and settles on the second app. */
.vig-fpal .vpal { left: 7%; transform: none; width: 168px; }
/* .l1/.l2 included: their typing animations are (0,3,0) and would beat the
   bare element selector — same specificity, later in source, wins the tie */
.vig-fpal .vpq i, .vig-fpal .vpq .l1, .vig-fpal .vpq .l2 { opacity: 1; animation: none; }
.vig-fpal .vpr { opacity: 1; animation: none; transform: none; }
.vig-fpal .vpring { opacity: 1; animation: none; transform: none; }
.vpal .vpcol { display: flex; flex-direction: column; gap: 3px; justify-content: center; }
.vpal .vpp { font-size: 8.5px; line-height: 1; color: var(--text-dim); }
.vpmenu {
  position: absolute; right: 7%; top: 22%; width: 128px; z-index: 5;
  border-radius: 9px; background: rgba(24,28,40,.97); border: 1px solid var(--stroke);
  padding: 6px; display: grid; gap: 4px;
  transform-origin: left center;
  opacity: 0; animation: vpmenu 7s infinite;
}
@keyframes vpmenu {
  0%, 24% { opacity: 0; transform: scale(.92); }
  30%, 86% { opacity: 1; transform: scale(1); }
  92%, 100% { opacity: 0; transform: scale(.95); }
}
.vpmenu .vpm { display: flex; align-items: center; gap: 6px; height: 19px; padding: 0 6px;
  border-radius: 5px; }
.vpmenu .vpm .vtile { width: 11px; height: 11px; border-radius: 3.5px; flex: none; }
.vpmenu .vpm .vpn { height: 4.5px; border-radius: 3px; background: rgba(255,255,255,.32); }
.vpmenu .m1 .vpn { width: 34px; }
.vpmenu .m2 .vpn { width: 46px; }
.vpmenu .m3 .vpn { width: 30px; }
.vpmenu .ck { font-size: 8px; color: var(--text); opacity: .9; }
.vpmenu .ckbd { margin-left: auto; font-size: 8px; color: var(--text-dim);
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  background: rgba(255,255,255,.09); border: 1px solid rgba(255,255,255,.12);
  border-radius: 3.5px; padding: 0 3px; }
.vpmenu .m2 { animation: vpm2 7s steps(1) infinite; }
@keyframes vpm2 {
  0%, 55% { background: none; }
  56%, 100% { background: rgba(79,139,255,.24); }
}
/* a 168px panel and a 128px picker collide on a one-column phone card;
   shrink both and pull the anchors in — same cure as the feature pages */
@media (max-width: 560px) {
  .vig-fpal .vpal { left: 5%; width: 132px; }
  .vpmenu { right: 5%; width: 96px; }
  .vpmenu .m1 .vpn { width: 20px; }
  .vpmenu .m2 .vpn { width: 30px; }
  .vpmenu .m3 .vpn { width: 18px; }
}
/* Static tableau for the palette miniatures: field filled, rows in, ring on;
   the caret is the only transient that leaves. */
@media (prefers-reduced-motion: reduce) {
  .vpal .vpq i, .vpal .vpr, .vpal .vpring, .vpmenu { opacity: 1 !important; transform: none !important; }
  .vpal .vpcaret { display: none !important; }
}

/* trust band */
.trust { margin-top: 16px; padding: 30px 34px; display: flex; gap: 26px; align-items: center; flex-wrap: wrap; }
.trust .shield { font-size: 34px; }
.trust p { color: var(--text-dim); font-size: 15px; max-width: 720px; }
.trust b { color: var(--text); }

/* pricing */
/* Three tiers now, not two: 1 Mac, 2 Macs, and the adjustable 3-to-10 card.
   auto-fit so the row reflows to 2 then 1 without needing its own breakpoint. */
.plans {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, 330px));
  justify-content: center; gap: 18px; margin-top: 44px;
}
@media (max-width: 1080px) { .plans { grid-template-columns: repeat(auto-fit, minmax(280px, 330px)); } }
/* A column, so the three Buy buttons land on one line however tall the card
   above them gets. The third card carries a stepper the other two do not, and
   without this its button sits lower than its neighbours' — three prices that
   should be compared at a glance, offered on three different baselines. */
.plan { padding: 30px 28px; text-align: center; position: relative;
  display: flex; flex-direction: column; }
.plan > .btn { margin-top: auto; }

/* The recommended card has to LOOK recommended. A border tint alone was doing
   the work of a raised card, and losing. */
.plan.best {
  border-color: color-mix(in srgb, var(--accent) 48%, transparent);
  background: color-mix(in srgb, var(--accent) 7%, var(--surface));
  box-shadow: 0 0 70px rgba(79,139,255,.16), 0 18px 50px rgba(0,0,0,.32);
}
@media (min-width: 1081px) { .plan.best { transform: translateY(-10px); } }

/* Flat accent, not the gradient — see THE GRADIENT RULE. This is a label, and a
   label that wears a button's clothes invites a click that goes nowhere. */
.plan .badge {
  position: absolute; top: -11px; left: 50%; transform: translateX(-50%);
  font-size: 11.5px; font-weight: 700; letter-spacing: .04em; color: #fff;
  background: var(--accent);
  border-radius: 999px; padding: 4px 13px; white-space: nowrap;
  box-shadow: 0 4px 14px rgba(79,139,255,.4);
}
.plan h3 { font-size: 13px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--text-dim); }
.plan .price { font-size: 46px; font-weight: 800; letter-spacing: -0.03em; margin: 10px 0 2px; }
.plan .price small { font-size: 16px; font-weight: 600; color: var(--text-dim); }
/* .plan .per is styled with the adjustable card, further down — it belongs to
   the stepper's story, not to this block. Do not restate it here: an identical
   selector later in the file wins, and the copy up here would be dead. */
.plan ul { list-style: none; margin: 18px 0 22px; display: grid; gap: 8px; font-size: 14px; color: var(--text-dim); }
.plan ul li::before { content: "✓  "; color: var(--accent); font-weight: 700; }
.pricing-note { text-align: center; margin-top: 18px; font-size: 13.5px; color: var(--text-dim); }

/* ---------- "Designed for macOS" ----------
   It sits directly before the pricing cards on purpose: the last question a
   buyer asks themselves before looking at a number is "is this a real Mac app
   or a web page in a costume", and every claim below is a measured fact rather
   than an adjective. */

/* The drifting row of app tiles. Two IDENTICAL sets side by side, each its own
   flex box with its own trailing gap, so one set is exactly half the track and
   translating by -50% lands precisely on the seam. (Putting the gap on the
   track instead leaves half a gap unaccounted for, and the row visibly jumps
   once per loop.) Masked at both ends so it fades rather than being clipped. */
.marquee {
  position: relative; margin-top: 46px; overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 11%, #000 89%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 11%, #000 89%, transparent);
}
.marquee .track { display: flex; width: max-content; animation: appdrift 38s linear infinite; }
.marquee .set { display: flex; gap: 20px; padding-right: 20px; }
@keyframes appdrift { from { transform: translateX(0); } to { transform: translateX(-50%); } }
/* Pointing at it stops it, so a tile can actually be looked at. */
.marquee:hover .track { animation-play-state: paused; }

.marquee .app {
  flex: none; width: 64px; height: 64px; border-radius: 16px;
  display: grid; place-items: center;
  border: 1px solid rgba(255,255,255,.15);
  box-shadow: 0 10px 24px rgba(0,0,0,.36), inset 0 1px 0 rgba(255,255,255,.20);
}
.marquee .app svg {
  width: 30px; height: 30px;
  fill: none; stroke: #fff; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: .96;
}
.marquee .a1  { background: linear-gradient(160deg,#4f8bff,#2857c9); }
.marquee .a2  { background: linear-gradient(160deg,#3d4657,#232a37); }
.marquee .a3  { background: linear-gradient(160deg,#6aa9ff,#3c7ae0); }
.marquee .a4  { background: linear-gradient(160deg,#ff5d7b,#d61f45); }
.marquee .a5  { background: linear-gradient(160deg,#ff8a3d,#e05612); }
.marquee .a6  { background: linear-gradient(160deg,#4fc3ff,#1f7fd6); }
.marquee .a7  { background: linear-gradient(160deg,#ff6b6b,#c92828); }
.marquee .a8  { background: linear-gradient(160deg,#3fd68b,#159957); }
.marquee .a9  { background: linear-gradient(160deg,#a56bff,#6d2fd6); }
.marquee .a10 { background: linear-gradient(160deg,#ffcc4d,#e0951a); }

.natfacts {
  display: flex; justify-content: center; flex-wrap: wrap; gap: 12px;
  margin: 42px auto 0; padding: 0; list-style: none; max-width: 900px;
}
.natfacts li {
  display: flex; flex-direction: column; gap: 2px; align-items: center;
  padding: 13px 20px; border-radius: 14px;
  background: var(--surface); border: 1px solid var(--stroke);
}
.natfacts b { font-size: 15.5px; font-weight: 700; letter-spacing: -.01em; }
.natfacts span { font-size: 12.5px; color: var(--text-dim); }

@media (max-width: 560px) {
  .marquee .app { width: 54px; height: 54px; border-radius: 14px; }
  .marquee .app svg { width: 25px; height: 25px; }
  .marquee .set { gap: 14px; padding-right: 14px; }
  .natfacts { gap: 9px; }
  .natfacts li { flex: 1 1 100%; }
}

/* FAQ */
.faq { max-width: 720px; margin: 40px auto 0; display: grid; gap: 10px; }
details { padding: 18px 22px; }
summary { cursor: pointer; font-weight: 600; font-size: 15.5px; list-style: none; display: flex; justify-content: space-between; align-items: center; }
summary::after { content: "+"; color: var(--text-dim); font-size: 20px; transition: transform .2s; }
details[open] summary::after { transform: rotate(45deg); }
details p { margin-top: 10px; color: var(--text-dim); font-size: 14.5px; }

footer { margin-top: 100px; padding: 34px 0 44px; border-top: 1px solid var(--stroke); }
footer .container { display: flex; gap: 18px; align-items: center; flex-wrap: wrap; color: var(--text-dim); font-size: 13.5px; }
footer img { width: 26px; height: 26px; }
footer .spacer { flex: 1; }
footer a { color: var(--text-dim); }

/* ---------- feature pages ---------- */
.crumb { display: flex; justify-content: center; margin: 26px 0 0; }
.crumb a { color: var(--text-dim); font-size: 13.5px; text-decoration: none; }
.crumb a:hover { color: var(--text); }
/* header.page-hero, not .page-hero: the element also carries .container, whose
   `padding: 0 24px` shorthand zeroes the vertical padding and outranks the bare
   `header { padding: 84px 0 40px }` above. Measured before this fix: the kicker's
   top edge sat at exactly the sticky nav's bottom edge — zero air, on every
   feature page, since the day they were written. */
header.page-hero { padding: 130px 0 8px; }

/* The page name, given the same badge as a section header on the landing page.
   It used to be a 13px grey line carrying the chord as an afterthought; the
   chord now has its own frame below the deck, and this says only what page you
   are on. */
header.page-hero > .kicker {
  width: fit-content; margin-inline: auto;
  padding: 7px 16px 6px; font-size: 12.5px; letter-spacing: .16em;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 13%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
}
header.page-hero > h1 { margin-top: 20px; }
.bigscene {
  position: relative; margin: 56px auto 0; max-width: 960px;
  aspect-ratio: 16 / 9.6; border-radius: 22px; overflow: hidden;
  border: 1px solid var(--stroke);
  background: #0e131f;
  box-shadow: 0 30px 80px rgba(0,0,0,.5);
}
.scenecap { text-align: center; margin: 18px auto 0; max-width: 660px; font-size: 13.5px; color: var(--text-dim); }
.frows { max-width: 960px; margin: 8px auto 0; padding: 0 24px; }
.frow { display: grid; grid-template-columns: 1.02fr .98fr; gap: 36px; align-items: center; padding: 68px 0 0; scroll-margin-top: 84px; }
.frow.flip .ftext { order: 2; }
.frow h2 { font-size: 23px; letter-spacing: -0.015em; margin-bottom: 8px;
  text-align: left; margin-top: 0; }   /* h2, not h3: no page may skip a level */
.frow p { color: var(--text-dim); font-size: 15px; }
.frow p + p { margin-top: 10px; }
.fscene {
  position: relative; height: 280px; border-radius: 16px; overflow: hidden;
  border: 1px solid var(--stroke);
  background:
    radial-gradient(340px 200px at 70% 6%, rgba(79,139,255,.24), transparent 60%),
    radial-gradient(300px 190px at 10% 96%, rgba(122,82,255,.18), transparent 55%),
    #0f1219;
}
/* A key you could press. The old flat chip read as a code span; these read as
   hardware, which matters on a product whose whole argument is muscle memory.
   Three cues do the work: a top-lit face (the light falls from above, as it
   does on a real keyboard), a dark bottom edge with a hard 2px shadow under it
   so the cap has THICKNESS, and a soft drop shadow beneath that to lift it off
   the page. Used inline in body text and, larger, inside .chordbox. */
.keycap {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 27px; height: 27px; padding: 0 8px; margin: 0 2px;
  border-radius: 8px; font-size: 13px; font-weight: 650; color: var(--text);
  background: linear-gradient(180deg, rgba(255,255,255,.17), rgba(255,255,255,.055));
  border: 1px solid rgba(255,255,255,.17);
  border-bottom-color: rgba(0,0,0,.45);
  box-shadow: 0 2px 0 rgba(0,0,0,.42), 0 3px 7px rgba(0,0,0,.34),
              inset 0 1px 0 rgba(255,255,255,.20);
  text-shadow: 0 1px 0 rgba(0,0,0,.35);
  vertical-align: middle;
}

/* The signature chord of a feature page, framed so it reads as an exhibit
   rather than as a footnote in the kicker — which is where it used to hide,
   at 13px, beside the page name. */
.chordbox {
  display: inline-flex; flex-direction: column; align-items: center; gap: 11px;
  margin: 30px auto 0; padding: 17px 30px 19px;
  border: 1px solid var(--stroke); border-radius: 18px;
  background: rgba(255,255,255,.04);
  -webkit-backdrop-filter: blur(20px) saturate(1.25);
  backdrop-filter: blur(20px) saturate(1.25);
  box-shadow: 0 14px 40px rgba(0,0,0,.28);
}
.chordbox .clabel {
  font-size: 11.5px; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--text-dim);
}
.chordbox .keys { display: flex; align-items: center; gap: 7px; }
.chordbox .keycap {
  min-width: 42px; height: 42px; padding: 0 13px; margin: 0;
  font-size: 17px; border-radius: 11px;
  box-shadow: 0 3px 0 rgba(0,0,0,.45), 0 5px 12px rgba(0,0,0,.38),
              inset 0 1px 0 rgba(255,255,255,.22);
}
.nextcard {
  display: flex; justify-content: space-between; align-items: center; gap: 18px;
  flex-wrap: wrap;
  max-width: min(960px, calc(100% - 48px));   /* it is the only block with no gutter otherwise */
  margin: 96px auto 0; padding: 26px 30px;
}
.nextcard .nlabel { color: var(--accent); font-size: 12.5px; text-transform: uppercase; letter-spacing: .1em; font-weight: 700; }
.nextcard h3 { font-size: 20px; margin-top: 4px; }
.nextcard > a { text-decoration: none; color: var(--text); }

/* Coloured, but NOT the gradient — see THE GRADIENT RULE near the top. Reading
   on to the next feature is navigation, not a purchase, and this button sits
   two screens above a Buy button that must still win. Accent border, accent
   text, and a fill that arrives on hover: unmistakably the accent colour
   without borrowing the one signal that means money. */
.nextcard .btn {
  color: color-mix(in srgb, var(--accent) 88%, #fff);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 42%, transparent);
  transition: background .16s ease, border-color .16s ease, color .16s ease,
              transform .15s, box-shadow .15s;
}
.nextcard .btn:hover {
  color: #fff;
  background: color-mix(in srgb, var(--accent) 30%, transparent);
  border-color: color-mix(in srgb, var(--accent) 70%, transparent);
  box-shadow: 0 8px 26px rgba(79,139,255,.24);
}

/* ---------- the closing band on a feature page ----------
   A visitor who has read a whole feature page and been convinced had nowhere
   to go: the only price link on these pages was in the nav, two thousand
   pixels back up. This is the door at the end of the corridor — the trial
   first, because someone weighing $19 against an unknown is really asking
   "will this work on MY setup", and the honest answer is to go and find out. */
.closer {
  max-width: min(960px, calc(100% - 48px));
  margin: 40px auto 0; padding: 40px 30px 42px;
  text-align: center;
}
.closer h2 { font-size: clamp(1.7rem, 3.4vw, 2.2rem); text-align: center; }
.closer p { margin: 12px auto 0; max-width: 560px; color: var(--text-dim); font-size: 15.5px; }
.closer .ladder {
  display: flex; justify-content: center; flex-wrap: wrap; gap: 10px;
  margin: 24px auto 0; padding: 0; list-style: none;
}
.closer .ladder li {
  display: flex; align-items: baseline; gap: 7px;
  padding: 9px 15px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--stroke);
  font-size: 14px; color: var(--text-dim);
}
.closer .ladder b { font-size: 17px; font-weight: 750; color: var(--text); letter-spacing: -.01em; }
.closer .ladder .best { border-color: color-mix(in srgb, var(--accent) 42%, transparent);
  background: color-mix(in srgb, var(--accent) 9%, var(--surface)); }
.closer .actions { display: flex; justify-content: center; flex-wrap: wrap; gap: 12px; margin-top: 26px; }
.closer .fineprint { margin-top: 16px; font-size: 13px; }
@media (max-width: 560px) {
  .closer { padding: 32px 22px 34px; }
  .closer .ladder li { width: 100%; justify-content: center; }
  .closer .actions .btn { width: 100%; }
}

@media (max-width: 860px) {
  .grid { grid-template-columns: 1fr 1fr; }
  /* .keep survives: on feature pages "All features" is the only way back */
  nav .links a:not(.cta):not(.keep) { display: none; }
}
@media (max-width: 760px) {
  .frow { grid-template-columns: 1fr; gap: 20px; }
  .frow.flip .ftext { order: 0; }
}
@media (max-width: 560px) {
  .grid, .plans { grid-template-columns: 1fr; }
  .card.wide { grid-column: auto; }
  nav { gap: 12px; padding: 10px 14px; }
  nav .links { gap: 12px; }
  nav a { white-space: nowrap; }
  /* the video keeps its own 16:9 on phones */

  /* A section title at its 34px floor sat only 7px below the hero's h1, and two
     headings that close together stop reading as two ranks. The floor drops on
     phones; the desktop sizes are untouched. */
  section > h2 { font-size: 1.95rem; }
  section > .lead { font-size: 16px; }

  /* The phone overrides for the stepper and the assistant launcher do NOT live
     here. Both components are defined further down the file, and at equal
     specificity the later rule wins whatever the media query says — a block up
     here would simply be dead. They sit beside their own components instead. */
}
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
  html { scroll-behavior: auto; }
  /* Static tableau: with motion off, the scenes settle on their key frame
     instead of an empty stage — narrators and transients simply leave. */
  .bwin, .vghost, .vglint, .vprev, .vpop,
  .vcheck, .vglow, .vveil.dim, .vfring { opacity: 1 !important; }
  .bghost, .bdrag, .vdragw, .bcursor, .kcursor, .vcursor,
  .kchip, .vripple, .vdots, .vfiledrop,
  .kdim, .kcross, .kseg, .kcheck, .kdockL, .kdockT { display: none !important; }
}

/* ================= the assistant ==================
   Injected on every page by assets/chat.js — the styles live here so the
   widget can never drift from the rest of the design system. */
/* Glass, not gradient — see THE GRADIENT RULE above. The assistant is a helper
   standing by, not the thing to click. The accent survives in the icon alone,
   which is enough to find it and not enough to compete. */
.ds-launcher {
  position: fixed; right: 22px; bottom: 22px; z-index: 80;
  display: flex; align-items: center; gap: 9px;
  padding: 11px 17px 11px 14px; cursor: pointer;
  font: inherit; font-size: 14.5px; font-weight: 620; color: var(--text);
  background: rgba(18, 22, 34, .82);
  -webkit-backdrop-filter: blur(20px) saturate(1.3);
  backdrop-filter: blur(20px) saturate(1.3);
  border: 1px solid var(--stroke);
  border-radius: 999px; box-shadow: 0 10px 34px rgba(0,0,0,.42);
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease,
              opacity .28s ease, visibility .28s ease;
}
.ds-launcher:hover {
  transform: translateY(-2px); box-shadow: 0 14px 40px rgba(0,0,0,.5);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
.ds-launcher svg { width: 18px; height: 18px; color: var(--accent); }
.ds-launcher[hidden] { display: none; }

/* Icon only on a phone: the written label costs a third of the screen's width
   to say what the speech bubble already says. Placed here, after the component
   it overrides — a media query earlier in the file loses at equal specificity. */
@media (max-width: 560px) {
  .ds-launcher { right: 16px; bottom: 16px; padding: 13px; gap: 0; }
  .ds-launcher span { display: none; }
  .ds-launcher svg { width: 20px; height: 20px; }
}

/* The nudge: a line beside the launcher, not a panel over the page. It sits
   just above the launcher and shares its glass, so it reads as the same object
   speaking rather than a new one arriving. It fades in — nothing about this
   should startle someone mid-sentence. */
.ds-nudge {
  position: fixed; right: 22px; bottom: 78px; z-index: 79;
  display: flex; align-items: stretch; max-width: min(320px, calc(100vw - 44px));
  background: rgba(18, 22, 34, .9);
  -webkit-backdrop-filter: blur(20px) saturate(1.3);
  backdrop-filter: blur(20px) saturate(1.3);
  border: 1px solid var(--stroke); border-radius: 16px;
  box-shadow: 0 12px 34px rgba(0,0,0,.42);
  opacity: 0; transform: translateY(6px);
  transition: opacity .34s ease, transform .34s ease;
}
.ds-nudge[data-in] { opacity: 1; transform: none; }
.ds-nudge button { font: inherit; background: none; border: 0; cursor: pointer; color: var(--text); }
.ds-nudge-open {
  flex: 1; text-align: left; padding: 12px 6px 12px 15px;
  font-size: 13.5px; line-height: 1.45;
}
.ds-nudge-open:hover { color: #fff; }
.ds-nudge-x {
  flex: none; padding: 0 13px 0 8px; font-size: 19px; line-height: 1;
  color: var(--text-dim); align-self: stretch;
}
.ds-nudge-x:hover { color: var(--text); }
/* It shares the launcher's standby: in front of the pricing cards, neither
   speaks. */
.ds-launcher[data-standby] ~ .ds-nudge { opacity: 0; visibility: hidden; pointer-events: none; }
@media (max-width: 560px) { .ds-nudge { display: none; } }

/* And it stands down entirely while the pricing cards are on screen. A visitor
   reading prices is the closest they ever get to buying; a second button in the
   corner at that moment is a competing exit, not a service. chat.js sets this
   attribute from an IntersectionObserver. */
.ds-launcher[data-standby] {
  opacity: 0; visibility: hidden; transform: translateY(8px); pointer-events: none;
}

.ds-panel {
  position: fixed; right: 22px; bottom: 22px; z-index: 81;
  /* Wider than it was: a 99-character licence key is the longest thing this
     panel ever shows, and it is the one message a visitor needs to read
     without effort. */
  width: min(440px, calc(100vw - 44px));
  height: min(620px, calc(100vh - 120px));
  display: flex; flex-direction: column; overflow: hidden;
  background: rgba(16,19,28,.86);
  -webkit-backdrop-filter: blur(30px) saturate(1.4);
  backdrop-filter: blur(30px) saturate(1.4);
  border: 1px solid var(--stroke); border-radius: var(--radius);
  box-shadow: 0 30px 80px rgba(0,0,0,.6);
  animation: ds-rise .22s cubic-bezier(.22,1,.36,1);
}
.ds-panel[hidden] { display: none; }
@keyframes ds-rise { from { opacity: 0; transform: translateY(14px) scale(.98); } }
@media (prefers-reduced-motion: reduce) {
  .ds-panel { animation: none; }
  .ds-launcher { transition: none; }
}

.ds-head {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 14px 13px 17px; border-bottom: 1px solid var(--stroke);
}
.ds-head strong { font-size: 14.5px; font-weight: 650; }
.ds-head .ds-sub { font-size: 12px; color: var(--text-dim); }
.ds-close {
  margin-left: auto; width: 30px; height: 30px; display: grid; place-items: center;
  border: 0; border-radius: 9px; cursor: pointer;
  background: var(--surface); color: var(--text-dim); font-size: 17px; line-height: 1;
}
.ds-close:hover { background: var(--surface-strong); color: var(--text); }

.ds-log {
  flex: 1; min-width: 0; overflow-y: auto; overflow-x: hidden;
  padding: 16px 17px; display: flex; flex-direction: column; gap: 13px;
}
/* overflow-wrap: anywhere is load-bearing, not polish. A licence key is 99
   characters with no space, hyphen or slash a browser may break on, so without
   this the bubble's min-content width is the whole key — it pushed the log 154px
   past its own edge and shifted the panel sideways. `anywhere` also lets the
   bubble SHRINK below that token, which `break-word` alone does not. */
.ds-msg {
  font-size: 14px; line-height: 1.62; max-width: 92%;
  min-width: 0; overflow-wrap: anywhere;
}
.ds-msg.user {
  align-self: flex-end; padding: 9px 13px; border-radius: 14px 14px 4px 14px;
  background: linear-gradient(135deg, var(--accent), #6f5bff); color: #fff;
}
.ds-msg.bot { align-self: flex-start; color: var(--text); }
.ds-msg.bot code {
  font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12.5px;
  background: rgba(255,255,255,.1); padding: 1px 5px; border-radius: 5px;
}
.ds-msg.err { align-self: flex-start; color: var(--accent-2); font-size: 13px; }
.ds-dots { display: inline-flex; gap: 4px; align-items: center; height: 20px; }
.ds-dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--text-dim); animation: ds-blink 1.3s infinite; }
.ds-dots i:nth-child(2) { animation-delay: .18s; }
.ds-dots i:nth-child(3) { animation-delay: .36s; }
@keyframes ds-blink { 0%,60%,100% { opacity: .25; } 30% { opacity: 1; } }

.ds-seeds { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 2px; }
.ds-seeds button {
  font: inherit; font-size: 12.5px; color: var(--text-dim); cursor: pointer;
  padding: 7px 12px; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--stroke);
}
.ds-seeds button:hover { background: var(--surface-strong); color: var(--text); }

/* The extra bottom padding replaces what the removed footnote used to give:
   the form is now the panel's last row, and against an 18px corner radius a
   12px gutter leaves the text field visibly crowding the rounded edge. */
.ds-form { display: flex; gap: 8px; padding: 12px 13px 15px; border-top: 1px solid var(--stroke); }
.ds-form textarea {
  flex: 1; resize: none; font: inherit; font-size: 14px; color: var(--text);
  background: var(--surface); border: 1px solid var(--stroke); border-radius: 12px;
  padding: 10px 12px; max-height: 96px; line-height: 1.5;
}
.ds-form textarea:focus { outline: none; border-color: var(--accent); }
.ds-form button {
  width: 40px; border: 0; border-radius: 12px; cursor: pointer; color: #fff;
  background: linear-gradient(135deg, var(--accent), #6f5bff);
}
.ds-form button:disabled { opacity: .4; cursor: default; }
/* .ds-note held the footnote under the panel — who answers, where the text
   goes, and the fallback address. Removed by Fred's call, pending a rework of
   the assistant; its styles go with it rather than sitting here unused. The
   privacy policy still carries the disclosure in full, under "The assistant". */

/* ---------- adjustable pricing card ---------- */
/* The per-Mac line is the argument, not a footnote. The ladder only sells if a
   buyer watches this number FALL while the total rises — a total that climbs
   reads as a penalty, a unit price that drops reads as a gain. So it gets the
   accent and enough size to be read on the way past. */
.plan .per {
  margin-top: -4px; margin-bottom: 4px;
  font-size: 14.5px; font-weight: 650; color: var(--accent);
}
.plan .per.bump { animation: ds-bump .42s cubic-bezier(.22,1,.36,1); }
@keyframes ds-bump {
  0% { transform: scale(1.14); color: #fff; }
  100% { transform: scale(1); }
}
.stepper {
  display: flex; align-items: center; gap: 10px; margin: 12px 0 14px;
  padding: 7px 8px; border: 1px solid var(--stroke); border-radius: 999px;
  background: rgba(255,255,255,.04);
}
/* A quantity control, not a call to action — see THE GRADIENT RULE. These two
   sat in the same gradient as the Buy button directly beneath them, so the card
   offered three equally loud things to press. */
.stepper button {
  flex: 0 0 30px; height: 30px; border-radius: 50%; cursor: pointer;
  font: inherit; font-size: 18px; line-height: 1; color: var(--text);
  background: var(--surface-strong); border: 1px solid var(--stroke);
  transition: transform .12s ease, opacity .12s ease, background .14s ease,
              border-color .14s ease, color .14s ease;
}
.stepper button:hover:not(:disabled) {
  transform: scale(1.09); color: #fff;
  background: color-mix(in srgb, var(--accent) 30%, transparent);
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
}
.stepper button:disabled { opacity: .28; cursor: default; }
.stepper-hint {
  flex: 1; text-align: center; font-size: 11.5px; line-height: 1.3;
  color: var(--text-dim);
}

/* This stepper is the pricing card's whole mechanic, and 30px is a small thing
   to ask a thumb to hit. It grows where there is no mouse to be precise with.
   Placed after the component, for the same reason as the launcher above. */
@media (max-width: 560px) {
  .stepper { padding: 8px 9px; }
  .stepper button { flex: 0 0 40px; height: 40px; font-size: 20px; }
  .stepper-hint { font-size: 12px; }
}
@media (prefers-reduced-motion: reduce) {
  .plan .per.bump { animation: none; }
  .stepper button:hover:not(:disabled) { transform: none; }
}

/* ---------- legal pages ---------- */
.legal { max-width: 760px; margin: 0 auto; padding: 40px 24px 90px; }
.legal h1 { font-size: 34px; letter-spacing: -.02em; margin-bottom: 8px; }
.legal .updated { color: var(--text-dim); font-size: 13.5px; margin-bottom: 34px; }
.legal h2 {
  font-size: 19px; margin: 38px 0 12px; padding-top: 24px;
  border-top: 1px solid var(--stroke);
}
.legal h2:first-of-type { border-top: 0; padding-top: 0; }
.legal h3 { font-size: 15.5px; margin: 22px 0 8px; }
.legal p, .legal li { color: #c6ccd9; font-size: 15px; line-height: 1.72; }
.legal p { margin-bottom: 14px; }
.legal ul { margin: 0 0 16px 20px; }
.legal li { margin-bottom: 7px; }
.legal strong { color: var(--text); font-weight: 640; }
.legal a { color: var(--accent); }
.legal .callout {
  background: var(--surface); border: 1px solid var(--stroke);
  border-left: 3px solid var(--accent);
  border-radius: 12px; padding: 15px 18px; margin: 20px 0;
}
.legal .callout p:last-child { margin-bottom: 0; }
footer .container a + a { margin-left: 16px; }
