/*
 * 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.
 *
 * Dark by default, light when the reader's device asks for it, and either when
 * the reader says so — see scripts/lib/stylesheet.mjs for the layering.
 *
 * The light values are the shopfront's, which differ from the application's in
 * exactly three: the app inverted its layers so cards float on warmth, and this
 * page did not. See the note on shopfrontColors in src/design/theme.ts.
 */

:root {
  color-scheme: dark;
  --background: #17150F;
  --surface: #211E18;
  --surface-raised: #2D2921;
  --surface-pressed: #3D382E;
  --text-primary: #FAF4E5;
  --text-secondary: #D5CAB2;
  --text-muted: #A99D84;
  --action-bg: #48B8A8;
  --action-text: #17150F;
  --border: #3D382E;
  --border-strong: #A99D84;
  --focus: #F8B828;
  --accent: #48B8A8;
  --accent-ink: #48B8A8;
  --accent-soft: #1E2D25;
  --success: #88B808;
  --success-ink: #88B808;
  --success-soft: #272D0E;
  --warning: #F8B828;
  --warning-ink: #F8B828;
  --warning-soft: #322912;
  --danger: #F86979;
  --danger-soft: #3C2320;
  --capsule: #48B8A8;
  --capsule-soft: #1E2D25;
  --child-coral: #F86878;
  --child-coral-ink: #F86979;
  --child-coral-soft: #3C2320;
  --child-leaf: #88B808;
  --child-leaf-ink: #88B808;
  --child-leaf-soft: #272D0E;
  --child-teal: #48B8A8;
  --child-teal-ink: #48B8A8;
  --child-teal-soft: #1E2D25;
  --child-amber: #F8B828;
  --child-amber-ink: #F8B828;
  --child-amber-soft: #322912;
  --child-plum: #A86EBE;
  --child-plum-ink: #B787C8;
  --child-plum-soft: #332630;
  --scrim: rgba(0, 0, 0, 0.70);
  --scrim-strong: rgba(0, 0, 0, 0.94);
  --on-scrim: #FFFFFF;
  --skeleton: #2D2921;
  --tint-teal: #48B8A8;
  --tint-coral: #F86979;
  --tint-leaf: #88B808;
  --tint-amber: #F8B828;
  --tint-ember: #FC6D14;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;
    --background: #FFFFFF;
    --surface: #FFFDF6;
    --surface-raised: #FAF4E5;
    --surface-pressed: #FAF4E5;
    --text-primary: #2E2A20;
    --text-secondary: #635B49;
    --text-muted: #877D66;
    --action-bg: #0F6B62;
    --action-text: #FFFFFF;
    --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;
    --child-plum: #A86EBE;
    --child-plum-ink: #815392;
    --child-plum-soft: #F6F1F9;
    --scrim: rgba(30, 27, 21, 0.55);
    --scrim-strong: rgba(30, 27, 21, 0.94);
    --on-scrim: #FFFFFF;
    --skeleton: #F1E8D2;
    --tint-teal: #0F6B62;
    --tint-coral: #BE3450;
    --tint-leaf: #4F6B0A;
    --tint-amber: #7D5300;
    --tint-ember: #B03A0F;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --background: #17150F;
  --surface: #211E18;
  --surface-raised: #2D2921;
  --surface-pressed: #3D382E;
  --text-primary: #FAF4E5;
  --text-secondary: #D5CAB2;
  --text-muted: #A99D84;
  --action-bg: #48B8A8;
  --action-text: #17150F;
  --border: #3D382E;
  --border-strong: #A99D84;
  --focus: #F8B828;
  --accent: #48B8A8;
  --accent-ink: #48B8A8;
  --accent-soft: #1E2D25;
  --success: #88B808;
  --success-ink: #88B808;
  --success-soft: #272D0E;
  --warning: #F8B828;
  --warning-ink: #F8B828;
  --warning-soft: #322912;
  --danger: #F86979;
  --danger-soft: #3C2320;
  --capsule: #48B8A8;
  --capsule-soft: #1E2D25;
  --child-coral: #F86878;
  --child-coral-ink: #F86979;
  --child-coral-soft: #3C2320;
  --child-leaf: #88B808;
  --child-leaf-ink: #88B808;
  --child-leaf-soft: #272D0E;
  --child-teal: #48B8A8;
  --child-teal-ink: #48B8A8;
  --child-teal-soft: #1E2D25;
  --child-amber: #F8B828;
  --child-amber-ink: #F8B828;
  --child-amber-soft: #322912;
  --child-plum: #A86EBE;
  --child-plum-ink: #B787C8;
  --child-plum-soft: #332630;
  --scrim: rgba(0, 0, 0, 0.70);
  --scrim-strong: rgba(0, 0, 0, 0.94);
  --on-scrim: #FFFFFF;
  --skeleton: #2D2921;
  --tint-teal: #48B8A8;
  --tint-coral: #F86979;
  --tint-leaf: #88B808;
  --tint-amber: #F8B828;
  --tint-ember: #FC6D14;
}

:root[data-theme="light"] {
  color-scheme: light;
  --background: #FFFFFF;
  --surface: #FFFDF6;
  --surface-raised: #FAF4E5;
  --surface-pressed: #FAF4E5;
  --text-primary: #2E2A20;
  --text-secondary: #635B49;
  --text-muted: #877D66;
  --action-bg: #0F6B62;
  --action-text: #FFFFFF;
  --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;
  --child-plum: #A86EBE;
  --child-plum-ink: #815392;
  --child-plum-soft: #F6F1F9;
  --scrim: rgba(30, 27, 21, 0.55);
  --scrim-strong: rgba(30, 27, 21, 0.94);
  --on-scrim: #FFFFFF;
  --skeleton: #F1E8D2;
  --tint-teal: #0F6B62;
  --tint-coral: #BE3450;
  --tint-leaf: #4F6B0A;
  --tint-amber: #7D5300;
  --tint-ember: #B03A0F;
}

:root[data-tint="teal"] {
  --accent: #48B8A8;
  --accent-ink: #48B8A8;
  --accent-soft: #1E2D25;
  --action-bg: #48B8A8;
  --action-text: #17150F;
  --focus: #F8B828;
  --capsule: #48B8A8;
  --capsule-soft: #1E2D25;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"])[data-tint="teal"] {
    --accent: #48B8A8;
    --accent-ink: #0F6B62;
    --accent-soft: #DDF1EE;
    --action-bg: #0F6B62;
    --action-text: #FFFFFF;
    --focus: #B03A0F;
    --capsule: #0F6B62;
    --capsule-soft: #DDF1EE;
  }
}

:root[data-theme="light"][data-tint="teal"] {
  --accent: #48B8A8;
  --accent-ink: #0F6B62;
  --accent-soft: #DDF1EE;
  --action-bg: #0F6B62;
  --action-text: #FFFFFF;
  --focus: #B03A0F;
  --capsule: #0F6B62;
  --capsule-soft: #DDF1EE;
}

:root[data-theme="dark"][data-tint="teal"] {
  --accent: #48B8A8;
  --accent-ink: #48B8A8;
  --accent-soft: #1E2D25;
  --action-bg: #48B8A8;
  --action-text: #17150F;
  --focus: #F8B828;
  --capsule: #48B8A8;
  --capsule-soft: #1E2D25;
}

:root[data-tint="coral"] {
  --accent: #F86878;
  --accent-ink: #F86979;
  --accent-soft: #3C2320;
  --action-bg: #F86979;
  --action-text: #17150F;
  --focus: #F8B828;
  --capsule: #F86979;
  --capsule-soft: #3C2320;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"])[data-tint="coral"] {
    --accent: #F86878;
    --accent-ink: #BE3450;
    --accent-soft: #FDE6E8;
    --action-bg: #BE3450;
    --action-text: #FFFFFF;
    --focus: #B03A0F;
    --capsule: #BE3450;
    --capsule-soft: #FDE6E8;
  }
}

:root[data-theme="light"][data-tint="coral"] {
  --accent: #F86878;
  --accent-ink: #BE3450;
  --accent-soft: #FDE6E8;
  --action-bg: #BE3450;
  --action-text: #FFFFFF;
  --focus: #B03A0F;
  --capsule: #BE3450;
  --capsule-soft: #FDE6E8;
}

:root[data-theme="dark"][data-tint="coral"] {
  --accent: #F86878;
  --accent-ink: #F86979;
  --accent-soft: #3C2320;
  --action-bg: #F86979;
  --action-text: #17150F;
  --focus: #F8B828;
  --capsule: #F86979;
  --capsule-soft: #3C2320;
}

:root[data-tint="leaf"] {
  --accent: #88B808;
  --accent-ink: #88B808;
  --accent-soft: #272D0E;
  --action-bg: #88B808;
  --action-text: #17150F;
  --focus: #F8B828;
  --capsule: #88B808;
  --capsule-soft: #272D0E;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"])[data-tint="leaf"] {
    --accent: #88B808;
    --accent-ink: #4F6B0A;
    --accent-soft: #EDF3D9;
    --action-bg: #4F6B0A;
    --action-text: #FFFFFF;
    --focus: #B03A0F;
    --capsule: #4F6B0A;
    --capsule-soft: #EDF3D9;
  }
}

:root[data-theme="light"][data-tint="leaf"] {
  --accent: #88B808;
  --accent-ink: #4F6B0A;
  --accent-soft: #EDF3D9;
  --action-bg: #4F6B0A;
  --action-text: #FFFFFF;
  --focus: #B03A0F;
  --capsule: #4F6B0A;
  --capsule-soft: #EDF3D9;
}

:root[data-theme="dark"][data-tint="leaf"] {
  --accent: #88B808;
  --accent-ink: #88B808;
  --accent-soft: #272D0E;
  --action-bg: #88B808;
  --action-text: #17150F;
  --focus: #F8B828;
  --capsule: #88B808;
  --capsule-soft: #272D0E;
}

:root[data-tint="amber"] {
  --accent: #F8B828;
  --accent-ink: #F8B828;
  --accent-soft: #322912;
  --action-bg: #F8B828;
  --action-text: #17150F;
  --focus: #48B8A8;
  --capsule: #F8B828;
  --capsule-soft: #322912;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"])[data-tint="amber"] {
    --accent: #F8B828;
    --accent-ink: #7D5300;
    --accent-soft: #FDF0D2;
    --action-bg: #7D5300;
    --action-text: #FFFFFF;
    --focus: #B03A0F;
    --capsule: #7D5300;
    --capsule-soft: #FDF0D2;
  }
}

:root[data-theme="light"][data-tint="amber"] {
  --accent: #F8B828;
  --accent-ink: #7D5300;
  --accent-soft: #FDF0D2;
  --action-bg: #7D5300;
  --action-text: #FFFFFF;
  --focus: #B03A0F;
  --capsule: #7D5300;
  --capsule-soft: #FDF0D2;
}

:root[data-theme="dark"][data-tint="amber"] {
  --accent: #F8B828;
  --accent-ink: #F8B828;
  --accent-soft: #322912;
  --action-bg: #F8B828;
  --action-text: #17150F;
  --focus: #48B8A8;
  --capsule: #F8B828;
  --capsule-soft: #322912;
}

:root[data-tint="ember"] {
  --accent: #FC6000;
  --accent-ink: #FC6D14;
  --accent-soft: #40230C;
  --action-bg: #FC6D14;
  --action-text: #17150F;
  --focus: #F8B828;
  --capsule: #FC6D14;
  --capsule-soft: #40230C;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"])[data-tint="ember"] {
    --accent: #FC6000;
    --accent-ink: #B03A0F;
    --accent-soft: #FBE6DA;
    --action-bg: #B03A0F;
    --action-text: #FFFFFF;
    --focus: #0F6B62;
    --capsule: #B03A0F;
    --capsule-soft: #FBE6DA;
  }
}

:root[data-theme="light"][data-tint="ember"] {
  --accent: #FC6000;
  --accent-ink: #B03A0F;
  --accent-soft: #FBE6DA;
  --action-bg: #B03A0F;
  --action-text: #FFFFFF;
  --focus: #0F6B62;
  --capsule: #B03A0F;
  --capsule-soft: #FBE6DA;
}

:root[data-theme="dark"][data-tint="ember"] {
  --accent: #FC6000;
  --accent-ink: #FC6D14;
  --accent-soft: #40230C;
  --action-bg: #FC6D14;
  --action-text: #17150F;
  --focus: #F8B828;
  --capsule: #FC6D14;
  --capsule-soft: #40230C;
}

/*
 * 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;

    /*
     * Two rows in the first column, one tall cell in the second.
     *
     * Measured before this existed: the preview ran to 1941px against 618px of
     * copy, leaving 1323px of white beside it. The steps are placed into that
     * gap rather than left in a band further down the page.
     *
     * Placed explicitly rather than reordered. The source order is copy,
     * preview, steps — which is the order a narrow screen stacks them in, and
     * the order they are read in — and only the wide layout moves the steps up
     * beside the picture.
     */
    row-gap: 2.75rem;

    /*
     * The first row takes its own height and the second takes what is left.
     *
     * Without this both rows share the surplus the tall preview creates, and
     * measuring showed that as a 297px hole between the trust line and the
     * steps — the steps floated in the middle of the column instead of
     * following the copy. `auto 1fr` puts the whole remainder below them.
     */
    grid-template-rows: auto minmax(0, 1fr);
  }

  .hero__copy {
    grid-column: 1;
    grid-row: 1;
  }

  .hero__steps {
    grid-column: 1;
    grid-row: 2;
    /* Top of its row, so the gap above them is the row gap and nothing more. */
    align-self: start;
  }

  .hero__preview {
    grid-column: 2;
    grid-row: 1 / span 2;
  }
}

.hero__steps {
  display: grid;
  gap: 1.25rem;
  /* The hero's copy column sets `justify-items: start`; this is its sibling and
     needs the full column rather than the width of its widest line. */
  width: 100%;
}

/*
 * A heading, not a second headline.
 *
 * The section h2 is up to 2.5rem, which beside a 3.9rem h1 in the same block of
 * the page reads as two competing titles rather than a title and a subheading.
 */
.hero__steps-title {
  font-size: clamp(1.45rem, 1.15rem + 0.85vw, 1.9rem);
}

.hero__steps .section-head {
  margin-bottom: 0;
}

.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));
}

/*
 * One per row, at every width.
 *
 * `auto-fit` would put two side by side in the hero's column, which is half the
 * height and so half the gap filled — and filling the gap is the only reason
 * these are there.
 */
.steps--stacked {
  grid-template-columns: minmax(0, 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);
}

/* --- Text Capture: a phone, and a price per tier ---------------------------
 *
 * The add-on gets a drawing and the tiers do not, and that is the point. The
 * three tiers are one product at three sizes and a row of matching cards says
 * so; Text Capture is a different shape of thing — a subscription, bought
 * separately, working from a phone with no app on it — and giving it a layout
 * of its own is how the page says that before anybody reads a word.
 */

/*
 * Phone left, prices right, and the phone is the narrow column: it is a fixed
 * drawing and the card beside it holds three price rows plus the copy that
 * explains them. Stacks below the same breakpoint `.detail` uses, where a
 * two-column split stops being one.
 */
.sms {
  display: grid;
  gap: 1.5rem;
  align-items: start;
}

@media (min-width: 56rem) {
  .sms {
    grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
    gap: 2.5rem;
  }
}

/* The card is already a `.plan`; this only stops it stretching to the phone. */
.sms__card {
  align-self: start;
}

/*
 * The handset.
 *
 * Centred and capped rather than full-width: on a phone this is a picture of a
 * phone, and one drawn edge to edge stops reading as an object and starts
 * reading as the page itself.
 */
.sms-phone {
  width: 100%;
  max-width: 19rem;
  margin-inline: auto;
  padding: 0.7rem;
  border: 1px solid var(--border-strong);
  border-radius: 34px;
  background: var(--surface);
  box-shadow: 0 24px 60px -30px color-mix(in srgb, var(--text-primary) 45%, transparent);
}

/*
 * `position: relative` for the speaker slot below, which is the one piece of
 * this drawing that has to sit over the screen rather than in the flow of it.
 */
.sms-phone__screen {
  position: relative;
  border-radius: 26px;
  background: var(--surface-pressed);
  padding: 1.4rem 0.8rem 1.1rem;
  display: grid;
  gap: 0.85rem;
}

/* The speaker slot. The one bit of handset that makes the rest read as one. */
.sms-phone__screen::before {
  content: '';
  position: absolute;
  top: 0.6rem;
  left: 50%;
  transform: translateX(-50%);
  width: 3.4rem;
  height: 0.3rem;
  border-radius: 999px;
  background: var(--border-strong);
}

/* The name at the top of a message thread. */
.sms-phone__bar {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding-bottom: 0.7rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.9rem;
  font-weight: 700;
}

.sms-phone__avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.7rem;
  height: 1.7rem;
  flex: none;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent-ink);
}

.sms-phone__avatar svg {
  width: 1rem;
  height: 1rem;
}

.sms-phone__thread {
  display: grid;
  gap: 0.6rem;
}

/*
 * A message. `max-width` in percent rather than `width`, so a bubble is as wide
 * as its words up to a point — a fixed width would make the one-line reply the
 * same size as the photograph above it, which is not what a thread looks like.
 *
 * Both bubbles are centred in the screen rather than pinned to opposite edges.
 * A real thread pins them, but this drawing is two messages rather than a
 * conversation, and pinned they read as a picture that has slipped off to one
 * side; centred, the photograph and the reply below it sit on one axis with
 * the handset around them.
 */
.sms-phone__bubble {
  max-width: 88%;
  padding: 0.6rem 0.75rem;
  border-radius: 18px;
  font-size: 0.85rem;
  line-height: 1.45;
  display: grid;
  gap: 0.45rem;
  justify-items: start;
  justify-self: center;
}

/* Outgoing: the parent's own message, in the action colour. */
.sms-phone__bubble--out {
  background: var(--action-bg);
  color: var(--action-text);
}

/* Incoming: what Storygarden sends back, drawn as the memory it just made. */
.sms-phone__bubble--in {
  background: var(--background);
  border: 1px solid var(--border);
}

/*
 * The photograph fills the bubble rather than sitting in it.
 *
 * Inset first, and the 0.75rem of padding around a picture drew a thick green
 * frame that read as a border on the photograph — the bubble's colour became
 * the loudest thing in the drawing, which is not what a message thread looks
 * like. Bled to the edges, the colour is left doing the one job it has: saying
 * which side of the conversation this is, behind the line of text below.
 *
 * `justify-self`, because the bubble is a grid with `justify-items: start` so
 * that the chip and the tick line stay the width of their own words.
 */
.sms-phone__photo {
  display: block;
  justify-self: stretch;
  width: calc(100% + 1.5rem);
  margin: -0.6rem -0.75rem 0;
  border-radius: 17px 17px 4px 4px;
  overflow: hidden;
}

.sms-phone__photo img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.sms-phone__saved {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--success-ink);
}

.sms-phone__saved svg {
  width: 0.9rem;
  height: 0.9rem;
  flex: none;
}

/*
 * The price ladder.
 *
 * A description list, because that is what it is: three tiers, and what the
 * add-on costs on each. Every row is a `<div>` wrapping its own `dt` and `dd`
 * so a row can be a grid of its own — grouping inside a `<dl>` is what the
 * wrapper is for, and it keeps the pairing intact for a screen reader.
 */
.sms-prices {
  margin: 0;
  display: grid;
  gap: 0.15rem;
  border: 1px solid var(--border);
  border-radius: 18px;
  overflow: hidden;
  background: var(--border);
}

/*
 * A row: the tier on the left, what the add-on costs on it on the right.
 *
 * Stacked below the breakpoint rather than wrapped. `flex-wrap` was tried and
 * decides per row, so on a narrow screen "Free" kept its price on the same line
 * while the two longer tier names pushed theirs onto a second — three rows in
 * two different shapes, which reads as a broken table rather than a ladder.
 * A grid that changes shape once changes all three at the same moment.
 */
.sms-price {
  display: grid;
  gap: 0.15rem 1rem;
  padding: 0.75rem 0.95rem;
  background: var(--surface);
}

@media (min-width: 30rem) {
  .sms-price {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
  }

  .sms-price dd {
    text-align: right;
  }
}

.sms-price dt,
.sms-price dd {
  margin: 0;
}

.sms-price__tier {
  display: block;
  font-weight: 700;
  font-size: 1rem;
}

.sms-price__hint {
  display: block;
  font-size: 0.82rem;
  color: var(--text-secondary);
}

.sms-price__amount {
  font-size: 1.45rem;
  font-weight: 800;
  letter-spacing: -0.02em;
}

.sms-price__period {
  margin-left: 0.3rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-secondary);
}

/* Same silence as `.plan__price--pending`, at the size of a row. */
.sms-price__pending {
  font-size: 0.9rem;
  font-weight: 650;
  color: var(--text-secondary);
}

.sms-price__year {
  display: block;
  font-size: 0.8rem;
  color: var(--text-secondary);
}

/*
 * 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);
}

/*
 * The three tier columns are centred; the feature label down the left is not.
 *
 * Left-aligned, a tick sat hard against the left edge of a column whose tint
 * ran the full width of it, so the marks drifted away from the header they
 * belong to and the table read as a list with some decoration rather than as a
 * grid. Centring puts every mark, number and price under the middle of its own
 * tier. The row header stays left because it is a label being read, not a value
 * being compared — centring prose is how a comparison table stops being
 * scannable down its first column.
 *
 * `thead th + th` rather than `:not(:first-child)`: the corner cell is empty
 * and has no alignment to state, and naming the tier cells positively keeps
 * this rule from reaching a fourth column that is not a tier.
 */
.compare thead th + th,
.compare tbody td {
  text-align: center;
}

.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);
}

/*
 * "a month", under the figure in the Text Capture row.
 *
 * A block, so the unit sits beneath the price rather than doubling the column's
 * width — three of these side by side is what decides how wide the table is on
 * a phone, and the table already scrolls at 36rem.
 */
.compare__unit {
  display: block;
  font-weight: 600;
  font-size: 0.78rem;
  color: var(--text-secondary);
}

/* The qualifier under a row header: "Add-on, coming soon". */
.compare__sub {
  display: block;
  font-weight: 600;
  font-size: 0.78rem;
  color: var(--text-secondary);
}

/* --- 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 story timeline inside the preview ---------------------------------
 *
 * A drawing of the application's story view: a vine with chapters down it and
 * the memories hung off either side. Every colour is a token, as the whole
 * stylesheet is — `tests/marketing-site.test.ts` fails on a hex literal here.
 */

.story {
  position: relative;
  padding-block: 0.4rem;
}

/*
 * One continuous line behind everything, not a segment per row. The site is
 * plain HTML with nothing virtualised, so unlike the application's list this
 * can be a single element — and a single element cannot develop hairline gaps
 * between rows at awkward pixel ratios.
 */
/*
 * Down the left, not the middle, and single column at every width.
 *
 * The application zigzags either side of a centre line when it has a desktop
 * to do it on. This preview never does: it sits in a panel beside the hero
 * copy, about 300px across, where a 52%-wide card with a photograph beside the
 * words leaves roughly 110px for the words. Measured in the browser, not
 * guessed — the first version did zigzag and every card came out as a column
 * of two-word lines.
 *
 * So this draws the phone-shaped version of the same screen, which is both
 * legible here and the layout most parents actually use.
 */
.story__spine {
  position: absolute;
  inset-block: 0;
  left: 6px;
  width: 2px;
  background: var(--border);
  border-radius: 1px;
}

.story__chapter {
  position: relative;
  width: fit-content;
  margin: 0.9rem 0 0.7rem 1.6rem;
  padding: 0.25rem 0.9rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 700;
  border: 1px solid;
}

.story__chapter--coral {
  background: var(--child-coral-soft);
  border-color: var(--child-coral-ink);
}
.story__chapter--leaf {
  background: var(--child-leaf-soft);
  border-color: var(--child-leaf-ink);
}
.story__chapter--teal {
  background: var(--child-teal-soft);
  border-color: var(--child-teal-ink);
}
.story__chapter--amber {
  background: var(--child-amber-soft);
  border-color: var(--child-amber-ink);
}

/*
 * The `--left` and `--right` modifiers are still emitted by the renderer and
 * deliberately do nothing here. Document order is chronological either way, so
 * dropping the visual alternation costs nothing that matters and the markup
 * stays the same shape as the component it is drawing.
 */
.story__entry,
.story__entry--left,
.story__entry--right {
  position: relative;
  width: auto;
  margin: 0;
  padding-left: 1.6rem;
}

.story__dot {
  position: absolute;
  top: 1.1rem;
  left: 1px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--background);
}

.story__body {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
}

.story__photo {
  flex: none;
  width: 76px;
  border-radius: 12px;
  overflow: hidden;
}

.story__photo img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  display: block;
}

.story__beginning {
  display: grid;
  gap: 0.2rem;
  padding: 0.9rem 1rem;
  border-radius: 16px;
  background: var(--accent-soft);
  margin-bottom: 0.2rem;
}

.story__beginning-title {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--accent-ink);
}

.story__born {
  font-size: 0.95rem;
  font-weight: 700;
}

.story__place {
  font-size: 0.82rem;
  color: var(--text-secondary);
}

/*
 * The map, with its pin at dead centre.
 *
 * Centre, because the image was cropped to put the point there — the same
 * trick the application uses, and the reason neither side needs any projection
 * arithmetic. Built and committed by scripts/build-site-assets.mjs, so no
 * visitor's browser ever asks a tile server for anything.
 */
.story__map {
  position: relative;
  display: block;
  margin-top: 0.4rem;
  border-radius: 12px;
  overflow: hidden;
  aspect-ratio: 16 / 9;
}

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

.story__map-pin {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 12px;
  height: 12px;
  margin: -6px 0 0 -6px;
  border-radius: 50% 50% 50% 0;
  transform: rotate(-45deg);
  background: var(--danger);
  border: 2px solid var(--background);
}

/* Required by the licence wherever the data appears. */
.story__map-credit {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.68rem;
  color: var(--text-secondary);
}

/*
 * The end of the vine: where the story is up to today.
 *
 * The date beside it is filled in by the browser and left empty here — these
 * pages are generated once and served until the next deploy, so a date baked
 * in at build time is the build's date, right on the day it shipped and wrong
 * every day after.
 */
.story__latest {
  position: relative;
  width: fit-content;
  margin: 0.9rem auto 0;
  padding: 0.25rem 0.9rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-secondary);
  background: var(--surface-raised);
}

/*
 * Below the breakpoint the zigzag becomes one column with the vine down the
 * left, which is what the application does on a phone. A 52%-wide card either
 * side of a centre line is unreadable at 380px.
 */
@media (max-width: 34rem) {
  .story__spine {
    left: 6px;
    margin-left: 0;
  }

  .story__entry,
  .story__entry--left,
  .story__entry--right {
    width: auto;
    margin: 0;
    padding-left: 1.6rem;
    padding-right: 0;
  }

  .story__entry--left .story__dot,
  .story__entry--right .story__dot {
    left: 1px;
    right: auto;
  }

  .story__chapter {
    margin-left: 1.6rem;
    margin-right: auto;
  }
}

/*
 * 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;
  }
}

/* ---------------------------------------------------------------------------
   The feed mock

   A drawing of what the app opens on, sitting beside `.preview*`, which draws
   a child's page. Two surfaces, two mocks: the hero shows the one you land on
   and `how-it-works` shows the one a story becomes.

   The ground is inverted here relative to `.preview`, and deliberately: the
   app's feed is white cards floating on warmth, and the shopfront theme keeps
   `--background` white, so the panel takes `--surface-pressed` and the cards
   take `--background`. That is the same relationship the app has, expressed in
   the one theme the site is allowed to use.

   Not one colour is written here, same as everywhere else in this file.
--------------------------------------------------------------------------- */

.feed {
  background: var(--surface-pressed);
  border: 1px solid var(--border);
  border-radius: 26px;
  overflow: hidden;
  box-shadow: 0 24px 60px -30px color-mix(in srgb, var(--text-primary) 45%, transparent);
}

.feed__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.6rem 0.9rem;
  background: var(--background);
  border-bottom: 1px solid var(--border);
}

.feed__mark img {
  display: block;
  width: auto;
  height: 1.6rem;
}

.feed__bar-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.feed__icon {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: 999px;
  background: var(--surface-pressed);
  color: var(--text-secondary);
}

.feed__icon svg {
  width: 1.05rem;
  height: 1.05rem;
}

/* The parent's own circle is an initial, because the app has nowhere to store
   a photograph of them — `profiles` has a display name and no image column. */
.feed__you {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-ink);
  font-size: 0.85rem;
  font-weight: 700;
}

.feed__body {
  display: grid;
  gap: 0.8rem;
  padding: 0.9rem;
}

.feed__composer {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem;
  background: var(--background);
  border: 1px solid var(--border);
  border-radius: 18px;
}

.feed__composer-prompt {
  flex: 1;
  padding: 0.55rem 0.9rem;
  border-radius: 999px;
  background: var(--surface-pressed);
  color: var(--text-secondary);
  font-size: 0.92rem;
}

.feed__composer-camera {
  display: grid;
  place-items: center;
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-ink);
}

.feed__composer-camera svg {
  width: 1.15rem;
  height: 1.15rem;
}

.feed__kids {
  display: flex;
  gap: 0.9rem;
}

.feed__kid {
  display: grid;
  justify-items: center;
  gap: 0.3rem;
  font-size: 0.72rem;
  color: var(--text-secondary);
}

/* The ring carries the child's accent, which is the same colour their cards
   are headed with in the app. */
.feed__ring {
  display: grid;
  place-items: center;
  width: 3.4rem;
  height: 3.4rem;
  padding: 3px;
  border-radius: 999px;
  border: 2px solid var(--border-strong);
  overflow: hidden;
}

.feed__ring img,
.feed__ring picture {
  width: 100%;
  height: 100%;
  border-radius: 999px;
  object-fit: cover;
  display: block;
}

.feed__ring--leaf { border-color: var(--child-leaf-ink); }
.feed__ring--coral { border-color: var(--child-coral-ink); }

.feed__ring--add {
  border-style: dashed;
  border-color: var(--border-strong);
  color: var(--text-secondary);
  background: var(--background);
  font-size: 1.4rem;
  line-height: 1;
}

.feed__month {
  align-self: start;
  justify-self: start;
  margin: 0.15rem 0 0;
  padding: 0.28rem 0.85rem;
  border-radius: 999px;
  background: var(--child-coral-soft);
  border: 1px solid var(--child-coral-ink);
  font-size: 0.78rem;
  font-weight: 600;
}

.feed__card {
  display: grid;
  gap: 0.55rem;
  padding: 0.9rem;
  background: var(--background);
  border: 1px solid var(--border);
  border-radius: 20px;
}

.feed__byline {
  display: flex;
  align-items: center;
  gap: 0.55rem;
}

.feed__avatar {
  display: block;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 999px;
  overflow: hidden;
}

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

.feed__who {
  display: grid;
  line-height: 1.25;
}

.feed__name {
  font-weight: 700;
  font-size: 0.92rem;
}

.feed__when {
  font-size: 0.74rem;
  color: var(--text-secondary);
}

.feed__card .preview__title {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
}

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

/* The photograph at the width of the card, which is the whole point of the
   card. 4:3 matches the app's hero. */
.feed__photo {
  display: block;
  aspect-ratio: 4 / 3;
  border-radius: 14px;
  overflow: hidden;
  background: var(--surface-pressed);
}

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

.feed__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0;
}

.feed__tag {
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  background: var(--child-leaf-soft);
  color: var(--child-leaf-ink);
  font-size: 0.72rem;
  font-weight: 600;
}

/*
 * The appearance picker.
 *
 * A details element, so it opens with no script and the choices are visible to
 * somebody who blocks it. The buttons inside only do something once the script
 * at the foot of the page attaches, which is the usual trade for progressive
 * enhancement: visible and inert beats hidden and clever.
 */
.appearance {
  position: relative;
}

.appearance > summary {
  list-style: none;
  cursor: pointer;
  padding: 0.45rem 0.8rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-secondary);
  font-size: 0.9rem;
  white-space: nowrap;
}

.appearance > summary::-webkit-details-marker {
  display: none;
}

.appearance > summary:hover {
  color: var(--text-primary);
  border-color: var(--border-strong);
}

.appearance__panel {
  position: absolute;
  right: 0;
  top: calc(100% + 0.5rem);
  z-index: 30;
  width: max-content;
  max-width: min(20rem, calc(100vw - 2rem));
  padding: 0.9rem;
  border-radius: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: 0 24px 60px -30px color-mix(in srgb, var(--text-primary) 45%, transparent);
}

.appearance__label {
  margin: 0 0 0.4rem;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-secondary);
}

.appearance__row {
  display: flex;
  gap: 0.4rem;
  margin-bottom: 0.9rem;
}

.appearance__row--wrap {
  flex-wrap: wrap;
  margin-bottom: 0;
}

.appearance__option,
.appearance__swatch {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.7rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--background);
  color: var(--text-secondary);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}

.appearance__option:hover,
.appearance__swatch:hover {
  border-color: var(--border-strong);
  color: var(--text-primary);
}

/*
 * The current choice, marked by the border and the weight as well as by the
 * tint — a ring in the accent alone would be invisible to exactly the reader
 * most likely to be changing the colour.
 */
.appearance__option[aria-pressed='true'],
.appearance__swatch[aria-pressed='true'] {
  border-color: var(--accent-ink);
  border-width: 2px;
  color: var(--accent-ink);
  font-weight: 650;
}

.appearance__dot {
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--text-primary) 18%, transparent);
}

/*
 * A swatch has to show a colour that is not the current one.
 *
 * Every other value here reads a custom property holding the accent in use,
 * and these five cannot: the picker shows what each choice *would* look like,
 * all at once, while the accent variables only ever hold the one chosen. So
 * the generator emits a variable per tint alongside them — same tokens, same
 * single source, no hexes in this file.
 */
.appearance__dot--teal {
  background: var(--tint-teal);
}
.appearance__dot--coral {
  background: var(--tint-coral);
}
.appearance__dot--leaf {
  background: var(--tint-leaf);
}
.appearance__dot--amber {
  background: var(--tint-amber);
}
.appearance__dot--ember {
  background: var(--tint-ember);
}

@media (max-width: 900px) {
  /* The header runs out of room before this does; the compact menu carries
     the links and this stays, because it is the control that is hardest to
     find anywhere else. */
  .appearance > summary {
    padding: 0.4rem 0.6rem;
  }
}
