/* ==========================================================================
   Great Scott Labs — site stylesheet
   Colour tokens are transcribed from the palette doc in C:\Dev-VoiceType
   (still named VoiceType_Palette.md — the Dev folder has not been renamed).
   If this file and Theme.cs disagree, Theme.cs wins.
   Lickety and Great Scott Labs share this one palette, so both logo sets
   drop in without a single new colour.
   Change a colour ONLY in the two :root blocks below.
   ========================================================================== */

/* --- Dark (default) ----------------------------------------------------- */
:root {
  --paper:        #11141C;
  --rail:         #1A1F2B;
  --surface:      #1B202B;
  --zebra:        #222836;
  --rail-selected:#2E3850;
  --ink:          #EDF1FA;
  --muted:        #A2ABC2;
  --faint:        #6E7893;
  --line:         #2C3444;
  --accent:       #5B8CFF;
  --on-accent:    #0A1024;
  --warn:         #E08A84;

  /* Semantic — deliberately NOT themed. Same values in both palettes.
     Red means recording. It must not become blue because the theme changed. */
  --sig-recording:   #E24B4A;
  --sig-transcribing:#EF9F27;
  --sig-done:        #1D9E75;

  --shadow: 0 1px 2px rgba(0,0,0,.4), 0 12px 40px rgba(0,0,0,.35);
}

/* --- Light --------------------------------------------------------------- */
[data-theme="light"] {
  --paper:        #DDE3F0;
  --rail:         #CBD5EA;
  --surface:      #F2F5FB;
  --zebra:        #E7ECF7;
  --rail-selected:#B6C4E2;
  --ink:          #0A1944;
  --muted:        #4E5878;
  --faint:        #7A83A0;
  --line:         #B9C3DB;
  --accent:       #1C3FD1;
  --on-accent:    #FFFFFF;
  --warn:         #A6403A;

  --shadow: 0 1px 2px rgba(10,25,68,.08), 0 12px 40px rgba(10,25,68,.10);
}

/* --- Type ---------------------------------------------------------------- */
:root {
  --display: "Poppins", "Segoe UI", system-ui, sans-serif;  /* matches the MP4s */
  --body:    "Inter", "Segoe UI", system-ui, sans-serif;
  --mono:    "IBM Plex Mono", "Cascadia Mono", Consolas, monospace;

  --wrap: 1180px;
  --prose: 64ch;
}

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

html { scroll-behavior: smooth; }

/* The paper lives on <html>, not <body>. body::before is the page-depth
   layer (see "Page depth" below) and sits at z-index -1, which is BEHIND
   body's own background — put the colour on body and the layer is
   invisible. It is done in CSS rather than as a <div> because 51 of the 68
   pages are generated, and anything added to their markup is wiped by the
   next build. */
html { background: var(--paper); }
body {
  margin: 0;
  background: transparent;
  color: var(--ink);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

img, video { max-width: 100%; display: block; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: 28px; }
.prose { max-width: var(--prose); }

/* ==========================================================================
   Type scale
   ========================================================================== */
h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.02;
  margin: 0;
  text-wrap: balance;
}

h1 { font-size: clamp(2.6rem, 6.2vw, 4.6rem); }
h2 { font-size: clamp(1.9rem, 3.8vw, 2.9rem); line-height: 1.06; }
h3 { font-size: clamp(1.15rem, 2vw, 1.4rem); line-height: 1.2; letter-spacing: -0.01em; }

p { margin: 0 0 1.15em; }
p:last-child { margin-bottom: 0; }

.lede { font-size: clamp(1.1rem, 1.6vw, 1.3rem); color: var(--muted); line-height: 1.55; }

/* The tracked-out eyebrow used in the animations. */
.eyebrow {
  font-family: var(--body);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 18px;
}

.accent { color: var(--accent); }
.dim    { color: var(--muted); }
.faint  { color: var(--faint); }
.good   { color: var(--sig-done); }
.bad    { color: var(--warn); }

code, .mono { font-family: var(--mono); font-size: 0.92em; }

/* ==========================================================================
   SIGNATURE — the caret.
   The product's whole job is putting text where the cursor is, so the caret
   is the section marker instead of generic 01 / 02 / 03 numbering.
   It blinks in exactly one place (the hero demo) and is static everywhere else.
   ========================================================================== */
.caret-head { display: flex; align-items: flex-start; gap: 16px; }
.caret-head::before {
  content: "";
  flex: 0 0 3px;
  width: 3px;
  height: 1.05em;
  margin-top: 0.06em;
  background: var(--accent);
  border-radius: 1px;
}

@keyframes blink { 0%, 48% { opacity: 1; } 52%, 100% { opacity: 0; } }
.caret {
  display: inline-block;
  width: 2px;
  height: 1.05em;
  vertical-align: text-bottom;
  background: var(--accent);
  animation: blink 1.06s step-end infinite;
}

/* ==========================================================================
   Header / footer
   ========================================================================== */
/* Every in-page link on the site lands under the sticky header without
   this. The header is 66px plus its border, so a target with no scroll
   margin arrives with its eyebrow and the top of its heading hidden — true
   of #buy and #try on all twelve Lickety pages and #how on the home page,
   not just the #features link that exposed it. 84px leaves a little air
   above the heading rather than butting it against the header. */
section[id], h2[id], h3[id] { scroll-margin-top: 84px; }

.site-head {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
}
.site-head .wrap {
  display: flex; align-items: center; gap: 26px;
  height: 66px;
}
.brand {
  font-family: var(--display); font-weight: 700; font-size: 1.02rem;
  letter-spacing: -0.01em; color: var(--ink); white-space: nowrap;
}
.brand:hover { text-decoration: none; color: var(--accent); }
.nav { display: flex; gap: 22px; margin-left: auto; align-items: center; }
.nav a { color: var(--muted); font-size: 0.9rem; font-weight: 500; }
.nav a:hover, .nav a[aria-current="page"] { color: var(--ink); text-decoration: none; }

.theme-toggle {
  background: var(--rail); color: var(--muted);
  border: 1px solid var(--line); border-radius: 7px;
  font: inherit; font-size: 0.78rem; font-weight: 600;
  padding: 6px 11px; cursor: pointer; white-space: nowrap;
}
.theme-toggle:hover { background: var(--rail-selected); color: var(--ink); }

.site-foot {
  border-top: 1px solid var(--line);
  margin-top: 120px; padding: 56px 0 72px;
  color: var(--faint); font-size: 0.88rem;
}
.foot-cols { display: flex; flex-wrap: wrap; gap: 56px 80px; margin-bottom: 44px; }
.foot-cols h4 {
  font-family: var(--body); font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--faint);
  margin-bottom: 14px;
}
.foot-cols ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
.foot-cols a { color: var(--muted); }

/* ==========================================================================
   Sections & rhythm
   ========================================================================== */
section { padding-block: clamp(60px, 8vw, 108px); }
section.tight { padding-block: clamp(40px, 5vw, 64px); }
.rule { height: 1px; background: var(--line); border: 0; margin: 0; }

/* overflow-x: clip rather than a per-breakpoint fudge on the lit stage
   behind the pitch card. That stage is deliberately wider than its column so
   the lamps have somewhere to hang, and at some widths its right edge lands
   a few pixels past the viewport. Clipping the section is the fix that
   cannot come back at a width nobody tested; `clip` rather than `hidden`
   because `hidden` would make this a scroll container. */
.hero { padding-top: clamp(56px, 9vw, 104px); padding-bottom: clamp(48px, 6vw, 76px);
        overflow-x: clip; overflow-y: visible; }
.hero h1 { margin-bottom: 22px; }
.hero .lede { max-width: 56ch; }

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-top: 34px; }
.btn {
  font-family: var(--body); font-weight: 600; font-size: 0.95rem;
  padding: 13px 24px; border-radius: 9px; border: 1px solid transparent;
  display: inline-flex; align-items: center; gap: 9px; cursor: pointer;
}
.btn:hover { text-decoration: none; }
.btn-primary { background: var(--accent); color: var(--on-accent); }
.btn-primary:hover { filter: brightness(1.1); }
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn-ghost:hover { border-color: var(--accent); color: var(--accent); }
.btn-note { color: var(--faint); font-size: 0.85rem; }

/* A control that is not a link, for an action that is not available yet.
   Written as a <span>, not an <a href="#">, on purpose: a dead anchor is a
   link that goes nowhere, which is worse than no link — it reads as broken
   rather than as "not yet", and a payment-processor reviewer clicking one
   writes back. aria-disabled tells a screen reader the same thing the muted
   colour tells everyone else. Pair it with one line of plain text saying
   when it opens; a greyed button with no explanation is its own small
   failure. */
.btn-soon {
  background: transparent; color: var(--faint);
  border-color: var(--line); border-style: dashed;
  cursor: default;
}
.btn-soon:hover { color: var(--faint); border-color: var(--line); }

/* The panel that explains what a downloaded tool actually is. Fixed to the
   bottom corner rather than inserted into the page: the downloads list is a
   grid and putting a row inside it would break the layout, and the tool
   pages would need a second variant. One banner serves both.

   It sits bottom-LEFT. Bottom-right is where browsers put their own download
   shelf on some platforms, and a panel that lands on top of the thing it is
   describing is worse than no panel. */
.dl-note {
  position: fixed; left: 20px; bottom: 20px; z-index: 60;
  width: min(380px, calc(100vw - 40px));
  padding: 20px 22px 18px;
  background: var(--surface); color: var(--muted);
  border: 1px solid var(--line); border-radius: 13px;
  box-shadow: var(--shadow);
  font-size: .9rem; line-height: 1.55;
  opacity: 0; visibility: hidden; transform: translateY(10px);
  transition: opacity .22s ease, transform .22s ease, visibility .22s;
}
.dl-note.is-up { opacity: 1; visibility: visible; transform: none; }
.dl-note h4 { font-size: 1rem; color: var(--ink); margin: 0 0 8px; }
.dl-note p { margin: 0 0 12px; }
.dl-note ol { margin: 0; padding-left: 20px; }
.dl-note li { margin-bottom: 9px; }
.dl-note li:last-child { margin-bottom: 0; }
.dl-note b { color: var(--ink); }
.dl-note-x {
  position: absolute; top: 10px; right: 12px;
  background: none; border: 0; cursor: pointer;
  color: var(--faint); font-size: 1.3rem; line-height: 1;
  padding: 4px 6px;
}
.dl-note-x:hover { color: var(--ink); }
@media (prefers-reduced-motion: reduce) {
  .dl-note { transition: none; transform: none; }
}

/* ==========================================================================
   MEDIA BANDS — where the animations live.
   16:9 to match the MP4s exactly, so nothing is letterboxed or cropped.
   ========================================================================== */
.band { margin-block: clamp(40px, 6vw, 72px); }
.band-full { max-width: 1360px; margin-inline: auto; padding-inline: 28px; }

.media {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--rail);
  border: 1px solid var(--line);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: var(--shadow);
}
.media img, .media video { width: 100%; height: 100%; object-fit: cover; }

/* The slot label. Delete these when the real MP4 goes in. */
.slot {
  position: absolute; left: 14px; bottom: 14px; z-index: 3;
  background: color-mix(in srgb, var(--paper) 82%, transparent);
  backdrop-filter: blur(8px);
  border: 1px solid var(--line); border-radius: 8px;
  padding: 8px 12px;
  font-family: var(--mono); font-size: 0.7rem; line-height: 1.5;
  color: var(--muted);
}
.slot b { color: var(--accent); font-weight: 600; }

.caption {
  margin-top: 14px; color: var(--faint); font-size: 0.85rem;
  max-width: 70ch;
}

/* ==========================================================================
   Cards & grids
   ========================================================================== */
.grid { display: grid; gap: 20px; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(258px, 1fr)); }

/* Fixed-count rows. auto-fit decides the column count from the available
   width, which on the home page produced an orphan: five cluster cards broke
   4 + 1 and four rule cards broke 3 + 1. Where the card count is known and
   the whole set is meant to read as one row, say so and step it down
   deliberately instead. */
.grid-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 16px; }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 18px; }
/* An explicit three. `.grid-3` is auto-fit and resolves to four columns at
   the full wrap width, which turns nine cards into 4 + 4 + 1 — the same
   orphan BL-019 fixed on the home page. The Lickety feature grid has nine
   cards and wants 3 x 3. */
.grid-3x { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }

/* Narrower cards need the padding and type to come in with them, or the
   text column inside gets too thin to read. */
.grid-5 .cluster { padding: 20px 18px 22px; }
.grid-5 .cluster h3 { font-size: 1.02rem; }
.grid-5 .cluster p { font-size: .86rem; }
.grid-4 .card { padding: 22px 22px 24px; }

@media (max-width: 1120px) { .grid-5 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 900px)  { .grid-4, .grid-3x { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .grid-5 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .grid-5, .grid-4, .grid-3x { grid-template-columns: minmax(0, 1fr); } }

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 13px;
  padding: 26px 26px 28px;
}
.card h3 { margin-bottom: 10px; }
.card p { color: var(--muted); font-size: 0.95rem; margin-bottom: 0; }
a.card { color: inherit; display: block; transition: border-color .15s, transform .15s; }
a.card:hover { text-decoration: none; border-color: var(--accent); transform: translateY(-2px); }
a.card .more { color: var(--accent); font-size: 0.88rem; font-weight: 600; margin-top: 14px; display: block; }

/* ==========================================================================
   The typed-text demo (hero signature)
   ========================================================================== */
.demo {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 13px;
  box-shadow: var(--shadow);
  overflow: hidden;
  max-width: 640px;
}
.demo-bar {
  display: flex; align-items: center; gap: 8px;
  padding: 11px 15px;
  background: var(--rail);
  border-bottom: 1px solid var(--line);
}
.demo-bar span { width: 9px; height: 9px; border-radius: 50%; background: var(--line); }
.demo-bar .title {
  width: auto; height: auto; border-radius: 0; background: none;
  margin-left: 8px; font-size: 0.78rem; color: var(--faint);
}
.demo-body {
  padding: 26px 24px 30px;
  font-family: var(--mono); font-size: 1rem; line-height: 1.75;
  min-height: 168px; color: var(--ink);
}
.demo-foot {
  display: flex; align-items: center; gap: 10px;
  padding: 13px 20px;
  border-top: 1px solid var(--line);
  background: var(--rail);
  font-size: 0.82rem; color: var(--muted);
}
.dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--sig-recording);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--sig-recording) 60%, transparent);
  animation: pulse 1.7s ease-out infinite;
}
@keyframes pulse {
  70%  { box-shadow: 0 0 0 9px color-mix(in srgb, var(--sig-recording) 0%, transparent); }
  100% { box-shadow: 0 0 0 0  color-mix(in srgb, var(--sig-recording) 0%, transparent); }
}
kbd {
  font-family: var(--mono); font-size: 0.78rem;
  background: var(--zebra); border: 1px solid var(--line);
  border-bottom-width: 2px; border-radius: 5px;
  padding: 2px 7px; color: var(--ink);
}

/* ==========================================================================
   Tables
   ========================================================================== */
table { width: 100%; border-collapse: collapse; font-size: 0.94rem; }
th, td { text-align: left; padding: 13px 16px; border-bottom: 1px solid var(--line); vertical-align: top; }
thead th {
  background: var(--rail);
  font-family: var(--body); font-size: 0.71rem; font-weight: 700;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--muted);
  border-bottom: 1px solid var(--line);
}
tbody tr:nth-child(even) { background: var(--zebra); }
td strong { color: var(--ink); }
.table-wrap {
  border: 1px solid var(--line); border-radius: 12px; overflow: hidden;
  overflow-x: auto;
}

/* ==========================================================================
   Small parts
   ========================================================================== */
.chip {
  display: inline-block;
  font-family: var(--mono); font-size: 0.8rem;
  background: var(--zebra); border: 1px solid var(--line);
  border-radius: 6px; padding: 4px 10px; margin: 0 6px 8px 0;
  color: var(--ink);
}
.chip.miss { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 35%, var(--line)); }
.chip.hit  { color: var(--sig-done); border-color: color-mix(in srgb, var(--sig-done) 35%, var(--line)); }

/* Two prices, the same size. The regular price used to sit inside the unit
   line at about a quarter the height, in the faint colour, struck through —
   small enough that the discount had nothing to measure itself against. An
   anchor nobody notices is not an anchor. Colour carries the hierarchy now,
   not size.

   It is also NOT struck through any more. $99 is what the price BECOMES, not
   what it was; a line through it says "was", which is both wrong and the
   permanent-fake-discount trick this brand argues against two sections up. */
.pricetag {
  display: inline-flex; align-items: flex-end; gap: 36px; flex-wrap: wrap;
  font-family: var(--display); font-weight: 700;
}
.pricecol { display: flex; flex-direction: column; gap: 6px; }
.pricetag .num {
  font-size: clamp(2.7rem, 5.6vw, 3.8rem); letter-spacing: -0.03em; line-height: 1;
}
.pricetag .lab {
  font-family: var(--body); font-weight: 500; font-size: .92rem; color: var(--muted);
}
.pricetag .then .num { color: var(--muted); }
.pricetag .then .lab { color: var(--faint); }
.pricetag .then[hidden] { display: none; }

.claim-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; }
.claim-list li { display: flex; gap: 13px; align-items: flex-start; color: var(--muted); }
.claim-list li::before {
  content: ""; flex: 0 0 3px; width: 3px; height: 1.05em; margin-top: 0.28em;
  background: var(--accent); border-radius: 1px;
}
.claim-list b { color: var(--ink); font-weight: 600; }

.next {
  display: flex; flex-wrap: wrap; gap: 12px 26px; align-items: center;
  padding: 24px 26px;
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px;
}
.next span { color: var(--faint); font-size: 0.85rem; }

/* A visible note to Paul in the mockup only. Delete before publishing. */
.mock-note {
  background: color-mix(in srgb, var(--accent) 9%, var(--surface));
  border: 1px dashed color-mix(in srgb, var(--accent) 45%, var(--line));
  border-radius: 10px; padding: 16px 20px;
  font-size: 0.86rem; color: var(--muted);
}
.mock-note b { color: var(--accent); }

/* ==========================================================================
   Responsive + accessibility floor
   ========================================================================== */
@media (max-width: 720px) {
  body { font-size: 16px; }
  .wrap { padding-inline: 20px; }
  .nav { gap: 14px; }
  .nav a.hide-sm { display: none; }
  .site-head .wrap { height: 60px; }
  .foot-cols { gap: 34px 44px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  html { scroll-behavior: auto; }
}

/* ==========================================================================
   TOOLS — added when the 44-tool catalog joined the site plan.
   The tool page is a TEMPLATE: 44 pages come off it, so everything here is
   a reusable component, not a one-off layout.
   ========================================================================== */

/* Cluster grid on /tools/ */
.cluster {
  display: block;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 13px;
  padding: 22px 24px 24px;
  color: inherit;
  transition: border-color .15s, transform .15s;
}
.cluster:hover { text-decoration: none; border-color: var(--accent); transform: translateY(-2px); }
.cluster h3 { font-size: 1.1rem; margin-bottom: 6px; }
.cluster .count {
  font-family: var(--mono); font-size: .74rem; color: var(--faint);
  display: block; margin-bottom: 12px;
}
.cluster p { color: var(--muted); font-size: .9rem; }

/* Flat tool list — used on cluster pages and in the /tools/ index */
.toollist { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.toollist li { border-bottom: 1px solid var(--line); }
.toollist a {
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  padding: 15px 4px; color: var(--ink);
}
.toollist a:hover { text-decoration: none; background: var(--zebra); }
.toollist .name { font-weight: 600; font-size: .98rem; }
.toollist .desc { color: var(--muted); font-size: .88rem; flex: 1 1 260px; }
.toollist .go { color: var(--accent); font-size: .85rem; font-weight: 600; margin-left: auto; }

/* Breadcrumb — every tool page is a landing page, so it must always be
   possible to get up and out from a cold arrival. */
.crumbs {
  font-size: .82rem; color: var(--faint);
  padding-top: 26px; display: flex; gap: 8px; flex-wrap: wrap;
}
.crumbs a { color: var(--muted); }
.crumbs span { color: var(--faint); }

/* The live tool itself, embedded in the page */
.toolframe {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 13px;
  box-shadow: var(--shadow);
  overflow: hidden;
  margin-top: 30px;
}
.toolframe-head {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 13px 20px; background: var(--rail); border-bottom: 1px solid var(--line);
  font-size: .82rem; color: var(--muted);
}
.toolframe-head .badge {
  font-family: var(--mono); font-size: .7rem; color: var(--sig-done);
  border: 1px solid color-mix(in srgb, var(--sig-done) 35%, var(--line));
  border-radius: 5px; padding: 2px 8px;
}
.toolframe-body {
  min-height: 340px; padding: 40px 24px;
  display: grid; place-items: center; text-align: center;
}
.dropzone {
  border: 2px dashed var(--line); border-radius: 11px;
  padding: 46px 34px; width: 100%; max-width: 520px;
  color: var(--muted);
}
.dropzone strong { display: block; color: var(--ink); font-size: 1.05rem; margin-bottom: 6px; }

/* FAQ block — this is the help corpus rendered visibly, and it is what
   makes a tool page substantial instead of thin. */
.faq { border-top: 1px solid var(--line); }
.faq details { border-bottom: 1px solid var(--line); }
.faq summary {
  cursor: pointer; list-style: none;
  padding: 17px 4px; font-weight: 600; font-size: .98rem;
  display: flex; align-items: center; gap: 14px;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::before {
  content: ""; flex: 0 0 3px; width: 3px; height: 1.05em;
  background: var(--accent); border-radius: 1px;
}
.faq summary:hover { color: var(--accent); }
.faq details p { padding: 0 4px 20px 21px; color: var(--muted); font-size: .94rem; }

/* Funnel card — ONE per tool page, tasteful, the deliberate inverse of the
   ad-choked pages these tools compete with. Refers to paid products
   descriptively, never by working name. */
.funnel {
  display: flex; gap: 20px; flex-wrap: wrap; align-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: 11px;
  padding: 22px 26px;
}
.funnel h3 { font-size: 1.05rem; margin-bottom: 6px; }
.funnel p { color: var(--muted); font-size: .92rem; margin: 0; }
.funnel .body { flex: 1 1 320px; }

/* Bundle strip — the $5–9 offline bundle pitch. Quiet, footer-adjacent,
   never a modal and never a nag. */
.bundle {
  border: 1px dashed var(--line); border-radius: 11px;
  padding: 20px 24px; display: flex; gap: 18px; flex-wrap: wrap;
  align-items: center; font-size: .92rem; color: var(--muted);
}
.bundle b { color: var(--ink); }

/* The embedded tool. An iframe rather than inlined markup, deliberately:
   each tool carries its own complete stylesheet and its own data-theme
   handling, so inlining 44 of them into the site shell means 44 chances for
   two palettes to fight. The frame keeps them isolated. */
.toolembed {
  display: block;
  width: 100%;
  min-height: 620px;
  border: 0;
  background: var(--surface);
}

/* ==========================================================================
   LAUNCH PRICE.
   Deliberately understated. No countdown clock, no pulsing badge, no "only
   3 left" — this brand's whole argument is that it doesn't do tricks, and a
   fake-urgency widget would undo more trust than the $20 is worth. A stated
   date is verifiable; a ticking timer is theatre.
   ========================================================================== */
.launch {
  display: inline-flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  border: 1px solid color-mix(in srgb, var(--sig-done) 35%, var(--line));
  border-radius: 8px;
  padding: 7px 13px;
  font-size: .85rem;
  color: var(--muted);
  margin-bottom: 18px;
}
.launch b { color: var(--sig-done); font-weight: 600; }
.launch[hidden] { display: none; }

/* ==========================================================================
   BRAND MARKS — Lickety and Great Scott Labs.
   Each logo ships as two files: _Light for a light background, _Dark for a
   dark one. Both are in the markup and CSS shows the right one, so the
   theme toggle swaps them with no JavaScript and no flash.
   Class names say which BACKGROUND the file is for, not which file it is.
   ========================================================================== */
.brandmark { display: inline-block; line-height: 0; }
.brandmark img { display: block; width: 100%; height: auto; }
.brandmark .on-light { display: none; }
.brandmark .on-dark  { display: block; }
[data-theme="light"] .brandmark .on-light { display: block; }
[data-theme="light"] .brandmark .on-dark  { display: none; }

/* Header brand. The GSL lockup is 5.18:1, which is exactly the shape a
   header wants. The text name stays in the markup for screen readers. */
.brand-lockup { width: 188px; }
.brand-lockup + .vh { position: absolute; }
@media (max-width: 480px) { .brand-lockup { width: 150px; } }

/* Visually hidden, still read aloud. */
.vh {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* Product logo above a page heading. Sized by width; the Lickety logo is
   4.44:1 so 300px gives a ~68px tall mark — present without shouting. */
.product-logo { width: 300px; margin-bottom: 26px; }
@media (max-width: 600px) { .product-logo { width: 230px; } }

/* ==========================================================================
   HOUSE CARD — the Lickety / photo-app placement on tool pages.
   This is the most dangerous component on the site. These tools win because
   they are the opposite of an ad-choked converter page, so a placement that
   READS as an ad spends the exact credibility the tools earn.
   The rules it is built to:
     · below the tool and the FAQ, never above, never sticky, never a modal
     · no animation, no countdown, no close button (a close button admits
       it is an intrusion)
     · the site's own card language — same radius, same border, same type
     · the logo is the only loud element; the copy is plain and honest
   ========================================================================== */
.housead {
  display: flex; gap: 22px; align-items: center; flex-wrap: wrap;
  background: color-mix(in srgb, var(--accent) 5%, var(--surface));
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: 11px;
  padding: 22px 26px;
}
.housead .logo { flex: 0 0 auto; width: 190px; }
.housead .body { flex: 1 1 260px; min-width: 0; }
/* Says what the block is. Every page carrying a house ad also carries a
   footer promising no ads, and a reader is entitled to ask what this is
   then. Naming it answers that before it is asked. Quiet on purpose — it is
   a label, not a headline. */
.housead-tag {
  font-family: var(--body);
  font-size: .68rem; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 7px;
}
.housead h3 {
  font-size: 1.05rem; margin-bottom: 6px;
  font-family: var(--display); letter-spacing: -0.01em;
}
.housead p { color: var(--muted); font-size: .92rem; margin: 0; }
.housead .price {
  display: block; margin-top: 10px;
  font-family: var(--mono); font-size: .8rem; color: var(--faint);
}
.housead .price b { color: var(--accent); font-weight: 600; }
.housead .cta { flex: 0 0 auto; margin-left: auto; }

/* The pair, for the image tools: Lickety and the photo app side by side.
   Below 820px they stack full width rather than shrinking to nothing. */
.housead-pair { display: grid; gap: 18px; grid-template-columns: 1fr 1fr; }
@media (max-width: 820px) { .housead-pair { grid-template-columns: 1fr; } }

/* In a pair there is half the width, so the logo goes above the copy and
   the button returns to the normal flow. Column + auto top margin on the
   button keeps the two CTAs on the same line however unequal the copy is —
   two buttons at two heights is the detail that makes a pair look unfinished. */
.housead-pair .housead {
  display: flex; flex-direction: column; align-items: stretch;
  gap: 0; padding: 22px 24px;
}
.housead-pair .housead .logo { width: 160px; margin-bottom: 16px; }
/* A lockup with a tagline under the word is taller per unit of width. Matched
   on width it dwarfs the one next to it, so it is matched on height instead. */
.housead .logo-tall { width: 150px; }
.housead-pair .housead .logo-tall { width: 126px; }
.housead-pair .housead .body { flex: 1 1 auto; }
.housead-pair .housead .cta { margin: 18px auto 0 0; }

/* A product with no logo yet. A typographic lockup in the brand's own
   voice, so it sits beside a real logo without looking broken. */
.wordlogo { display: block; margin-bottom: 16px; }
.wordlogo .name {
  font-family: var(--display); font-weight: 700;
  font-size: 1.45rem; letter-spacing: -0.02em; color: var(--ink);
  display: block; line-height: 1;
}
.wordlogo .kicker {
  font-size: .68rem; font-weight: 600; letter-spacing: .18em;
  text-transform: uppercase; color: var(--accent);
  display: block; margin-top: 7px;
}
.soon {
  display: inline-block; margin-left: 9px; vertical-align: 3px;
  font-family: var(--body); font-size: .62rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--faint); border: 1px solid var(--line);
  border-radius: 4px; padding: 2px 7px;
}

/* ==========================================================================
   LOGO STING — the opening animation on the home page.
   Shown ONCE per visitor and then never again (localStorage), skippable on
   any key, click or scroll, and never shown at all to someone who asks for
   reduced motion. The page is fully rendered underneath the whole time, so
   nothing is actually waiting on it.
   ========================================================================== */
.sting {
  position: fixed; inset: 0; z-index: 999;
  background: #05070f;
  display: grid; place-items: center;
  opacity: 1; transition: opacity .5s ease;
}
.sting[hidden] { display: none; }
.sting.is-done { opacity: 0; pointer-events: none; }
.sting video {
  width: 100%; height: 100%;
  object-fit: contain;
}
/* The clip goes from near-white to near-black, so the control carries its
   own backdrop rather than trusting the frame behind it. */
.sting .skip {
  position: absolute; right: 20px; bottom: 18px;
  background: rgba(5,7,15,.62); color: rgba(237,241,250,.82);
  backdrop-filter: blur(6px);
  border: 1px solid rgba(237,241,250,.3); border-radius: 7px;
  font: inherit; font-size: .78rem; letter-spacing: .04em;
  padding: 7px 15px; cursor: pointer;
}
.sting .skip:hover { background: rgba(5,7,15,.85); color: #fff; border-color: rgba(237,241,250,.6); }

@media (prefers-reduced-motion: reduce) { .sting { display: none !important; } }

/* The Lickety pages carry the most header items, and at 390px the lockup
   plus the nav ran 12px past the viewport. Shrink the lockup and tighten
   the gaps rather than dropping a link — the price pill is the one thing a
   visitor on that page is looking for. */
@media (max-width: 430px) {
  .brand-lockup { width: 122px; }
  .site-head .wrap { gap: 12px; padding-inline: 16px; }
  .nav { gap: 11px; }
  .nav a { font-size: .84rem; }
  .theme-toggle { padding: 5px 9px; font-size: .74rem; }
}

/* Below ~560px the header runs out of room, and what it does when it runs out
   matters: left alone, flex refuses to shrink the links and the whole PAGE
   gets wider than the phone, so every section below picks up a horizontal
   scrollbar. Adding one nav item was enough to trigger it (420px of nav in a
   390px viewport). The nav scrolls sideways on its own instead — the items
   stay full size and reachable, and the page width stays honest.

   This is why the header is checked at 390px after any nav change. */
@media (max-width: 560px) {
  .site-head .wrap { gap: 10px; }
  .nav {
    min-width: 0;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 2px;
  }
  .nav::-webkit-scrollbar { display: none; }
  .nav a { white-space: nowrap; }
  .nav > * { flex: 0 0 auto; }
}

/* The mono price line is wide; on a phone it broke mid-phrase ("buy /
   once"). Smaller, and the separators become the break points. */
@media (max-width: 560px) {
  .housead .price { font-size: .72rem; line-height: 1.6; }
  .housead h3 { font-size: 1rem; }
}

/* ==========================================================================
   THE KEEP-IT STRIP — "download this tool" under the embedded tool.
   This is the catalogue's thesis in one control: the buy-once brand saying
   here is a free one you can actually keep. It sits under the tool rather
   than over it, and it is stated as a fact about the file rather than as an
   offer, because it is one.
   ========================================================================== */
.toolframe-foot {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 13px 20px;
  border-top: 1px solid var(--line);
  background: var(--rail);
  font-size: .84rem; color: var(--muted);
}
.toolframe-foot span { flex: 1 1 280px; }
.toolframe-foot .keepbtn { padding: 8px 15px; font-size: .84rem; white-space: nowrap; }
.framelink { margin-left: auto; font-size: .82rem; font-weight: 600; white-space: nowrap; }

/* ==========================================================================
   DOWNLOADS PAGE
   ========================================================================== */
.dl-bundle {
  background: color-mix(in srgb, var(--accent) 7%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
  border-radius: 13px;
  padding: 30px 32px;
  display: flex; gap: 26px; flex-wrap: wrap; align-items: center;
}
.dl-bundle .body { flex: 1 1 320px; }
.dl-bundle h2 { font-size: clamp(1.4rem, 2.4vw, 1.9rem); margin-bottom: 10px; }
.dl-bundle p { color: var(--muted); font-size: .96rem; margin-bottom: 0; }
.dl-bundle .act { flex: 0 0 auto; text-align: right; }
.dl-bundle .amount {
  font-family: var(--display); font-weight: 700;
  font-size: 2.5rem; letter-spacing: -.03em; color: var(--accent);
  display: block; line-height: 1;
}
.dl-bundle .once {
  display: block; margin: 6px 0 16px;
  font-family: var(--mono); font-size: .74rem; color: var(--faint);
}
@media (max-width: 680px) { .dl-bundle .act { text-align: left; } }

/* One row per tool: name, what it does, and the file. */
.dl-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.dl-list li {
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  padding: 14px 4px; border-bottom: 1px solid var(--line);
}
.dl-list .name { font-weight: 600; font-size: .96rem; flex: 0 0 auto; }
.dl-list .name a { color: var(--ink); }
.dl-list .name a:hover { color: var(--accent); }
.dl-list .desc { color: var(--muted); font-size: .87rem; flex: 1 1 240px; }
.dl-list .size { font-family: var(--mono); font-size: .74rem; color: var(--faint); flex: 0 0 auto; }
.dl-list .get {
  flex: 0 0 auto; font-size: .84rem; font-weight: 600; color: var(--accent);
  border: 1px solid var(--line); border-radius: 7px; padding: 5px 12px;
}
.dl-list .get:hover { text-decoration: none; border-color: var(--accent); }

/* Shown only by JavaScript, and only when the page was opened from a local
   file path. Invisible on a real server. */
.localnote {
  margin: 18px 0 0;
  padding: 12px 16px;
  border: 1px dashed color-mix(in srgb, var(--accent) 40%, var(--line));
  border-radius: 9px;
  background: color-mix(in srgb, var(--accent) 6%, var(--surface));
  color: var(--muted);
  font-size: .86rem;
}
.localnote b { color: var(--ink); }
.localnote em { color: var(--ink); font-style: normal; font-weight: 600; }

/* ==========================================================================
   THE DICTATION DEMO
   A looping, text-based simulation of Lickety shaping a dictation.

   Built in HTML rather than as a video, for the same reasons the tagline
   cards are HTML: it is crisp at any size, it follows the theme, it weighs
   nothing, and the words are real text a crawler and a screen reader can
   both read.

   The story it tells is deliberately NOT the competitor's story. Theirs
   shows an AI tidying up rambling speech — fillers removed, corrections
   applied. This one shows a deterministic pipeline doing exactly what it
   was told: a spoken trigger produces a list, every time. Same input, same
   output. That is the product's actual argument.
   ========================================================================== */
.vtdemo {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 13px;
  box-shadow: var(--shadow);
  overflow: hidden;
  width: 100%;
}

/* Title bar. The app name changes per scene — the point being that the text
   lands wherever you already were. */
.vtdemo-bar {
  display: flex; align-items: center; gap: 8px;
  padding: 11px 15px;
  background: var(--rail);
  border-bottom: 1px solid var(--line);
}
.vtdemo-bar i { width: 9px; height: 9px; border-radius: 50%; background: var(--line); }
.vtdemo-bar .app {
  margin-left: 8px; font-size: .78rem; color: var(--faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.vtdemo-body {
  padding: 22px 24px;
  min-height: 268px;
  font-family: var(--mono);
  font-size: .95rem;
  line-height: 1.75;
  color: var(--ink);
  display: flex; flex-direction: column; justify-content: flex-start;
}
@media (max-width: 560px) { .vtdemo-body { font-size: .86rem; min-height: 300px; } }

/* What was heard — quiet, and clearly a different thing from the result. */
.vtdemo-heard {
  color: var(--faint);
  border-left: 2px solid var(--line);
  padding-left: 13px;
  margin-bottom: 18px;
  min-height: 1.75em;
  transition: opacity .3s;
}
.vtdemo-heard .cursor {
  display: inline-block; width: 7px; height: 1.05em;
  vertical-align: text-bottom; background: var(--accent);
  animation: blink 1.06s step-end infinite;
}

/* The result — each line fades up as the pipeline produces it. */
.vtdemo-out { margin: 0; }
.vtdemo-out .ln {
  opacity: 0;
  transform: translateY(5px);
  transition: opacity .34s ease, transform .34s ease;
  min-height: 1.75em;
  white-space: pre-wrap;
}
.vtdemo-out .ln.in { opacity: 1; transform: none; }
.vtdemo-out .bullet { color: var(--accent); }
.vtdemo-out .was {
  color: var(--warn);
  text-decoration: line-through;
  text-decoration-thickness: 1.5px;
}
.vtdemo-out .now { color: var(--sig-done); }
.vtdemo-out .note {
  margin-top: 10px;
  font-family: inherit;
  font-size: .8rem;
  letter-spacing: .02em;
  color: var(--muted);
}

/* Footer: the live state, and the name of the feature being shown. */
.vtdemo-foot {
  display: flex; align-items: center; gap: 11px; flex-wrap: wrap;
  padding: 12px 18px;
  border-top: 1px solid var(--line);
  background: var(--rail);
  font-family: var(--body);
  font-size: .8rem; color: var(--muted);
}
.vtdemo-foot .label { margin-left: auto; color: var(--faint); font-size: .74rem; text-align: right; }

/* The microphone level. Bars animate only while a scene is "listening",
   so an idle demo is not burning a repaint budget forever. */
.vtdemo-wave { display: inline-flex; align-items: flex-end; gap: 3px; height: 14px; }
.vtdemo-wave span {
  width: 3px; height: 4px; border-radius: 2px;
  background: var(--sig-recording); opacity: .45;
}
.vtdemo.is-listening .vtdemo-wave span {
  animation: vtbar .62s ease-in-out infinite; opacity: 1;
}
.vtdemo.is-listening .vtdemo-wave span:nth-child(2) { animation-delay: .09s; }
.vtdemo.is-listening .vtdemo-wave span:nth-child(3) { animation-delay: .19s; }
.vtdemo.is-listening .vtdemo-wave span:nth-child(4) { animation-delay: .28s; }
.vtdemo.is-listening .vtdemo-wave span:nth-child(5) { animation-delay: .14s; }
@keyframes vtbar { 0%,100% { height: 4px; } 50% { height: 14px; } }

.vtdemo-foot .state { font-variant-numeric: tabular-nums; }

/* Scene dots, so a visitor can see there is more than one and jump. */
.vtdemo-dots { display: inline-flex; gap: 6px; margin-left: 2px; }
.vtdemo-dots button {
  width: 7px; height: 7px; padding: 0; border-radius: 50%;
  border: 0; background: var(--line); cursor: pointer;
}
.vtdemo-dots button[aria-current="true"] { background: var(--accent); }

/* A caption under the whole thing, for the homepage where the demo needs
   to say which product it is. */
.vtdemo-cap {
  margin-top: 14px; font-size: .82rem; color: var(--faint);
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}

@media (prefers-reduced-motion: reduce) {
  .vtdemo-out .ln { opacity: 1; transform: none; transition: none; }
  .vtdemo.is-listening .vtdemo-wave span { animation: none; }
}


/* ==========================================================================
   Page depth — the side margins on a wide screen

   On a 1920px screen the 1180px column leaves 370px of dead space each side.
   This puts the content column forward of that space. It is one fixed layer
   behind everything, it never touches the column's own colours, and it
   ignores the pointer, so it cannot change the contrast of a single word.

   The two themes move in OPPOSITE directions, and that is deliberate:

   - Dark --paper is #11141C. There is plenty of room above it, so dark mode
     lifts the column off its margins.
   - Light --paper is #DDE3F0. There is almost no room above it before the
     page goes white — an earlier attempt lifted the light column and washed
     --paper's blue-grey out, which looked worse than no treatment at all.
     So light mode leaves the middle at --paper, keeping all of its colour,
     and deepens the margins instead.

   Same job both ways: the column reads forward of its margins. In neither
   theme does the middle wash out, and in neither theme does an edge ever
   get brighter than the content.

   Eleven stops on a smootherstep curve rather than two stops and a straight
   line, because a browser interpolates linearly between stops and the eye
   finds the kink where one run meets the next.

   BANDING, and why there is no dither here. An 8-bit panel has 256 levels
   per channel. A gradient this subtle across a 370px margin crosses a
   colour step every 20 to 40 pixels, and a panel that does not dither draws
   each one as a faint vertical stripe — measured here as flat runs of up to
   130px in dark mode. Fine noise over the top is the standard fix and it
   was built and tested; on the monitors this actually gets looked at it
   read as dirt, and the banding did not read at all, so it is off. If a
   future monitor shows stripes, uncomment body::after below. Every
   other curve was tried first: the flattest option still leaves 10px
   plateaus, so dithering is the only real cure, not a gentler ramp.
   ========================================================================== */
body::before {
  content: "";
  position: fixed; inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(90deg, rgba(255,255,255,0.0000) 0.0%, rgba(255,255,255,0.0004) 4.5%, rgba(255,255,255,0.0025) 9.1%, rgba(255,255,255,0.0071) 13.6%, rgba(255,255,255,0.0141) 18.2%, rgba(255,255,255,0.0228) 22.7%, rgba(255,255,255,0.0322) 27.3%, rgba(255,255,255,0.0409) 31.8%, rgba(255,255,255,0.0479) 36.4%, rgba(255,255,255,0.0525) 40.9%, rgba(255,255,255,0.0546) 45.5%, rgba(255,255,255,0.0550) 50.0%, rgba(255,255,255,0.0546) 54.5%, rgba(255,255,255,0.0525) 59.1%, rgba(255,255,255,0.0479) 63.6%, rgba(255,255,255,0.0409) 68.2%, rgba(255,255,255,0.0322) 72.7%, rgba(255,255,255,0.0228) 77.3%, rgba(255,255,255,0.0141) 81.8%, rgba(255,255,255,0.0071) 86.4%, rgba(255,255,255,0.0025) 90.9%, rgba(255,255,255,0.0004) 95.5%, rgba(255,255,255,0.0000) 100.0%);
}
[data-theme="light"] body::before {
  background: linear-gradient(90deg, rgba(138,157,199,0.5200) 0.0%, rgba(138,157,199,0.5166) 4.0%, rgba(138,157,199,0.4966) 8.0%, rgba(138,157,199,0.4530) 12.0%, rgba(138,157,199,0.3865) 16.0%, rgba(138,157,199,0.3041) 20.0%, rgba(138,157,199,0.2159) 24.0%, rgba(138,157,199,0.1335) 28.0%, rgba(138,157,199,0.0670) 32.0%, rgba(138,157,199,0.0234) 36.0%, rgba(138,157,199,0.0034) 40.0%, rgba(138,157,199,0.0000) 44.0%, rgba(138,157,199,0.0000) 56.0%, rgba(138,157,199,0.0034) 60.0%, rgba(138,157,199,0.0234) 64.0%, rgba(138,157,199,0.0670) 68.0%, rgba(138,157,199,0.1335) 72.0%, rgba(138,157,199,0.2159) 76.0%, rgba(138,157,199,0.3041) 80.0%, rgba(138,157,199,0.3865) 84.0%, rgba(138,157,199,0.4530) 88.0%, rgba(138,157,199,0.4966) 92.0%, rgba(138,157,199,0.5166) 96.0%, rgba(138,157,199,0.5200) 100.0%);
}

/* The dither, if a panel ever needs it. Uncomment the rule to turn it on.

body::after {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)' opacity='0.085'/%3E%3C/svg%3E");
  opacity: .55;
}
[data-theme="light"] body::after { opacity: .62; }
*/

/* Nothing to see on a phone — there are no margins to treat, and a fixed
   full-viewport layer is one more thing for a weak GPU to composite. */
@media (max-width: 900px) { body::before { display: none; } }


/* ==========================================================================
   Worked examples — "say this, get this"

   The feature pages live or die on these. A competitor's page describes a
   formatting feature; this shows the sentence going in and the text coming
   out, which is the one thing a reader can check against their own job.

   Built as a definition list so it reads correctly to a screen reader and
   to a search engine: the spoken phrase is the term, the result is the
   definition. A table would imply a grid that isn't there.
   ========================================================================== */
/* A numbered sequence. The site had no list idiom at all before this — every
   other page is h3-then-paragraph — so one is defined here once rather than
   left to inline styles on the page that happened to need it first. Used
   where the content genuinely is ordered steps ("press this, then say that")
   and a run of headings would hide that it is a sequence. The counter sits in
   the margin so the text block still lines up with the prose above it. */
.steps { list-style: none; margin: 22px 0 0; padding: 0; counter-reset: step; }
.steps > li {
  counter-increment: step;
  position: relative;
  padding: 0 0 0 46px;
  margin-bottom: 16px;
  color: var(--muted);
}
.steps > li:last-child { margin-bottom: 0; }
.steps > li::before {
  content: counter(step);
  position: absolute; left: 0; top: -1px;
  width: 28px; height: 28px;
  display: grid; place-items: center;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--rail);
  font-family: var(--mono); font-size: .82rem; font-weight: 500;
  color: var(--accent);
}
.steps > li strong { color: var(--ink); }

.sayget { margin: 26px 0 0; }
.sayget dl {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  /* stretch, not baseline. Every cell carries the same 13px top padding and
     the same line-height, so the first lines align anyway — but with baseline
     a cell stops at its own text, and a row whose right side wraps to more
     lines than its left leaves an unpainted strip under the short cell. It
     shows the moment one side of a pair is longer than the other, which is
     most of them. */
  align-items: stretch;
  gap: 0;
  margin: 0;
  border: 1px solid var(--line);
  border-radius: 11px;
  overflow: hidden;
}
.sayget dt, .sayget dd {
  margin: 0;
  padding: 13px 18px;
  font-family: var(--mono);
  font-size: .92rem;
  line-height: 1.5;
  border-top: 1px solid var(--line);
}
.sayget dl > dt:first-of-type,
.sayget dl > dt:first-of-type + .sg-arrow,
.sayget dl > dt:first-of-type + .sg-arrow + dd { border-top: 0; }
.sayget dt { color: var(--muted); }
.sayget dd { color: var(--ink); font-weight: 500; }
.sayget .sg-arrow {
  padding: 13px 4px;
  color: var(--accent);
  border-top: 1px solid var(--line);
  font-family: var(--mono);
  user-select: none;
}
.sayget dt::before {
  content: "\201C";
  color: var(--faint);
}
.sayget dt::after {
  content: "\201D";
  color: var(--faint);
}
/* Zebra, by row of three. nth-child counts every child, so the stride is 6. */
.sayget dl > *:nth-child(6n+4),
.sayget dl > *:nth-child(6n+5),
.sayget dl > *:nth-child(6n+6) { background: var(--zebra); }

.sayget .sg-note {
  margin: 10px 2px 0;
  font-size: .85rem;
  color: var(--faint);
}

/* Stacked on a phone: the arrow turns into a down-pointing one and the two
   halves sit on top of each other, because three columns in 350px gives
   each one about nine characters. */
@media (max-width: 620px) {
  .sayget dl { grid-template-columns: 1fr; }
  .sayget dt { padding-bottom: 4px; border-top: 1px solid var(--line); }
  .sayget .sg-arrow { padding: 0 18px; border-top: 0; }
  .sayget .sg-arrow::after { content: "\2193"; }
  .sayget .sg-arrow > span { display: none; }
  .sayget dd { padding-top: 4px; border-top: 0; }
  .sayget dl > *:nth-child(6n+4),
  .sayget dl > *:nth-child(6n+5),
  .sayget dl > *:nth-child(6n+6) { background: transparent; }
}

/* A screenshot of the application. Framed so it sits on either theme
   without needing a light and a dark copy of the image. */
.shot {
  margin: 30px 0 0;
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
  background: var(--rail);
  box-shadow: var(--shadow);
}
.shot img { display: block; width: 100%; height: auto; }
/* A screenshot of the application is the one thing on these pages that cannot
   be read at prose width. Measured on `voice-notes-windows.html`: a 1227px
   capture inside a 686px `.prose` renders at 56%, which puts the app's 13px
   body text at about 7px — half the size of the paragraph beside it. The
   figure therefore lives as a direct child of `.wrap` rather than `.prose`,
   so it fills the column (1124px at the capped width, 92% of natural) and
   the app's text lands near 12px. It is still a reduction: the only way past
   that is a capture of a narrower window, not a bigger box. */
.shot-wide { margin-top: 30px; }

/* On a phone the same capture is down to 31%, which no amount of layout
   fixes — 1227px of application will not fit in 378px. Below the tablet
   breakpoint the figure becomes a scroller instead, holding the image at
   its natural 1:1 size, where its text is as legible as it gets, and letting
   the reader pan it. This is the treatment `.table-wrap` already uses on
   the comparison table, for the same reason. */
@media (max-width: 760px) {
  .shot-wide { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .shot-wide img { width: auto; max-width: none; }   /* natural size: 1:1, no resampling */
  .shot-wide figcaption { position: sticky; left: 0; }
}

.shot figcaption {
  padding: 12px 18px;
  font-size: .85rem;
  color: var(--muted);
  border-top: 1px solid var(--line);
}


/* ==========================================================================
   The lit stage behind the pitch card

   The same rig as the logo sting, in CSS: two arc lamps above the frame,
   tungsten-white at the core and cooling to the accent at the cone edge.
   They sweep while the card is settling, then lock off to the sides at card
   height, aimed inward, so each cone rakes an outer edge and the middle of
   the card is left alone. The sting recipe's rule applies — readability is
   bought with the stage, never by lighting the thing standing on it.

   Everything here is behind the card and ignores the pointer. The card has
   its own opaque --surface, so no beam can touch the contrast of the text.

   The whole rig is a finite animation. It ignites, sweeps, converges, locks,
   and then stops — no infinite keyframe left running in a hero, which is the
   same rule the product videos and the dictation demo follow.
   ========================================================================== */
.spotlit { position: relative; isolation: isolate; }
.spotlit > .pitch { position: relative; z-index: 1; }

/* The stage is much bigger than the card, because lamps need somewhere to
   hang and a sweep needs somewhere to go. It clips its own beams. */
.spot-stage {
  --hang: 116px;            /* room above the card for the lamps to hang in */
  position: absolute;
  inset: calc(var(--hang) * -1) -72px -48px;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity .6s ease;

  /* Fade the stage out at its own edges. Without this every cone ends on a
     straight cut where the stage stops, which reads as a box, not a beam. */
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0%, #000 4%, #000 64%, transparent 97%),
    linear-gradient(to right, transparent 0%, #000 8%, #000 92%, transparent 100%);
          mask-image:
    linear-gradient(to bottom, transparent 0%, #000 4%, #000 64%, transparent 97%),
    linear-gradient(to right, transparent 0%, #000 8%, #000 92%, transparent 100%);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}
.spotlit.is-lit .spot-stage { opacity: 1; }
@media (max-width: 1080px) { .spot-stage { --hang: 92px; inset: calc(var(--hang) * -1) -34px -40px; } }
/* Stacked layout: the lamps hang in the gap above the card, so the top
   inset stays smaller than that gap or a cone lands on the button above. */
@media (max-width: 640px)  { .spot-stage { --hang: 54px; inset: calc(var(--hang) * -1) -12px -30px; } }

/* A lamp. The bulb is the transform-origin at the top edge of the element;
   everything below it is the cone.

   The cone is a CONIC gradient fanning out from that same point, not a
   clipped rectangle. A clip-path cuts the cone off on a straight line
   wherever the polygon ends, and because the colour still has substance out
   there, the cut shows as a hard diagonal. A conic gradient has no edge to
   cut — it just runs out of light. Angles are measured from straight up, so
   180deg is straight down and the wedge is centred on that. */
.beam {
  position: absolute;
  top: -6%;
  height: 168%;
  width: 760px;
  margin-left: -380px;
  transform-origin: 50% 0%;
  will-change: transform, opacity;
  opacity: 0;

  /* Light adds to what is behind it. Without this the cone is a grey panel
     laid over the page instead of a beam. */
  mix-blend-mode: screen;

  /* White core cooling to the accent at the cone edge, which is the sting's
     own description of these lamps. */
  background: conic-gradient(from 172.5deg at 50% 0%,
      rgba(70,118,235,0)     0deg,
      rgba(78,125,240,.14)   2.2deg,
      rgba(150,186,255,.44)  5.0deg,
      rgba(236,243,255,.82)  7.5deg,
      rgba(150,186,255,.44) 10.0deg,
      rgba(78,125,240,.14)  12.8deg,
      rgba(70,118,235,0)    15deg);

  /* Down the cone: brightest just below the bulb, gone before the stage
     ends, so the beam fades out rather than meeting the stage edge. */
  -webkit-mask-image: linear-gradient(to bottom,
      rgba(0,0,0,.28) 0%, rgba(0,0,0,1) 10%, rgba(0,0,0,.52) 38%,
      rgba(0,0,0,.14) 55%, rgba(0,0,0,0) 66%);
          mask-image: linear-gradient(to bottom,
      rgba(0,0,0,.28) 0%, rgba(0,0,0,1) 10%, rgba(0,0,0,.52) 38%,
      rgba(0,0,0,.14) 55%, rgba(0,0,0,0) 66%);
}
/* Two bulbs hung along the top of the stage, left and right of centre.
   In CSS a POSITIVE rotation swings the bottom of the element to the LEFT
   (clockwise, so a 6-o'clock hand moves toward 7). So the left lamp splays
   outward on a positive angle and the right lamp on a negative one. Getting
   this backwards aims both cones across the card's face, which is the one
   thing the rig exists to avoid. */
.beam-l { left: 30%; transform: rotate(7deg); }
.beam-r { left: 70%; transform: rotate(-7deg); }

/* Haze. The motes are a child of the beam and wear the same conic wedge as
   a mask, so they can only be seen where there is light to catch them.
   Loose on the stage they read as a couple of stray dots in empty space —
   dirt on the screen rather than dust in a beam. */
.beam::after {
  content: "";
  position: absolute; inset: 0;
  opacity: 0;
  -webkit-mask-image: conic-gradient(from 174deg at 50% 0%,
      transparent 0deg, rgba(0,0,0,.6) 3deg, #000 6deg,
      #000 6.5deg, rgba(0,0,0,.6) 9deg, transparent 12deg);
          mask-image: conic-gradient(from 174deg at 50% 0%,
      transparent 0deg, rgba(0,0,0,.6) 3deg, #000 6deg,
      #000 6.5deg, rgba(0,0,0,.6) 9deg, transparent 12deg);
  background-image:
    radial-gradient(1.7px 1.7px at 48.1% 12%, rgba(255,255,255,.85), transparent 100%),
    radial-gradient(1.3px 1.3px at 51.4% 19%, rgba(255,255,255,.62), transparent 100%),
    radial-gradient(1.8px 1.8px at 47.4% 27%, rgba(255,255,255,.58), transparent 100%),
    radial-gradient(1.2px 1.2px at 52.3% 31%, rgba(255,255,255,.66), transparent 100%),
    radial-gradient(1.6px 1.6px at 49.2% 38%, rgba(255,255,255,.44), transparent 100%),
    radial-gradient(1.3px 1.3px at 46.4% 43%, rgba(255,255,255,.52), transparent 100%),
    radial-gradient(1.7px 1.7px at 53.4% 47%, rgba(255,255,255,.40), transparent 100%),
    radial-gradient(1.2px 1.2px at 50.3% 22%, rgba(255,255,255,.58), transparent 100%),
    radial-gradient(1.4px 1.4px at 45.8% 52%, rgba(255,255,255,.34), transparent 100%),
    radial-gradient(1.5px 1.5px at 54.1% 56%, rgba(255,255,255,.30), transparent 100%),
    radial-gradient(1.2px 1.2px at 49.7% 60%, rgba(255,255,255,.26), transparent 100%),
    radial-gradient(1.3px 1.3px at 52.8% 9%,  rgba(255,255,255,.70), transparent 100%);
}

/* Three short lamps filling the gap between the two sweeping ones.

   These only shine. They strike with the others and then hold — no sweep,
   nothing to follow — so the eye still goes to the two that move. Their
   bulbs sit on the same line as the outer pair, and they stop short: the
   cone runs out of light about two thirds of the way down the hang, which
   leaves clear air between the bottom of the light and the top of the card.
   They are lighting the space above the card, not the card. */
.beam-mid {
  position: absolute;
  /* Nudged up so the bulbs line up with the sweeping pair, whose element
     starts above the stage. Measured, not guessed: without it these strike
     about twelve pixels low and the rig looks like two rigs. */
  top: calc(var(--hang) * -0.10);
  height: calc(var(--hang) * 0.84);
  width: 220px;
  margin-left: -110px;
  transform-origin: 50% 0%;
  opacity: 0;
  mix-blend-mode: screen;

  /* Wider wedge than the sweeping pair. Over this short a throw a 7-degree
     cone is a needle; these have to read as diffused light in the gap. */
  background: conic-gradient(from 160deg at 50% 0%,
      rgba(70,118,235,0)     0deg,
      rgba(78,125,240,.09)   4.5deg,
      rgba(140,178,255,.26) 11deg,
      rgba(222,234,255,.60) 20deg,
      rgba(140,178,255,.26) 29deg,
      rgba(78,125,240,.09)  35.5deg,
      rgba(70,118,235,0)    40deg);

  /* Reaches full just below the bulb and is gone by the bottom of the
     element, so the beam ends in air rather than on an edge. */
  -webkit-mask-image: linear-gradient(to bottom,
      rgba(0,0,0,.45) 0%, rgba(0,0,0,1) 18%, rgba(0,0,0,.62) 52%,
      rgba(0,0,0,.20) 78%, rgba(0,0,0,0) 100%);
          mask-image: linear-gradient(to bottom,
      rgba(0,0,0,.45) 0%, rgba(0,0,0,1) 18%, rgba(0,0,0,.62) 52%,
      rgba(0,0,0,.20) 78%, rgba(0,0,0,0) 100%);
}
/* Spread across the middle, with a slight fan so the rig does not look
   stamped out. Positive angle splays left, as with the outer pair. */
.m1 { left: 41%; transform: rotate(5deg); }
.m2 { left: 50%; transform: rotate(0deg); }
.m3 { left: 59%; transform: rotate(-5deg); }

/* Below about 640 the hang is only 54px. Three cones in that much air read
   as clutter rather than as a rig, so the middle one holds the gap alone. */
@media (max-width: 640px) {
  .m1, .m3 { display: none; }
  .beam-mid { width: 150px; margin-left: -75px; }
}

/* The pool the cones throw behind the card. Mostly hidden by the card
   itself — what is seen is the margin of it around the edges, which is what
   makes the card look like it is standing in the light. */
.spot-pool {
  position: absolute; left: 50%; top: 56%;
  width: 118%; height: 72%;
  transform: translate(-50%, -50%);
  mix-blend-mode: screen;
  background: radial-gradient(ellipse at 50% 50%,
      rgba(128,168,255,.20) 0%, rgba(84,130,246,.09) 48%, rgba(84,130,246,0) 74%);
  opacity: 0;
}

/* Haze. The motes live INSIDE the cone, as a child of the beam, so they
   inherit its clip-path and its falloff mask and can only ever be seen
   where there is light to catch them. Loose on the stage they read as a
   couple of stray dots in empty space — dirt on the screen rather than
   dust in a beam. */
.beam::after {
  content: "";
  position: absolute; inset: -8% -26%;
  opacity: 0;
  background-image:
    radial-gradient(1.7px 1.7px at 18% 22%, rgba(255,255,255,.85), transparent 100%),
    radial-gradient(1.3px 1.3px at 71% 14%, rgba(255,255,255,.62), transparent 100%),
    radial-gradient(1.8px 1.8px at 36% 61%, rgba(255,255,255,.55), transparent 100%),
    radial-gradient(1.2px 1.2px at 86% 48%, rgba(255,255,255,.66), transparent 100%),
    radial-gradient(1.6px 1.6px at 57% 83%, rgba(255,255,255,.40), transparent 100%),
    radial-gradient(1.3px 1.3px at  8% 72%, rgba(255,255,255,.48), transparent 100%),
    radial-gradient(1.7px 1.7px at 94% 79%, rgba(255,255,255,.36), transparent 100%),
    radial-gradient(1.2px 1.2px at 46% 33%, rgba(255,255,255,.58), transparent 100%),
    radial-gradient(1.4px 1.4px at 27% 44%, rgba(255,255,255,.46), transparent 100%),
    radial-gradient(1.5px 1.5px at 63% 27%, rgba(255,255,255,.52), transparent 100%),
    radial-gradient(1.2px 1.2px at 40% 90%, rgba(255,255,255,.32), transparent 100%),
    radial-gradient(1.3px 1.3px at 78% 66%, rgba(255,255,255,.42), transparent 100%);
}

/* ---- the cue -------------------------------------------------------------
   Ignition, two sweeps, convergence, lock.

   The lock angles are the whole point. Each lamp ends splayed OUTWARD —
   left lamp to the left, right lamp to the right — so each cone runs down
   past its own outer edge of the card rather than across its face. The card
   finishes raked on both edges with its middle left alone, which is the
   only way the text stays as readable lit as it was unlit.
-------------------------------------------------------------------------- */
@keyframes beamL {
    0%   { transform: rotate(7deg);   opacity: 0; }
    7%   { transform: rotate(7deg);   opacity: .60; }
   26%   { transform: rotate(-16deg); opacity: .82; }
   48%   { transform: rotate(21deg);  opacity: .74; }
   68%   { transform: rotate(-4deg);  opacity: .88; }
  100%   { transform: rotate(24deg);  opacity: 1; }
}
@keyframes beamR {
    0%   { transform: rotate(-7deg);  opacity: 0; }
    7%   { transform: rotate(-7deg);  opacity: .60; }
   26%   { transform: rotate(15deg);  opacity: .84; }
   48%   { transform: rotate(-20deg); opacity: .72; }
   68%   { transform: rotate(3deg);   opacity: .88; }
  100%   { transform: rotate(-24deg); opacity: 1; }
}
/* The short lamps strike with the others and then just hold. */
@keyframes midRise { 0% { opacity: 0; } 9% { opacity: .62; } 55% { opacity: .72; } 100% { opacity: .92; } }
/* 55% to 100% across the convergence, as the sting does it. */
@keyframes spotRise { 0% { opacity: 0; } 20%, 55% { opacity: .55; } 100% { opacity: 1; } }
/* The haze drifts while the lamps are moving and is still once they lock. */
@keyframes dustDrift {
    0% { opacity: 0;   transform: translate3d(0, 0, 0); }
   14% { opacity: .85; }
   86% { opacity: .85; }
  100% { opacity: .6;  transform: translate3d(-9px, -26px, 0); }
}

.spotlit.is-lit .beam-l    { animation: beamL   3.9s cubic-bezier(.42,.02,.28,1) forwards; }
.spotlit.is-lit .beam-r    { animation: beamR   3.9s cubic-bezier(.42,.02,.28,1) forwards; }
.spotlit.is-lit .beam-mid   { animation: midRise   3.9s ease-out forwards; }
.spotlit.is-lit .spot-pool  { animation: spotRise  3.9s ease-out forwards; }
.spotlit.is-lit .beam::after { animation: dustDrift 3.9s linear   forwards; }

/* Light theme: the page is already pale, so there is nothing for a white
   beam to be brighter than, and screen blending on a light ground does
   nothing at all. The light reads instead as a denser, cooler shaft laid on
   the paper, and the motes go dark. Same rig, same angles, same timing —
   only what the stage is made of changes. */
/* Light theme. Measured, not eyeballed: at the first values these cones
   peaked at a colour distance of 53 from the page in the outer pair and 27
   in the middle three, and only at a single hotspot near the bulb — on an
   ordinary office monitor that is a smudge, not a beam. Two changes: the
   middle lamps now carry the same weight as the outer pair, and both get a
   falloff that holds most of the way down instead of dying at 38%. */
[data-theme="light"] .beam {
  mix-blend-mode: normal;
  background: conic-gradient(from 172.5deg at 50% 0%,
      rgba(28,63,209,0)     0deg,
      rgba(30,66,206,.085)  2.2deg,
      rgba(36,72,212,.255)  5.0deg,
      rgba(44,84,224,.44)   7.5deg,
      rgba(36,72,212,.255) 10.0deg,
      rgba(30,66,206,.085) 12.8deg,
      rgba(28,63,209,0)    15deg);
}
[data-theme="light"] .beam,
[data-theme="light"] .beam-mid {
  -webkit-mask-image: linear-gradient(to bottom,
      rgba(0,0,0,.42) 0%, rgba(0,0,0,1) 12%, rgba(0,0,0,.80) 40%,
      rgba(0,0,0,.40) 58%, rgba(0,0,0,0) 72%);
          mask-image: linear-gradient(to bottom,
      rgba(0,0,0,.42) 0%, rgba(0,0,0,1) 12%, rgba(0,0,0,.80) 40%,
      rgba(0,0,0,.40) 58%, rgba(0,0,0,0) 72%);
}
/* Second pass. At .38 these still read as washed next to the outer pair —
   measured at a colour distance of 65 from the page against the outer
   lamps' 100. A fill light should be softer than the key light, but not
   half of it, and on an ordinary monitor half of it is nothing. */
[data-theme="light"] .beam-mid {
  mix-blend-mode: normal;
  background: conic-gradient(from 160deg at 50% 0%,
      rgba(28,63,209,0)     0deg,
      rgba(30,66,206,.10)   4.5deg,
      rgba(36,72,212,.30)  11deg,
      rgba(46,88,228,.58)  20deg,
      rgba(36,72,212,.30)  29deg,
      rgba(30,66,206,.10)  35.5deg,
      rgba(28,63,209,0)    40deg);
}
[data-theme="light"] .spot-pool {
  mix-blend-mode: normal;
  background: radial-gradient(ellipse at 50% 50%,
      rgba(28,63,209,.11) 0%, rgba(28,63,209,.05) 48%, rgba(28,63,209,0) 74%);
}
[data-theme="light"] .beam::after {
  background-image:
    radial-gradient(1.6px 1.6px at 18% 22%, rgba(10,25,68,.30), transparent 100%),
    radial-gradient(1.2px 1.2px at 71% 14%, rgba(10,25,68,.22), transparent 100%),
    radial-gradient(1.7px 1.7px at 36% 61%, rgba(10,25,68,.20), transparent 100%),
    radial-gradient(1.1px 1.1px at 86% 48%, rgba(10,25,68,.24), transparent 100%),
    radial-gradient(1.5px 1.5px at 57% 83%, rgba(10,25,68,.15), transparent 100%),
    radial-gradient(1.2px 1.2px at  8% 72%, rgba(10,25,68,.17), transparent 100%),
    radial-gradient(1.6px 1.6px at 94% 79%, rgba(10,25,68,.13), transparent 100%),
    radial-gradient(1.1px 1.1px at 46% 33%, rgba(10,25,68,.21), transparent 100%),
    radial-gradient(1.3px 1.3px at 27% 44%, rgba(10,25,68,.16), transparent 100%),
    radial-gradient(1.4px 1.4px at 63% 27%, rgba(10,25,68,.19), transparent 100%);
}

/* No motion requested: the lamps are already locked when the page arrives.
   The stage still reads as lit — it just never performs. */
@media (prefers-reduced-motion: reduce) {
  .spot-stage { opacity: 1; transition: none; }
  .spotlit .beam-l    { transform: rotate(24deg);  opacity: 1; animation: none; }
  .spotlit .beam-r    { transform: rotate(-24deg); opacity: 1; animation: none; }
  .spotlit .beam-mid    { opacity: .92; animation: none; }
  .spotlit .spot-pool   { opacity: 1;  animation: none; }
  .spotlit .beam::after { opacity: .6; animation: none; }
}


/* ==========================================================================
   The home-page pitch card

   The Lickety page gets the working demo. This gets a door. Three claims on
   a rotation, each with its own button into /lickety/ — because a visitor
   who came here for a free CSV splitter has no reason to care about an
   Outlook window dictating itself, and a visitor who arrived from
   licketyapp.com has already seen it.

   The slides are stacked absolutely, so the card has to carry its own
   height or the hero jumps every 6.5 seconds. min-height is set from the
   tallest slide measured at each breakpoint, not guessed.
   ========================================================================== */
.pitch {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
  box-shadow: var(--shadow);
  padding: 26px 28px 20px;
  cursor: pointer;
  transition: border-color .2s ease, transform .2s ease;
}
.pitch:hover { border-color: var(--accent); transform: translateY(-2px); }
@media (prefers-reduced-motion: reduce) {
  .pitch, .pitch:hover { transition: none; transform: none; }
}

/* The tag line: logo, then the platform. The logo already says
   "Voice Typing", so this does not repeat it. */
.pitch-tag {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 0 22px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--line);
}
.pitch-mark { width: 190px; flex: none; }
.pitch-for {
  font-family: var(--body);
  font-size: .78rem; letter-spacing: .07em; text-transform: uppercase;
  color: var(--faint);
}

.pitch-stage { position: relative; min-height: 232px; }
@media (max-width: 900px) { .pitch-stage { min-height: 244px; } }
@media (max-width: 420px) { .pitch-stage { min-height: 248px; } }

.pitch-slide {
  position: absolute; inset: 0;
  opacity: 0; visibility: hidden;
  transition: opacity .55s ease;
}
.pitch-slide.is-on { opacity: 1; visibility: visible; }

.pitch-h {
  font-family: var(--display);
  font-size: clamp(1.75rem, 3.1vw, 2.3rem);
  line-height: 1.12;
  letter-spacing: -.015em;
  color: var(--ink);
  margin: 0 0 16px;
}
.pitch-s {
  font-size: 1rem; line-height: 1.6;
  color: var(--prose);
  margin: 0 0 26px;
  max-width: 42ch;
}
.pitch-go { pointer-events: auto; }

/* Same dots as the demo, so the two cards read as one family. */
.pitch-dots {
  display: inline-flex; gap: 7px;
  margin-top: 18px; padding-top: 2px;
}
.pitch-dots button {
  width: 8px; height: 8px; padding: 0; border-radius: 50%;
  border: 0; background: var(--line); cursor: pointer;
  transition: background .2s ease;
}
.pitch-dots button:hover { background: var(--muted); }
.pitch-dots button[aria-current="true"] { background: var(--accent); }
.pitch-dots button:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* No motion requested: show the first slide and stop. The dots still work,
   they just swap instantly. */
@media (prefers-reduced-motion: reduce) {
  .pitch-slide { transition: none; }
}


/* ==========================================================================
   The signup form
   One form on the whole site, and it asks for two things. It is styled to
   look like what it is — a short form — rather than like a conversion
   funnel, because the page around it is promising restraint.
   ========================================================================== */
.signup {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
  padding: 30px 28px;
  box-shadow: var(--shadow);
}
.signup h2 {
  font-family: var(--display); font-size: 1.15rem;
  letter-spacing: -0.01em; margin-bottom: 20px;
}
.signup label {
  display: block; margin-bottom: 7px;
  font-size: .82rem; font-weight: 600; letter-spacing: .02em;
  color: var(--muted);
}
.signup input[type="text"],
.signup input[type="email"] {
  display: block; width: 100%; margin-bottom: 18px;
  padding: 11px 13px;
  font: inherit; font-size: .96rem;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 8px;
}
.signup input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}
.signup .btn { width: 100%; margin-top: 4px; justify-content: center; }
.signup-note {
  margin: 16px 0 0;
  font-size: .82rem; line-height: 1.5; color: var(--faint);
}

/* The honeypot. Off-screen rather than display:none — some bots skip hidden
   fields, and a field they can see is a field they will fill in. Kept out of
   the tab order and out of the accessibility tree by the markup. */
.hp {
  position: absolute; left: -9999px;
  width: 1px; height: 1px; overflow: hidden;
}

/* A plain mailto that should read as the answer to the page it is on. */
.mailto { font-family: var(--mono); font-weight: 500; }

/* A .media that is a link rather than a player — a still that stands in for a
   video playing on another page. It needs to look clickable without pretending
   to be a play button, because clicking it navigates rather than plays. */
.media-link { display: block; text-decoration: none; }
.media-link:hover img { transform: scale(1.015); }
.media-link img { transition: transform .4s ease; }
.media-cue {
  position: absolute; left: 14px; bottom: 14px; z-index: 3;
  background: color-mix(in srgb, var(--paper) 86%, transparent);
  backdrop-filter: blur(8px);
  border: 1px solid var(--line); border-radius: 8px;
  padding: 8px 13px;
  font-size: .82rem; font-weight: 600; color: var(--ink);
}
.media-link:hover .media-cue { border-color: var(--accent); color: var(--accent); }

/* ==========================================================================
   Help search
   One input and a list. The search is the page, so the input is large and
   everything else gets out of its way.
   ========================================================================== */
.helpsearch { margin-top: 34px; max-width: 680px; }
.helpsearch input {
  display: block; width: 100%;
  padding: 16px 18px;
  font: inherit; font-size: 1.05rem;
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line); border-radius: 12px;
  box-shadow: var(--shadow);
}
.helpsearch input::placeholder { color: var(--faint); }
.helpsearch input:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent);
}
.helpsearch-note { margin: 10px 2px 0; font-size: .82rem; color: var(--faint); }

.hcount {
  font-family: var(--mono); font-size: .78rem; color: var(--faint);
  margin: 0 0 14px;
}

/* One answer. Collapsed it is the question; opened it is the whole thing. */
.hrow, .htool {
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--surface);
  margin-bottom: 10px; padding: 0;
}
.hrow > summary, .htool > summary {
  cursor: pointer; list-style: none;
  padding: 14px 18px;
  display: flex; gap: 14px; align-items: baseline; flex-wrap: wrap;
}
.hrow > summary::-webkit-details-marker,
.htool > summary::-webkit-details-marker { display: none; }
.hrow > summary:hover, .htool > summary:hover { background: var(--rail); border-radius: 10px; }
.hq { font-weight: 600; color: var(--ink); flex: 1 1 auto; }
.hfrom, .hn {
  font-family: var(--mono); font-size: .72rem; color: var(--faint);
  white-space: nowrap;
}
.hrow .ha {
  padding: 0 18px 4px; color: var(--muted); font-size: .94rem; line-height: 1.6;
}
.hrow[open] .ha { display: none; }
.hrow .hfull { display: none; padding: 0 18px 4px; }
.hrow[open] .hfull { display: block; }
.hrow .hfull p {
  color: var(--muted); font-size: .94rem; line-height: 1.65; margin: 0 0 12px;
}
.hlink {
  display: inline-block; margin: 0 18px 16px;
  font-size: .86rem; font-weight: 600;
}
.hrow mark, .htool mark {
  background: color-mix(in srgb, var(--accent) 26%, transparent);
  color: var(--ink); border-radius: 3px; padding: 0 2px;
}

/* Browse by product. */
.hbrowse { max-width: 760px; }
.htool > ul { margin: 0; padding: 0 18px 10px 38px; }
.htool > ul li {
  color: var(--muted); font-size: .92rem; line-height: 1.7; list-style: disc;
}

/* The escalation slot — shown only when the search found nothing. This is
   where a help agent's answer will go; see section 8 of site.js. */
.hask {
  border: 1px solid var(--line); border-left: 3px solid var(--accent);
  border-radius: 11px; background: var(--surface);
  padding: 26px 28px; max-width: 680px;
}
.hask h2 { font-family: var(--display); font-size: 1.1rem; margin-bottom: 10px; }
.hask p { margin-bottom: 14px; }
