/* Zovie Software — zoviesoftware.com
 *
 * Built from the Figma frames 303:14850 (desktop, 1742) and 308:18309
 * (mobile, 393).
 *
 * The layout system, the tokens and the two interactive pieces are shared with
 * getgrowl.app rather than reinvented here: the ruled band, the token names,
 * the Apple-style carousel and the FAQ accordion all come from that site (and,
 * before it, from ericfillingame.com). Where a comment says "as growl does",
 * that is a deliberate match so the two sites cannot drift apart.
 */

:root {
  /* Read from the Figma variables on the frames above. The site is light-only,
     so there is one mode and no dark overrides. */
  --text-primary: #000000;
  --text-secondary: #404040;
  --text-inverse: #ffffff;
  --bg: #ffffff;
  --bg-inverse: #000000;
  --border: #e5e5e5;
  --rule: var(--border);
  /* The carousel button with nowhere left to go, and the tonal button. */
  --accent-disabled: #e0e0e0;
  /* `Surface/surface` — the ground under the search pill, the ⋮ button and a
     highlighted palette row. */
  --surface: #f5f5f5;
  --surface-hover: #ececec;
  /* `Text/disabled` is 40% black, which the nav's links and the palette's
     group labels both take; `Text/muted` is the ⌘K grey. */
  --text-disabled: rgba(0, 0, 0, 0.4);
  --text-muted: #777169;
  /* `Color/bg/surface/primary` — the card grounds behind the artwork. */
  --surface-primary: #f5f5f5;

  /* ── The ruled band ──
     Each row is three columns with a 10px gap between them: gutter, content,
     gutter. The hairlines sit on the *inner* edge of each gutter, so the rule
     is 10px outside the column and the content starts 10px past the rule.
     Collapsing the two into one 20px inset would put the rules against the
     screen edge. Desktop is a 1200 column inside 1220; mobile a 353 inside
     393. */
  --inset: 10px;
  --col: 1200px;
  --band: 1220px;

  /* ── Type ──
     Five faces, five jobs, as the frames use them — the same split as
     getgrowl.app. `Font/heading` resolves to Satoshi and `Font/body` to Inter;
     that mapping is settled rather than guessed, since the style names come
     back as Text/heading-sm and Text/body-lg on 303:15919. */
  --sans: "DM Sans", system-ui, sans-serif;
  --body: "Inter", system-ui, sans-serif;
  --heading: "Satoshi", system-ui, sans-serif;
  --serif: "Roboto Serif", Georgia, "Times New Roman", serif;
  /* The eyebrows are SF Mono in the comps. ui-monospace resolves to it on
     Apple hardware and to a sane mono elsewhere; no webfont is loaded for it. */
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* Where the carousel's first card starts, measured from the window's edge.
     The track itself still bleeds off both sides. */
  --carousel-lead: 60px;

  /* The header's height, which the hero subtracts from the viewport to fill
     exactly the first screenful. It is load-bearing rather than a note: the
     header is sized *from* it below, so the two cannot drift apart. */
  --header-h: 72px;
}

@media (min-width: 1024px) {
  :root {
    /* 291 at 1742: half the leftover outside the band, or the gutter,
       whichever is larger, plus the 10px gap, plus the track's own 20. Built
       from the tokens so it cannot drift from where the columns land. */
    --carousel-lead: calc(
      max((100vw - var(--band)) / 2, var(--inset)) + var(--inset) + 20px
    );
    --header-h: 86px;
  }
}

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

html {
  scroll-behavior: smooth;
  /* The seams span the window; this keeps their overhang from scrolling. */
  overflow-x: clip;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text-primary);
  font-family: var(--body);
  font-size: 18px;
  line-height: 28px;
  -webkit-font-smoothing: antialiased;
}

a {
  color: inherit;
}

img {
  display: block;
  max-width: 100%;
  /* Without this the width/height attributes win on height while max-width
     clamps the width, so an image scaled down keeps its full intrinsic height
     — and any author `aspect-ratio` below is ignored. */
  height: auto;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  padding: 0.75rem 1rem;
  background: var(--bg-inverse);
  color: var(--text-inverse);
  z-index: 100;
}

.skip-link:focus {
  left: 0;
}

:focus-visible {
  outline: 2px solid var(--text-primary);
  outline-offset: 2px;
}

/* ── The band ─────────────────────────────────────────────────────────────
   Every row draws the hairline at its *bottom* edge plus the two marks where
   that line crosses the vertical rules, which is how the comps place them:
   the Group frames sit at each row's closing y, centred on the rail. A row
   never draws the line above it, because the row before already did. */

.band {
  position: relative;
  width: 100%;
}

/* An anchored row has to clear the bar that sits over it — the revealed copy
   on desktop, the pinned one on a phone — or the jump lands its rule and
   eyebrow underneath and the section looks like it starts at its heading. */
.band[id] {
  scroll-margin-top: var(--header-h);
}

.band:not(.band--last)::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: 1px;
  background: var(--rule);
  pointer-events: none;
}

.band-row {
  display: flex;
  gap: var(--inset);
  width: 100%;
  max-width: 393px;
  margin-inline: auto;
}

.band-gutter {
  width: var(--inset);
  flex-shrink: 0;
  border-color: var(--rule);
  border-style: solid;
  border-width: 0;
}

.band-gutter--l {
  border-right-width: 1px;
}

/* The gutter keeps its width either way — it is what holds the column at the
   comp's measurements — so turning the rails off only drops the border. */
.band--norails .band-gutter {
  border-width: 0;
}

.band-gutter--r {
  border-left-width: 1px;
}

.band-col {
  position: relative;
  flex: 1;
  min-width: 0;
}

/* The crossing mark — a 4px dot sitting on the intersection, with both rules
   running straight through underneath.

   303:15170 draws it as two circles, a 20px white one under a 4px black one,
   which punches a hole in the lines and rings the dot. That reads as a target
   rather than a grid; the dot alone is the intent, after the dot grids on
   elevenlabs.io. Hung off the column at -10px because that is where the rail
   is, so the two share a box and cannot land on different rows. */
.band:not(.band--last) .band-col::before,
.band:not(.band--last) .band-col::after {
  content: "";
  position: absolute;
  bottom: 0;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  /* One element, painted by a gradient: the page's own colour clears the 20px
     the comp reserves, and the 4px dot sits in the middle of that clearing.

     Two stacked inset shadows drew this at first and left a hairline ring at
     the circle's edge, where the inner shadow's boundary did not quite cover
     the outer one's. A gradient has no such seam — and the clearing's own
     edge is invisible because it is the background painted on the
     background. */
  background: radial-gradient(
    circle at 50% 50%,
    var(--text-primary) 0 1.9px,
    var(--bg) 2.1px 100%
  );
  z-index: 2;
  pointer-events: none;
}

.band:not(.band--last) .band-col::before {
  left: calc(-1 * var(--inset));
  transform: translate(-50%, 50%);
}

.band:not(.band--last) .band-col::after {
  right: calc(-1 * var(--inset));
  transform: translate(50%, 50%);
}

@media (min-width: 1024px) {
  .band-row {
    max-width: 1742px;
  }

  .band-gutter {
    width: auto;
    min-width: var(--inset);
    flex: 1;
  }

  .band-col {
    /* Do not grow past the column the frame draws, but *do* shrink under it,
       or every viewport between 1024 and 1240 overflows sideways. */
    flex: 0 1 var(--col);
  }

  /* The 10px to the next row. It goes on the band, never on the column: a
     margin inside the flex row stretches the gutters past it and the rails
     overshoot the hairline by exactly that much. */
  .band--gap {
    margin-bottom: var(--inset);
  }
}

/* ── Header ───────────────────────────────────────────────────────────────
   303:14851. The nav has no gutters or rails of its own in the comp — just
   the hairline under it — so it is a plain capped row rather than a band.

   Below 1024 the inline links and the search pill both go and the ⋮ menu
   becomes the whole navigation, which is how 308:18312 draws it. */

/* Pinned on a phone, scrolling away on desktop — desktop lets it leave and
   brings the copy below back instead. A bar that simply stuck would sit over
   the hero from the first pixel.

   The rule is an inset shadow rather than a border: the bar is sticky, so a
   border would add a pixel to its height and nudge the page down the moment
   it appeared. Same reason the band draws its rules this way. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  min-height: var(--header-h);
  background: var(--bg);
  transition: box-shadow 200ms ease;
}

.site-header.is-scrolled {
  box-shadow: inset 0 -1px 0 0 var(--rule);
}

/* The second pass: a fixed copy that slides down once the reader is past the
   reveal distance and stays for the rest of the page.

   `visibility` rides along with the transform so the copy is out of the tab
   order and the accessibility tree while it is off screen — otherwise the
   page would offer every nav link twice. Transitioning visibility *with* the
   transform is what keeps it hidden for the whole slide out and flips it at
   the start of the slide in. */
.reveal-nav {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 50;
  display: none;
  padding-inline: var(--inset);
  background: var(--bg);
  box-shadow: inset 0 -1px 0 0 var(--rule);
  visibility: hidden;
  transform: translateY(-100%);
  transition: transform 300ms ease-in-out, visibility 300ms ease-in-out;
}

.reveal-nav.is-shown {
  visibility: visible;
  transform: translateY(0);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem;
  width: 100%;
  max-width: var(--col);
  margin-inline: auto;
  padding: 0.875rem 1.25rem;
}

.nav-left,
.nav-right {
  display: flex;
  align-items: center;
}

.nav-left {
  gap: 40px;
}

.nav-right {
  gap: 12px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  font-family: var(--mono);
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.1px;
  line-height: 1.45;
  color: var(--text-primary);
  text-decoration: none;
}

/* Icons carry their own squircle in the PNG's alpha, so anything drawn behind
   them traces a matching border-radius. */
.brand-icon {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  border-radius: 22.5%;
}

.nav-links {
  display: none;
  align-items: center;
  gap: 20px;
}

/* The links are pills in the comp — 43 tall on an 80 minimum, set in
   Text/body-sm at `Text/disabled`, which is 40% black rather than the body
   grey. They only take a ground on hover. */
.nav-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 80px;
  height: 43px;
  padding: 4px 14px;
  border-radius: 1000px;
  font-family: var(--body);
  font-size: 14px;
  font-weight: 400;
  line-height: 20px;
  color: var(--text-disabled);
  text-decoration: none;
  white-space: nowrap;
  transition: background 160ms ease, color 160ms ease;
}

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

.search-pill {
  display: none;
  align-items: center;
  gap: 10px;
  min-width: 80px;
  height: 43px;
  padding: 4px 18px 4px 14px;
  background: var(--surface);
  border: none;
  border-radius: 1000px;
  color: var(--text-disabled);
  cursor: pointer;
  transition: background 160ms ease;
}

.search-pill:hover {
  background: var(--surface-hover);
}

.search-pill svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

.search-pill-label {
  font-family: var(--body);
  font-size: 14px;
  line-height: 20px;
}

.search-pill-kbd {
  font-family: var(--body);
  font-size: 16px;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.08px;
  color: var(--text-muted);
}

.menu {
  position: relative;
}

.menu-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 43px;
  height: 43px;
  padding: 0;
  background: var(--surface);
  border: none;
  border-radius: 1000px;
  color: var(--text-primary);
  cursor: pointer;
  transition: background 160ms ease;
}

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

.menu-btn svg {
  width: 4px;
  height: 16px;
}

/* 318:19478 — a 300 card whose rows are 55 tall, each holding a 40px pill
   inset 8px from the edge. The pill is what takes the ground on hover, so
   the label sits 12 inside it and 20 from the card. */
.menu-card {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  z-index: 60;
  width: 300px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 10px 20px rgba(107, 107, 107, 0.25);
}

.menu-card[hidden] {
  display: none;
}

.menu-row {
  display: flex;
  align-items: center;
  height: 55px;
  padding: 0 8px;
  text-decoration: none;
}

/* The ring goes round the pill, not the row: the row runs the full width of
   the card, so a ring on it reads as a box drawn over the menu. */
.menu-row:focus-visible {
  outline: none;
}

.menu-row:focus-visible > span {
  outline: 2px solid var(--text-primary);
  outline-offset: -2px;
}

.menu-row > span {
  display: flex;
  flex: 1;
  align-items: center;
  height: 40px;
  padding: 0 12px;
  border-radius: 6px;
  font-family: var(--body);
  font-size: 16px;
  line-height: 24px;
  color: var(--text-primary);
  white-space: nowrap;
  transition: background 160ms ease;
}

.menu-row:hover > span,
.menu-row:focus-visible > span {
  background: var(--surface);
}

@media (min-width: 1024px) {
  /* It scrolls away here; the revealed copy is what comes back. */
  .site-header {
    position: relative;
    z-index: auto;
    box-shadow: inset 0 -1px 0 0 var(--rule);
  }

  .reveal-nav {
    display: block;
  }

  .header-inner {
    padding: 22.5px 0;
  }

  .nav-links,
  .search-pill {
    display: flex;
  }

  .nav-right {
    gap: 20px;
  }
}

/* ── Command palette ──────────────────────────────────────────────────────
   A 618px card at a 20px radius over a page that dims to 10% black under a
   4px blur, after ericfillingame.com's. The field is 70 tall; rows are 60,
   with their group labels and tags at 40% black. */

.palette {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  justify-content: center;
  background: rgba(0, 0, 0, 0.1);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.palette[hidden] {
  display: none;
}

.palette-card {
  /* 318:19945 sits 294 from the top of a 1080 frame; 30vh is the phone's
     stand-in, where a fixed 294 would be most of the screen. */
  margin-top: 30vh;
  height: fit-content;
  width: 618px;
  max-width: calc(100vw - 40px);
  overflow: hidden;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 10px 20px rgba(107, 107, 107, 0.25);
  animation: palette-in 160ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

@media (min-width: 1024px) {
  .palette-card {
    margin-top: 294px;
  }
}

@keyframes palette-in {
  from {
    opacity: 0;
    transform: translateY(-8px) scale(0.99);
  }
}

.palette-field {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 70px;
  padding: 0 16px;
  border-bottom: 1px solid var(--border);
}

.palette-field > svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: var(--text-muted);
}

.palette-field input {
  flex: 1;
  min-width: 0;
  background: none;
  border: none;
  outline: none;
  font-family: var(--body);
  font-size: 16px;
  line-height: 24px;
  color: var(--text-primary);
}

.palette-field input::placeholder {
  color: var(--text-disabled);
}

.palette-field kbd {
  padding: 2px 6px;
  background: var(--surface);
  border-radius: 4px;
  font-family: var(--body);
  font-size: 14px;
  line-height: 20px;
  color: var(--text-disabled);
}

/* 420 is exactly what the comp's five rows and three group labels come to,
   which is why the card lands at 492. No bottom padding: at 424 the list
   overflowed its own maximum and scrolled by four pixels. */
.palette-list {
  max-height: min(60vh, 420px);
  overflow-y: auto;
}

.palette-group {
  display: flex;
  align-items: center;
  height: 40px;
  margin: 0;
  padding: 12px 8px 4px 16px;
  font-family: var(--body);
  font-size: 14px;
  font-weight: 500;
  line-height: 20px;
  text-transform: uppercase;
  color: var(--text-disabled);
}

.palette-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  height: 60px;
  padding: 0 16px;
  cursor: pointer;
  transition: background 160ms ease;
}

.palette-row[aria-selected="true"] {
  background: var(--surface);
}

.palette-lead {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

/* Every glyph is Text/muted, About's included — it is a line drawing of the
   mark, not the colour app icon. The slot sizes to its glyph rather than
   pinning 20, because About's box is 24 and the comp keeps the same 12px gap
   either way, so the labels start 36 and 32 in. */
.palette-icon {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
}

.palette-label {
  overflow: hidden;
  font-family: var(--body);
  font-size: 16px;
  line-height: 24px;
  color: var(--text-primary);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.palette-tag {
  flex-shrink: 0;
  padding: 2px 4px;
  border-radius: 4px;
  font-family: var(--body);
  font-size: 14px;
  line-height: 20px;
  color: var(--text-disabled);
}

.palette-trailing {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  color: var(--text-muted);
}

.palette-trailing svg {
  width: 20px;
  height: 20px;
}

.palette-empty {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 32px 24px 28px;
  text-align: center;
}

.palette-empty p {
  margin: 0;
  font-family: var(--body);
  font-size: 14px;
  line-height: 20px;
  color: var(--text-primary);
}

.palette-empty p + p {
  color: var(--text-muted);
}


/* ── Type ─────────────────────────────────────────────────────────────── */

.eyebrow {
  margin: 0;
  font-family: var(--mono);
  font-size: 20px;
  font-weight: 600;
  line-height: 1.45;
  letter-spacing: -0.1px;
  text-transform: uppercase;
  color: var(--text-primary);
}

.section-heading {
  margin: 0;
  font-family: var(--serif);
  font-size: 30px;
  font-weight: 500;
  line-height: normal;
  letter-spacing: -0.15px;
  font-variation-settings: "GRAD" 0, "wdth" 100;
}

@media (min-width: 1024px) {
  .section-heading {
    font-size: 60px;
    letter-spacing: -0.3px;
  }
}

/* A heading band: eyebrow over serif heading, centred. */
.heading-block {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 40px 20px;
  text-align: center;
}

@media (min-width: 1024px) {
  .heading-block {
    padding: 50px 0;
  }
}

/* ── Buttons ──────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-width: 80px;
  padding: 16px 20px;
  border: none;
  border-radius: 1000px;
  font-family: var(--sans);
  font-size: 20px;
  font-weight: 500;
  letter-spacing: -0.1px;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: opacity 160ms ease, background 160ms ease;
}

.btn--primary {
  background: var(--bg-inverse);
  color: var(--text-inverse);
  font-weight: 600;
}

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

.btn:hover {
  opacity: 0.8;
}

/* The design-system Button — 303:16112 and its siblings, and the nav's own.
   A pill with a 14/20 bold label in Font/body and 15 across, at a fixed
   height rather than one padding gives it. It is a different component from
   the hero's buttons, which are DM Sans 20 on their own padding. */
.btn--pill {
  min-width: 80px;
  height: 48px;
  gap: 2px;
  /* 17 against 15: the trailing symbol carries its own bearing inside a 30px
     slot, so an even inset reads as light on the left. 303:16112. */
  padding: 4px 15px 4px 17px;
  font-family: var(--body);
  font-size: 14px;
  font-weight: 700;
  line-height: 20px;
  letter-spacing: 0;
}

/* The nav's own is 43 on an even 14 — it has no symbol to balance. */
.btn--nav {
  height: 43px;
  padding: 4px 14px;
}

/* The frame gives the symbol a 30px slot; the glyph drawn inside it is the
   ~18 an SF Symbol actually inks at that size.

   Two classes deep on purpose: the base `.btn svg` below matches just as
   specifically and is declared later, so a single class loses to it. */
.btn.btn--pill svg {
  width: 30px;
  height: 30px;
}

.btn[disabled],
.btn[aria-disabled="true"] {
  background: var(--accent-disabled);
  color: var(--text-inverse);
  cursor: default;
  opacity: 1;
}

.btn svg {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

/* ── Hero ─────────────────────────────────────────────────────────────── */

/* The hero fills the first screenful and centres its content in it. The comps
   draw the row at a fixed 700 on a phone and 1080 on desktop, which is one
   viewport apiece at the sizes they are drawn for; taking the viewport
   directly holds that intent on the screens they do not cover.

   svh, not vh: on iOS vh is the *largest* viewport, so the row would sit a
   toolbar's height too tall and its bottom rule would start off-screen. The
   vh line above it is the fallback for browsers without svh.

   min-height, so a short window grows the row rather than crushing the copy. */
.hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 32px;
  min-height: calc(100vh - var(--header-h));
  min-height: calc(100svh - var(--header-h));
  /* The comps' 99 (and desktop's 100) is not padding they chose — it is what
     centring 502 of content in a 700 row leaves over. Flexbox does that job
     now, so this is only a floor for windows too short to centre in; at 99 a
     phone's first screenful overflowed by the difference.

     10 across, not 20: the phone comp nests the hero one 10px frame inside
     the 353 column (308:18335), so the copy gets 333 to wrap in. At 313 the
     headline takes a third line. */
  padding: 40px 10px;
  text-align: center;
}

.hero-icon {
  width: 70px;
  height: 70px;
  border-radius: 22.5%;
}

.hero-text {
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 1000px;
}

.hero-headline {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.hero .eyebrow {
  font-size: 18px;
}

.hero h1 {
  margin: 0;
  font-family: var(--serif);
  font-size: 32px;
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.16px;
  font-variation-settings: "GRAD" 0, "wdth" 100;
}

/* The subhead changes face across the breakpoint, which is the comps' own
   resolution rather than a substitution: the phone sets it as Text/body-lg
   (`Font/body` → Inter 18/28, 308:18343) and desktop as Text/heading-md
   (`Font/heading` → Satoshi 24/32, 303:15757). getgrowl.app splits the same
   role the same way. */
.hero-sub {
  margin: 0;
  font-family: var(--body);
  font-size: 18px;
  font-weight: 400;
  line-height: 28px;
  color: var(--text-secondary);
}

/* 293 inside the 333 column — the actions container insets its buttons by 20
   either side (308:18344). */
.hero-actions {
  display: flex;
  flex-direction: column;
  gap: 20px;
  width: 100%;
  max-width: 293px;
}

.hero-actions .btn {
  width: 100%;
  /* 54 on a phone against desktop's 56: the label drops to 18. */
  font-size: 18px;
}

@media (min-width: 1024px) {
  .hero {
    gap: 40px;
    padding: 50px 20px;
  }

  .hero-icon {
    width: 100px;
    height: 100px;
    /* 318:19100 — the card shadow at a tenth rather than a quarter, which is
       what lifts the mark off the page at this size. The phone's 70px icon
       (308:18666) carries none, so this stays in the desktop block.

       box-shadow, not drop-shadow: the squircle lives in the PNG's alpha and
       the radius above traces it, and a filter is what Safari clips to a hard
       rectangle once a layer is promoted. */
    box-shadow: 0 10px 20px rgba(107, 107, 107, 0.1);
  }

  .hero-text {
    gap: 24px;
  }

  .hero-headline {
    gap: 16px;
  }

  .hero .eyebrow {
    font-size: 20px;
  }

  .hero h1 {
    font-size: 55px;
    line-height: 1;
    letter-spacing: -0.275px;
  }

  .hero-sub {
    font-family: var(--heading);
    font-size: 24px;
    line-height: 32px;
  }

  .hero-actions {
    flex-direction: row;
    justify-content: center;
    max-width: none;
  }

  .hero-actions .btn {
    width: auto;
    height: 56px;
    font-size: 20px;
  }
}

/* ── Carousel ─────────────────────────────────────────────────────────────
   A row of cards that scrolls sideways, after the gallery on apple.com's
   iPhone pages and the port of it already running on ericfillingame.com and
   getgrowl.app.

   The first card lines up with the content column like everything else on the
   page, and the track runs out to both edges of the window, so the next card
   is cut off on the right and, once you are past the first, the previous one
   peeks in the left gutter.

   Scrolling is native: trackpad, touch and shift-wheel all move it, and snap
   points settle it on a card. The buttons are a second way to do the same
   thing, one card per press. Their disabled state is read off the scroll
   position rather than kept as an index, so the two can never disagree. */

.carousel-track {
  display: flex;
  gap: 40px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  /* Out to the window either side, which assumes the column is centred in it. */
  margin-inline: calc(50% - 50vw);
  padding-inline: var(--carousel-lead);
  scroll-padding-inline: var(--carousel-lead);
  padding-block: 40px;
}

.carousel-track::-webkit-scrollbar {
  display: none;
}

.carousel-card {
  flex: 0 0 auto;
  width: 250px;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.carousel-card img {
  width: 100%;
  /* The comps draw a taller image on a phone than on desktop. The artwork is
     one landscape composition, so the phone crops it from the centre, where
     the mockups sit. */
  aspect-ratio: 250 / 300;
  object-fit: cover;
  object-position: center;
  border-radius: 20px;
  background: var(--surface-primary);
}

/* These carry no padding of their own. The carousel insets them below; the
   showcase insets its whole body. When the padding lived here as well, the
   showcase applied both and its copy sat twice as far in as the frame draws
   it — while the buttons, which have no inner padding, stayed put and looked
   out of line with it. */
.card-title {
  margin: 0;
  font-family: var(--body);
  font-size: 16px;
  font-weight: 400;
  line-height: 24px;
  color: var(--text-secondary);
}

.card-copy {
  margin: 0;
  font-family: var(--body);
  font-size: 18px;
  line-height: 28px;
  color: var(--text-secondary);
}

/* The carousel's own inset, and its copy, which the phone sets smaller than
   the showcase's. */
.carousel-card .card-title,
.carousel-card .card-copy {
  padding-inline: 10px;
}

.carousel-card .card-copy {
  font-size: 14px;
  line-height: 20px;
}

.carousel-controls {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 20px;
}

.carousel-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: none;
  border-radius: 50%;
  background: var(--bg-inverse);
  color: var(--text-inverse);
  cursor: pointer;
  transition: background 200ms ease;
}

.carousel-btn:disabled {
  background: var(--accent-disabled);
  cursor: default;
}

.carousel-btn svg {
  width: 20px;
  height: 20px;
}

@media (min-width: 1024px) {
  .carousel-track {
    gap: 20px;
    padding-block: 20px;
  }

  .carousel-card {
    width: 689px;
  }

  .carousel-card img {
    aspect-ratio: 689 / 445;
  }

  .carousel-card .card-title,
  .carousel-card .card-copy {
    padding-inline: 20px;
  }

  .carousel-card .card-copy {
    font-size: 18px;
    line-height: 28px;
  }

  /* Text/heading-sm — Satoshi Bold on desktop, where the phone takes Inter
     for the same role. The same split the hero's subhead makes. */
  .card-title {
    font-family: var(--heading);
    font-size: 20px;
    font-weight: 700;
    line-height: 28px;
  }
}

/* ── Growl showcase ───────────────────────────────────────────────────── */

/* 303:15948: 40 top and bottom on a phone, and on desktop 20 over the card
   against 40 under it — the row does not close symmetrically. */
.showcase {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 40px 0;
}

.showcase-art {
  width: 100%;
  aspect-ratio: 353 / 400;
  object-fit: cover;
  border-radius: 20px;
  background: var(--surface-primary);
}

.showcase-body {
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding-inline: 10px;
}

.showcase-text {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.showcase-actions {
  display: flex;
  gap: 20px;
}

@media (min-width: 1024px) {
  .showcase {
    padding: 20px 20px 40px;
  }

  .showcase-art {
    aspect-ratio: 1160 / 738;
  }

  .showcase-body {
    gap: 32px;
    padding-inline: 20px;
  }
}

/* ── FAQ ──────────────────────────────────────────────────────────────────
   An accordion. The comps draw every row open, because the FAQ_row component
   is shown in its open state — but six answers expanded is a wall of prose
   before the first question, so the rows rest closed, as they do on
   getgrowl.app. If they were meant to start open, that is one line in the
   script. */

.faq-list {
  margin-inline: auto;
  padding: 0 20px 40px;
}

.faq-row {
  border-top: 1px solid var(--rule);
  padding: 40px 0;
}

/* The rules separate one question from the next, so the first has nothing to
   be separated from — its line only drew a second divider under the heading. */
.faq-row:first-child {
  border-top: none;
}

.faq-row h3 {
  margin: 0;
}

.faq-q {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  width: 100%;
  padding: 0 0 0 36px;
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
}

.faq-q > span:first-child {
  flex: 1;
  min-width: 0;
  text-align: center;
  font-family: var(--serif);
  font-size: 20px;
  font-weight: 400;
  line-height: normal;
  letter-spacing: -0.1px;
  color: var(--text-secondary);
  font-variation-settings: "GRAD" 0, "wdth" 100;
  transition: color 160ms ease;
}

.faq-q:hover > span:first-child {
  color: var(--text-primary);
}

.faq-sign {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 2px;
}

.faq-sign svg {
  width: 20px;
  height: 20px;
  stroke: var(--text-secondary);
  stroke-width: 1.6;
  stroke-linecap: round;
  fill: none;
}

/* The plus's vertical stroke, hidden once the row opens — leaving a minus,
   which is the state every comp draws. */
.faq-q[aria-expanded="true"] .faq-sign .bar {
  display: none;
}

.faq-a {
  display: none;
  padding: 12px 36px 0;
}

.faq-a[data-open="true"] {
  display: block;
}

.faq-a p {
  margin: 0;
  text-align: center;
  font-family: var(--body);
  font-size: 18px;
  line-height: 28px;
  color: var(--text-secondary);
}

.faq-more {
  display: flex;
  flex-direction: column;
  gap: 8px;
  border-top: 1px solid var(--rule);
  padding: 40px 0;
  text-align: center;
  color: var(--text-secondary);
}

.faq-more p {
  margin: 0;
}

.faq-more p:last-child {
  font-family: var(--sans);
  font-size: 16px;
  letter-spacing: -0.08px;
  line-height: 1.45;
}

.faq-more a {
  font-weight: 600;
  text-decoration: underline;
  transition: opacity 160ms ease;
}

.faq-more a:hover {
  opacity: 0.6;
}

@media (min-width: 1024px) {
  .faq-list {
    width: 800px;
    max-width: 100%;
    padding: 0 0 50px;
  }

  .faq-q > span:first-child {
    font-size: 24px;
    letter-spacing: -0.12px;
  }

  .faq-more {
    gap: 10px;
    padding: 20px 0;
  }

  .faq-more p:last-child {
    font-size: 20px;
    letter-spacing: -0.1px;
  }
}

/* ── Footer ───────────────────────────────────────────────────────────────
   303:15843 on desktop, 308:18543 on a phone. The two are not one block at
   two sizes: desktop sets the labels in Font/body (Inter 18/28) with a black
   address, the phone in DM Sans at 20 and 16 with a grey one, and the gap
   under the email line is 20 there against 40 here. */

.site-footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 40px;
  padding: 50px 20px;
  text-align: center;
}

.footer-contact {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 40px;
}

.footer-email {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 20px;
  margin: 0;
  font-family: var(--sans);
  font-size: 20px;
  line-height: 1.45;
  letter-spacing: -0.1px;
  color: var(--text-secondary);
}

.footer-email a {
  color: inherit;
  font-weight: 600;
  text-decoration: underline;
  text-underline-position: from-font;
  transition: opacity 160ms ease;
}

.footer-email a:hover {
  opacity: 0.6;
}

.footer-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 20px;
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.45;
  letter-spacing: -0.08px;
  color: var(--text-secondary);
}

.footer-nav a {
  color: inherit;
  text-decoration: none;
  transition: color 160ms ease;
}

.footer-nav a:hover {
  color: var(--text-primary);
}

/* The bullets are the one thing set in the primary ink at both sizes. */
.footer-sep {
  font-family: var(--sans);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.08px;
  line-height: 1.45;
  color: var(--text-primary);
}

@media (min-width: 1024px) {
  .site-footer {
    padding: 100px 0;
  }

  .footer-contact {
    gap: 20px;
  }

  .footer-email,
  .footer-nav {
    font-family: var(--body);
    font-size: 18px;
    line-height: 28px;
    letter-spacing: 0;
  }

  .footer-email a {
    font-family: var(--sans);
    font-size: 20px;
    line-height: 1.45;
    letter-spacing: -0.1px;
    color: var(--text-primary);
  }
}

/* ── Motion ───────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
  }
}
