/*
 * TextLaunchPad marketing site.
 *
 * No framework and no build step. The whole site is one HTML file and this,
 * which is the right size of tool for six sections of mostly-static content -
 * and it means the page ships no JavaScript, which is most of why it will
 * outrun every competitor's landing page on a phone.
 *
 * The tokens are lifted from the app's own design system
 * (frontend/src/assets/scss/_design-system.scss) so the site and the product
 * look like the same company. If you change a brand color, change it in both.
 */

/*
 * One palette, two values each, via light-dark().
 *
 * The alternative is a light block plus a copy of the dark block inside a
 * prefers-color-scheme query, plus a second copy under [data-theme="dark"] for
 * the toggle to switch on - three places where a color has to be kept in
 * step. Here `color-scheme` decides which half of each pair is used, and the
 * toggle only has to change that one property.
 *
 * Each token is declared twice on purpose: the plain value first, then the
 * light-dark() version. A browser too old for light-dark() ignores the second
 * declaration and gets a working light theme rather than an unstyled page.
 */
:root {
  color-scheme: light dark;

  --primary: #0d9488;
  --primary: light-dark(#0d9488, #2dd4bf);
  --primary-hover: #0f766e;
  --primary-hover: light-dark(#0f766e, #5eead4);
  --primary-soft: #ccfbf1;
  --primary-soft: light-dark(#ccfbf1, #134e4a);
  --primary-contrast: #ffffff;
  --primary-contrast: light-dark(#ffffff, #042f2e);

  --bg: #ffffff;
  --bg: light-dark(#ffffff, #0b1220);
  --bg-alt: #f6f8fb;
  --bg-alt: light-dark(#f6f8fb, #0f1728);
  --surface: #ffffff;
  --surface: light-dark(#ffffff, #131c2e);
  --border: #dbe2ec;
  --border: light-dark(#dbe2ec, #24304a);
  --border-strong: #becbdc;
  --border-strong: light-dark(#becbdc, #35425f);

  --text: #0f172a;
  --text: light-dark(#0f172a, #e6edf6);
  --text-muted: #55627a;
  --text-muted: light-dark(#55627a, #a3b0c4);
  --text-subtle: #8593ab;
  --text-subtle: light-dark(#8593ab, #64748b);

  --shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
  --shadow: light-dark(
    0 1px 2px rgba(15, 23, 42, 0.06),
    0 1px 2px rgba(0, 0, 0, 0.4)
  );
  --shadow-lg: 0 8px 30px rgba(15, 23, 42, 0.08);
  --shadow-lg: light-dark(
    0 8px 30px rgba(15, 23, 42, 0.08),
    0 8px 30px rgba(0, 0, 0, 0.45)
  );

  --radius: 10px;
  --radius-lg: 16px;
  --wrap: 1120px;
  --gap: 24px;
}

/* What the toggle sets. Absent, the system decides. */
:root[data-theme="light"] {
  color-scheme: light;
}

:root[data-theme="dark"] {
  color-scheme: dark;
}

/* ---------- base ---------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  /* The sticky header would otherwise sit on top of whatever an anchor
     jumped to. */
  scroll-padding-top: 88px;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  * {
    transition: none !important;
    animation: none !important;
  }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family:
    ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica,
    Arial, sans-serif;
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4 {
  margin: 0 0 0.5em;
  line-height: 1.2;
  letter-spacing: -0.02em;
  font-weight: 650;
}

h1 {
  font-size: clamp(2.1rem, 5vw, 3.4rem);
}
h2 {
  font-size: clamp(1.6rem, 3.2vw, 2.3rem);
}
h3 {
  font-size: 1.12rem;
  letter-spacing: -0.01em;
}

p {
  margin: 0 0 1em;
}

a {
  color: var(--primary);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

strong {
  font-weight: 650;
}

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: 12px;
  top: 12px;
  z-index: 100;
  background: var(--surface);
  padding: 10px 16px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
}

:where(a, button, summary):focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 3px;
  border-radius: 6px;
}

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: 22px;
}

.wrap--narrow {
  max-width: 760px;
}

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 18px;
  border-radius: 999px;
  border: 1px solid transparent;
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background 0.15s ease,
    border-color 0.15s ease,
    color 0.15s ease;
}

/* A colored shadow rather than a gray one, so the button looks lit rather
   than raised. Lifts a pixel on hover; the reduced-motion rule kills it. */
.btn--primary {
  background: var(--primary);
  color: var(--primary-contrast);
  box-shadow: 0 6px 18px -6px color-mix(in srgb, var(--primary) 70%, transparent);
  transition:
    background 0.15s ease,
    transform 0.15s ease,
    box-shadow 0.15s ease;
}
.btn--primary:hover {
  background: var(--primary-hover);
  transform: translateY(-1px);
  box-shadow: 0 10px 24px -8px color-mix(in srgb, var(--primary) 80%, transparent);
}

.btn--ghost {
  border-color: var(--border-strong);
  color: var(--text);
  background: transparent;
}
.btn--ghost:hover {
  background: var(--bg-alt);
  border-color: var(--primary);
}

.btn--quiet {
  color: var(--text-muted);
  padding-inline: 10px;
}
.btn--quiet:hover {
  color: var(--text);
}

.btn--lg {
  padding: 14px 26px;
  font-size: 16px;
}

.btn--block {
  display: flex;
  width: 100%;
}

/* ---------- header ---------- */

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: saturate(150%) blur(10px);
  border-bottom: 1px solid var(--border);
}

.nav__inner {
  display: flex;
  align-items: center;
  gap: 20px;
  /* Tall enough for the stacked lockup to breathe. */
  min-height: 72px;
}

/*
 * The wordmark carries the name, so there is no text beside it. Height is
 * fixed and width follows, because the artwork is 2.86:1 and hard-coding both
 * would squash it the day the logo is redrawn.
 */
.brand {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  flex: none;
}

.brand .logo {
  /* The lockup stacks two words, so the second one is roughly a fifth of the
     total height - it needs more overall height than a single-line logo would
     to stay legible. */
  height: 38px;
  width: auto;
}

@media (min-width: 900px) {
  .brand .logo {
    height: 44px;
  }
}

/*
 * Which of the two logo files is shown.
 *
 * Light is the default because that is what a browser with no preference and
 * no JavaScript gets. The dark one appears when the system asks for it, unless
 * the toggle has explicitly said light - and always when the toggle has said
 * dark.
 */
.logo {
  display: block;
}

.logo--dark {
  display: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .logo--light {
    display: none;
  }
  :root:not([data-theme="light"]) .logo--dark {
    display: block;
  }
}

:root[data-theme="dark"] .logo--light {
  display: none;
}

:root[data-theme="dark"] .logo--dark {
  display: block;
}

/* The toggle. Shows the theme it will switch you to. */
.themed {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: 999px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
}

.themed:hover {
  background: var(--bg-alt);
  border-color: var(--border);
  color: var(--text);
}

.themed svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Moon while light is showing, sun while dark is. Mirrors the logo rules. */
.themed__sun {
  display: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .themed__sun {
    display: block;
  }
  :root:not([data-theme="light"]) .themed__moon {
    display: none;
  }
}

:root[data-theme="dark"] .themed__sun {
  display: block;
}

:root[data-theme="dark"] .themed__moon {
  display: none;
}

.nav__links {
  display: none;
  gap: 24px;
  margin-inline: auto;
}

.nav__links a {
  color: var(--text-muted);
  text-decoration: none;
  font-size: 15px;
  font-weight: 500;
}
.nav__links a:hover {
  color: var(--text);
}

.nav__actions {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
}

@media (min-width: 900px) {
  .nav__links {
    display: flex;
  }
  .nav__actions {
    margin-left: 0;
  }
}

/* ---------- hero ---------- */

/*
 * A faint grid behind a wash of brand color.
 *
 * Flat backgrounds everywhere were most of why the page read as plain - every
 * section the same color, so nothing had any depth and the eye had nothing to
 * catch on. The grid is 2% opacity: it should register as texture rather than
 * as a grid anyone notices.
 */
.hero {
  position: relative;
  isolation: isolate;
  padding: clamp(56px, 9vw, 104px) 0 clamp(40px, 6vw, 72px);
  background:
    radial-gradient(
      ellipse 70% 60% at 50% -10%,
      color-mix(in srgb, var(--primary) 15%, transparent),
      transparent 70%
    ),
    var(--bg);
}

.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    linear-gradient(var(--text) 1px, transparent 1px),
    linear-gradient(90deg, var(--text) 1px, transparent 1px);
  background-size: 46px 46px;
  opacity: 0.022;
  /* Fades out before the bottom edge so there is no hard line where it stops. */
  mask-image: radial-gradient(ellipse 80% 70% at 50% 20%, #000 40%, transparent);
}

/*
 * One column on a phone, two once there is room. The copy stays left-aligned
 * at both sizes - centered text beside a left-aligned panel reads as a mistake.
 */
.hero__inner {
  display: grid;
  gap: clamp(32px, 5vw, 56px);
  grid-template-columns: 1fr;
  align-items: center;
}

@media (min-width: 940px) {
  .hero__inner {
    grid-template-columns: 1.05fr 0.95fr;
  }
}

.hero__copy {
  min-width: 0;
}

.eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.09em;
  font-size: 12px;
  font-weight: 700;
  color: var(--primary);
  margin-bottom: 14px;
}

.lede {
  font-size: clamp(1.02rem, 1.6vw, 1.2rem);
  color: var(--text-muted);
  max-width: 56ch;
}

.hero__cta {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  margin: 30px 0 14px;
}

.hero__note {
  font-size: 14px;
  color: var(--text-subtle);
  margin: 0;
}

/* ---------- proof strip (placeholder) ---------- */

.strip {
  border-block: 1px solid var(--border);
  background: var(--bg-alt);
  padding: 22px 0;
}

.strip__inner {
  text-align: center;
}

.strip__label {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-subtle);
  margin: 0 0 6px;
  font-weight: 600;
}

/* Visibly unfinished on purpose - it should look wrong until it is real. */
.strip__todo {
  margin: 0;
  font-size: 14px;
  color: var(--text-subtle);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  padding: 10px 16px;
  display: inline-block;
}

/* ---------- the facts band ---------- */

.facts {
  border-block: 1px solid var(--border);
  background:
    linear-gradient(
      180deg,
      color-mix(in srgb, var(--primary) 7%, transparent),
      transparent
    ),
    var(--bg-alt);
}

.facts__inner {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 26px 20px;
  padding-block: 34px;
}

@media (min-width: 860px) {
  .facts__inner {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

.fact__n {
  margin: 0 0 4px;
  font-size: clamp(1.9rem, 4vw, 2.5rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--primary);
  font-variant-numeric: tabular-nums;
}

/* The dash in "9-7" should read as a range, not as a minus the size of the
   numerals either side of it. */
.fact__n span {
  font-size: 0.55em;
  vertical-align: 0.16em;
  margin-inline: 0.06em;
  color: var(--text-subtle);
}

.fact__l {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--text-muted);
  max-width: 24ch;
}

/* ---------- the comparison ---------- */

.vs {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  margin-top: var(--gap);
  font-size: 14.5px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--surface);
}

/* The empty corner cell of the header row. Present so the table has a
   proper header for the row labels, invisible so it does not read as one. */
.vs__blank {
  display: block;
  width: 1px;
}

.vs__caption {
  caption-side: top;
  text-align: left;
  padding-bottom: 10px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-subtle);
}

.vs th,
.vs td {
  padding: 13px 16px;
  text-align: left;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

.vs tbody tr:last-child th,
.vs tbody tr:last-child td {
  border-bottom: 0;
}

.vs thead th {
  font-size: 12px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-subtle);
  background: var(--bg-alt);
}

.vs tbody th {
  font-weight: 500;
  color: var(--text-muted);
  width: 34%;
}

.vs td {
  color: var(--text);
}

/* The winning cell in each row, whichever column it lands in - the last row
   is deliberately not ours, and marking it the same way is the point. */
.vs .vs__mine {
  color: var(--text);
  font-weight: 600;
  background: color-mix(in srgb, var(--primary) 7%, transparent);
}

.vs thead .vs__mine {
  color: var(--primary);
}

@media (max-width: 620px) {
  .vs,
  .vs thead,
  .vs tbody,
  .vs th,
  .vs td,
  .vs tr {
    display: block;
  }
  /* The header row is meaningless once the columns stack; the row label above
     each pair carries it instead. */
  .vs thead {
    display: none;
  }
  .vs tbody tr {
    border-bottom: 1px solid var(--border);
    padding: 6px 0;
  }
  .vs tbody tr:last-child {
    border-bottom: 0;
  }
  .vs tbody th {
    width: auto;
    border-bottom: 0;
    padding-bottom: 2px;
    font-size: 12.5px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
  }
  .vs tbody td {
    border-bottom: 0;
    padding-block: 3px;
  }
  .vs tbody td::before {
    content: "Resold: ";
    color: var(--text-subtle);
  }
  .vs tbody td.vs__mine::before {
    content: "Here: ";
    color: var(--primary);
  }
}

/* ---------- sections ---------- */

.section {
  padding: clamp(56px, 8vw, 96px) 0;
}

.section--alt {
  background: var(--bg-alt);
  border-block: 1px solid var(--border);
}

/*
 * A short label above each heading, with a rule leading into it.
 *
 * Seven sections that all began with a bare h2 gave the page one rhythm and no
 * sense of where you were in it. This is the cheapest way to give each one a
 * beat of its own without another box.
 */
.kicker {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 10px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--primary);
}

.kicker::before {
  content: "";
  width: 22px;
  height: 2px;
  border-radius: 2px;
  background: var(--primary);
  flex: none;
}

.section h2 {
  max-width: 720px;
}

.section__lede {
  font-size: 1.05rem;
  color: var(--text-muted);
  max-width: 660px;
  margin-bottom: 36px;
}


@media (min-width: 780px) {
  .ui__list {
    border-bottom: 0;
    border-right: 1px solid var(--border);
  }
}


.row:last-child {
  border-bottom: 0;
}


@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .tag--question {
    color: #60a5fa;
  }
  :root:not([data-theme="light"]) .tag--out {
    color: #fbbf24;
  }
}

:root[data-theme="dark"] .tag--question {
  color: #60a5fa;
}
:root[data-theme="dark"] .tag--out {
  color: #fbbf24;
}


/* ---------- cards ---------- */

.cards {
  display: grid;
  gap: var(--gap);
  grid-template-columns: 1fr;
}

@media (min-width: 700px) {
  .cards--2,
  .cards--3 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 1000px) {
  .cards--3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/*
 * Cards lift slightly on hover and the top edge picks up the brand color.
 *
 * Nothing on the page moved before, which is part of what made it feel like a
 * document rather than a product. Kept to 2px and 140ms - enough to feel
 * responsive, not enough to be a effect anyone has to sit through. The
 * reduced-motion rule at the top of the file turns the transition off.
 */
.card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 26px;
  box-shadow: var(--shadow);
  transition:
    transform 0.14s ease,
    border-color 0.14s ease,
    box-shadow 0.14s ease;
}

.card::after {
  content: "";
  position: absolute;
  inset: -1px -1px auto;
  height: 2px;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: linear-gradient(90deg, var(--primary), transparent 70%);
  opacity: 0;
  transition: opacity 0.14s ease;
}

.card:hover {
  transform: translateY(-2px);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-lg);
}

.card:hover::after {
  opacity: 1;
}

/* The sprite itself never renders; only its symbols, via <use>. */
.sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.card__icon {
  width: 22px;
  height: 22px;
  margin-bottom: 12px;
  display: block;
  fill: none;
  stroke: var(--primary);
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.card h3 {
  margin-bottom: 8px;
}

.card p {
  margin: 0;
  color: var(--text-muted);
  font-size: 15.5px;
}

.card--tight {
  padding: 22px;
}

/* ---------- the two routes ---------- */

.routes {
  display: grid;
  gap: var(--gap);
  grid-template-columns: 1fr;
  margin-top: var(--gap);
}

@media (min-width: 820px) {
  .routes {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.route {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 22px;
  background: var(--surface);
}

.routes--hero {
  grid-template-columns: 1fr;
  margin-top: 0;
}

.route--ours {
  border-color: var(--primary);
}

.route__who {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-subtle);
  margin: 0 0 14px;
}

.route--ours .route__who {
  color: var(--primary);
}

.route__path {
  list-style: none;
  margin: 0 0 14px;
  padding: 0;
  display: flex;
  align-items: stretch;
  gap: 0;
}

.node {
  flex: 1;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 6px;
  text-align: center;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-muted);
  background: var(--bg-alt);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
}

.node span {
  display: block;
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-subtle);
}

/* The arrow between nodes, drawn as a spacer rather than a character so it
   scales with the boxes and never wraps onto its own line. */
.node + .node {
  margin-left: 26px;
  position: relative;
}

.node + .node::before {
  content: "";
  position: absolute;
  left: -20px;
  top: 50%;
  width: 14px;
  height: 1px;
  background: var(--border-strong);
}

.node + .node::after {
  content: "";
  position: absolute;
  left: -10px;
  top: 50%;
  width: 5px;
  height: 5px;
  border-top: 1px solid var(--border-strong);
  border-right: 1px solid var(--border-strong);
  transform: translateY(-50%) rotate(45deg);
}

.node--mine {
  border-color: var(--primary);
  color: var(--text);
  background: var(--primary-soft);
}

.node--mine span {
  color: var(--primary-hover);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .node--mine span {
    color: var(--primary);
  }
}

:root[data-theme="dark"] .node--mine span {
  color: var(--primary);
}

.node--theirs {
  border-style: dashed;
}

.route__note {
  margin: 0;
  font-size: 14px;
  color: var(--text-muted);
}

.callout {
  margin-top: var(--gap);
  border-left: 3px solid var(--primary);
  background: var(--surface);
  border-radius: var(--radius);
  padding: 18px 22px;
}

.callout p {
  margin: 0;
  color: var(--text-muted);
  font-size: 15.5px;
}

/* ---------- steps ---------- */

.steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--gap);
  grid-template-columns: 1fr;
  counter-reset: step;
}

@media (min-width: 640px) {
  .steps {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 1040px) {
  .steps {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

.step {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 24px;
}

.step__n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 999px;
  background: var(--primary-soft);
  color: var(--primary-hover);
  font-weight: 700;
  font-size: 14px;
  margin-bottom: 12px;
}

@media (prefers-color-scheme: dark) {
  .step__n {
    color: var(--primary);
  }
}

.step h3 {
  margin-bottom: 6px;
}

.step p {
  margin: 0;
  color: var(--text-muted);
  font-size: 15px;
}

/* ---------- pricing ---------- */

.plans {
  display: grid;
  gap: var(--gap);
  grid-template-columns: 1fr;
  align-items: start;
}

@media (min-width: 900px) {
  .plans {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.plan {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 28px 26px;
  box-shadow: var(--shadow);
}

/* The recommended tier stands slightly proud of the other two, and only where
   there is room for three columns - stacked on a phone, "raised" is nonsense. */
.plan--featured {
  border-color: var(--primary);
  box-shadow: var(--shadow-lg);
  background:
    linear-gradient(
      180deg,
      color-mix(in srgb, var(--primary) 6%, transparent),
      transparent 40%
    ),
    var(--surface);
}

@media (min-width: 900px) {
  .plan--featured {
    transform: scale(1.035);
  }
}

.plan__flag {
  position: absolute;
  top: -12px;
  left: 26px;
  margin: 0;
  background: var(--primary);
  color: var(--primary-contrast);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 4px 12px;
  border-radius: 999px;
}

.plan__price {
  display: flex;
  align-items: baseline;
  gap: 5px;
  margin: 2px 0 18px;
}

.plan__amt {
  font-size: 2.3rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

.plan__per {
  color: var(--text-subtle);
  font-size: 15px;
}

.plan__limits {
  list-style: none;
  margin: 0 0 22px;
  padding: 0;
  display: grid;
  gap: 9px;
}

.plan__limits li {
  color: var(--text-muted);
  font-size: 15px;
  padding-left: 24px;
  position: relative;
}

.plan__limits li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 0.62em;
  width: 9px;
  height: 5px;
  border-left: 2px solid var(--primary);
  border-bottom: 2px solid var(--primary);
  transform: rotate(-45deg);
}

.plan__limits strong {
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* ---------- FAQ ---------- */

/* Each question is its own surface rather than a row in a list, so an open one
   reads as a panel and the closed ones read as things to press. */
.faq {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 2px 18px;
  margin-bottom: 10px;
  transition: border-color 0.14s ease;
}

.faq:hover,
.faq[open] {
  border-color: var(--border-strong);
}

.faq[open] {
  background: var(--bg-alt);
}

.faq summary {
  cursor: pointer;
  list-style: none;
  padding: 16px 34px 16px 0;
  font-weight: 600;
  position: relative;
}

.faq summary::-webkit-details-marker {
  display: none;
}

.faq summary::after {
  content: "";
  position: absolute;
  right: 8px;
  top: 25px;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--text-subtle);
  border-bottom: 2px solid var(--text-subtle);
  transform: rotate(45deg);
  transition: transform 0.15s ease;
}

.faq[open] summary::after {
  transform: rotate(-135deg);
  top: 28px;
}

.faq p {
  margin: 0 0 18px;
  color: var(--text-muted);
  font-size: 15.5px;
  max-width: 68ch;
}

/* ---------- fine print ---------- */

.fineprint {
  margin-top: 26px;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--text-subtle);
  max-width: 72ch;
}

/* ---------- closing CTA ---------- */

.close {
  padding: clamp(56px, 8vw, 92px) 0;
  text-align: center;
  background:
    radial-gradient(
      ellipse 60% 70% at 50% 110%,
      color-mix(in srgb, var(--primary) 14%, transparent),
      transparent 70%
    ),
    var(--bg);
}

.close__inner p {
  color: var(--text-muted);
  max-width: 480px;
  margin-inline: auto;
  margin-bottom: 26px;
}

/* ---------- footer ---------- */

.foot {
  border-top: 1px solid var(--border);
  background: var(--bg-alt);
  padding-top: 48px;
}

.foot__inner {
  display: grid;
  gap: 34px;
  grid-template-columns: 1fr;
}

@media (min-width: 760px) {
  .foot__inner {
    grid-template-columns: 1.3fr 2fr;
  }
}

/*
 * Size only. No display.
 *
 * This carried `display: block`, which is the same specificity as
 * `.logo--dark { display: none }` and comes later in the file, so it won the
 * cascade and both logos rendered - the light one and the relit one stacked on
 * top of each other in the footer. The header never showed it because nothing
 * there re-declares display.
 *
 * `.logo` already sets block. Which of the two is shown is the light/dark
 * rules' job, and this has no business having an opinion about it.
 */
.foot__logo {
  height: 40px;
  width: auto;
}

.foot__brand p {
  color: var(--text-subtle);
  font-size: 14.5px;
  margin: 14px 0 0;
  max-width: 34ch;
}

.foot__links {
  display: grid;
  gap: 26px;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
}

.foot__links h4 {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-subtle);
  margin-bottom: 12px;
}

.foot__links a {
  display: block;
  color: var(--text-muted);
  text-decoration: none;
  font-size: 15px;
  padding: 3px 0;
}

.foot__links a:hover {
  color: var(--primary);
}

.foot__legal {
  margin-top: 40px;
  padding: 20px 22px 30px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  justify-content: space-between;
}

.foot__legal p {
  margin: 0;
  font-size: 13px;
  color: var(--text-subtle);
}

/* ---------------------------------------------------------------------------
   The drawn product

   A picture of the app that is not a picture of anyone's prospect list. Built
   from the same tokens as everything else, so it follows the theme toggle and
   costs one more request than a flat color would - which is none.

   Everything here is decoration and is hidden from screen readers in the
   markup. It carries no information the page does not also say in words.
   --------------------------------------------------------------------------- */
.appshot {
  position: relative;
  min-width: 0;
  /* Room at the bottom-left for the floating card to hang off the frame. */
  padding: 0 0 clamp(24px, 3vw, 34px) 0;
}

.appshot__frame {
  position: relative;
  z-index: 1;
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}

/* Window chrome. Three dots and a title is the shorthand for "this is an
   application", and it costs six elements. */
.appshot__chrome {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 11px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-alt);
}

.appshot__dot {
  width: 9px;
  height: 9px;
  border-radius: 999px;
  background: var(--border-strong);
}

.appshot__title {
  margin-left: 8px;
  font-size: 12px;
  font-weight: 650;
  color: var(--text-subtle);
}

.appshot__body {
  display: grid;
  grid-template-columns: 52px 1fr;
  min-height: 268px;
}

.appshot__rail {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px 8px;
  border-right: 1px solid var(--border);
  background: var(--bg-alt);
}

.appshot__railitem {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 7px 6px;
  border-radius: 7px;
  color: var(--text-subtle);
}

.appshot__railitem svg {
  width: 15px;
  height: 15px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.appshot__railitem--on {
  background: var(--primary-soft);
  color: var(--primary-hover);
}

.appshot__main {
  padding: 14px;
  min-width: 0;
}

.appshot__pills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 12px;
}

.appshot__pill {
  font-size: 11px;
  font-weight: 600;
  padding: 4px 9px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-muted);
  white-space: nowrap;
}

.appshot__pill--on {
  border-color: transparent;
  background: var(--primary);
  color: var(--primary-contrast);
}

.appshot__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 7px;
}

.appshot__row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--bg);
}

.appshot__avatar {
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 999px;
  background: linear-gradient(
    135deg,
    color-mix(in srgb, var(--primary) 38%, transparent),
    color-mix(in srgb, var(--primary) 12%, transparent)
  );
}

.appshot__lines {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  gap: 5px;
}

/*
  The message, as bars.

  Inventing a conversation to sell software means writing both halves of an
  exchange that never happened and attributing one of them to a homeowner. The
  shape of a reply is the part that matters here; the chip beside it is what is
  actually being sold.
*/
.appshot__name,
.appshot__snippet {
  display: block;
  height: 7px;
  border-radius: 4px;
  background: var(--text);
}

.appshot__name {
  width: 38%;
  opacity: 0.5;
}
.appshot__name--sm { width: 28%; }
.appshot__name--md { width: 33%; }

.appshot__snippet {
  width: 78%;
  height: 6px;
  opacity: 0.17;
}
.appshot__snippet--sm { width: 56%; }
.appshot__snippet--md { width: 68%; }

/* The one thing left legible: what the reply meant. */
.appshot__chip {
  flex: none;
  font-size: 10.5px;
  font-weight: 650;
  padding: 3px 8px;
  border-radius: 999px;
  white-space: nowrap;
  border: 1px solid transparent;
}

.appshot__chip--yes {
  background: color-mix(in srgb, var(--primary) 18%, transparent);
  border-color: color-mix(in srgb, var(--primary) 40%, transparent);
  color: var(--primary-hover);
}

.appshot__chip--ask {
  background: color-mix(in srgb, #f59e0b 16%, transparent);
  border-color: color-mix(in srgb, #f59e0b 38%, transparent);
  color: light-dark(#b45309, #fbbf24);
}

.appshot__chip--no,
.appshot__chip--stop {
  background: color-mix(in srgb, var(--text-subtle) 14%, transparent);
  border-color: var(--border-strong);
  color: var(--text-muted);
}

/* Lifted off the corner so the frame reads as a surface with something in
   front of it rather than one flat rectangle. */
.appshot__float {
  position: absolute;
  z-index: 2;
  left: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 14px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--surface);
  box-shadow: var(--shadow-lg);
}

.appshot__floaticon {
  width: 16px;
  height: 16px;
  flex: none;
  fill: none;
  stroke: var(--primary);
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.appshot__floatlabel {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-subtle);
  margin: 0;
}

.appshot__floatvalue {
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
  margin: 0;
  white-space: nowrap;
}

.appshot__floatnote {
  font-size: 11.5px;
  color: var(--text-muted);
  margin: 0;
  white-space: nowrap;
}

/* Under 560px the rail and the chips fight the list for width, and the
   illustration stops illustrating. The copy above it already says all of
   this, so it steps out of the way rather than compressing. */
@media (max-width: 559px) {
  .appshot {
    display: none;
  }
}

/* An icon per step, sitting opposite the number. The steps were four
   identical boxes distinguished only by a digit. */
.step {
  position: relative;
}

.step__icon {
  position: absolute;
  top: 22px;
  right: 22px;
  width: 22px;
  height: 22px;
  fill: none;
  stroke: var(--primary);
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.42;
}

/* The tier most people should be on, lit rather than only outlined. */
.plan--featured {
  position: relative;
}

.plan--featured::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  pointer-events: none;
  background: radial-gradient(
    ellipse 80% 55% at 50% 0%,
    color-mix(in srgb, var(--primary) 13%, transparent),
    transparent 70%
  );
}

/* ---------------------------------------------------------------------------
   Showcase: copy beside a drawn panel

   Three claims the page made in sentences and never showed. Alternating sides
   so a run of them does not read as a list, and stacking to one column early -
   a 300px-wide panel of controls is not a picture of anything.
   --------------------------------------------------------------------------- */
.showcase {
  display: grid;
  gap: clamp(24px, 4vw, 48px);
  grid-template-columns: 1fr;
  align-items: center;
  margin-top: clamp(32px, 5vw, 56px);
}

@media (min-width: 900px) {
  .showcase {
    grid-template-columns: 1fr 1fr;
  }

  /* The panel first in source order stays first for a screen reader; only the
     visual order flips, and only where there are two columns to flip. */
  .showcase--flip .showcase__copy {
    order: 2;
  }
}

.showcase__copy h3 {
  font-size: clamp(20px, 2.4vw, 25px);
  margin-bottom: 10px;
}

.showcase__copy p {
  color: var(--text-muted);
}

/* A list of things that are true, marked rather than bulleted. */
.ticks {
  list-style: none;
  margin: 16px 0 0;
  padding: 0;
  display: grid;
  gap: 9px;
}

.ticks li {
  position: relative;
  padding-left: 26px;
  color: var(--text-muted);
  font-size: 15px;
}

.ticks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 6px;
  width: 14px;
  height: 8px;
  border-left: 2px solid var(--primary);
  border-bottom: 2px solid var(--primary);
  transform: rotate(-45deg);
}

/* The drawn panel itself. */
.panel {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  min-width: 0;
}

.panel__head {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-alt);
}

.panel__icon {
  width: 16px;
  height: 16px;
  flex: none;
  fill: none;
  stroke: var(--primary);
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.panel__title {
  font-size: 13px;
  font-weight: 650;
  color: var(--text);
}

.panel__state {
  margin-left: auto;
  font-size: 11px;
  font-weight: 650;
  padding: 3px 9px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--primary) 16%, transparent);
  color: var(--primary-hover);
}

.panel__body {
  padding: 16px;
}

.field {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-top: 14px;
}

.field:first-child {
  margin-top: 0;
}

.field__label {
  font-size: 12px;
  font-weight: 650;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-subtle);
}

.field__value {
  font-size: 17px;
  font-weight: 700;
  color: var(--text);
}

.field__hint {
  font-size: 12.5px;
  color: var(--text-muted);
  margin: 2px 0 0;
}

.meter {
  height: 7px;
  border-radius: 999px;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  overflow: hidden;
  margin-top: 8px;
}

.meter span {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--primary);
}

.days {
  display: flex;
  gap: 6px;
  margin-top: 8px;
}

.day {
  flex: 1 1 0;
  text-align: center;
  font-size: 12px;
  font-weight: 650;
  padding: 6px 0;
  border-radius: 7px;
  border: 1px solid var(--border);
  color: var(--text-subtle);
}

.day--on {
  border-color: transparent;
  background: color-mix(in srgb, var(--primary) 16%, transparent);
  color: var(--primary-hover);
}

/* The sequence, as a spine with steps hanging off it. */
.seq {
  list-style: none;
  margin: 0;
  padding: 16px 16px 16px 18px;
  display: grid;
  gap: 0;
}

.seq__step {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 0 11px 26px;
}

/* The dot, and the line joining it to the next one. Drawn on the step rather
   than as a separate element so a step can be added without touching this. */
.seq__step::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 11px;
  height: 11px;
  border-radius: 999px;
  border: 2px solid var(--primary);
  background: var(--surface);
}

.seq__step::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 50%;
  width: 1px;
  height: 100%;
  background: var(--border-strong);
}

.seq__step:last-child::after {
  display: none;
}

.seq__when {
  flex: none;
  width: 58px;
  font-size: 12.5px;
  font-weight: 650;
  color: var(--text-muted);
}

.seq__bar {
  flex: 1 1 auto;
  height: 8px;
  border-radius: 4px;
  background: var(--text);
  opacity: 0.16;
}

.seq__bar--sm { max-width: 58%; }
.seq__bar--md { max-width: 74%; }

/* Where it stops. The hollow dot and the muted rule say "this is an end",
   which is the whole claim the step is making. */
.seq__step--stop::before {
  border-color: var(--text-subtle);
  border-style: dashed;
}

.seq__stop {
  font-size: 13px;
  font-weight: 650;
  color: var(--primary-hover);
}

/* Suppression: four ticks and the one send they stopped. */
.checks {
  list-style: none;
  margin: 0;
  padding: 16px 16px 4px;
  display: grid;
  gap: 9px;
  grid-template-columns: 1fr 1fr;
}

.check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13.5px;
  color: var(--text-muted);
}

.check__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex: none;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  background: color-mix(in srgb, var(--primary) 18%, transparent);
  color: var(--primary-hover);
}

.blocked {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 12px 16px 16px;
  padding: 11px 12px;
  border-radius: 9px;
  border: 1px dashed var(--border-strong);
  background: var(--bg-alt);
}

.blocked__tag {
  flex: none;
  font-size: 11px;
  font-weight: 700;
  padding: 3px 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--text-subtle) 18%, transparent);
  color: var(--text-muted);
}

.blocked__bar {
  flex: 1 1 auto;
  height: 7px;
  border-radius: 4px;
  background: var(--text);
  opacity: 0.13;
}

.blocked__why {
  flex: none;
  font-size: 12.5px;
  color: var(--text-muted);
}

@media (max-width: 520px) {
  .checks {
    grid-template-columns: 1fr;
  }
}

/* ---------------------------------------------------------------------------
   The facts strip, with the shape of each number under it

   Four bare figures is the one thing on a page nobody stops for. Each graphic
   is drawn from its own number, so a figure edited without its picture would
   look wrong rather than quietly disagree.
   --------------------------------------------------------------------------- */
.fact__viz {
  display: block;
  margin-top: 12px;
  max-width: 24ch;
}

.fact__track {
  display: block;
  height: 5px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--text-subtle) 20%, transparent);
  overflow: hidden;
}

.fact__track > span {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--primary);
}

/* Twenty-four ticks. The lit ones are the hours sending is allowed. */
.fact__viz--day {
  display: flex;
  gap: 2px;
}

.fact__viz--day i {
  flex: 1 1 0;
  height: 12px;
  border-radius: 2px;
  background: color-mix(in srgb, var(--text-subtle) 20%, transparent);
}

.fact__viz--day i.on {
  background: var(--primary);
}

/* Three lists, drawn as three sheets. */
.fact__viz--stack {
  display: grid;
  gap: 3px;
}

.fact__viz--stack i {
  height: 5px;
  border-radius: 999px;
  background: var(--primary);
}

.fact__viz--stack i:nth-child(2) { width: 76%; opacity: 0.66; }
.fact__viz--stack i:nth-child(3) { width: 52%; opacity: 0.4; }

/* The intent check is not one of the three lists, and the panel should not
   imply it is. */
.check--intent {
  grid-column: 1 / -1;
  padding-top: 9px;
  border-top: 1px dashed var(--border-strong);
}

/* ---------------------------------------------------------------------------
   After the questions

   A pricing page raises one more question than it answers, and the last thing
   before the footer should be the answer rather than another heading.
   --------------------------------------------------------------------------- */
.askmore {
  margin-top: clamp(24px, 4vw, 36px);
  padding: clamp(22px, 3vw, 30px);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-strong);
  background:
    radial-gradient(
      ellipse 70% 120% at 0% 0%,
      color-mix(in srgb, var(--primary) 12%, transparent),
      transparent 70%
    ),
    var(--surface);
  text-align: center;
}

.askmore__q {
  margin: 0 0 6px;
  font-size: 19px;
  font-weight: 700;
  color: var(--text);
}

.askmore__a {
  margin: 0 auto;
  max-width: 56ch;
  color: var(--text-muted);
}

/* ---------------------------------------------------------------------------
   The header on a phone

   Below 900px the link row hides, which left the wordmark and both buttons on
   one line - and at 390px that came to about 26px more than the viewport, so
   every page on the site scrolled sideways. A horizontal scrollbar on a
   marketing page reads as broken before anybody has read a word.

   Tightened rather than hidden. Dropping "Sign in" would save the space and
   cost a returning subscriber the only way back in from their phone.
   --------------------------------------------------------------------------- */
@media (max-width: 460px) {
  .nav__inner {
    gap: 8px;
  }

  /* 2.86:1 artwork, so height is what drives the width it takes. */
  .brand .logo {
    height: 30px;
  }

  .nav__actions {
    gap: 4px;
  }

  .nav__actions .btn {
    font-size: 13.5px;
    padding: 8px 13px;
  }

  .themed {
    width: 30px;
    height: 30px;
  }
}


/*
 * The smallest phones still in use, around 320px.
 *
 * At that width the wordmark, the toggle, "Sign in" and the trial button come
 * to 314px of content in 257px of room. Tightening cannot close a 57px gap, so
 * something has to go, and it is the "Sign in" link: it is duplicated in the
 * footer under Account, whereas the trial button is the only one of the two
 * that is not repeated anywhere above the fold.
 *
 * Dropped rather than clipped, and deliberately so. There was an
 * `overflow-x: clip` on the root here for a while as a catch-all, and what it
 * actually bought at this width was a primary call to action with its right
 * half cut off and no scrollbar to reveal it - a hidden symptom rather than a
 * fixed layout. It is gone: the widths below are correct on their own, which
 * is checkable, and clip on the root has side effects worth not carrying for
 * a problem that no longer exists.
 */
@media (max-width: 380px) {
  .nav__actions .btn--quiet {
    display: none;
  }

  .wrap {
    padding-inline: 16px;
  }

  /* Two columns leaves each figure about 118px, which is narrower than the
     labels want and turns every one of them into four wrapped lines. */
  .facts__inner {
    grid-template-columns: 1fr;
    gap: 20px;
  }
}

/* ---------------------------------------------------------------------------
   A wireframe per step

   The four steps described a sequence and showed none of it. These sit at
   about 214px inside a card in the four-column layout, so everything is one
   line and nothing relies on a label longer than two words.

   The Twilio credentials are masked rather than filled with a plausible SID:
   a screenshot of that screen with anything real in it is a screenshot of live
   keys, and a fake one still teaches the shape of a key to anyone reading.
   --------------------------------------------------------------------------- */
.stepviz {
  margin-top: 16px;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-alt);
  display: grid;
  gap: 8px;
}

.sv__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.sv__key {
  font-size: 11px;
  font-weight: 650;
  color: var(--text-subtle);
  white-space: nowrap;
}

.sv__val {
  font-size: 12px;
  font-weight: 700;
  color: var(--text);
}

/* Dots, not characters - there is nothing here to read. */
.sv__mask {
  flex: 1 1 auto;
  height: 16px;
  border-radius: 5px;
  border: 1px solid var(--border-strong);
  background-image: radial-gradient(
    circle,
    var(--text-subtle) 1.1px,
    transparent 1.2px
  );
  background-size: 7px 7px;
  background-position: center left 6px;
  background-repeat: repeat-x;
  max-width: 96px;
  opacity: 0.75;
}

.sv__foot {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}

.sv__pill {
  font-size: 10px;
  font-weight: 650;
  padding: 3px 7px;
  border-radius: 999px;
  border: 1px solid var(--border-strong);
  color: var(--text-muted);
  white-space: nowrap;
}

.sv__pill--ok {
  border-color: transparent;
  background: color-mix(in srgb, var(--primary) 18%, transparent);
  color: var(--primary-hover);
}

.sv__pill--wait {
  border-style: dashed;
}

.sv__pill--ask {
  border-color: transparent;
  background: color-mix(in srgb, #f59e0b 16%, transparent);
  color: light-dark(#b45309, #fbbf24);
}

/* Step 2: a file, and its columns pointed at the fields they fill. */
.sv__file {
  justify-self: start;
  font-size: 10.5px;
  font-weight: 650;
  padding: 3px 8px;
  border-radius: 5px;
  border: 1px dashed var(--border-strong);
  color: var(--text-muted);
}

.sv__map {
  display: flex;
  align-items: center;
  gap: 7px;
}

.sv__col {
  width: 42px;
  height: 7px;
  border-radius: 4px;
  background: var(--text);
  opacity: 0.18;
  flex: none;
}

.sv__col--sm { width: 28px; }
.sv__col--md { width: 36px; }

.sv__arrow {
  font-size: 11px;
  color: var(--text-subtle);
  flex: none;
}

.sv__field {
  font-size: 11px;
  color: var(--text-muted);
  white-space: nowrap;
}

/* Step 3: the batch, against the day. */
.sv__ticks {
  display: flex;
  gap: 3px;
}

.sv__ticks i {
  flex: 1 1 0;
  height: 9px;
  border-radius: 2px;
  background: color-mix(in srgb, var(--text-subtle) 22%, transparent);
}

.sv__ticks i.on {
  background: var(--primary);
}

/* Step 4: what came back, already sorted. */
.sv__reply {
  display: flex;
  align-items: center;
  gap: 7px;
}

.sv__bar {
  flex: 1 1 auto;
  height: 7px;
  border-radius: 4px;
  background: var(--text);
  opacity: 0.16;
}

.sv__bar--sm { max-width: 40%; }
.sv__bar--md { max-width: 58%; }

/* ---------------------------------------------------------------------------
   Opt-out rate by sending number

   A chart rather than a row of bars. The previous version drew five lengths
   with no scale, no values and no labels, so it illustrated nothing - a reader
   could not tell what was being measured or which number was the bad one.

   Everything here exists to make one sentence visible: an account-wide average
   hides a single number going wrong. So the numbers are individually labeled,
   the rates are printed, and the threshold is a line you can see four of them
   sitting under and one sitting past.

   The scale is 0-4%. Real opt-out rates are fractions of a percent - the
   measured figure on the account this was built from is 1.66% - so a chart
   drawn to 100% would show five slivers and prove nothing.
   --------------------------------------------------------------------------- */
.numbers {
  margin-top: clamp(24px, 4vw, 36px);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow);
  overflow: hidden;
}

.numbers__head {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 13px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-alt);
}

.numbers__icon {
  width: 16px;
  height: 16px;
  flex: none;
  fill: none;
  stroke: var(--primary);
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.numbers__title {
  font-size: 13px;
  font-weight: 650;
  color: var(--text);
}

.numbers__period {
  margin-left: auto;
  font-size: 11.5px;
  color: var(--text-subtle);
}

/*
  One grid shared by the scale row and every bar, so the threshold caption sits
  exactly above the line the bars are measured against. Declared once here and
  inherited, because two grids drifting apart is how a chart starts lying.
*/
.numbers__scale,
.num {
  display: grid;
  grid-template-columns: 78px 1fr 46px 62px;
  align-items: center;
  gap: 12px;
}

.numbers__scale {
  padding: 12px 16px 0;
}

/* Sits in the track column, at the threshold's own position. */
.numbers__mark {
  grid-column: 2;
  position: relative;
  font-size: 11px;
  font-weight: 650;
  color: var(--text-subtle);
  padding-left: 50%;
  white-space: nowrap;
}

.numbers__mark::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -4px;
  width: 1px;
  height: 10px;
  border-left: 1px dashed var(--text-subtle);
}

.numbers__list {
  list-style: none;
  margin: 0;
  padding: 6px 16px 16px;
  display: grid;
  gap: 8px;
}

/* Masked to the last four. A sending number printed on a public page is a
   number published to be texted. */
.num__id {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  color: var(--text-muted);
  white-space: nowrap;
}

.num__track {
  position: relative;
  height: 10px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--text-subtle) 15%, transparent);
  overflow: visible;
}

.num__fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--primary);
}

/* The threshold, continued through every row. Together with the caption above
   they read as one line across the chart. */
.num__track::after {
  content: "";
  position: absolute;
  left: 50%;
  top: -5px;
  bottom: -5px;
  border-left: 1px dashed var(--text-subtle);
}

.num__val {
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
  font-weight: 650;
  color: var(--text-muted);
  text-align: right;
}

/* The one that is wrong. Color alone would not carry it - hence the value,
   the position past the line, and the word. */
.num--hot .num__fill {
  background: light-dark(#dc2626, #f87171);
}

.num--hot .num__id,
.num--hot .num__val {
  color: light-dark(#b91c1c, #fca5a5);
}

.num__flag {
  font-size: 10.5px;
  font-weight: 700;
  padding: 3px 9px;
  border-radius: 999px;
  text-align: center;
  background: color-mix(in srgb, #dc2626 15%, transparent);
  color: light-dark(#b91c1c, #fca5a5);
}

@media (max-width: 560px) {
  .numbers__scale,
  .num {
    grid-template-columns: 62px 1fr 42px;
    gap: 8px;
  }
  /* No room for a word beside a bar at this width; the color and the value
     past the line still say it. */
  .num__flag {
    display: none;
  }
  .numbers__mark {
    font-size: 10px;
  }
}

/* ---------------------------------------------------------------------------
   Message variations

   One template above, three messages from it below, with the parts that
   differ picked out. Two highlight colors doing two different jobs: the
   brand color is a choice the spinner made, the neutral one is a merge field
   filled from the prospect. Conflating them would suggest the name was
   spun too.

   The syntax is the product's own - see shared/spinner.mjs. Inventing a
   prettier one here would teach a prospect something to unlearn on their
   first template.
   --------------------------------------------------------------------------- */
.spin {
  padding: 14px 16px 16px;
}

.spin__label {
  margin: 0 0 7px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-subtle);
}

.spin__label--out {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px dashed var(--border-strong);
}

/* Monospace, because it is something you type rather than something you
   read. */
.spin__template {
  margin: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px;
  line-height: 1.75;
  color: var(--text-muted);
}

.showcase__copy code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9em;
  padding: 1px 5px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--text-subtle) 14%, transparent);
  color: var(--text);
}

/* An alternative the sender will choose between. */
.spin__opt {
  padding: 1px 4px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--primary) 17%, transparent);
  color: var(--primary-hover);
  font-weight: 600;
}

/* A merge field. Never spun - it comes from the prospect's row. */
.spin__merge {
  padding: 1px 4px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--text-subtle) 16%, transparent);
  color: var(--text-muted);
}

.spin__outs {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 9px;
}

.spin__outs li {
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--text-muted);
  padding: 9px 11px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
}

/* What the spinner picked for this one. Same color as the alternatives it
   came from, so the eye connects the two halves without a caption. */
.spin__pick {
  font-weight: 650;
  color: var(--primary-hover);
}

.spin__filled {
  font-weight: 650;
  color: var(--text);
}

/* ---------------------------------------------------------------------------
   Bar widths

   These were inline style="width: N%" attributes, which worked everywhere I
   tested and were dead on the deployed site: vercel.json sends
   `style-src 'self'` with no 'unsafe-inline', so the browser dropped every one
   of them. A bar with no width set fills its container, so all five bars in
   the opt-out chart rendered identically at 100% - a chart that showed five
   numbers as the same number, which is worse than showing nothing.

   The local preview server sent no CSP, so the page under test was not the
   page that shipped. It sends the real headers now - see
   scratchpad/preview.js.

   Classes rather than 'unsafe-inline', because loosening a policy to make a
   bar chart work is the wrong trade: 'unsafe-inline' on style-src is a real
   weakening, and these values are fixed illustration data that belong in a
   stylesheet anyway.
   --------------------------------------------------------------------------- */
.w-0 { width: 0%; }
.w-10 { width: 10%; }
.w-15 { width: 15%; }
.w-22-5 { width: 22.5%; }
.w-30 { width: 30%; }
.w-68 { width: 68%; }
.w-77-5 { width: 77.5%; }
.w-100 { width: 100%; }

/* The axis. Sits in the track column, on the same grid as the bars, so 0% and
   4% line up with the ends of the tracks they describe. */
.numbers__axis {
  display: grid;
  grid-template-columns: 78px 1fr 46px 62px;
  gap: 12px;
  padding: 0 16px 6px;
}

.numbers__axis > span {
  grid-column: 2;
  display: flex;
  justify-content: space-between;
  font-size: 10.5px;
  color: var(--text-subtle);
  font-variant-numeric: tabular-nums;
}

/* Both labels share one grid cell and sit at its two ends. */
.numbers__axis > span:first-child {
  grid-row: 1;
}
.numbers__axis > span:last-child {
  grid-row: 1;
  justify-content: flex-end;
}

@media (max-width: 560px) {
  .numbers__axis {
    grid-template-columns: 62px 1fr 42px;
    gap: 8px;
  }
}
