/*
 * GENERATED — do not edit.
 *
 * Written by scripts/build-site.mjs from src/design/theme.ts and
 * web/site/site.css. Change a colour in theme.ts and change it here; change it
 * here and lose it on the next build.
 *
 * Light only, on purpose — see scripts/lib/stylesheet.mjs for why.
 */

:root {
  color-scheme: light;
  --background: #FFFFFF;
  --surface: #FFFDF6;
  --surface-raised: #FAF4E5;
  --surface-pressed: #FAF4E5;
  --text-primary: #2E2A20;
  --text-secondary: #635B49;
  --text-muted: #877D66;
  --action-bg: #88B808;
  --action-text: #2E2A20;
  --border: #E5D9BE;
  --border-strong: #877D66;
  --focus: #B03A0F;
  --accent: #48B8A8;
  --accent-ink: #0F6B62;
  --accent-soft: #DDF1EE;
  --success: #88B808;
  --success-ink: #4F6B0A;
  --success-soft: #EDF3D9;
  --warning: #F8B828;
  --warning-ink: #7D5300;
  --warning-soft: #FDF0D2;
  --danger: #BE3450;
  --danger-soft: #FDE6E8;
  --capsule: #0F6B62;
  --capsule-soft: #DDF1EE;
  --child-coral: #F86878;
  --child-coral-ink: #BE3450;
  --child-coral-soft: #FDE6E8;
  --child-leaf: #88B808;
  --child-leaf-ink: #4F6B0A;
  --child-leaf-soft: #EDF3D9;
  --child-teal: #48B8A8;
  --child-teal-ink: #0F6B62;
  --child-teal-soft: #DDF1EE;
  --child-amber: #F8B828;
  --child-amber-ink: #7D5300;
  --child-amber-soft: #FDF0D2;
  --scrim: rgba(30, 27, 21, 0.55);
  --on-scrim: #FFFFFF;
  --skeleton: #F1E8D2;
}

/*
 * Storygarden — marketing site.
 *
 * Not one colour is written here. Every value comes from a custom property
 * that `scripts/build-site.mjs` emits from `src/design/theme.ts`, so this site
 * and the application are the same brand by construction rather than by
 * somebody remembering to update two files. Adding a literal hex to this file
 * is the one change that would break that, and it is what the generated
 * `site.css` header says at the top of the published stylesheet.
 *
 * No web fonts. The privacy page names every company that sees a request from
 * this domain, and adding a font host would make that page wrong — which is a
 * worse outcome than a system font stack. Personality comes from size, weight,
 * and space instead.
 */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--background);
  color: var(--text-primary);
  font-family:
    ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
    'Helvetica Neue', Arial, sans-serif;
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

h1,
h2,
h3 {
  line-height: 1.15;
  letter-spacing: -0.021em;
  margin: 0;
  text-wrap: balance;
}

h1 {
  font-size: clamp(2.15rem, 1.35rem + 3.4vw, 3.9rem);
  font-weight: 800;
}

h2 {
  font-size: clamp(1.65rem, 1.2rem + 1.7vw, 2.5rem);
  font-weight: 750;
}

h3 {
  font-size: 1.2rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

p {
  margin: 0;
  text-wrap: pretty;
}

a {
  color: var(--accent-ink);
  text-underline-offset: 0.18em;
}

a:hover {
  text-decoration-thickness: 2px;
}

/*
 * One focus style for the whole site, and it is never removed. `:focus-visible`
 * rather than `:focus` so a mouse click on a button does not leave a ring
 * behind, while every keyboard arrival is unmistakable. The colour clears 3:1
 * against both the ground and a card — `scripts/check-contrast.mjs` asserts it.
 */
:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
  border-radius: 6px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--surface);
  color: var(--text-primary);
  padding: 0.85rem 1.25rem;
  border-radius: 0 0 14px 0;
  font-weight: 650;
}

.skip:focus {
  left: 0;
}

.wrap {
  width: min(72rem, 100% - 2.5rem);
  margin-inline: auto;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --- buttons --------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 2.875rem;
  padding: 0.7rem 1.35rem;
  border: 1px solid transparent;
  border-radius: 999px;
  font: inherit;
  font-weight: 650;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: transform 140ms ease, background-color 140ms ease, border-color 140ms ease;
}

.btn--primary {
  background: var(--action-bg);
  color: var(--action-text);
}

.btn--primary:hover {
  transform: translateY(-1px);
}

.btn--secondary {
  background: var(--surface);
  color: var(--text-primary);
  border-color: var(--border-strong);
}

.btn--secondary:hover {
  background: var(--surface-pressed);
  transform: translateY(-1px);
}

.btn--quiet {
  background: transparent;
  color: var(--text-primary);
  padding-inline: 0.75rem;
}

.btn--quiet:hover {
  background: var(--surface-pressed);
}

.btn--block {
  width: 100%;
}

/*
 * The one place `--text-muted` belongs on this site.
 *
 * The token is documented as placeholders and disabled labels, and it only
 * clears 3:1 — enough for a decorative cue, not for anything a visitor has to
 * read. A disabled control is exempt from the contrast minimum and is supposed
 * to look unavailable. Every other small grey on this page was measured in the
 * browser against its real background and three of them failed at 3.7:1, which
 * is why they are all `--text-secondary` now.
 */
.btn[aria-disabled='true'] {
  background: var(--skeleton);
  color: var(--text-muted);
  border-color: var(--border);
  cursor: not-allowed;
  transform: none;
}

/* --- header ---------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in srgb, var(--background) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}

.site-header__bar {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  min-height: 5rem;
  padding-block: 0.5rem;
}

.wordmark {
  flex: none;
  display: block;
  line-height: 0;
}

/*
 * Sized by height, not width.
 *
 * The mark sits above the word in this lockup, so the artwork is nearly as
 * tall as it is wide (1024x686). At `width: 132px` that is 88px of logo in a
 * 68px bar — it overflowed the header and touched the border below it. Height
 * is the dimension the bar actually constrains, so that is the one to set.
 */
.wordmark img {
  height: 60px;
  width: auto;
}

.site-nav {
  flex: 1;
}

.site-nav ul,
.site-menu ul,
.site-footer ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-nav ul {
  display: flex;
  gap: 0.35rem;
}

.site-nav a {
  display: block;
  padding: 0.55rem 0.8rem;
  border-radius: 999px;
  color: var(--text-secondary);
  font-weight: 600;
  font-size: 0.97rem;
  text-decoration: none;
}

.site-nav a:hover {
  background: var(--surface-pressed);
  color: var(--text-primary);
}

/*
 * The current page is marked with a word to a screen reader (`aria-current`)
 * and with weight and colour to everyone else. Never with colour alone.
 */
.site-nav a[aria-current='page'] {
  color: var(--accent-ink);
  font-weight: 750;
  background: var(--accent-soft);
}

.site-header__actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: none;
}

.menu-toggle {
  display: none;
  align-items: center;
  gap: 0.55rem;
  min-height: 2.75rem;
  padding: 0.55rem 1rem;
  margin-left: auto;
  background: var(--surface);
  color: var(--text-primary);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  font: inherit;
  font-weight: 650;
  cursor: pointer;
}

.menu-toggle__bars,
.menu-toggle__bars::before,
.menu-toggle__bars::after {
  display: block;
  width: 17px;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
}

.menu-toggle__bars {
  position: relative;
}

.menu-toggle__bars::before,
.menu-toggle__bars::after {
  content: '';
  position: absolute;
  left: 0;
}

.menu-toggle__bars::before {
  top: -5px;
}

.menu-toggle__bars::after {
  top: 5px;
}

.site-menu {
  border-top: 1px solid var(--border);
  padding: 0.75rem 0 1.25rem;
}

.site-menu ul {
  width: min(72rem, 100% - 2.5rem);
  margin-inline: auto;
  display: grid;
  gap: 0.25rem;
}

.site-menu a:not(.btn) {
  display: block;
  padding: 0.8rem 0.6rem;
  border-radius: 12px;
  color: var(--text-primary);
  font-weight: 600;
  text-decoration: none;
}

.site-menu a:not(.btn):hover {
  background: var(--surface-pressed);
}

.site-menu .btn {
  margin-top: 0.5rem;
}

@media (max-width: 62rem) {
  .site-nav,
  .site-header__actions {
    display: none;
  }

  .menu-toggle {
    display: inline-flex;
  }
}

@media (max-width: 30rem) {
  /*
   * The mark and the wordmark are stacked in the logo, so it is nearly as tall
   * as it is wide. At the full size that pushes the hero headline most of a
   * thumb-length down the first screen on a phone.
   */
  .wordmark img {
    height: 46px;
  }

  .site-header__bar {
    min-height: 4rem;
    gap: 0.75rem;
  }
}

/* --- sections -------------------------------------------------------------- */

section {
  padding-block: clamp(2.5rem, 1.5rem + 3.5vw, 4.5rem);
}

.section-head {
  max-width: 44rem;
  margin-bottom: 2rem;
  display: grid;
  gap: 1rem;
}

.section-head--center {
  margin-inline: auto;
  text-align: center;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  align-self: start;
  padding: 0.3rem 0.8rem;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-ink);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.section-head--center .eyebrow {
  justify-self: center;
}

.lede {
  font-size: clamp(1.05rem, 1rem + 0.35vw, 1.25rem);
  color: var(--text-secondary);
}

.tinted {
  background: var(--surface-pressed);
}

/* --- hero ------------------------------------------------------------------ */

/*
 * A wash of the mark's own hues across the top of the page.
 *
 * On a white ground the logo's colours had nowhere to live above the fold
 * except the button, which made the page read as "white site, orange button"
 * rather than as this brand. Each stop is one of the five sampled hues at very
 * low alpha, so the ground stays white where the text sits and warms only at
 * the edges. `color-mix` keeps it derived from the tokens rather than a set of
 * hand-picked pastels that would drift.
 */
.hero {
  padding-top: clamp(1.5rem, 1rem + 2vw, 2.5rem);
  background:
    radial-gradient(60rem 28rem at 8% -12%, color-mix(in srgb, var(--accent) 13%, transparent), transparent 70%),
    radial-gradient(46rem 24rem at 88% -8%, color-mix(in srgb, var(--child-teal) 14%, transparent), transparent 70%),
    radial-gradient(38rem 22rem at 62% 4%, color-mix(in srgb, var(--child-amber) 12%, transparent), transparent 72%),
    radial-gradient(34rem 20rem at 30% 10%, color-mix(in srgb, var(--child-leaf) 9%, transparent), transparent 72%);
}

.hero__grid {
  display: grid;
  gap: clamp(2.5rem, 1rem + 5vw, 4.5rem);
  /* Stacked, there is nothing to centre against, so this only applies below
     the breakpoint where the two columns appear. */
  align-items: center;
}

@media (min-width: 62rem) {
  .hero__grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
    /*
     * Top-aligned, not centred.
     *
     * This is the whole of the "too much space above the headline" problem.
     * The preview panel beside the copy is much taller, so centring pushed the
     * eyebrow and the headline halfway down the screen. Reducing the padding
     * barely moved it, because padding was never what was doing it.
     */
    align-items: start;
  }
}

.hero__copy {
  display: grid;
  gap: 1.5rem;
  justify-items: start;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.hero__trust {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--text-secondary);
  font-size: 0.95rem;
  font-weight: 600;
}

.hero__trust svg {
  flex: none;
  color: var(--success-ink);
}

/* --- cards ----------------------------------------------------------------- */

.grid {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 22px;
  padding: 1.6rem;
  display: grid;
  gap: 0.6rem;
  align-content: start;
}

.card p {
  color: var(--text-secondary);
  font-size: 0.98rem;
}

.card__icon {
  width: 2.75rem;
  height: 2.75rem;
  display: grid;
  place-items: center;
  border-radius: 14px;
  background: var(--accent-soft);
  color: var(--accent-ink);
  margin-bottom: 0.35rem;
}

.card__icon--leaf {
  background: var(--child-leaf-soft);
  color: var(--child-leaf-ink);
}

.card__icon--teal {
  background: var(--child-teal-soft);
  color: var(--child-teal-ink);
}

.card__icon--amber {
  background: var(--child-amber-soft);
  color: var(--child-amber-ink);
}

.card__icon--capsule {
  background: var(--capsule-soft);
  color: var(--capsule);
}

.card__icon--coral {
  background: var(--child-coral-soft);
  color: var(--child-coral-ink);
}

/* --- steps ----------------------------------------------------------------- */

.steps {
  counter-reset: step;
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
}

.step {
  counter-increment: step;
  display: grid;
  gap: 0.6rem;
  align-content: start;
  padding: 1.6rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 22px;
}

.step::before {
  content: counter(step);
  display: grid;
  place-items: center;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 50%;
  background: var(--action-bg);
  color: var(--action-text);
  font-weight: 750;
  font-size: 1.05rem;
  margin-bottom: 0.35rem;
}

.step p {
  color: var(--text-secondary);
  font-size: 0.98rem;
}

/* --- alternating detail rows ----------------------------------------------- */

.detail {
  display: grid;
  gap: clamp(2rem, 1rem + 4vw, 3.75rem);
  align-items: center;
  padding-block: clamp(2.5rem, 1.5rem + 3vw, 4rem);
}

@media (min-width: 56rem) {
  .detail {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .detail--flip .detail__media {
    order: -1;
  }
}

.detail__copy {
  display: grid;
  gap: 1rem;
  justify-items: start;
}

.detail__copy ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 0.6rem;
  color: var(--text-secondary);
}

.detail__copy li {
  display: grid;
  grid-template-columns: 1.35rem 1fr;
  gap: 0.65rem;
  align-items: start;
}

.detail__copy li svg {
  margin-top: 0.35rem;
  color: var(--success-ink);
}

.detail__media img {
  width: 100%;
  border-radius: 26px;
  border: 1px solid var(--border);
  aspect-ratio: 1 / 1;
  object-fit: cover;
}

/* --- the photo band -------------------------------------------------------- */

.photo-strip {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}

.photo-strip img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: 22px;
  border: 1px solid var(--border);
}

/* --- pricing --------------------------------------------------------------- */

.plan {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 24px;
  padding: 1.75rem;
  display: grid;
  gap: 1rem;
  align-content: start;
}

.plan--feature {
  border-color: var(--accent-ink);
  border-width: 2px;
}

/*
 * Smaller than a section heading. Four plan cards sit side by side on a wide
 * screen, and at the page's `h2` size "Family Package" and "Text Capture" each
 * break across two lines while the shorter names do not — which reads as the
 * cards being different shapes rather than the names being different lengths.
 */
.plan h2 {
  font-size: 1.4rem;
  font-weight: 750;
}

/*
 * `justify-self`, not `align-self`. The card is a single-column grid, so
 * `align-self` moves the badge in the block axis and leaves it stretched the
 * full width of the card — a pill the width of the column reads as a banner.
 */
.plan__badge {
  justify-self: start;
  padding: 0.25rem 0.7rem;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-ink);
  font-size: 0.8rem;
  font-weight: 700;
}

.plan__price {
  font-size: 1.9rem;
  font-weight: 800;
  letter-spacing: -0.02em;
}

.plan__price span {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text-secondary);
  letter-spacing: 0;
}

/*
 * The state a price is actually in today: not decided. Rendered as a real
 * sentence rather than a dash, because "—" reads as free.
 */
.plan__price--pending {
  font-size: 1.05rem;
  font-weight: 650;
  color: var(--text-secondary);
  letter-spacing: 0;
}

.plan ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.55rem;
  color: var(--text-secondary);
  font-size: 0.98rem;
}

.plan li {
  display: grid;
  grid-template-columns: 1.35rem 1fr;
  gap: 0.6rem;
  align-items: start;
}

.plan li svg {
  margin-top: 0.35rem;
  color: var(--success-ink);
}

.plan__note {
  font-size: 0.88rem;
  color: var(--text-secondary);
}

/* --- a selectable plan ------------------------------------------------------ */

/*
 * The card is a label for a hidden radio, so the whole thing is clickable and
 * the group is arrow-key navigable without a line of JavaScript.
 */
/*
 * Two rows: the card takes whatever height the tallest tier needs, and the
 * button sits on the floor of every column. Without the `1fr` the buttons
 * stagger with the copy above them, which reads as three unrelated cards
 * rather than a row of choices.
 */
.plan-choice {
  display: grid;
  grid-template-rows: 1fr auto;
  gap: 0.75rem;
}

.plan-choice .plan {
  cursor: pointer;
  transition: border-color 140ms ease, box-shadow 140ms ease, transform 140ms ease;
}

.plan-choice:hover .plan {
  border-color: var(--border-strong);
}

/*
 * The chosen one. Border *and* a ring, so the selected state is not carried by
 * a one-pixel colour change alone.
 *
 * A sibling combinator, not `:has()`. `:has(input:checked)` was tried first and
 * measured wrong in the browser: after clicking a card, the newly checked one
 * had no ring and the previously checked one kept its own — the selector was
 * not being re-evaluated when the live `:checked` state moved. `~` from the
 * input invalidates reliably and is supported further back, and the markup
 * already puts the input before the label for exactly this.
 */
.plan-choice__input:checked ~ .plan {
  border-color: var(--action-bg);
  border-width: 2px;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--action-bg) 22%, transparent);
}

/*
 * Somebody arrowing through the group with a keyboard, who would otherwise see
 * the selection move with nothing to say where they are. The input is visually
 * hidden, so its own focus ring is invisible and has to be borrowed by the card.
 */
.plan-choice__input:focus-visible ~ .plan {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}

/* The button is only the primary action on the tier actually chosen. */
.plan-choice__input:not(:checked) ~ .btn--primary {
  background: var(--surface);
  color: var(--text-primary);
  border-color: var(--border-strong);
}

.plan__name {
  font-size: 1.4rem;
  font-weight: 750;
  letter-spacing: -0.01em;
  line-height: 1.15;
}

/* Not for sale yet: calmer than a tier, and never mistakable for one. */
.plan--soon {
  border-style: dashed;
}

.plan__badge--soon {
  background: var(--warning-soft);
  color: var(--warning-ink);
}

/*
 * The comparison table.
 *
 * Row headers are `<th scope="row">`, not bold cells: a screen reader reading
 * "Yes" out of the Standard column has to be able to say what it is yes to.
 */
.compare {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 0.97rem;
  min-width: 36rem;
  border: 1px solid var(--border);
  border-radius: 20px;
  overflow: hidden;
}

.compare th,
.compare td {
  padding: 0.8rem 1rem;
  text-align: left;
  border-bottom: 1px solid var(--border);
}

.compare tbody tr:last-child th,
.compare tbody tr:last-child td {
  border-bottom: 0;
}

/* Each tier in its own colour from the mark, and never colour alone — the
   column header still says which tier it is. */
.compare__tier {
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.compare__tier--free {
  color: var(--child-teal-ink);
  background: var(--child-teal-soft);
}

.compare__tier--standard {
  color: var(--success-ink);
  background: var(--success-soft);
}

.compare__tier--family {
  color: var(--child-amber-ink);
  background: var(--child-amber-soft);
}

/* The tier the cards above also badge. Said in words, not just tinted. */
.compare__pick {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: none;
  opacity: 0.85;
}

.compare__col--pick {
  background: color-mix(in srgb, var(--success-soft) 55%, transparent);
}

.compare tbody th {
  font-weight: 650;
  color: var(--text-primary);
}

.compare tbody tr:nth-child(even) th,
.compare tbody tr:nth-child(even) td:not(.compare__col--pick) {
  background: var(--surface);
}

.compare tbody tr:hover th,
.compare tbody tr:hover td {
  background: var(--surface-pressed);
}

.compare__yes {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  background: var(--success-soft);
  color: var(--success-ink);
}

.compare__no {
  color: var(--text-secondary);
  font-size: 1.1rem;
}

.compare__value {
  font-weight: 700;
  color: var(--text-primary);
}

.compare__note {
  color: var(--text-secondary);
  font-style: italic;
}

/* --- callout / notice ------------------------------------------------------ */

.notice {
  display: grid;
  gap: 0.5rem;
  padding: 1.25rem 1.4rem;
  border-radius: 18px;
  border: 1px solid var(--border);
  background: var(--surface);
  font-size: 0.98rem;
  color: var(--text-secondary);
}

.notice strong {
  color: var(--text-primary);
}

.notice--warn {
  background: var(--warning-soft);
  border-color: var(--warning);
  color: var(--warning-ink);
}

.notice--warn strong {
  color: var(--warning-ink);
}

/* --- final call to action --------------------------------------------------- */

.cta {
  text-align: center;
  display: grid;
  gap: 1.5rem;
  justify-items: center;
  padding: clamp(2.75rem, 1.5rem + 5vw, 4.5rem) clamp(1.25rem, 4vw, 3rem);
  border-radius: 30px;
  background: var(--accent-soft);
  border: 1px solid var(--border);
}

.cta p {
  max-width: 34rem;
  color: var(--text-secondary);
}

.cta .hero__actions {
  justify-content: center;
}

/* --- footer ---------------------------------------------------------------- */

.site-footer {
  border-top: 1px solid var(--border);
  padding-block: 3.5rem 2.5rem;
  background: var(--surface-pressed);
}

.site-footer__grid {
  display: grid;
  gap: 2.25rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}

.site-footer__brand {
  display: grid;
  gap: 0.9rem;
  align-content: start;
}

.site-footer__brand img {
  width: 140px;
}

.site-footer__brand p {
  color: var(--text-secondary);
  font-size: 0.95rem;
  max-width: 20rem;
}

.site-footer h2 {
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: 0.9rem;
}

.site-footer ul {
  display: grid;
  gap: 0.55rem;
}

.site-footer a {
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 0.97rem;
}

.site-footer a:hover {
  color: var(--accent-ink);
  text-decoration: underline;
}

.site-footer__legal {
  margin-top: 2.75rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
  color: var(--text-secondary);
  font-size: 0.9rem;
}

/* --- long-form prose (privacy, terms, security, support) -------------------- */

.prose {
  max-width: 44rem;
  margin-inline: auto;
}

.prose h1 {
  font-size: clamp(2rem, 1.4rem + 2.2vw, 2.9rem);
  margin-bottom: 0.75rem;
}

.prose h2 {
  font-size: 1.4rem;
  margin: 2.75rem 0 0.85rem;
}

.prose h3 {
  font-size: 1.05rem;
  margin: 1.75rem 0 0.5rem;
}

.prose p,
.prose ul,
.prose ol {
  margin: 0 0 1.1rem;
}

.prose ul,
.prose ol {
  padding-left: 1.3rem;
}

.prose li {
  margin-bottom: 0.5rem;
}

.prose .lede {
  margin-bottom: 1.5rem;
}

.prose .card {
  display: block;
  margin: 1.75rem 0;
}

.prose .card h2 {
  margin-top: 0;
}

.prose table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 1.5rem;
  font-size: 0.95rem;
}

.prose th,
.prose td {
  text-align: left;
  padding: 0.65rem 0.55rem;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

.prose th {
  color: var(--text-secondary);
  font-weight: 650;
}

/* Wide content scrolls inside its own box; the page body never does. */
.scroll {
  overflow-x: auto;
}

.prose footer {
  margin-top: 3rem;
  color: var(--text-secondary);
  font-size: 0.9rem;
}

/* --- the product preview ---------------------------------------------------- */

/*
 * A drawn approximation of the timeline, not a screenshot and not a working
 * app. It is labelled as an example in visible text, because a preview that
 * could be mistaken for live data is a lie told in CSS.
 */
.preview-figure {
  margin: 0;
}

.preview {
  /*
   * The panel is the warm one and the cards inside it are white, not the other
   * way round. Inverted first, and on a white page `surface` (a near-white
   * cream) sat on `background` (white) with about one percent of separation —
   * the mock read as a flat smudge. Warm panel, white cards, and the timeline
   * reads as a timeline.
   */
  background: var(--surface-pressed);
  border: 1px solid var(--border);
  border-radius: 26px;
  padding: 1.15rem;
  /*
   * Derived from the theme rather than a black literal, so the shadow warms up
   * with the rest of the palette and stays the app's `elevation.card` in
   * spirit — that one is `palette.bark900` at low opacity, not black.
   */
  box-shadow: 0 24px 60px -30px color-mix(in srgb, var(--text-primary) 45%, transparent);
}

.preview__chrome {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0 0.35rem 0.9rem;
  border-bottom: 1px solid var(--border);
}

.preview__kids {
  display: flex;
  gap: 0.4rem;
  flex: 1;
}

.preview__kid {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.7rem 0.3rem 0.35rem;
  border-radius: 999px;
  font-size: 0.85rem;
  font-weight: 650;
  color: var(--text-secondary);
  background: var(--background);
}

.preview__kid[data-active] {
  background: var(--child-teal-soft);
  color: var(--child-teal-ink);
}

.preview__avatar {
  /*
   * A photograph of the child, circular, which is what the app's own switcher
   * shows. It was a bare coloured dot first: the initial that sat here had no
   * colour that worked, because the fill is a bright accent in both themes
   * while every token dark enough to read on it flips light in dark mode. A
   * face needs no contrast ratio and says more than a letter.
   */
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  overflow: hidden;
  flex: none;
  background: var(--surface-raised);
}

.preview__avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.preview__day {
  padding: 1.1rem 0.35rem 0.5rem;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

/*
 * Today's date, written in by the browser.
 *
 * `.preview__day` is uppercase, which would shout the month name, so this
 * resets to normal case. It is empty in the HTML and stays empty without
 * JavaScript — see the note in layout.mjs for why an empty heading is the
 * right fallback and a build-time date is not.
 */
.preview__date {
  text-transform: none;
  font-weight: 600;
  letter-spacing: 0;
}

.preview__card {
  display: grid;
  gap: 0.5rem;
  padding: 0.9rem;
  border-radius: 16px;
  background: var(--background);
  border: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
  margin-bottom: 0.6rem;
}

/* A drawn card title. A paragraph rather than a heading — see preview.mjs. */
.preview__title {
  font-size: 0.98rem;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.01em;
}

.preview__card p {
  font-size: 0.87rem;
  color: var(--text-secondary);
}

.preview__row {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex-wrap: wrap;
}

.preview__chip {
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  font-size: 0.74rem;
  font-weight: 700;
  background: var(--surface-raised);
  color: var(--text-secondary);
}

.preview__chip--milestone {
  background: var(--child-amber-soft);
  color: var(--child-amber-ink);
}

.preview__chip--capsule {
  background: var(--capsule-soft);
  color: var(--capsule);
}

/*
 * Stand-ins for photographs, drawn in CSS. Real family pictures are used
 * elsewhere on the page with permission; inventing a child's photograph for a
 * fake timeline card is not something this product should do even in a mockup.
 */
.preview__photos {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.4rem;
}

.preview__photo {
  display: block;
  border-radius: 10px;
  overflow: hidden;
  background: var(--surface-raised);
}

/*
 * A square crop, sized by the grid rather than by the file.
 *
 * `aspect-ratio` on the tile plus `object-fit: cover` means a photograph of any
 * shape lands square without letterboxing, and the row cannot shift as the
 * three load — the space is reserved before the bytes arrive.
 */
.preview__photo img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  display: block;
}

/*
 * The video sits at roughly the width of one photo tile above it, so the card
 * keeps the same rhythm rather than turning into a single tall block. The clip
 * is square, and cropping it to a letterbox would lose either the moon at the
 * top or the child at the bottom — the whole subject of the shot.
 */
.preview__media {
  position: relative;
  width: min(48%, 210px);
}

.preview__video {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 12px;
  background: var(--surface-raised);
}

/*
 * A duration, the way a video in the timeline carries one. Placed over the
 * clip's bottom-left, where this footage is darkest, and given its own scrim
 * so it stays legible whichever frame is showing — the contrast of text on a
 * moving picture cannot be measured once and trusted.
 */
.preview__duration {
  position: absolute;
  left: 0.4rem;
  bottom: 0.4rem;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--on-scrim);
  /*
   * Solid, not the translucent scrim.
   *
   * `--scrim` is 55% opaque, and measured over the card it left this at
   * 3.86:1 — under the 4.5 it needs. Over video it would be worse and
   * unpredictable, which is the case a single measurement cannot cover. An
   * opaque pill is what every video player does, and it is 14.3:1 whatever
   * frame is behind it.
   */
  background: var(--text-primary);
}

.preview__caption {
  margin-top: 0.9rem;
  text-align: center;
  font-size: 0.82rem;
  color: var(--text-secondary);
}

/* --- motion ---------------------------------------------------------------- */

/*
 * Nothing on this site needs motion to be understood, so honouring the
 * preference costs nothing and removing it is not a judgement call.
 */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
