/* ------------------------------------------------------------------
     Red Pen, on the web.

     The app's own system, unchanged: ink ground, bone text, hairlines
     instead of cards, and the accent kept for what was actually
     delivered. One theme, because the app ships one.
     ------------------------------------------------------------------ */
  :root {
    --ink: #0B0B0C;
    --surface: #131315;
    --well: #171719;
    --rule: #26262A;
    --rule-strong: #3A3A40;
    --bone: #F2EFE9;
    --muted: #8A8A90;
    --faint: #5A5A61;
    --ghost: #3E3E44;
    --pen: #D6301F;
    --pen-dim: #8F2418;
    --on-pen: #FFF8F5;

    --display: "Instrument Serif", Georgia, "Times New Roman", serif;
    --sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    --mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;

    --measure: 34rem;
    --gutter: clamp(1.25rem, 5vw, 2.5rem);

    color-scheme: dark;
  }

  *, *::before, *::after { box-sizing: border-box; }
  html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

  body {
    margin: 0;
    background: var(--ink);
    color: var(--bone);
    font-family: var(--sans);
    font-size: 17px;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
  }

  /* ---------------------------------------------------- type roles */
  .eyebrow {
    font-family: var(--mono);
    font-size: 0.6875rem;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: lowercase;
    color: var(--faint);
    margin: 0;
  }
  .eyebrow--pen { color: var(--pen); }

  .says {
    font-family: var(--display);
    font-weight: 400;
    font-size: clamp(2.25rem, 7.5vw, 3.5rem);
    line-height: 1.08;
    letter-spacing: -0.012em;
    text-wrap: balance;
    margin: 0;
  }
  .says em { font-style: italic; color: var(--muted); }

  /* Hero scale only — the document pages set their own <h1> in `.says` at
     the base size, and 4.75rem over a privacy policy is a shout. */
  .hero h1.says { font-size: clamp(2.75rem, 9.5vw, 4.75rem); }

  h3 {
    font-family: var(--sans);
    font-weight: 600;
    font-size: 1.0625rem;
    line-height: 1.35;
    margin: 0;
  }

  p { margin: 0; }
  .body { color: var(--muted); max-width: var(--measure); }
  .body strong { color: var(--bone); font-weight: 500; }
  .small { font-size: 0.9375rem; }
  .note { font-family: var(--mono); font-size: 0.75rem; color: var(--faint); letter-spacing: 0.02em; }

  a { color: inherit; }
  a:focus-visible, button:focus-visible {
    outline: 2px solid var(--pen);
    outline-offset: 3px;
  }

  /* ---------------------------------------------------- structure */
  .wrap {
    width: 100%;
    max-width: 62rem;
    margin: 0 auto;
    padding-inline: var(--gutter);
  }

  section { padding-block: clamp(4rem, 11vw, 8rem); }
  section + section { border-top: 1px solid var(--rule); }

  .stack { display: flex; flex-direction: column; gap: 1.5rem; }
  .stack--tight { gap: 0.75rem; }
  .stack--loose { gap: 2.5rem; }

  .section-head { display: flex; flex-direction: column; gap: 1.25rem; max-width: 44rem; }

  /* ------------------------------------------------- the header mark

     Her eyes beside the wordmark, small and quiet. She blinks up here for
     the whole visit — the one thing on the page that is alive while you read
     it, which is the argument the product makes about itself. */
  .brand { display: flex; align-items: center; gap: 0.625rem; }
  .brand__eyes { display: block; width: clamp(19px, 2.4vw, 23px); height: auto; }
  .brand__eyes path { fill: var(--bone); fill-rule: evenodd; }

  /* ------------------------------------------------ arriving on scroll

     Twelve pixels and a fade, once, never on the way back up. Anything
     bigger turns reading into waiting. Applied under `.js` so the page is
     whole for anyone the script never reaches. */
  .js .section-head,
  .js .beat,
  .js .doing,
  .js .lane,
  .js .tier,
  .js .story,
  .js .close,
  .js .figures {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity 640ms cubic-bezier(0.2, 0.8, 0.3, 1),
                transform 640ms cubic-bezier(0.2, 0.8, 0.3, 1);
  }
  .js .is-in { opacity: 1; transform: none; }

  /* ---------------------------------------------------- the icon

     The app icon, drawn rather than dropped in as a PNG — it is the same
     619x618 artwork the app ships, so on the web it can blink. The ground is
     the icon's own #030712 rather than the page's ink, which is what makes a
     tile read as a tile against the ground it sits on. */
  .icon {
    width: clamp(72px, 14vw, 104px);
    aspect-ratio: 1;
    flex: none;
    background: #030712;
    border: 1px solid var(--rule);
    border-radius: 22.5%;
    display: grid;
    place-items: center;
    animation: icon-in 760ms cubic-bezier(0.2, 0.8, 0.3, 1) both;
  }
  /* ...but only once she is on screen — see "Motion". */
  .icon__eyes {
    display: block;
    width: 60%;
    height: auto;
    animation: eyes-in 620ms 220ms ease both;
  }
  .icon__eyes path { fill: var(--bone); fill-rule: evenodd; }

  /* She arrives the way the app opens: the tile settles, then she looks up. */
  @keyframes icon-in { from { opacity: 0; transform: scale(0.94); } to { opacity: 1; transform: scale(1); } }
  @keyframes eyes-in { from { opacity: 0; } to { opacity: 1; } }

  /* ---------------------------------------------------- hero */
  .hero {
    min-height: min(100svh, 60rem);
    display: flex;
    flex-direction: column;
    padding-block: clamp(1.5rem, 4vw, 2.5rem) clamp(3rem, 8vw, 5rem);
  }
  .hero__top { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
  .hero__mid {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: clamp(1.75rem, 4vw, 2.5rem);
    padding-block: clamp(2.5rem, 8vw, 4rem);
  }
  .hero__lede { max-width: 32rem; color: var(--muted); font-size: clamp(1rem, 2.4vw, 1.125rem); }
  .hero__lede strong { color: var(--bone); font-weight: 500; }

  /* The two phones. They ship with their own alpha and their own shadow, so
     they sit on the hero without a plate under them. Below the words on a
     phone, beside them once there is room. */
  .hero__words { display: flex; flex-direction: column; gap: clamp(1.75rem, 4vw, 2.5rem); }
  .hero__art { display: flex; justify-content: center; min-width: 0; }
  .hero__phones {
    display: block;
    width: min(100%, 19rem);
    height: auto;
    max-height: 48svh;
    object-fit: contain;
    object-position: center bottom;
  }
  /* Stacked but roomy: on a tablet the phones can be larger without pushing
     the install buttons off the first screen. */
  @media (min-width: 40rem) {
    .hero__phones { width: min(100%, 24rem); max-height: 56svh; }
  }

  /* Two columns only once the words column can still hold both install
     buttons on one line — below that the phones go under them. */
  @media (min-width: 60rem) {
    .hero__mid {
      display: grid;
      grid-template-columns: minmax(0, 1fr) minmax(0, 0.92fr);
      align-items: center;
      gap: clamp(2rem, 5vw, 3.5rem);
    }
    /* The headline gives up the width it used to have to itself, so it gives
       up some size with it — 4.75rem over a half-width column is six lines. */
    .hero h1.says { font-size: clamp(2.5rem, 4.4vw, 3.75rem); }
    .hero__art { justify-content: flex-end; }
    .hero__phones { width: 100%; max-width: 30rem; max-height: min(78svh, 44rem); }
  }

  /* ---------------------------------------------------- buttons */
  .actions { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; }
  .action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    font-family: var(--sans);
    font-weight: 600;
    font-size: 0.9375rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    text-decoration: none;
    padding: 1rem 1.5rem;
    border: 1px solid var(--pen);
    background: var(--pen);
    color: var(--on-pen);
    transition: background-color 140ms ease, color 140ms ease,
                border-color 140ms ease, transform 140ms cubic-bezier(0.2, 0.8, 0.3, 1);
  }
  .action:hover { background: #E23A28; border-color: #E23A28; transform: translateY(-2px); }
  .action:active { transform: translateY(0); transition-duration: 60ms; }
  .action:focus-visible {
    outline: 2px solid var(--bone);
    outline-offset: 3px;
  }
  .action--quiet {
    background: transparent;
    color: var(--bone);
    border-color: var(--rule-strong);
  }
  .action--quiet:hover { border-color: var(--bone); background: transparent; transform: translateY(-2px); }

  /* ---------------------------------------------------- the promise demo */
  .demo {
    border-top: 1px solid var(--rule-strong);
    border-bottom: 1px solid var(--rule);
    padding-block: 1.5rem;
    max-width: 40rem;
  }
  .demo__label { margin-bottom: 1rem; }
  .demo__row {
    display: flex;
    align-items: baseline;
    gap: 1rem;
    min-height: 2rem;
  }
  .demo__title {
    position: relative;
    flex: 1;
    min-width: 0;
    font-size: 1.0625rem;
    color: var(--bone);
  }
  .demo__text {
    display: inline-block;
    overflow: hidden;
    white-space: nowrap;
    vertical-align: bottom;
    max-width: 100%;
  }
  .demo__strike {
    position: absolute;
    left: -2%;
    top: 56%;
    width: 104%;
    height: 14px;
    transform: translateY(-50%);
    overflow: visible;
    pointer-events: none;
  }
  .demo__strike path {
    fill: none;
    stroke: var(--pen);
    stroke-width: 3.6;
    stroke-linecap: round;
    stroke-dasharray: 420;
    stroke-dashoffset: 420;
  }
  .demo__meta {
    font-family: var(--mono);
    font-size: 0.8125rem;
    color: var(--faint);
    white-space: nowrap;
    opacity: 0;
  }
  .demo__kept {
    font-family: var(--mono);
    font-size: 0.6875rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--pen);
    white-space: nowrap;
    opacity: 0;
  }

  /* One 11s loop, every part reading off the same clock so nothing drifts. */
  @keyframes demo-type   { 0%,4% { width: 0; } 26%,100% { width: 100%; } }
  @keyframes demo-meta   { 0%,27% { opacity: 0; } 33%,100% { opacity: 1; } }
  @keyframes demo-strike { 0%,44% { stroke-dashoffset: 420; } 56%,100% { stroke-dashoffset: 0; } }
  @keyframes demo-kept   { 0%,57% { opacity: 0; } 63%,90% { opacity: 1; } 96%,100% { opacity: 0; } }
  @keyframes demo-fade   { 0%,88% { opacity: 1; } 95%,100% { opacity: 0.001; } }

  .demo__row      { animation: demo-fade 11s linear infinite; }
  .demo__text     { animation: demo-type 11s steps(30, end) infinite; }
  .demo__meta     { animation: demo-meta 11s linear infinite; }
  .demo__strike path { animation: demo-strike 11s cubic-bezier(0.2, 0.8, 0.3, 1) infinite; }
  .demo__kept     { animation: demo-kept 11s linear infinite; }

  /* ---------------------------------------------------- figures */
  .figures {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: 2rem 1.5rem;
    border-top: 1px solid var(--rule-strong);
    padding-top: 1.75rem;
  }
  .figure { display: flex; flex-direction: column; gap: 0.375rem; }
  .figure__value {
    font-family: var(--mono);
    font-size: clamp(2rem, 5vw, 2.75rem);
    line-height: 1;
    color: var(--bone);
  }
  .figure--earned .figure__value { color: var(--pen); }
  .figure__label {
    font-family: var(--mono);
    font-size: 0.6875rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--faint);
  }

  /* ---------------------------------------------------- beats */
  .beats { display: flex; flex-direction: column; }
  .beat {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 2rem;
    padding-block: clamp(2.5rem, 6vw, 4rem);
    align-items: start;
  }
  .beat::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 1px;
    background: var(--rule);
    transform-origin: left center;
  }
  .beat:first-child::before { background: var(--rule-strong); }
  .beat__words { display: flex; flex-direction: column; gap: 1rem; max-width: 32rem; }
  .beat__index {
    font-family: var(--mono);
    font-size: 0.6875rem;
    letter-spacing: 0.12em;
    color: var(--ghost);
  }
  .beat__title { font-family: var(--display); font-size: clamp(1.625rem, 3.6vw, 2rem); line-height: 1.14; margin: 0; font-weight: 400; }

  @media (min-width: 56rem) {
    .beat { grid-template-columns: 1fr 1fr; gap: 3.5rem; }
    .beat--flip .beat__words { order: 2; }
  }

  /* ------------------------------------------- lifted UI fragments */
  .frag {
    background: var(--surface);
    border: 1px solid var(--rule);
    padding: 1.375rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 1rem;
    font-size: 0.9375rem;
  }
  .frag__eyebrow {
    font-family: var(--mono);
    font-size: 0.625rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--faint);
  }
  .frag__rule { height: 1px; background: var(--rule); }

  .prow { display: flex; align-items: baseline; gap: 0.875rem; padding-block: 0.5rem; }
  .prow + .prow { border-top: 1px solid var(--rule); }
  .prow__time { font-family: var(--mono); font-size: 0.75rem; color: var(--faint); min-width: 3.25rem; }
  .prow__title { flex: 1; min-width: 0; }
  .prow__len { font-family: var(--mono); font-size: 0.75rem; color: var(--faint); }
  .prow--kept .prow__title { color: var(--faint); position: relative; }
  .prow--kept .prow__title::after {
    content: "";
    position: absolute;
    left: -2px;
    right: -2px;
    top: 57%;
    height: 2px;
    background: var(--pen);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 460ms cubic-bezier(0.2, 0.8, 0.3, 1);
  }
  .beat.is-in .prow--kept:nth-child(1) .prow__title::after { transform: scaleX(1); transition-delay: 260ms; }
  .beat.is-in .prow--kept:nth-child(2) .prow__title::after { transform: scaleX(1); transition-delay: 420ms; }

  .turn { padding-left: 0.875rem; border-left: 2px solid var(--pen); }
  .turn--mine {
    padding-left: 0;
    padding-right: 0.875rem;
    border-left: none;
    border-right: 2px solid var(--rule-strong);
    text-align: right;
    color: var(--muted);
    margin-left: auto;
    max-width: 88%;
  }
  .turn__her { font-family: var(--display); font-size: 1.1875rem; line-height: 1.3; }

  .chips { display: flex; flex-wrap: wrap; gap: 0.5rem; }
  .chip {
    font-family: var(--mono);
    font-size: 0.75rem;
    color: var(--bone);
    border: 1px solid var(--rule-strong);
    padding: 0.375rem 0.75rem;
  }
  .chip--active { background: var(--pen); border-color: var(--pen); color: var(--on-pen); }

  .fakebtn {
    text-align: center;
    font-weight: 600;
    font-size: 0.8125rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    background: var(--pen);
    color: var(--on-pen);
    padding: 0.875rem 1rem;
  }

  .clock {
    font-family: var(--mono);
    font-size: 1.75rem;
    color: var(--bone);
    letter-spacing: 0.02em;
  }

  /* ---------------------------------------------------- what it does

     The six store screens, told rather than shown. Each is a serif title and
     one line under it, ruled off like a ledger — the page's own grammar, so
     the section needs no device of its own.

     No numbers: these are six things, not six steps, and "Three steps" below
     owns the only sequence on the page. No accent either — the store artwork
     sets one word of each headline in the app's red, which works when they
     are six separate images and does not when they are six lines of one grid.
     Six red words would outshout the buttons and the Pro mark, which are the
     only places on this page the red is earned. */
  /* Ruled off top and bottom, hairlines between — so the block closes on a
     full-width rule instead of trailing off on two short ones. */
  .does {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    border-top: 1px solid var(--rule-strong);
    border-bottom: 1px solid var(--rule-strong);
  }
  .doing {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.3125rem;
    padding-block: 1.5rem;
  }
  /* A pseudo-element rather than a border, so the rule can be drawn on
     arrival instead of simply being there. See "Motion" at the foot. */
  .doing::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 1px;
    background: var(--rule);
    transform-origin: left center;
  }
  .doing:last-child::after { display: none; }
  .doing__title {
    font-family: var(--display);
    font-weight: 400;
    font-size: clamp(1.4375rem, 3.2vw, 1.625rem);
    line-height: 1.14;
    margin: 0;
  }
  .doing__sub { color: var(--muted); font-size: 0.9375rem; max-width: 26rem; }

  /* Two columns as soon as there is room, because six of these stacked is a
     scroll rather than a list — the point of the section is that you can see
     the whole app at once. */
  @media (min-width: 50rem) {
    .does { grid-template-columns: 1fr 1fr; column-gap: clamp(2rem, 5vw, 4rem); }
    .doing { padding-block: 1.75rem; }
    /* The bottom row is two children wide once the grid splits. */
    .doing:nth-last-child(-n + 2)::after { display: none; }
  }

  /* ---------------------------------------------------- lanes */
  .lanes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: 0;
    border-top: 1px solid var(--rule-strong);
  }
  .lane {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 1.75rem 1.5rem 1.75rem 0;
    border-bottom: 1px solid var(--rule);
  }
  .lane__stake { font-family: var(--display); font-size: 1.3125rem; line-height: 1.25; color: var(--bone); }
  .lane__who { color: var(--muted); font-size: 0.875rem; }

  /* ---------------------------------------------------- pricing */
  .tiers { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0; border-top: 1px solid var(--rule-strong); }
  @media (min-width: 48rem) { .tiers { grid-template-columns: 1fr 1fr; } }
  .tier { padding: 2rem 1.75rem 2rem 0; display: flex; flex-direction: column; gap: 1.25rem; border-bottom: 1px solid var(--rule); }
  .tier + .tier { border-left: none; }
  @media (min-width: 48rem) { .tier + .tier { border-left: 1px solid var(--rule); padding-left: 2rem; } }
  .tier ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.625rem; }
  .tier li { position: relative; padding-left: 1.5rem; color: var(--muted); font-size: 0.9375rem; }
  .tier li::before { content: "—"; position: absolute; left: 0; color: var(--ghost); font-family: var(--mono); }
  .tier--pro li::before { content: "+"; color: var(--pen); }

  .tier__head { display: flex; flex-direction: column; gap: 0.625rem; }
  .tier__price {
    margin: 0;
    font-family: var(--display);
    font-size: 2.25rem;
    line-height: 1;
    color: var(--bone);
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
  }
  .tier__per {
    font-family: var(--mono);
    font-size: 0.75rem;
    letter-spacing: 0.02em;
    color: var(--faint);
  }
  .tier--pro .tier__per { color: var(--muted); }

  /* ---------------------------------------------------- story */
  .story { max-width: 36rem; display: flex; flex-direction: column; gap: 1.25rem; }
  .story p { color: var(--muted); }
  .story .last { color: var(--bone); }

  /* ---------------------------------------------------- close */
  .close { display: flex; flex-direction: column; gap: 2rem; align-items: flex-start; }
  .close__lockup { display: flex; flex-wrap: wrap; align-items: center; gap: clamp(1.25rem, 3vw, 2rem); }
  .close__lockup .says { flex: 1 1 16rem; }

  footer {
    border-top: 1px solid var(--rule);
    padding-block: 2.5rem 3.5rem;
    display: flex;
    flex-wrap: wrap;
    gap: 1rem 2rem;
    align-items: center;
    justify-content: space-between;
  }
  footer nav { display: flex; flex-wrap: wrap; gap: 1.25rem; }
  footer a { font-family: var(--mono); font-size: 0.75rem; color: var(--faint); text-decoration: none; }
  footer a:hover { color: var(--bone); }

  @media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
      animation-duration: 0.001ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.001ms !important;
    }
    .icon, .icon__eyes { animation: none; opacity: 1; transform: none; }
    .js .section-head, .js .beat, .js .doing, .js .lane,
    .js .tier, .js .story, .js .close, .js .figures { opacity: 1; transform: none; }
    .prow--kept .prow__title::after { transform: scaleX(1); }
    /* The demo's resting state is its finished state: a promise kept. */
    .demo__text { width: 100%; }
    .demo__meta, .demo__kept { opacity: 1; }
    .demo__strike path { stroke-dashoffset: 0; }
  }

  /* ==================================================================
     Document pages — the story, the privacy policy, the terms.

     One measure, hairlines for section breaks, and numbers set in the
     mono face so a clause can be pointed at. Nothing here is a card.
     ================================================================== */
  .doc { max-width: 42rem; }
  .doc__head {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    padding-bottom: 1.75rem;
    border-bottom: 1px solid var(--rule-strong);
  }
  .doc__body { display: flex; flex-direction: column; gap: 2.75rem; padding-top: 2.75rem; }
  .doc p { color: var(--muted); max-width: 38rem; }
  .doc .says { color: var(--bone); max-width: none; }
  .doc p + p { margin-top: 0.875rem; }
  .doc strong { color: var(--bone); font-weight: 500; }
  .doc a { color: var(--bone); text-decoration: underline; text-decoration-color: var(--rule-strong); text-underline-offset: 3px; }
  .doc a:hover { text-decoration-color: var(--pen); }

  .clause { display: flex; flex-direction: column; gap: 0.875rem; }
  .clause__no {
    font-family: var(--mono);
    font-size: 0.6875rem;
    letter-spacing: 0.12em;
    color: var(--pen);
  }
  .clause h2 {
    font-family: var(--display);
    font-weight: 400;
    font-size: clamp(1.5rem, 3.4vw, 1.875rem);
    line-height: 1.15;
    margin: 0;
  }
  .clause h3 {
    font-family: var(--sans);
    font-weight: 600;
    font-size: 0.9375rem;
    margin: 0.5rem 0 0;
  }
  .clause ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.625rem; }
  .clause li { position: relative; padding-left: 1.5rem; color: var(--muted); max-width: 38rem; }
  .clause li::before { content: "—"; position: absolute; left: 0; color: var(--ghost); font-family: var(--mono); }
  .clause li strong { color: var(--bone); font-weight: 500; }

  .steps { list-style: none; margin: 0; padding: 0; counter-reset: step; display: flex; flex-direction: column; gap: 0.625rem; }
  .steps li { position: relative; padding-left: 1.75rem; color: var(--muted); counter-increment: step; }
  .steps li::before {
    content: counter(step);
    position: absolute;
    left: 0;
    font-family: var(--mono);
    font-size: 0.75rem;
    color: var(--faint);
  }

  /* The bar every page but the landing wears: mark, name, way back. */
  .topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding-block: clamp(1.25rem, 3vw, 1.75rem);
  }
  .topbar a { text-decoration: none; }
  .topbar .note:hover { color: var(--bone); }

  /* ==================================================================
     The swap — what the phone does now, against what it does here.

     Two columns, and the colour carries the argument: the left is faint
     because it is the life you already have, the right is bone because it
     is the one being offered. On a narrow screen it stacks and the arrow
     keeps the pairing legible.
     ================================================================== */
  .swap { border-top: 1px solid var(--rule-strong); }
  .swap__row {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.25rem;
    padding-block: 1.25rem;
    border-bottom: 1px solid var(--rule);
  }
  .swap__was { color: var(--faint); }
  .swap__arrow { font-family: var(--mono); font-size: 0.75rem; color: var(--ghost); }
  .swap__is { color: var(--bone); }
  .swap__is strong { font-weight: 500; }

  @media (min-width: 46rem) {
    .swap__row {
      grid-template-columns: 1fr 1.5rem 1fr;
      gap: 1.75rem;
      align-items: baseline;
      padding-block: 1.375rem;
    }
    .swap__arrow { text-align: center; }
  }

  /* Her voice, standing on its own. Used once. */
  .quote {
    border-left: 2px solid var(--pen);
    padding: 0.25rem 0 0.25rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.875rem;
    max-width: 36rem;
  }
  .quote__line {
    font-family: var(--display);
    font-size: clamp(1.375rem, 3.4vw, 1.75rem);
    line-height: 1.24;
    color: var(--bone);
  }

/* A beat with nothing beside it.

   `.beat` is a two-column grid — words on the left, a lifted fragment of UI on
   the right — and splitting it for three one-line steps would leave half the
   row empty at every width above 56rem. This variant keeps the ledger rules
   and the serif titles, and lets the measure do the constraining. */
.beats--plain { list-style: none; margin: 0; padding: 0; }
@media (min-width: 56rem) {
  .beats--plain .beat { grid-template-columns: minmax(0, 1fr); }
}

/* --------------------------------------------------------------------------
   Motion

   One loud moment and a lot of quiet ones.

   The loud one is the hero, and it is loud on purpose: the page's whole
   argument is a sentence in two halves — say it, then do it — so the two
   halves arrive separately, with a beat between them long enough to be felt.
   Everything after that is the page ruling itself as you read, which is what
   the app does to a day.

   Everything here is gated on `.js`, so the page without a script is simply
   finished, and everything collapses to its end state under
   `prefers-reduced-motion` — the block at the top of the file already does
   that for the arrivals, and this section adds its own.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------- the hero rises

   Each line is masked by its own strip of overflow and pushed below it, then
   let up. `.says` runs a 1.08 line-height, which cuts the descenders off a
   line like "Say what you'll do." — hence the padding under each mask and the
   equal negative margin, which gives the y a place to hang without opening a
   gap between the two lines. */
.rise__line {
  display: block;
  overflow: hidden;
  padding-bottom: 0.16em;
  margin-bottom: -0.16em;
}
.rise__in { display: block; }

/* `lit` is set once the real faces are in — see site.js. Animating serif copy
   before Instrument Serif arrives means animating the fallback and then
   swapping mid-flight, which is worse than a beat of stillness. */
.js .rise__in,
.js .rise__after { opacity: 0; }

.js.lit .rise__in {
  opacity: 1;
  animation: rise-line 900ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
.js.lit .rise__line:nth-child(1) .rise__in { animation-delay: 60ms; }
/* The pause between the halves. Not a rhythm — a hesitation. */
.js.lit .rise__line:nth-child(2) .rise__in { animation-delay: 280ms; }

/* The whole sequence is done inside 1.4s, and the install buttons start
   fading at 640ms. A hero that makes someone wait two seconds for the button
   they came to press has stopped being a hero and started being a toll. */
.js.lit .rise__after {
  animation: rise-soft 700ms cubic-bezier(0.2, 0.8, 0.3, 1) both;
}
.js.lit .hero__top { animation-delay: 0ms; }
.js.lit .hero__lede { animation-delay: 520ms; }
.js.lit .actions { animation-delay: 640ms; }
/* Last in, and the only thing on the page allowed to keep the reader waiting
   — by then the words have been readable for half a second. */
.js.lit .hero__art { animation-delay: 760ms; }

@keyframes rise-line {
  from { transform: translateY(108%); }
  to   { transform: translateY(0); }
}
@keyframes rise-soft {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* ------------------------------------------------------ the rules draw

   The ledger gets ruled as you reach it: every hairline between the six
   things, and every rule above a step, arrives from the left rather than
   being there already. Slower than the fade it travels with, so the line is
   still moving when the words have settled. */
.js .doing::after,
.js .beat::before {
  transform: scaleX(0);
  transition: transform 820ms cubic-bezier(0.2, 0.8, 0.3, 1) 60ms;
}
.js .doing.is-in::after,
.js .beat.is-in::before { transform: scaleX(1); }

/* The stagger site.js writes is an inline `transition-delay` on the row, and
   transition-delay does not inherit — so a pseudo-element would draw every
   rule at once underneath text that arrives one row at a time. These repeat
   the same 70ms step by hand, offset just past the words they sit under. */
.js .doing:nth-child(1)::after { transition-delay: 60ms; }
.js .doing:nth-child(2)::after { transition-delay: 130ms; }
.js .doing:nth-child(3)::after { transition-delay: 200ms; }
.js .doing:nth-child(4)::after { transition-delay: 270ms; }
.js .doing:nth-child(5)::after { transition-delay: 340ms; }
.js .doing:nth-child(6)::after { transition-delay: 410ms; }
.js .beat:nth-child(1)::before { transition-delay: 60ms; }
.js .beat:nth-child(2)::before { transition-delay: 130ms; }
.js .beat:nth-child(3)::before { transition-delay: 200ms; }

/* --------------------------------------------------- the tiers fill in

   What you get, one line at a time — the list reads as a list being written
   rather than a block that appeared. Four and five items, so the delays are
   spelled out rather than counted. */
.js .tier li {
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 520ms ease, transform 520ms cubic-bezier(0.2, 0.8, 0.3, 1);
}
.js .tier.is-in li { opacity: 1; transform: none; }
.js .tier.is-in li:nth-child(1) { transition-delay: 140ms; }
.js .tier.is-in li:nth-child(2) { transition-delay: 210ms; }
.js .tier.is-in li:nth-child(3) { transition-delay: 280ms; }
.js .tier.is-in li:nth-child(4) { transition-delay: 350ms; }
.js .tier.is-in li:nth-child(5) { transition-delay: 420ms; }

/* --------------------------------------------------------- she arrives

   The icon's entrance used to run at page load, which spent it on an empty
   screen — the close section is the last thing on the page. It waits now. */
.js .icon { animation: none; opacity: 0; }
.js .icon__eyes { animation: none; }
.js .close.is-in .icon {
  animation: icon-in 760ms cubic-bezier(0.2, 0.8, 0.3, 1) 120ms both;
}
.js .close.is-in .icon__eyes {
  animation: eyes-in 620ms 420ms ease both;
}

/* ------------------------------------------------------- small motions */

/* An underline that is drawn rather than switched on. */
footer a,
.note a {
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0 1px;
  transition: background-size 260ms cubic-bezier(0.2, 0.8, 0.3, 1), color 180ms ease;
}
footer a:hover,
.note a:hover { background-size: 100% 1px; }

/* Focus has to be visible everywhere, not only on the buttons. */
a:focus-visible {
  outline: 2px solid var(--bone);
  outline-offset: 3px;
}

/* ----------------------------------------------------------- stillness */
@media (prefers-reduced-motion: reduce) {
  .js .rise__in,
  .js .rise__after,
  .js.lit .rise__in,
  .js.lit .rise__after {
    opacity: 1;
    animation: none;
    transform: none;
  }
  .js .doing::after,
  .js .beat::before { transform: scaleX(1); }
  .js .tier li { opacity: 1; transform: none; }
  .js .icon { opacity: 1; animation: none; }
  .js .icon__eyes { opacity: 1; animation: none; }
  footer a, .note a { transition: none; }
}
