/*
 * The home page. The privacy, support and invitation pages keep style.css.
 *
 * Colours are the application's own (constants/theme.ts): the brand teal and
 * its dark-mode partner, the paper and ink of the list, and inkRed — the line
 * drawn through an item when it is ticked, which is this page's one flourish.
 */

@font-face {
  font-family: "Bricolage Grotesque";
  src: url("/assets/fonts/bricolage-grotesque-latin-800.woff2") format("woff2");
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}

:root {
  --teal: #007F86;
  --teal-pressed: #06666B;
  --teal-deep: #063B3F;
  --teal-glow: #34D3D1;
  --ice: #DDF5FA;
  --paper: #FAFBF7;
  --surface: #FFFFFF;
  --surface-muted: #EEF6F2;
  --border: #D9E5E0;
  --ink: #101918;
  --muted: #5B6866;
  --ink-red: #C2413A;
  --ink-red-bright: #FF6B63;

  --display: "Bricolage Grotesque", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  --gutter: max(20px, calc((100vw - 1200px) / 2));

  color-scheme: light dark;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #0F1514;
    --surface: #17211F;
    --surface-muted: #1E302E;
    --border: #2E4642;
    --ink: #EFF6F3;
    --muted: #B6C3C0;
    --ink-red: #FF6B63;
  }
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 17px/1.6 var(--body);
  overflow-x: hidden;
}

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

a { color: inherit; }

:focus-visible {
  outline: 3px solid var(--teal-glow);
  outline-offset: 3px;
  border-radius: 6px;
}

h1, h2, h3 { font-family: var(--display); font-weight: 800; text-wrap: balance; }

.skip-link {
  position: fixed;
  top: 12px;
  left: 12px;
  z-index: 100;
  padding: 10px 14px;
  border-radius: 10px;
  background: var(--surface);
  color: var(--ink);
  transform: translateY(-160%);
}

.skip-link:focus { transform: none; }

/* --- The aisle heading, as the app draws one ------------------------------ */

/*
 * Every label the same size, the sections' and the cards': about 0.85rem on
 * a phone, growing to 1.05rem on a laptop.
 */
.aisle-label {
  margin: 0 0 14px;
  color: var(--teal);
  font-size: clamp(0.85rem, 0.6rem + 0.6vw, 1.05rem);
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

@media (prefers-color-scheme: dark) {
  .aisle-label { color: var(--teal-glow); }
}

/* Read by screen readers, not shown. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Header ---------------------------------------------------------------- */

.site-header {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 24px var(--gutter);
}

.brand { flex: none; }
.brand img { width: 190px; height: 40px; }

/* The same links as the other pages' headers, beside the badge. */
.site-nav {
  display: flex;
  gap: 4px 24px;
  margin-left: auto;
}

.site-nav a {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  color: rgba(255, 255, 255, 0.9);
  font-weight: 700;
  text-decoration: none;
}

.site-nav a:hover { text-decoration: underline; }

/* Below about 760px there is no room beside the badge: the footer has them. */
@media (max-width: 760px) {
  .site-nav { display: none; }
}

.store-status {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  padding: 9px 16px;
  border: 1px solid rgba(255, 255, 255, 0.32);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.12);
  color: #FFFFFF;
  font-size: 0.85rem;
  font-weight: 700;
}

.store-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--teal-glow);
  box-shadow: 0 0 0 4px rgba(52, 211, 209, 0.25);
}

/* --- Hero ------------------------------------------------------------------ */

.hero {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr);
  align-items: center;
  gap: 48px;
  min-height: 860px;
  padding: 140px var(--gutter) 90px;
  overflow: hidden;
  background:
    radial-gradient(circle at 85% 30%, rgba(52, 211, 209, 0.45), transparent 42%),
    radial-gradient(circle at 10% 0%, rgba(221, 245, 250, 0.28), transparent 32%),
    linear-gradient(140deg, #006B72 0%, var(--teal) 50%, #0E9E9E 100%);
  color: #FFFFFF;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 28px;
  padding: 6px 16px 6px 6px;
  border: 1px solid rgba(255, 255, 255, 0.26);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.eyebrow-tick {
  display: grid;
  width: 26px;
  height: 26px;
  place-items: center;
  border-radius: 50%;
  background: var(--ice);
  color: var(--teal-pressed);
  letter-spacing: 0;
}

/*
 * "Less wandering." is 7.5em wide in this face, and the copy column is a
 * little under half the screen, so 6vw keeps it to one line beside the
 * phones. Stacked, it has the full width and grows (below).
 */
.hero h1 {
  margin: 0;
  font-size: clamp(3.4rem, 6vw, 4.9rem);
  line-height: 0.92;
  letter-spacing: -0.035em;
}

.hero-done { color: var(--ice); }

/*
 * The signature: "Less wandering" is ticked off the way the app ticks an item —
 * a rounded red line drawn through the words. In the app's own red for a dark
 * background, since the hero is teal whatever the system setting.
 *
 * Each word carries its own stroke, because the two wrap onto separate lines
 * on a phone and one box would draw a single line between them. The first
 * stroke runs on across the word space, so on one line the two read as one,
 * and the second starts as the first finishes, so it draws as one movement.
 * A box over the word rather than a background, which would sit behind it.
 */
.tick-off {
  position: relative;
  display: inline-block;
}

.tick-off::after {
  content: "";
  position: absolute;
  left: -0.04em;
  right: -0.04em;
  top: 54%;
  height: 0.085em;
  border-radius: 999px;
  background: var(--ink-red-bright);
  transform: scaleX(0);
  transform-origin: left center;
  animation: tick-off 240ms cubic-bezier(0.5, 0, 1, 1) 650ms forwards;
}

.tick-off-first::after { right: -0.3em; }

.tick-off-last::after {
  left: 0;
  animation: tick-off 320ms cubic-bezier(0, 0, 0.2, 1) 890ms forwards;
}

@keyframes tick-off {
  to { transform: scaleX(1); }
}

.hero-intro {
  max-width: 34em;
  margin: 30px 0 34px;
  color: rgba(255, 255, 255, 0.9);
  font-size: 1.15rem;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 26px;
}

.button {
  display: inline-flex;
  min-height: 52px;
  align-items: center;
  justify-content: center;
  padding: 12px 22px;
  border-radius: 16px;
  font-weight: 800;
  text-decoration: none;
}

.button-light {
  background: #FFFFFF;
  color: var(--teal-pressed);
  box-shadow: 0 16px 34px rgba(4, 61, 66, 0.25);
}

.button-deep {
  background: var(--teal-deep);
  color: #FFFFFF;
}

.text-link {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  color: rgba(255, 255, 255, 0.92);
  font-weight: 700;
}

.hero-facts {
  display: grid;
  grid-template-columns: repeat(3, auto);
  justify-content: start;
  gap: 0;
  margin: 52px 0 0;
  padding: 0;
  list-style: none;
}

/* Each fact has a small icon, beside it here and above it on a phone. */
.hero-facts li {
  display: grid;
  grid-template-columns: auto auto;
  align-items: center;
  column-gap: 10px;
  padding: 0 22px;
  border-left: 1px solid rgba(255, 255, 255, 0.26);
}

.hero-facts svg {
  grid-row: 1 / span 2;
  width: 22px;
  height: 22px;
  color: var(--teal-glow);
}

.hero-facts strong,
.hero-facts span { grid-column: 2; }

.hero-facts li:first-child { padding-left: 0; border-left: 0; }

.hero-facts strong { font-size: 0.9rem; }

.hero-facts span {
  color: rgba(255, 255, 255, 0.72);
  font-size: 0.8rem;
}

/* Real screenshots, framed. Both dimensions are the artwork's own. */
.phones {
  position: relative;
  height: 640px;
}

.phone {
  position: absolute;
  margin: 0;
  border: 9px solid #062F32;
  border-radius: 44px;
  overflow: hidden;
  background: #062F32;
  box-shadow: 0 40px 80px rgba(3, 40, 43, 0.38), 0 0 0 1px rgba(255, 255, 255, 0.16);
}

.phone img { width: 100%; height: auto; border-radius: 35px; }

.phone-front {
  width: 290px;
  left: 4%;
  top: 0;
  z-index: 2;
  transform: rotate(-4deg);
}

.phone-back {
  width: 250px;
  right: 2%;
  top: 70px;
  transform: rotate(6deg);
}

/* --- How it helps --------------------------------------------------------- */

.how { padding: 110px var(--gutter); }

/*
 * No width limit on the heading: "A list that walks the shop with you." is
 * about 16em, which fits the column on one line from a tablet up, and held to
 * 760px it broke after "walks" on a laptop. The lede keeps its own measure.
 */
.section-head { margin-bottom: 52px; }

.section-head h2,
.privacy h2,
.closing h2 {
  margin: 0;
  font-size: clamp(2.4rem, 4.6vw, 4.2rem);
  line-height: 1;
  letter-spacing: -0.03em;
}

/*
 * On a phone it is two lines, "A list that walks / the shop with you.", the
 * longer being about 8.6em. Below about 360px wide 2.4rem made that three
 * lines broken mid-phrase, so the size is held to the column over 8.8 there.
 */
.section-head h2 {
  font-size: min(clamp(2.4rem, 4.6vw, 4.2rem), calc((100vw - 2 * var(--gutter)) / 8.8));
}

/*
 * Like the heading above it, one line where it fits — about 44em, so from a
 * laptop up — and otherwise lines of even length rather than a full line and
 * a stray word.
 */
.lede {
  max-width: 46em;
  margin: 20px 0 0;
  color: var(--muted);
  font-size: 1.08rem;
  text-wrap: balance;
}

/*
 * A tablet's column is a little short of the 44em, so from 760px the text is
 * held to the column over 46 — a few percent smaller, with room for an iPad's
 * own system face being wider than the one it was measured in. Narrower than
 * that it would get too small, and balances onto two lines instead.
 */
@media (min-width: 760px) {
  .lede { font-size: min(1.08rem, calc((100vw - 2 * var(--gutter)) / 46)); }
}

.cards {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 20px;
}

.card {
  grid-column: span 4;
  padding: 32px;
  border: 1px solid var(--border);
  border-radius: 28px;
  background: var(--surface);
  box-shadow: 0 18px 44px rgba(15, 55, 53, 0.06);
  overflow: hidden;
}

.card h3 {
  margin: 0 0 14px;
  font-size: 1.7rem;
  line-height: 1.08;
  letter-spacing: -0.02em;
}

.card p { margin: 0; color: var(--muted); }

/* Two classes, so the card's muted paragraph colour cannot outrank it. */
.card .aisle-label { margin-bottom: 12px; color: var(--teal); }

@media (prefers-color-scheme: dark) {
  .card .aisle-label { color: var(--teal-glow); }
}

/*
 * The cards that show the app itself, each a window into its screen rising
 * from the card's bottom edge. One to a row from a tablet up, the words on
 * the left and the window on the right. Stacked full width, the screenshot
 * would be drawn larger than the page's own text, so on a laptop its column
 * is held to 280px, the size it is on a tablet, and the words take the rest.
 *
 * The words sit in the middle three of five rows, so the two empty rows
 * either side centre them beside the window.
 */
.card-window {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 280px);
  grid-template-rows: 1fr auto auto auto 1fr;
  column-gap: 48px;
  padding-bottom: 0;
}

.card-window > :not(.card-shot) { grid-column: 1; }
.card-window > .aisle-label { grid-row: 2; }
.card-window > h3 { grid-row: 3; }
.card-window > h3 + p { grid-row: 4; }

.card-window .card-shot {
  grid-column: 2;
  grid-row: 1 / -1;
  align-self: end;
  width: 100%;
  margin: 0;
}

/*
 * A window rising from the bottom edge leaves the card no bottom padding, so
 * the words would centre low by half of it. A margin under them, beside the
 * window only, gives that half back.
 */
@media (min-width: 641px) {
  .card-window > h3 + p { margin-bottom: 32px; }
  .card.card-offline > h3 + p { margin-bottom: 0; }
}

.card-aisle,
.card-quick,
.card-usuals,
.card-offline { grid-column: span 12; }

/*
 * A window into the app, rising from the card's bottom edge. Narrower than
 * the card and trimmed to the typed letters and the first suggestions, so
 * the app's own text stays smaller than the page's.
 */
.card-shot {
  width: 78%;
  max-height: 270px;
  margin: 28px auto 0;
  border: 1px solid var(--border);
  border-bottom: 0;
  border-radius: 18px 18px 0 0;
  object-fit: cover;
  object-position: top;
}

/*
 * A window holding a screenshot: the picture sets the height and the window
 * crops it, top first, as the image alone did. The stage is the picture's own
 * box, so anything drawn over it is placed in the screenshot's proportions.
 */
.shot { overflow: hidden; }
.shot-stage { position: relative; }
.shot-stage img { display: block; width: 100%; height: auto; }
.shot-stage > span { display: none; }

.card-offline {
  background: var(--teal-deep);
  border-color: transparent;
  color: #FFFFFF;
}

.card-offline .aisle-label { color: var(--teal-glow); }
.card-offline p { color: rgba(255, 255, 255, 0.74); }

/*
 * The other windows rise from the card's bottom edge showing the top of the
 * app; this one is their mirror, hanging from the top edge and showing the
 * bottom of the list, down to the Add an item box, with the words beneath.
 * It comes first in the page so it does so at every width, and its negative
 * top margin cancels the card's padding. It keeps the app's own space below
 * the box, so it is drawn whole rather than held to the others' height: it
 * hangs from the top, so it has nothing of theirs to line up with. A border
 * pale enough to show against the dark card.
 */
.card.card-offline { padding-bottom: 32px; }

.card.card-offline .card-shot {
  align-self: start;
  margin: -32px 0 0;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-top: 0;
  border-radius: 0 0 18px 18px;
  max-height: none;
}

/*
 * All the Usuals, drawn in HTML to the app's own measurements, until there is
 * a screenshot of it (features/lists/screens/all-usuals-screen.tsx, with the
 * default 20pt list text). Every size is in the app's points, scaled by
 * the window's width over a 390pt phone, so it is drawn at the same scale as
 * the screenshots beside it. Light colours whatever the setting, as they are.
 */
.app-usuals {
  container-type: inline-size;
  aspect-ratio: 600 / 486;
  overflow: hidden;
  background: #FAFBF7;
  color: #101918;
  line-height: 1;
}

.app-usuals > * {
  --pt: calc(100cqw / 390);
  margin: 0 calc(12 * var(--pt));
}

.au-head {
  display: flex;
  align-items: center;
  gap: calc(12 * var(--pt));
  padding: calc(14 * var(--pt)) 0 calc(12 * var(--pt));
}

.au-back {
  display: grid;
  flex: none;
  width: calc(44 * var(--pt));
  height: calc(44 * var(--pt));
  place-items: center;
  border-radius: calc(10 * var(--pt));
  background: #EEF6F2;
  color: #60706E;
}

.au-head svg { width: calc(24 * var(--pt)); height: calc(24 * var(--pt)); }

.au-title {
  display: block;
  font-size: calc(30 * var(--pt));
  font-weight: 800;
  line-height: calc(35 * var(--pt));
}

.au-list {
  display: block;
  margin-top: calc(3 * var(--pt));
  color: #007F86;
  font-size: calc(13 * var(--pt));
  font-weight: 700;
  line-height: calc(18 * var(--pt));
}

.au-panel { border-top: 1px solid #D9E5E0; }

.au-section {
  margin: 0;
  padding: calc(10 * var(--pt)) calc(2 * var(--pt)) calc(6 * var(--pt));
  color: #007F86;
  font-size: calc(12 * var(--pt));
  font-weight: 800;
  line-height: calc(16 * var(--pt));
}

.au-section:not(:first-child) { padding-top: calc(16 * var(--pt)); }

.au-row {
  display: flex;
  align-items: center;
  gap: calc(10 * var(--pt));
  min-height: calc(44 * var(--pt));
  padding: calc(5 * var(--pt)) calc(2 * var(--pt));
  border-bottom: 1px solid #D9E5E0;
}

.au-row.is-selected { background: #EEF6F2; }

/* The empty circle and the tick share one place, so one can become the other. */
.au-tick {
  position: relative;
  flex: none;
  width: calc(24 * var(--pt));
  height: calc(24 * var(--pt));
}

.au-tick svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.au-off { color: #60706E; }
.au-on { color: #007F86; opacity: 0; }
.au-row.is-selected .au-off { opacity: 0; }
.au-row.is-selected .au-on { opacity: 1; }

.au-name {
  display: block;
  font-size: calc(20 * var(--pt));
  font-weight: 700;
  line-height: calc(25 * var(--pt));
}

.au-sub {
  display: block;
  margin-top: calc(2 * var(--pt));
  overflow: hidden;
  color: #5B6866;
  font-size: calc(17 * var(--pt));
  font-weight: 500;
  line-height: calc(22 * var(--pt));
  white-space: nowrap;
  text-overflow: ellipsis;
}

.au-copy { min-width: 0; }

.card-plus {
  grid-column: span 12;
  background: linear-gradient(140deg, var(--teal), #0E9E9E);
  border-color: transparent;
  color: #FFFFFF;
}

.card-plus .aisle-label { color: var(--ice); }
.card-plus p { color: rgba(255, 255, 255, 0.85); }

.card-plus .card-note {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.25);
  font-size: 0.92rem;
}

/* --- In. Out. Done. -------------------------------------------------------- */

.trip {
  padding: 100px var(--gutter);
  background: var(--teal-deep);
  color: #FFFFFF;
}

.trip .aisle-label { color: var(--teal-glow); }

.trip-title {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0 24px;
  margin: 0 0 64px;
  /*
   * As large as one line allows. The three words and the tick measure
   * 6.04em, plus the two 24px gaps between them, so the line's width less
   * the gaps, over 6.3, fills it with a little to spare.
   */
  font-size: clamp(3.6rem, calc((100vw - 2 * var(--gutter) - 48px) / 6.3), 12rem);
  line-height: 0.9;
  letter-spacing: -0.04em;
}

.trip-title span:nth-child(2) { color: var(--teal-glow); }
.trip-title span:nth-child(3) { color: var(--ice); }

.steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.steps li {
  display: grid;
  align-content: start;
  gap: 4px;
  padding-top: 22px;
  border-top: 1px solid rgba(255, 255, 255, 0.22);
}

/* Each step's number beside an icon for it: add, the route round, the tick. */
.steps .step-number {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
  color: var(--teal-glow);
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.14em;
}

.steps .step-number svg { width: 24px; height: 24px; }

.steps strong { font-size: 1.25rem; }

/* Larger beside a headline of 100px and more; a phone keeps them as they are. */
@media (min-width: 641px) {
  .steps strong { font-size: 1.5rem; }
  .steps li > span:last-child { font-size: 1.125rem; }
}
.steps li > span:last-child { color: rgba(255, 255, 255, 0.7); }

/* --- Privacy --------------------------------------------------------------- */

/*
 * The heading's column is the wider: under 1.4 to 1 it breaks into three
 * lines on a laptop, and the promises still sit on a line each beside it.
 */
.privacy {
  display: grid;
  grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
  gap: 64px;
  align-items: center;
  padding: 110px var(--gutter);
  background: var(--ice);
  color: var(--teal-deep);
}

.privacy .aisle-label { color: var(--teal); }

/*
 * Two lines beside the promises needs 11.3em of the heading's column, which
 * is 1.45 / 2.45 of the line less the gap; on wide screens the shared size
 * outgrows it, so here it is held to the column too.
 */
.privacy h2 {
  font-size: min(clamp(2.4rem, 4.6vw, 4.2rem), calc((100vw - 2 * var(--gutter) - 64px) * 0.5918 / 11.3));
}

/*
 * The page's own gesture once more: the thing ShopLister never does, struck
 * through in red. A screen reader hears "Never" before it, which the line
 * says to everybody else.
 */
.privacy-never {
  margin: 28px 0 0;
  color: color-mix(in srgb, currentColor 55%, transparent);
  font-family: var(--display);
  /* One line always, so one stroke: it is 13.9em, held to the column over 14.4. */
  font-size: min(clamp(1.5rem, 1rem + 1.6vw, 2.4rem), calc((100vw - 2 * var(--gutter)) / 14.4));
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.02em;
  white-space: nowrap;
}

.privacy-strike {
  position: relative;
  display: inline-block;
}

.privacy-strike::after {
  content: "";
  position: absolute;
  left: -0.04em;
  right: -0.04em;
  top: 54%;
  height: 0.085em;
  border-radius: 999px;
  background: var(--ink-red);
}

/* The promises, each with the tick the app gives a chosen row. */
.promises {
  display: grid;
  gap: 14px;
  margin: 0 0 30px;
  padding: 0;
  list-style: none;
  font-size: clamp(1.05rem, 0.9rem + 0.5vw, 1.3rem);
  line-height: 1.45;
}

.promises li {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  text-wrap: pretty;
}

.promises svg {
  flex: none;
  width: 1.35em;
  height: 1.35em;
  margin-top: 0.05em;
  color: var(--teal);
}

@media (prefers-color-scheme: dark) {
  .privacy { background: #10292A; color: var(--ink); }
  .privacy .aisle-label { color: var(--teal-glow); }
  .privacy-strike::after { background: var(--ink-red-bright); }
  .promises svg { color: var(--teal-glow); }
}

/* --- Closing --------------------------------------------------------------- */

.closing {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 110px 20px;
  background:
    radial-gradient(circle at 50% 0%, rgba(221, 245, 250, 0.3), transparent 40%),
    linear-gradient(140deg, #006B72, #0E9E9E);
  color: #FFFFFF;
  text-align: center;
}

/*
 * The app's own icon, as on a home screen. The icon is the same teal as the
 * section, so a white edge and a shadow lift it off. Both dimensions are
 * given; the icon is square.
 */
.closing-app {
  position: relative;
  width: 124px;
  height: 124px;
  margin-bottom: 34px;
}

.closing-app img {
  display: block;
  width: 124px;
  height: 124px;
  box-sizing: border-box;
  border: 2px solid rgba(255, 255, 255, 0.85);
  border-radius: 28px;
  box-shadow: 0 18px 40px rgba(3, 40, 43, 0.35);
}

/*
 * An iPhone installing it: the icon dimmed, and a white circle in its middle
 * that fills like a clock hand sweeping round. Drawn only with motion; still,
 * the icon is simply there.
 */
.install {
  display: none;
  position: absolute;
  inset: 0;
  place-items: center;
  border-radius: 28px;
  background: rgba(0, 0, 0, 0.45);
}

.install svg { width: 46px; height: 46px; }

.install-ring {
  fill: none;
  stroke: #FFFFFF;
  stroke-width: 2.5;
}

.install-pie {
  fill: none;
  stroke: #FFFFFF;
  stroke-width: 15;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  transform: rotate(-90deg);
  transform-origin: 20px 20px;
}

.closing h2 { max-width: 14em; }

.closing p {
  max-width: 34em;
  margin: 24px 0 0;
  color: rgba(255, 255, 255, 0.86);
}

.closing .closing-ask { margin-top: 10px; }

.closing-email { margin-top: 28px; }

.closing .closing-address {
  margin-top: 12px;
  font-size: 0.9rem;
  color: rgba(255, 255, 255, 0.7);
}

/* On release: the App Store badge, in place of "Coming soon". */
.store-badge { display: inline-flex; margin-top: 28px; }
.store-badge img { width: 160px; height: 53px; }

/* --- Footer ---------------------------------------------------------------- */

/* The other pages carry the same footer, from style.css: change both together. */
.site-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 32px;
  padding: 32px var(--gutter) 28px;
  background: #052F32;
  color: rgba(255, 255, 255, 0.74);
  font-size: 0.95rem;
}

.site-footer p { margin: 0; }

/* The wordmark, which the closing section gave up for the icon. Both
   dimensions, from the artwork's 1240 x 260. */
.footer-brand {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
}

.footer-brand img { display: block; width: 124px; height: 26px; }

/* The copyright line, a row of its own under the rest. */
.footer-legal {
  flex-basis: 100%;
  padding-top: 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.55);
  font-size: 0.8rem;
}

.site-footer nav { display: flex; flex-wrap: wrap; gap: 4px 24px; }

.site-footer a {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
}

/* --- Smaller screens ------------------------------------------------------- */

@media (max-width: 1000px) {
  .hero { grid-template-columns: minmax(0, 1fr); min-height: 0; padding-bottom: 70px; }
  /*
   * The widest line stacked, "wandering." or "More done.", is about 5.25em, so
   * the size is also held to the column's width over 5.4. That only bites
   * below about 360px wide, where 3.4rem would drop the full stop onto a line
   * of its own and break "More done." in two.
   */
  .hero h1 {
    font-size: min(clamp(3.4rem, 11vw, 6.2rem), calc((100vw - 2 * var(--gutter)) / 5.4));
  }
  .phones { width: min(100%, 560px); height: 600px; margin: 0 auto; }

  .card-plus { grid-column: span 12; }

  /* A tablet's card is narrower: the window takes a share of it instead. */
  .card-window {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.8fr);
    column-gap: 32px;
  }

  .privacy { grid-template-columns: minmax(0, 1fr); gap: 28px; }
  .privacy h2 { font-size: clamp(2.4rem, 4.6vw, 4.2rem); }
}

@media (max-width: 640px) {
  body { font-size: 16px; }

  .site-header { padding-top: 18px; }
  .brand img { width: 150px; height: 31px; }
  .store-status { padding: 7px 12px; font-size: 0.75rem; }

  /* Hidden from sight only: a screen reader still hears the whole sentence. */
  .store-long {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /*
   * Tighter on a phone, so the phones below peek onto the first screen: with
   * everything stacked they began just under it, and somebody opening the
   * site saw no app at all without scrolling.
   */
  .hero { padding-top: 112px; gap: 28px; }

  /* "Made for the weekly shop" is the room the phones need here; a tablet
     and a laptop keep it. */
  .eyebrow { display: none; }
  .hero-intro { margin: 22px 0 26px; font-size: 1.05rem; }

  /*
   * And the facts go below the phones, so the app is what follows the
   * buttons. The copy's own box steps aside, its parts become the hero's,
   * and order moves only the facts; a screen reader still meets them where
   * they are written.
   */
  .hero {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
  }

  .hero-copy { display: contents; }
  .phones { order: 1; margin-top: 34px; }
  .hero-facts { order: 2; align-self: stretch; }

  /* The badge reads as one line or not at all. */
  .store-status { white-space: nowrap; }

  /* Three across rather than a stack: a third of the height. */
  .hero-facts {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px;
    margin-top: 30px;
  }

  .hero-facts li,
  .hero-facts li:first-child {
    grid-template-columns: minmax(0, 1fr);
    align-content: start;
    row-gap: 2px;
    padding: 0;
    border: 0;
  }

  .hero-facts svg { grid-row: auto; margin-bottom: 4px; }
  .hero-facts strong,
  .hero-facts span { grid-column: 1; }
  .hero-facts strong { font-size: 0.82rem; line-height: 1.25; }
  .hero-facts span { font-size: 0.72rem; line-height: 1.3; }

  /*
   * At fixed sizes the pair needs about 390px to sit apart, and narrower they
   * slide over each other until the dark one is mostly hidden. So each is a
   * share of the width, capped at its old size, which keeps the overlap small
   * at any phone width; the height follows, a screenshot being 2.16 times as
   * tall as it is wide, and 1.24 times the width is what the pair needs.
   */
  .phones { height: min(470px, (100vw - 2 * var(--gutter)) * 1.24); }
  .phone { border-width: 7px; border-radius: 36px; }
  .phone img { border-radius: 29px; }
  .phone-front { width: min(210px, 58%); left: 0; }
  .phone-back { width: min(180px, 50%); right: 0; top: 56px; }

  .how,
  .trip,
  .privacy,
  .closing { padding-top: 76px; padding-bottom: 76px; }

  .card { padding: 26px; border-radius: 22px; }

  /*
   * As on a laptop: the screenshot centred, rising from the card's bottom
   * edge with its top corners rounded. The card's padding above has to come
   * off the bottom again, or it floats above the edge.
   */
  .card-window { display: block; padding-bottom: 0; }
  .card.card-offline { padding-bottom: 26px; }

  .card-window .card-shot {
    width: 100%;
    max-width: 340px;
    margin: 24px auto 0;
    border: 1px solid var(--border);
    border-bottom: 0;
    border-radius: 16px 16px 0 0;
  }

  .card.card-offline .card-shot {
    margin: -26px auto 24px;
    border-radius: 0 0 16px 16px;
  }

  /*
   * Too narrow for the three words on one line, they wrapped as two and one,
   * with Out. pushed to the far edge and Done. under In. Stacked instead, a
   * word to a line like the steps beneath, and larger for having the room.
   */
  .trip-title {
    flex-direction: column;
    gap: 0;
    margin-bottom: 48px;
    font-size: clamp(3.6rem, 21vw, 6rem);
  }

  .steps { grid-template-columns: minmax(0, 1fr); gap: 18px; }
}

/*
 * The narrowest phones, about 320px. Kept on one line, the badge would take
 * its room from the logo and squash it out of proportion, so the logo is made
 * smaller instead (1240 × 260, as above).
 */
@media (max-width: 360px) {
  .brand img { width: 129px; height: 27px; }
}

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

/*
 * Everything here waits for the js-motion class, which the page's head adds
 * only when scripts run and nobody has asked for reduced motion. Without it
 * the page is drawn whole and still, and the screenshots are as taken.
 *
 * Positions over a screenshot are its own pixels as fractions of it: a 600
 * wide image, so left and width are over 600 and top and height over its
 * height. Measured from the images, not judged by eye.
 */

/* Parts that arrive as they scroll into view. */
.js-motion [data-reveal] {
  transition:
    opacity 700ms ease var(--delay, 0s),
    transform 700ms cubic-bezier(0.2, 0.8, 0.2, 1) var(--delay, 0s);
}

.js-motion [data-reveal]:not(.is-in) {
  opacity: 0;
  transform: translateY(28px);
}

.js-motion .shot-stage > span { display: block; position: absolute; }

/* Smart Aisle: Apples is ticked off, greyed and struck, as the app does. */
.js-motion .shot-aisle .shot-fade {
  left: 6%;
  top: 40.74%;
  width: 20.33%;
  height: 8.64%;
  background: rgba(250, 251, 247, 0.55);
  opacity: 0;
}

.js-motion .shot-aisle .shot-strike {
  left: 6%;
  top: 43.72%;
  width: 19.33%;
  height: 1.65%;
  border-radius: 999px;
  background: #C2413A;
  transform: scaleX(0);
  transform-origin: left center;
}

/* No signal: the badge comes up, then Cheese is ticked off in the dark. */
.js-motion .shot-offline .shot-fade {
  left: 6%;
  top: 23.77%;
  width: 21.5%;
  height: 8.97%;
  background: rgba(15, 21, 20, 0.5);
  opacity: 0;
}

.js-motion .shot-offline .shot-strike {
  left: 6.17%;
  top: 27.35%;
  width: 21%;
  height: 1.79%;
  border-radius: 999px;
  background: #FF6B63;
  transform: scaleX(0);
  transform-origin: left center;
}

/* Sized to its window, which is 226px wide on the smallest phone. */
.shot-offline { container-type: inline-size; }

.shot-badge {
  align-items: center;
  gap: 0.35em;
  padding: 0.3em 0.7em 0.3em 0.55em;
  border: 1px solid rgba(52, 211, 209, 0.45);
  border-radius: 999px;
  background: rgba(6, 47, 50, 0.92);
  color: var(--teal-glow);
  font-size: clamp(8px, 3.7cqw, 11.5px);
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.shot-badge svg { width: 1.3em; height: 1.3em; }

.js-motion .shot-offline .shot-badge {
  display: inline-flex;
  top: 0.9%;
  right: 4%;
  opacity: 0;
  transform: translateY(-6px) scale(0.9);
}

/*
 * The moments wait for is-playing, which the page script adds once the
 * window is midway up the screen: when a card has merely appeared at the
 * bottom, the eye is still on the card above.
 */
.js-motion .card.is-playing .shot-fade {
  opacity: 1;
  transition: opacity 240ms ease var(--moment, 0.35s);
}

.js-motion .card.is-playing .shot-strike {
  transform: scaleX(1);
  transition: transform 380ms cubic-bezier(0.2, 0.8, 0.2, 1) var(--moment, 0.35s);
}

.js-motion .card-offline { --moment: 0.95s; }

.js-motion .card-offline.is-playing .shot-badge {
  opacity: 1;
  transform: none;
  transition:
    opacity 300ms ease 0.25s,
    transform 420ms cubic-bezier(0.3, 1.5, 0.5, 1) 0.25s;
}

/*
 * Quick Add: the typed "Sa" is hidden under a patch of the box's white, with
 * a caret at its left edge; the patch narrows a letter at a time. Then the
 * suggestions, hidden under the list's paper, are uncovered a row at a time.
 */
.js-motion .shot-quick .shot-typing {
  right: 65.67%;
  top: 23.87%;
  width: 7.5%;
  height: 7.82%;
  background: #FFFFFF;
}

.js-motion .shot-quick .shot-typing::before {
  content: "";
  position: absolute;
  left: 0;
  top: 18%;
  bottom: 18%;
  width: 2px;
  background: #4E6EF2;
  animation: caret 0.9s step-end infinite;
}

.js-motion .shot-quick .shot-suggestions {
  left: 0;
  top: 39.09%;
  width: 100%;
  height: 60.91%;
  background: #FAFBF7;
}

.js-motion .card-quick.is-playing .shot-typing {
  animation: type-sa 900ms step-end 0.35s forwards;
}

.js-motion .card-quick.is-playing .shot-suggestions {
  animation: list-sa 450ms step-end 1.3s forwards;
}

@keyframes caret {
  50% { opacity: 0; }
}

/* "S" ends at the screenshot's x 181, "a" and the caret at 206. */
@keyframes type-sa {
  0% { width: 7.5%; }
  40% { width: 4.17%; }
  80% { width: 0; opacity: 1; }
  100% { width: 0; opacity: 0; }
}

/* Row boundaries below the box: 250, 318, 385 and 455 of 486. */
@keyframes list-sa {
  0% { clip-path: inset(0 0 0 0); }
  20% { clip-path: inset(20.3% 0 0 0); }
  40% { clip-path: inset(43.2% 0 0 0); }
  60% { clip-path: inset(65.9% 0 0 0); }
  80% { clip-path: inset(88.5% 0 0 0); }
  100% { clip-path: inset(100% 0 0 0); }
}

/* All the Usuals: Milk is chosen, then Bread, each with a small pop. */
.js-motion .card-usuals .au-row[data-pick] {
  background: transparent;
  transition: background-color 250ms ease var(--pick);
}

.js-motion .card-usuals .au-row[data-pick] .au-off {
  opacity: 1;
  transition: opacity 150ms ease var(--pick);
}

.js-motion .card-usuals .au-row[data-pick] .au-on {
  opacity: 0;
  transform: scale(0.4);
  transition:
    opacity 150ms ease var(--pick),
    transform 380ms cubic-bezier(0.3, 1.6, 0.5, 1) var(--pick);
}

.js-motion .card-usuals.is-playing .au-row[data-pick] { background: #EEF6F2; }
.js-motion .card-usuals.is-playing .au-row[data-pick] .au-off { opacity: 0; }
.js-motion .card-usuals.is-playing .au-row[data-pick] .au-on { opacity: 1; transform: none; }

/*
 * In. Out. Done.: the words land in turn, and Done. gets its tick. It waits
 * for is-playing, as the app windows do, so it runs once it is midway up.
 */
.trip-tick {
  display: inline-block;
  width: 0.42em;
  height: 0.42em;
  margin: 0.04em 0 0 0.06em;
  vertical-align: top;
  fill: none;
  stroke: var(--teal-glow);
  stroke-width: 3.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.js-motion .trip-title > span {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.3em);
  transition:
    opacity 1000ms ease,
    transform 1200ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* A beat between words, as the steps they name take their turn. */
.js-motion .trip-title > span:nth-child(2) { transition-delay: 600ms; }
.js-motion .trip-title > span:nth-child(3) { transition-delay: 1200ms; }
.js-motion .trip-title.is-playing > span { opacity: 1; transform: none; }

.js-motion .trip-tick path {
  stroke-dasharray: 26;
  stroke-dashoffset: 26;
}

/*
 * The steps follow on as part of the same sequence, one at a time, once the
 * tick by Done. is drawing.
 */
.js-motion .steps li {
  opacity: 0;
  transform: translateY(18px);
}

.js-motion .trip-title.is-playing ~ .steps li {
  opacity: 1;
  transform: none;
  transition:
    opacity 700ms ease var(--step),
    transform 800ms cubic-bezier(0.2, 0.8, 0.2, 1) var(--step);
}

.js-motion .trip-title.is-playing .trip-tick path {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 700ms cubic-bezier(0.6, 0, 0.3, 1) 2.2s;
}

/*
 * The hero's phones lean a little towards the pointer and drift up as the
 * page scrolls, the front one more than the back, so they read as two
 * objects at different depths. The page script sets --px and --py from -1
 * to 1, and --sy from 0 to 1 across the hero's height.
 */
/*
 * Your data: once it is midway up, the red line strikes through what is
 * never done, then each promise gets its tick in turn.
 */
.js-motion .privacy-strike::after {
  transform: scaleX(0);
  transform-origin: left center;
}

.js-motion .privacy.is-playing .privacy-strike::after {
  transform: scaleX(1);
  transition: transform 650ms cubic-bezier(0.2, 0.8, 0.2, 1) 0.3s;
}

.js-motion .promises svg {
  opacity: 0;
  transform: scale(0.4);
  transition:
    opacity 150ms ease var(--pick),
    transform 380ms cubic-bezier(0.3, 1.6, 0.5, 1) var(--pick);
}

.js-motion .privacy.is-playing .promises svg { opacity: 1; transform: none; }

/*
 * The closing icon installs once it is midway up: the circle fills,
 * then the dimming lifts and the icon gives a small pop as it arrives.
 */
.js-motion .install { display: grid; }

.js-motion .closing-app.is-playing .install-pie {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 2000ms cubic-bezier(0.45, 0.05, 0.4, 1) 0.4s;
}

.js-motion .closing-app.is-playing .install {
  opacity: 0;
  transition: opacity 350ms ease 2.5s;
}

.js-motion .closing-app.is-playing img {
  animation: arrive 500ms cubic-bezier(0.3, 1.5, 0.5, 1) 2.5s;
}

@keyframes arrive {
  40% { transform: scale(1.08); }
}

.js-motion .phone {
  transition: transform 600ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.js-motion .phone-front {
  transform:
    translate3d(calc(var(--px, 0) * -12px), calc(var(--py, 0) * -10px + var(--sy, 0) * -48px), 0)
    rotate(calc(-4deg + var(--px, 0) * 2deg));
}

.js-motion .phone-back {
  transform:
    translate3d(calc(var(--px, 0) * 7px), calc(var(--py, 0) * 5px + var(--sy, 0) * -18px), 0)
    rotate(calc(6deg + var(--px, 0) * -1.5deg));
}

.js-motion .phones { animation: phones-in 1000ms cubic-bezier(0.2, 0.8, 0.2, 1) 150ms both; }

@keyframes phones-in {
  from { opacity: 0; transform: translateY(40px); }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .tick-off::after { animation: none; transform: none; }
}
