/* ==========================================================================
   InfiniTask — site stylesheet
   Tokens ported from Xcode Project InfiniTask/InfiniTask/Theme.swift.
   Do not invent values here; if the app changes, change Theme.swift first.
   ========================================================================== */

/* -------------------------------------------------- font
   Every style in the app is SF Rounded. `ui-rounded` resolves to SF Pro
   Rounded on Apple hardware (most of the audience). Nunito is the
   self-hosted fallback for Windows and Android, so the site makes no
   third-party requests at runtime. */
@font-face {
  font-family: 'Nunito';
  src: url('../fonts/nunito-latin-var.woff2') format('woff2-variations');
  font-weight: 400 800;
  font-style: normal;
  font-display: swap;
}

:root {
  --font: ui-rounded, 'SF Pro Rounded', 'Nunito', -apple-system,
          BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  /* --- adaptive surfaces (Assets.xcassets *.colorset) --- */
  --app-bg: #E8EBF2;
  --card-bg: #FEFFFF;
  --card-bg-2: #F2F4F8;
  --divider: #E0E0E5;
  --text-1: #1C1C1C;
  --text-2: #9195BB;
  --text-3: #AEAEB2;
  --section-header: #6B6E8A;
  --card-grad-a: #FFFFFF;
  --card-grad-b: #FAFAFA;
  --card-stroke: rgba(0, 0, 0, .05);
  --card-stroke-solid: #FFFFFF;        /* cardSurface() light stroke */

  /* --- fixed accents (Theme.swift 88-101) --- */
  --purple: #9A61ED;
  --purple-dark: #534AB7;
  --blue: #008DF1;
  --folder-blue-top: #10D3FE;      /* FolderColor.blue gradientTop */
  --folder-blue-bottom: #008DF1;   /* FolderColor.blue gradientBottom */
  --sky-blue: #1696F1;             /* top of the Capture sky fade */
  --teal: #1D9E75;
  --coral: #FF0060;
  --pink: #D4537E;
  --subheader: #6B6E8A;
  --imp-high: #E5484D;
  --imp-med: #E8A33D;

  /* --- folder palette: DISPLAY names, not enum cases --- */
  --manilla-a: #F9E1AA;  --manilla-b: #EAB578;
  --blue-a:    #10D3FE;  --blue-b:    #008DF1;
  --green-a:   #00D4B8;  --green-b:   #01BABA;
  --teal-a:    #41E9F4;  --teal-b:    #00CDDF;
  --purple-a:  #BD74FF;  --purple-b:  #9A61ED;
  --lavender-a:#9C8EFF;  --lavender-b:#675AE2;
  --pink-a:    #F371C5;  --pink-b:    #D05EA8;
  --forest-a:  #5FB87E;  --forest-b:  #2F7A4C;
  --orange-a:  #FFBC82;  --orange-b:  #F08121;

  /* --- shape (AppRadius / AppShadow) --- */
  --r-card: 20px;
  --r-button: 20px;
  --r-small: 8px;
  --r-large: 32px;
  --shadow-card: 0 4px 12px rgba(0, 0, 0, .06);
  --shadow-card-hover: 0 10px 24px rgba(14, 16, 30, .14);
  --shadow-btn: 0 4px 8px rgba(0, 0, 0, .12);
  --shadow-lift: 0 18px 50px rgba(28, 28, 28, .16);
  /* device drop shadow: light from the top-left, so it falls down and right.
     Three layers — contact, mid, ambient — so it reads soft rather than muddy. */
  --shadow-device:
    1px 2px 4px rgba(14, 16, 30, .09),
    3px 8px 18px rgba(14, 16, 30, .11),
    8px 26px 48px rgba(14, 16, 30, .15),
    14px 44px 80px rgba(14, 16, 30, .10);

  /* --- layout (AppLayout) --- */
  --reading: 640px;
  --grid: 960px;
  --wide: 1120px;
  --gutter: 16px;
  --seg-track: #DADDE7;   /* segmented pill, deselected fill (Theme.swift) */
  --glass-tint: #FFFFFF;      /* the pane itself */
  --glass-rim:  #FFFFFF;      /* specular edge */
  --glass-glow: #FFFFFF;      /* light caught inside */
  --glass-keyline: rgba(14, 16, 31, 0.12); /* the dark hairline outside the rim */
  --glass-bright: 1.08;
  --nav-pill-h: 46px;   /* both nav pills share this */
  --nav-h: 52px;   /* measured: nav mark 28px + .7rem padding block */
  --band: clamp(64px, 9vw, 116px);
}

/* Dark: applied by system preference, unless the page forces light. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --app-bg: #1E2030;
    --seg-track: #191B26;
    --card-bg: #282B40;
    --card-bg-2: #2E3148;
    --divider: #383B54;
    --text-1: #F0F0F5;
    --text-2: #9195BB;
    --text-3: #6B6D88;
    --section-header: #8487A8;
    --card-grad-a: #3C3F58;
    --card-grad-b: #292B40;
    --card-stroke: rgba(255, 255, 255, .07);
    --card-stroke-solid: rgba(255, 255, 255, .12);   /* subtle in dark; white would glare */
    --shadow-card: none;
    --shadow-card-hover: 0 10px 24px rgba(0, 0, 0, .45);
  --glass-tint: #454A63;
  --glass-rim:  #FFFFFF;
  --glass-glow: #FFFFFF;
  --glass-keyline: rgba(0, 0, 0, .42);
  --glass-bright: 1.15;
    --glass-tint: #454A63;
    --glass-rim:  #FFFFFF;
    --glass-glow: #FFFFFF;
    --glass-keyline: rgba(0, 0, 0, .42);
    --glass-bright: 1.15;
    --shadow-lift: 0 18px 50px rgba(0, 0, 0, .45);
    --shadow-device:
      1px 2px 4px rgba(0, 0, 0, .34),
      3px 8px 18px rgba(0, 0, 0, .34),
      8px 26px 48px rgba(0, 0, 0, .40),
      14px 44px 80px rgba(0, 0, 0, .28);
  }
}
:root[data-theme='dark'] {
  --app-bg: #1E2030;
  --seg-track: #191B26;
  --card-bg: #282B40;
  --card-bg-2: #2E3148;
  --divider: #383B54;
  --text-1: #F0F0F5;
  --text-2: #9195BB;
  --text-3: #6B6D88;
  --section-header: #8487A8;
  --card-grad-a: #3C3F58;
  --card-grad-b: #292B40;
  --card-stroke: rgba(255, 255, 255, .07);
  --card-stroke-solid: rgba(255, 255, 255, .12);   /* subtle in dark; white would glare */
  --shadow-card: none;
  --shadow-card-hover: 0 10px 24px rgba(0, 0, 0, .45);
  --shadow-lift: 0 18px 50px rgba(0, 0, 0, .45);
  --shadow-device:
    1px 2px 4px rgba(0, 0, 0, .34),
    3px 8px 18px rgba(0, 0, 0, .34),
    8px 26px 48px rgba(0, 0, 0, .40),
    14px 44px 80px rgba(0, 0, 0, .28);
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

body {
  margin: 0;
  font-family: var(--font);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--text-1);
  background: var(--app-bg);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; height: auto; display: block; }
a { color: var(--blue); text-decoration: none; }
a:hover { text-decoration: underline; }
:focus-visible { outline: 3px solid var(--blue); outline-offset: 3px; border-radius: 6px; }

h1, h2, h3, h4 {
  margin: 0 0 .5em; line-height: 1.15; letter-spacing: -.02em;
  text-wrap: balance;          /* stops a single word orphaning on the last line */
}
p, li { text-wrap: pretty; }   /* same idea, lighter touch, for prose */
h1 { font-size: clamp(2.1rem, 6vw, 3.6rem); font-weight: 600; }
h2 { font-size: clamp(1.9rem, 4.6vw, 2.9rem); font-weight: 600; }
h3 { font-size: clamp(1.5rem, 3.3vw, 2.25rem); font-weight: 600; }   /* caps at 36px ~1090px vw */
h4 { font-size: 1.0625rem; font-weight: 600; }
p  { margin: 0 0 1.1em; }
p:last-child { margin-bottom: 0; }

.lede { font-size: clamp(1.05rem, 2.1vw, 1.3rem); color: var(--text-1); opacity: .82; }
.muted { color: var(--text-2); }
.small { font-size: .875rem; }

/* .sectionHeaderText() — 13pt semibold, 0.4 kerning, uppercase */
.section-label {
  font-size: .8125rem;
  font-weight: 600;
  letter-spacing: .4px;
  text-transform: uppercase;
  color: var(--text-2);
  margin: 0 0 .35em;
}

.wrap { width: 100%; max-width: var(--grid); margin-inline: auto; padding-inline: var(--gutter); }
.wrap--reading { max-width: var(--reading); }
.wrap--wide { max-width: var(--wide); }
.center { text-align: center; }

.band { padding-block: var(--band); }
.band--tint { background: var(--card-bg-2); }
/* The bridge: the hero ends on #6858E6 and the Capture sky opens on #1696F1,
   so this band runs one into the other instead of dropping the page back to
   grey between two saturated sections. Both ends are within 3/255 of their
   neighbours, so neither seam is visible.
   Text is white and at full strength here. .lede carries opacity .82 by
   default, which is fine on the page ground and quietly eats the contrast on
   a saturated one. */
.band--bridge {
  /* The band already carries a big top padding to clear the hero's phones, so
     the copy sits low in the band and white on #1396F1 is only 3.15:1, under
     AA for body text. The answer is not a pile of empty padding below it,
     which just made a 400px hole between the two headings. The blue is pushed
     down the ramp instead, so the copy keeps its dark purple and the band can
     be short. */
  padding-bottom: clamp(6.5rem, 9vw, 7.5rem);   /* floor 104px, see below */
  /* The hero's showcase carries a negative bottom margin that collapses out of
     the hero, so the hero's own background paints over this band's top 140 to
     260px depending on viewport. The first 44% is therefore held flat at the
     hero's exact final colour: whatever the overhang turns out to be, the
     hero's last pixel meets an identical one and there is no step. Both ends
     are taken from the neighbours rather than typed in, so neither seam can
     drift if either section is retuned. Craig's #6957E5 and #1396F1 are within
     3/255 of these.
     The hold is a bigger FRACTION of a shorter band, which is why it went from
     38% to 44%: 260px of overhang is 39% of this band against 30% of the old
     one. */
  background: linear-gradient(to bottom,
    #6858E6 0%,                 /* the hero's last gradient stop, exactly */
    #6858E6 44%,                /* still behind the overhang at its deepest */
    /* Anchored to the bottom, not to a percentage of the band. The copy always
       ends well clear of the band's foot (the padding floor is 104px), and the
       blue always starts 64px above it, so the copy clears the fast part of
       the ramp by at least 24px at EVERY size. A percentage cannot promise
       that: it drifts with the band's height, and on a short band it put the
       lede at 4.38:1, under AA. */
    #4F6AE9 calc(100% - 64px),
    var(--sky-blue) 100%);      /* the Capture sky's first stop, exactly */
  color: #fff;
}
.band--bridge h2,
.band--bridge .lede { color: #fff; opacity: 1; }
/* Full white, not the usual dimmed label: at 13px it needs the whole 4.5:1
   and the .82 alpha alone dropped it to 3.76. */
.band--bridge .section-label { color: #fff; }
.band--edge { border-block: 1px solid var(--divider); }

/* -------------------------------------------------- nav
   A floating split nav: a transparent fixed rail holding two pills, brand on
   the left and links on the right. The rail ignores pointer events so it never
   blocks the page behind it; the pills take them back. */
.nav {
  position: fixed;
  top: clamp(10px, 1.6vw, 18px);
  left: 0; right: 0;
  z-index: 200;
  display: flex; align-items: center; justify-content: flex-end;
  gap: .75rem;
  padding-inline: clamp(12px, 2vw, 20px);
  pointer-events: none;
}
.nav > * { pointer-events: auto; }

/* Liquid Glass, approximated.

   The thing that separates glass from frosting is REFRACTION, not blur. A
   thick pane bends light most where it curves, at the rim, and barely at all
   through the flat middle. Frosting blurs evenly and reads as plastic.

   Craig found a WebGL shader that does this properly, by displacing a texture
   through a rounded-box lens. It cannot drive this pill: the shader owns its
   texture, and the nav sits over live scrolling page content that no browser
   will hand to WebGL per frame. backdrop-filter: url(#displacement) would do
   real refraction, but Safari parses the syntax and then applies nothing, so
   it cannot even be feature-detected, and Safari is where this gets judged.

   So the lensing is faked the way the scroll veil fakes progressive blur: the
   pane carries a light blur, and a second masked layer stacks more blur on top
   of it in a ring hugging the rim. Cumulative, because a backdrop-filter on a
   child filters what the parent already filtered. The middle stays legible,
   the edge goes soft and bright, and the eye reads thickness.

   Five layers in total:
   1. a light backdrop blur + saturation through the middle, so colour behind
      bleeds through richer and stays READABLE, which heavy frosting kills
   2. a faint top-down tint, because real glass is not uniformly transparent
   3. the edge lens: extra blur and brightness masked to a ring at the rim
   4. a SPECULAR RIM, bright along the top edge, dimmer at the bottom
   5. an inner glow, as if light were caught inside the pane
   The rim is a gradient border, which CSS cannot do directly, so it is a
   masked pseudo-element ring. */
.nav__pill {
  position: relative;
  min-height: var(--nav-pill-h);
  box-sizing: border-box;
  background:
    /* two soft blooms caught in the upper curve, where the rim peaks */
    radial-gradient(58% 150% at 16% -18%,
      color-mix(in srgb, var(--glass-rim) 34%, transparent),
      transparent 68%),
    radial-gradient(40% 130% at 86% -14%,
      color-mix(in srgb, var(--glass-rim) 26%, transparent),
      transparent 70%),
    linear-gradient(to bottom,
      color-mix(in srgb, var(--glass-tint) 62%, transparent),
      color-mix(in srgb, var(--glass-tint) 40%, transparent));
  /* Deliberately lighter than it was. 18px frosted the pane flat; glass this
     thick should still let you read what is behind it. */
  backdrop-filter: blur(7px) saturate(190%) brightness(var(--glass-bright));
  -webkit-backdrop-filter: blur(7px) saturate(190%) brightness(var(--glass-bright));
  border: 0;
  box-shadow:
    /* the keyline sits OUTSIDE the border box, so it lands outside the bright
       rim: dark line, then bright rim, then pane, which is the order measured
       off Craig's reference (bg 167 -> keyline 95 -> rim 239 -> pane 221). */
    0 0 0 1px var(--glass-keyline),
    inset 0 0 18px color-mix(in srgb, var(--glass-glow) 14%, transparent),
    0 1px 2px rgba(14, 16, 30, .05),
    0 8px 26px rgba(14, 16, 30, .09);
}

/* the edge lens — the refraction stand-in.
   farthest-side keeps the ellipse inscribed in the box, so on a wide pill it
   follows the shape instead of pinching into a circle. The blur here lands ON
   TOP of the pane's own, so the rim ends up at roughly 22px against 7px in the
   middle: a gradient of thickness across the pane, which is what a lens is. */
.nav__pill::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  backdrop-filter: blur(15px) brightness(1.06) saturate(120%);
  -webkit-backdrop-filter: blur(15px) brightness(1.06) saturate(120%);
  -webkit-mask: radial-gradient(farthest-side at 50% 50%,
    rgba(0, 0, 0, 0) 42%, rgba(0, 0, 0, .55) 74%, #000 100%);
          mask: radial-gradient(farthest-side at 50% 50%,
    rgba(0, 0, 0, 0) 42%, rgba(0, 0, 0, .55) 74%, #000 100%);
  pointer-events: none;
}

/* Anything inside a glass pill has to clear the same two layers. */
.nav__pill > * { position: relative; z-index: 2; }

/* the specular rim, above the lens layer */
.nav__pill::before {
  content: '';
  position: absolute; inset: 0; z-index: 1;
  border-radius: inherit;
  padding: 1px;
  /* Conic, not linear, because the light does not run corner to corner: it
     peaks on the two UPPER SHOULDERS and bounces again along the bottom.
     Sampled off Craig's reference against a 216 interior, the rim reads 253 at
     both upper shoulders, 239 top and bottom centre, and 208 on the lower
     flanks. The peaks sit near 75 and 285 degrees rather than the reference's
     69 and 291, because this pill is wider and a wider pill pushes its
     shoulders toward the horizontal. */
  background: conic-gradient(from 0deg at 50% 50%,
    color-mix(in srgb, var(--glass-rim) 58%, transparent) 0deg,
    color-mix(in srgb, var(--glass-rim) 95%, transparent) 75deg,
    color-mix(in srgb, var(--glass-rim) 22%, transparent) 95deg,
    color-mix(in srgb, var(--glass-rim) 12%, transparent) 130deg,
    color-mix(in srgb, var(--glass-rim) 58%, transparent) 180deg,
    color-mix(in srgb, var(--glass-rim) 12%, transparent) 230deg,
    color-mix(in srgb, var(--glass-rim) 24%, transparent) 265deg,
    color-mix(in srgb, var(--glass-rim) 95%, transparent) 285deg,
    color-mix(in srgb, var(--glass-rim) 58%, transparent) 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}
@supports not ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {
  .nav__pill { border: 1px solid color-mix(in srgb, var(--glass-rim) 55%, transparent); }
  .nav__pill::before { display: none; }
}

.nav__home {
  display: flex; align-items: center; gap: .5rem;
  padding: 0 16px 0 8px;
  border-radius: 999px;                /* same shape and height as the links pill */
  text-decoration: none;
}
.nav__home:hover { text-decoration: none; }
.nav__mark { width: 30px; height: 30px; border-radius: 26%; }

/* The wordmark asset is flat white with an alpha channel, so it is used as a
   CSS mask and painted with currentColor. Text stays in the markup for screen
   readers, copy-paste, and as the fallback where masks are unsupported. */
.nav__word { font-size: 1.05rem; font-weight: 800; letter-spacing: -.02em; color: var(--text-1); }
@supports ((-webkit-mask-image: url('')) or (mask-image: url(''))) {
  .nav__word {
    display: block;
    width: 108px; height: 20px;        /* asset is 900x170, ratio 5.29:1 */
    text-indent: -200vw; overflow: hidden; white-space: nowrap;
    background-color: currentColor;
    -webkit-mask: url('../img/logo/wordmark-white.png') no-repeat center / contain;
          mask: url('../img/logo/wordmark-white.png') no-repeat center / contain;
  }
}

.nav__links {
  display: flex; align-items: center; gap: 2px;
  padding: 0 6px;
  border-radius: 999px;                /* links pill is fully rounded */
}
.nav__links a {
  /* above the pane's lens and rim layers, both of which are positioned and
     would otherwise paint over in-flow text */
  position: relative; z-index: 2;
  /* Tracking, not labels. The pill carries six items now, and its width is
     what decides where the nav drops to the bottom of the screen, so the
     padding is as tight as it can be while the targets stay comfortable. */
  padding: .42rem .64rem;
  border-radius: 999px;
  font-weight: 600; font-size: .9375rem;
  color: var(--text-1); opacity: .72;
  transition: background .18s ease, opacity .18s ease, color .18s ease;
}
.nav__links a:hover {
  opacity: 1; text-decoration: none;
  background: color-mix(in srgb, var(--text-1) 7%, transparent);
}
/* Icon-only, so it gets a square hit area rather than the text links'
   asymmetric padding. Sized to match their optical height, not their box. */
.nav__links .nav__home {
  display: grid; place-items: center;
  width: 30px; height: 30px;
  padding: 0;
  margin-right: 1px;
}
.nav__links .nav__home svg { display: block; width: 17px; height: 17px; }

.nav__links a[aria-current='page'] {
  opacity: 1; color: var(--blue);
  background: color-mix(in srgb, var(--blue) 11%, transparent);
}

/* Inverts with the theme: near-black on light, near-white on dark. Uses the
   existing text/surface tokens, so it flips automatically and always sits at
   maximum contrast against whatever the glass is sitting on. */
.nav__links .nav__cta {
  margin-left: 4px;
  padding: .42rem .9rem;
  background: var(--text-1);
  color: var(--card-bg);
  opacity: 1;
  font-weight: 700;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .16),
    0 1px 2px rgba(14, 16, 30, .16),
    0 3px 10px rgba(14, 16, 30, .18);
  transition: background-color .18s ease, color .18s ease, transform .18s cubic-bezier(.2,.7,.3,1);
}
/* Hover just inverts it, and nothing else moves. The two tokens swap, so it
   flips with the theme the same way the resting state does: white to black on
   dark, black to white on light, label following either way. */
.nav__links .nav__cta:hover {
  background: var(--card-bg);
  color: var(--text-1);
}
.nav__links .nav__cta:active { transform: scale(.97); }

/* The pinned icon sits centred at the top, so on anything but a wide window it
   collides with the links pill. A fixed breakpoint is the wrong tool here: the
   pill's width depends on its text, which moves with font size and zoom. JS
   measures the real gap and sets .nav--bottom, which drops the pill to the
   bottom of the screen, where it is closer to thumbs anyway.
   The media query is the no-JS fallback. */
.nav--bottom {
  top: auto;
  bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  justify-content: center;
}
body.has-bottom-nav {
  padding-bottom: calc(var(--nav-pill-h) + 34px + env(safe-area-inset-bottom, 0px));
}
@media (max-width: 780px) {
  html:not(.js) .nav {
    top: auto;
    bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    justify-content: center;
  }
  html:not(.js) body { padding-bottom: calc(var(--nav-pill-h) + 34px); }
}

@media (max-width: 560px) {
  :root { --nav-pill-h: 42px; }
  .nav { gap: .5rem; padding-inline: 10px; }
  .nav__links { padding: 0 5px; }
  .nav__links a { padding: .38rem .55rem; font-size: .8125rem; }
  .nav__links .nav__home { width: 26px; height: 26px; margin-right: 0; }
  .nav__links .nav__home svg { width: 16px; height: 16px; }
  .nav__links .nav__cta { padding: .38rem .78rem; margin-left: 2px; }
}
/* Six items now instead of four, so the last squeeze is tighter tracking
   rather than dropping a label. Measured at 320px: the pill still clears both
   gutters, so nothing has to be hidden. */
@media (max-width: 400px) {
  .nav__links a { padding: .38rem .42rem; font-size: .78rem; }
  .nav__links { padding: 0 4px; }
}

/* -------------------------------------------------- scroll veil
   The iOS scroll edge effect: content passing under the pinned icon and the
   links pill blurs and fades away instead of colliding with them.

   Six stacked layers, each blurred a little harder and masked to a band a
   little higher up. That is the only way to fake a progressive blur in CSS:
   one backdrop-filter can do exactly one radius, and masking a single layer
   just cuts the blur off with a visible edge. Because the layers are siblings
   in one stacking context, each one samples the blurred output of the ones
   below it, so the blur accumulates towards the top.

   ============ EVERY KNOB IS IN THIS BLOCK ============
   Nothing below it needs touching to retune the effect. Add ?tune to the URL
   for live sliders. */
@property --veil-scrim {
  syntax: '<color>'; inherits: true; initial-value: #E8EBF2;
}
:root {
  --veil-h: 120px;             /* how far down the page the effect reaches */
  --veil-blur: 10px;           /* blur at the very top edge, in effect */
  --veil-scrim-alpha: .38;     /* how solid the fade is at the top edge, 0-1 */
  --veil-scrim-stop: 38%;      /* how far down the fade has gone completely */

  /* A second, darker fade over the top edge only. The blur alone leaves a hard
     line where it starts, and over a bright background the scrim reads as a
     pale wash. This sits on top of both and softens the edge back down. */
  --veil-shade: #1E2030;       /* on-palette: the dark app background */
  --veil-shade-alpha: .12;     /* strength at the very top edge, 0-1 */
  --veil-shade-stop: 40%;      /* how far down it has gone completely */
  --veil-start: 65px;          /* scroll position where it starts to appear */
  --veil-ramp: 180px;          /* further scrolling until it is fully on */

  --veil-scrim: var(--app-bg); /* what content fades towards. JS retargets this
                                  per section, see data-veil in the markup. */
  --veil: 0;                   /* 0-1 fade, written by JS. Not for hand-editing. */
}

.veil {
  position: fixed; inset: 0 0 auto 0;
  height: var(--veil-h);
  z-index: 190;                /* under the pill (200) and the icon (210), so
                                  both stay crisp on top of the blur */
  pointer-events: none;
  opacity: var(--veil);
  display: none;               /* six backdrop-filters are not free. JS only
                                  switches them on once they are needed. */
}
.veil.is-on { display: block; }

.veil > i { position: absolute; inset: 0; display: block; }

/* Layer blur. Gaussian blurs compose as the root of the sum of squares, so six
   equal layers read as about 2.45x one of them. Dividing by that keeps
   --veil-blur meaning roughly what it says at the top edge. */
.veil > i {
  -webkit-backdrop-filter: blur(calc(var(--veil-blur) / 2.45));
          backdrop-filter: blur(calc(var(--veil-blur) / 2.45));
}

/* Each mask starts hard at the top and dissolves lower down. The overlap
   between them is what stops you seeing six separate bands. */
.veil > i:nth-child(1) { -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 60%, transparent 100%); mask-image: linear-gradient(to bottom, #000 0%, #000 60%, transparent 100%); }
.veil > i:nth-child(2) { -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 48%, transparent  84%); mask-image: linear-gradient(to bottom, #000 0%, #000 48%, transparent  84%); }
.veil > i:nth-child(3) { -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 36%, transparent  68%); mask-image: linear-gradient(to bottom, #000 0%, #000 36%, transparent  68%); }
.veil > i:nth-child(4) { -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 24%, transparent  52%); mask-image: linear-gradient(to bottom, #000 0%, #000 24%, transparent  52%); }
.veil > i:nth-child(5) { -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 12%, transparent  36%); mask-image: linear-gradient(to bottom, #000 0%, #000 12%, transparent  36%); }
.veil > i:nth-child(6) { -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000  0%, transparent  20%); mask-image: linear-gradient(to bottom, #000 0%, #000  0%, transparent  20%); }

/* The fade itself. Painted over the blur, in the colour of whatever section is
   currently passing underneath. */
.veil::after {
  content: ''; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(to bottom,
    color-mix(in srgb, var(--veil-scrim) calc(var(--veil-scrim-alpha) * 100%), transparent) 0%,
    transparent var(--veil-scrim-stop));
  transition: background .35s ease;   /* @property is what lets this animate */
}

/* The darker top edge, over everything else, and deliberately shorter than the
   scrim so it reads as a soft edge rather than a second wash. */
.veil::before {
  content: ''; position: absolute; inset: 0; z-index: 2;
  background: linear-gradient(to bottom,
    color-mix(in srgb, var(--veil-shade) calc(var(--veil-shade-alpha) * 100%), transparent) 0%,
    transparent var(--veil-shade-stop));
}

/* Someone who has asked for less transparency gets the fade without the blur. */
@media (prefers-reduced-transparency: reduce) {
  .veil > i { -webkit-backdrop-filter: none; backdrop-filter: none; }
}

/* -------------------------------------------------- buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  padding: .85rem 1.6rem;
  border: 0; border-radius: var(--r-button);
  font: inherit; font-weight: 700; font-size: 1rem;
  cursor: pointer; text-decoration: none;
  box-shadow: var(--shadow-btn);
  transition: transform .15s ease, filter .15s ease;
}
.btn:hover { transform: translateY(-1px); filter: brightness(1.05); text-decoration: none; }
.btn:active { transform: translateY(0) scale(.98); filter: brightness(.98); }
.btn--primary { background: var(--blue); color: #fff; }
.btn--light { background: #fff; color: var(--purple-dark); }
.btn--ghost {
  background: rgba(255, 255, 255, .16); color: #fff;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .35);
}

/* -------------------------------------------------- hero */
.hero {
  position: relative;
  /* Sampled from Background-Colour2 in Assets.xcassets, the warm Pro card.
     Mostly vertical, with a golden lean to the top right, as in the artwork. */
  background:
    radial-gradient(120% 80% at 88% -6%, rgba(255, 214, 140, .85), transparent 58%),
    linear-gradient(172deg,
      #F9C275 0%,
      #F1A488 9%,
      #DA8CA8 16%,
      #9A66CC 26%,
      #7A5CDD 46%,
      #6A57E4 68%,
      #6858E6 100%);   /* same hues, deepened where the copy sits */
  color: #fff;
  padding-top: calc(var(--nav-h) + 12px);
  /* The showcase has to spill DOWNWARD into the band below, which is what the
     bridge gradient's flat hold is built around. But the fan's right-hand card
     swings past the viewport on a narrow screen and took the whole document
     with it: 143px of horizontal scroll at 414px wide.
     clip, not hidden. hidden on one axis forces the other to auto, which would
     kill the downward spill. clip leaves overflow-y visible.
     And it belongs HERE, not on body: overflow-x on body would make it a
     scroll container and break the position: sticky that pins the unfold. */
  overflow-x: clip;
  overflow-y: visible;
}
.hero__inner { position: relative; z-index: 2; padding-block: clamp(2.5rem, 7vw, 4.5rem) 0; text-align: center; }

/* ---------------- the morphing app icon
   One element does both jobs: the big logo in the hero, and the small
   back-to-top button pinned at the corner once you scroll. JS writes the
   geometry in pixels each frame, so nothing swaps and nothing jumps. */
.brand-slot { height: var(--brand-slot-h, 200px); }

.brand {
  position: fixed; top: 0; left: 0; z-index: 210;
  display: block; width: 1px; height: 1px;   /* JS sizes and places it */
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.brand:hover { text-decoration: none; }
.brand__mark {
  position: absolute; top: 0; left: 0;
  max-width: none;               /* the global img rule would clamp it to the 1px parent */
  border-radius: 24%;
  box-shadow: 0 22px 50px rgba(30, 20, 70, .38);
  transition: box-shadow .3s ease;
}
@supports ((-webkit-mask-image: url('')) or (mask-image: url(''))) {
  }
/* once it is the corner button it needs to read against whatever is behind */
.brand.is-pinned .brand__mark { box-shadow: 0 2px 10px rgba(14, 16, 30, .22); }
/* Static wordmark above the headline. Deliberately not part of the morphing
   icon: it stays put and scrolls away with the rest of the hero. */
.hero__wordmark {
  display: block;
  margin: 72px auto clamp(1.1rem, 2.4vw, 1.6rem);   /* clear space under the icon */
  color: #fff; font-size: clamp(2rem, 7vw, 3rem); font-weight: 600;
  letter-spacing: -.02em; line-height: 1;
}
@supports ((-webkit-mask-image: url('')) or (mask-image: url(''))) {
  .hero__wordmark {
    width: clamp(180px, 31vw, 280px);
    aspect-ratio: 900 / 170;
    text-indent: -200vw; overflow: hidden; white-space: nowrap;
    background-color: currentColor;
    -webkit-mask: url('../img/logo/wordmark-white.png') no-repeat center / contain;
          mask: url('../img/logo/wordmark-white.png') no-repeat center / contain;
  }
}

.hero h1 { color: #fff; margin-bottom: .5em; }
.hero__lede {
  max-width: 46ch; margin: 0 auto 2rem;
  font-size: clamp(1.02rem, 2.1vw, 1.22rem); line-height: 1.55;
  color: #fff;
}

/* ---------------- CTA: Apple mark, label, chevron */
.cta {
  display: inline-flex; align-items: center; gap: .7rem;
  padding: .95rem 1.5rem .95rem 1.4rem;
  border-radius: 999px;
  background: #fff; color: var(--purple-dark);
  font-weight: 700; font-size: clamp(1rem, 1.7vw, 1.08rem);
  text-decoration: none;
  box-shadow: 0 2px 6px rgba(26, 12, 64, .16), 0 12px 30px rgba(26, 12, 64, .22);
  transition: transform .2s cubic-bezier(.2,.7,.3,1), box-shadow .2s ease;
}
.cta:hover {
  text-decoration: none; transform: translateY(-2px);
  box-shadow: 0 4px 10px rgba(26, 12, 64, .18), 0 18px 42px rgba(26, 12, 64, .3);
}
.cta:active { transform: translateY(0) scale(.99); }
.cta__apple { width: 17px; height: 21px; flex: none; }
.cta__chev  { width: 9px;  height: 15px; flex: none; opacity: .55; transition: transform .2s ease; }
.cta:hover .cta__chev { transform: translateX(3px); }

/* ---------------- four pillars */
.pillars {
  list-style: none; margin: clamp(2.25rem, 5vw, 3.25rem) auto 0; padding: 0;
  display: grid; gap: clamp(.6rem, 1.6vw, 1.1rem);
  grid-template-columns: repeat(4, 1fr);
  max-width: 660px;
}
.pillar { display: flex; flex-direction: column; align-items: center; gap: .7rem; }
.pillar__icon {
  display: grid; place-items: center;
  width: clamp(52px, 8vw, 62px); aspect-ratio: 1;
  border-radius: 30%;
  color: #fff;
  background: linear-gradient(to bottom, rgba(255,255,255,.26), rgba(255,255,255,.12));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .5),
    inset 0 0 18px rgba(255, 255, 255, .1),
    0 4px 14px rgba(26, 12, 64, .16);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.pillar__icon svg { width: 52%; height: 52%; }
.pillar__label {
  font-size: clamp(.8125rem, 1.5vw, .9375rem); font-weight: 600; line-height: 1.25;
  color: rgba(255, 255, 255, .95);
}

/* The showcase hangs into the band below, so that band needs clearance or the
   devices sit on top of its heading. */
.hero + .band { padding-top: clamp(230px, 30vw, 340px); }

/* ---------------- three screens, fanning apart as you scroll
   --fan runs 0 to 1 and is set from JS. At 0 the outer two sit tucked behind
   the centre; at 1 they have swung out and up. */
.hero__showcase {
  position: relative; z-index: 2;
  margin-top: clamp(2rem, 5vw, 3.25rem);
  margin-bottom: clamp(-260px, -24vw, -140px);
  perspective: 1800px;
  pointer-events: none;
}
.fan {
  --fan: 0;
  position: relative;
  width: min(100%, 720px);
  margin-inline: auto;
  display: grid;
  place-items: end center;
  transform-style: preserve-3d;
}
.fan__card {
  grid-area: 1 / 1;
  width: clamp(168px, 30vw, 250px);
  transition: transform .35s cubic-bezier(.2,.7,.3,1);
  will-change: transform;
}
.fan__card--centre {
  z-index: 3;
  transform: rotateX(6deg) rotateY(-11deg) rotate(1.5deg);
}
.fan__card--left {
  z-index: 2;
  transform:
    translateX(calc(-6% - var(--fan) * 62%))
    translateY(calc(var(--fan) * -22px))
    rotateX(6deg) rotateY(-11deg)
    rotate(calc(-2deg - var(--fan) * 7deg));   /* no scale: all three match */
}
.fan__card--right {
  z-index: 1;
  transform:
    translateX(calc(6% + var(--fan) * 62%))
    translateY(calc(var(--fan) * -14px))
    rotateX(6deg) rotateY(-11deg)
    rotate(calc(3deg + var(--fan) * 8deg));   /* no scale: all three match */
}
.fan__card--centre,
.fan__card--left,
.fan__card--right {
  box-shadow:
    inset 0 1.5px 1px rgba(255, 255, 255, .26),
    inset 0 -1.5px 1px rgba(0, 0, 0, .45),
    0 0 0 1px rgba(0, 0, 0, .3),
    22px 50px 90px rgba(18, 10, 48, .45),
    8px 18px 34px rgba(18, 10, 48, .3);
}

/* No scroll choreography for anyone who has asked for less motion: show them
   already fanned so the composition still reads. */
@media (prefers-reduced-motion: reduce) {
  .fan { --fan: 1 !important; }
  .fan__card { transition: none; }
}

@media (max-width: 720px) {
  .fan__card { width: clamp(132px, 34vw, 190px); }
  .fan__card--centre { transform: rotateX(4deg) rotateY(-7deg) rotate(1deg); }
}

/* -------------------------------------------------- device frame */
/* The gradient and rim highlights sit on the frame itself, so the gloss lives
   in the bezel and never washes over the screenshot. */
.device {
  position: relative;
  width: clamp(230px, 62vw, 300px);
  margin-inline: auto;
  border-radius: 46px;
  padding: 10px;
  background: linear-gradient(148deg,
    #5b6178 0%, #3c4155 22%, #262a37 55%, #191b24 78%, #101219 100%);
  box-shadow:
    inset 0 1.5px 1px rgba(255, 255, 255, .26),   /* top rim catches the light */
    inset 0 -1.5px 1px rgba(0, 0, 0, .45),        /* bottom rim falls away */
    0 0 0 1px rgba(0, 0, 0, .3),
    var(--shadow-device);
}
.device img { border-radius: 37px; display: block; width: 100%; }
.device--lg { width: clamp(250px, 70vw, 340px); }

/* The feature devices, each turned to its own angle so the run does not read
   as five copies of one mock-up. Art on the LEFT of a row leans left and shows
   its left side; art on the RIGHT leans right and shows its right. The lean
   also varies in strength, hardest at the top where the reader is fresh and
   easing off down the page as the screenshots get busier.

   Every one of these is generated, not typed. The extrusion direction falls
   out of the rotation: for each angle the point 1px behind the front face is
   projected through the same matrix CSS will build, and the resulting screen
   offset sets the step for all eighteen layers. Reuse one set of offsets
   across different angles and the bodies point the wrong way. */
.device--tilt-today, .device--tilt-organise, .device--tilt-triage, .device--tilt-write, .device--tilt-focus { transform-origin: 50% 50%; }

/* Today, art right — right/bottom faces visible, body runs +0.325,+0.472 per px of depth */
.device--tilt-today {
  transform: perspective(1200px) rotateX(30deg) rotateY(-19deg) rotateZ(17deg);
  box-shadow:
    inset 0 1.5px 1px rgba(255, 255, 255, .26),
    inset 0 -1.5px 1px rgba(0, 0, 0, .45),
    0 0 0 1px rgba(0, 0, 0, .3),
    0.42px 0.60px 0 rgb(82, 87, 108),
    0.83px 1.21px 0 rgb(78, 84, 103),
    1.25px 1.81px 0 rgb(74, 80, 99),
    1.66px 2.41px 0 rgb(71, 76, 94),
    2.08px 3.02px 0 rgb(68, 72, 90),
    2.49px 3.62px 0 rgb(64, 69, 86),
    2.91px 4.23px 0 rgb(60, 65, 81),
    3.33px 4.83px 0 rgb(57, 61, 77),
    3.74px 5.43px 0 rgb(54, 58, 72),
    4.16px 6.04px 0 rgb(50, 54, 68),
    4.57px 6.64px 0 rgb(46, 50, 64),
    4.99px 7.24px 0 rgb(43, 46, 59),
    5.40px 7.85px 0 rgb(40, 43, 55),
    5.82px 8.45px 0 rgb(36, 39, 51),
    6.24px 9.06px 0 rgb(32, 35, 46),
    6.65px 9.66px 0 rgb(29, 31, 42),
    7.07px 10.26px 0 rgb(26, 28, 37),
    7.48px 10.87px 0 rgb(22, 24, 33),
    16px 24px 40px rgba(10, 12, 28, .42);
}

/* A place for everything, art left — left/bottom faces visible, body runs -0.292,+0.358 per px of depth */
.device--tilt-organise {
  transform: perspective(1200px) rotateX(22deg) rotateY(17deg) rotateZ(-14deg);
  box-shadow:
    inset 0 1.5px 1px rgba(255, 255, 255, .26),
    inset 0 -1.5px 1px rgba(0, 0, 0, .45),
    0 0 0 1px rgba(0, 0, 0, .3),
    -0.37px 0.46px 0 rgb(82, 87, 108),
    -0.75px 0.91px 0 rgb(78, 84, 103),
    -1.12px 1.37px 0 rgb(74, 80, 99),
    -1.49px 1.83px 0 rgb(71, 76, 94),
    -1.87px 2.29px 0 rgb(68, 72, 90),
    -2.24px 2.74px 0 rgb(64, 69, 86),
    -2.61px 3.20px 0 rgb(60, 65, 81),
    -2.99px 3.66px 0 rgb(57, 61, 77),
    -3.36px 4.12px 0 rgb(54, 58, 72),
    -3.73px 4.57px 0 rgb(50, 54, 68),
    -4.11px 5.03px 0 rgb(46, 50, 64),
    -4.48px 5.49px 0 rgb(43, 46, 59),
    -4.85px 5.95px 0 rgb(40, 43, 55),
    -5.23px 6.40px 0 rgb(36, 39, 51),
    -5.60px 6.86px 0 rgb(32, 35, 46),
    -5.97px 7.32px 0 rgb(29, 31, 42),
    -6.35px 7.78px 0 rgb(26, 28, 37),
    -6.72px 8.23px 0 rgb(22, 24, 33),
    -15px 18px 40px rgba(10, 12, 28, .42);
}

/* One swipe sorted, art right — right/bottom faces visible, body runs +0.438,+0.202 per px of depth */
.device--tilt-triage {
  transform: perspective(1200px) rotateX(13deg) rotateY(-26deg) rotateZ(7deg);
  box-shadow:
    inset 0 1.5px 1px rgba(255, 255, 255, .26),
    inset 0 -1.5px 1px rgba(0, 0, 0, .45),
    0 0 0 1px rgba(0, 0, 0, .3),
    0.56px 0.26px 0 rgb(82, 87, 108),
    1.12px 0.52px 0 rgb(78, 84, 103),
    1.68px 0.77px 0 rgb(74, 80, 99),
    2.24px 1.03px 0 rgb(71, 76, 94),
    2.80px 1.29px 0 rgb(68, 72, 90),
    3.36px 1.55px 0 rgb(64, 69, 86),
    3.92px 1.81px 0 rgb(60, 65, 81),
    4.48px 2.07px 0 rgb(57, 61, 77),
    5.04px 2.32px 0 rgb(54, 58, 72),
    5.60px 2.58px 0 rgb(50, 54, 68),
    6.16px 2.84px 0 rgb(46, 50, 64),
    6.72px 3.10px 0 rgb(43, 46, 59),
    7.28px 3.36px 0 rgb(40, 43, 55),
    7.84px 3.61px 0 rgb(36, 39, 51),
    8.40px 3.87px 0 rgb(32, 35, 46),
    8.96px 4.13px 0 rgb(29, 31, 42),
    9.52px 4.39px 0 rgb(26, 28, 37),
    10.08px 4.65px 0 rgb(22, 24, 33),
    22px 10px 40px rgba(10, 12, 28, .42);
}

/* Notebooks for the rest, art left — left/bottom faces visible, body runs -0.242,+0.425 per px of depth */
.device--tilt-write {
  transform: perspective(1200px) rotateX(26deg) rotateY(14deg) rotateZ(-19deg);
  box-shadow:
    inset 0 1.5px 1px rgba(255, 255, 255, .26),
    inset 0 -1.5px 1px rgba(0, 0, 0, .45),
    0 0 0 1px rgba(0, 0, 0, .3),
    -0.31px 0.54px 0 rgb(82, 87, 108),
    -0.62px 1.09px 0 rgb(78, 84, 103),
    -0.93px 1.63px 0 rgb(74, 80, 99),
    -1.24px 2.17px 0 rgb(71, 76, 94),
    -1.54px 2.72px 0 rgb(68, 72, 90),
    -1.85px 3.26px 0 rgb(64, 69, 86),
    -2.16px 3.80px 0 rgb(60, 65, 81),
    -2.47px 4.34px 0 rgb(57, 61, 77),
    -2.78px 4.89px 0 rgb(54, 58, 72),
    -3.09px 5.43px 0 rgb(50, 54, 68),
    -3.40px 5.97px 0 rgb(46, 50, 64),
    -3.71px 6.52px 0 rgb(43, 46, 59),
    -4.02px 7.06px 0 rgb(40, 43, 55),
    -4.32px 7.60px 0 rgb(36, 39, 51),
    -4.63px 8.15px 0 rgb(32, 35, 46),
    -4.94px 8.69px 0 rgb(29, 31, 42),
    -5.25px 9.23px 0 rgb(26, 28, 37),
    -5.56px 9.78px 0 rgb(22, 24, 33),
    -12px 22px 40px rgba(10, 12, 28, .42);
}

/* One Task at a time, art right — right/bottom faces visible, body runs +0.259,+0.184 per px of depth */
.device--tilt-focus {
  transform: perspective(1200px) rotateX(11deg) rotateY(-15deg) rotateZ(5deg);
  box-shadow:
    inset 0 1.5px 1px rgba(255, 255, 255, .26),
    inset 0 -1.5px 1px rgba(0, 0, 0, .45),
    0 0 0 1px rgba(0, 0, 0, .3),
    0.33px 0.24px 0 rgb(82, 87, 108),
    0.66px 0.47px 0 rgb(78, 84, 103),
    0.99px 0.71px 0 rgb(74, 80, 99),
    1.32px 0.94px 0 rgb(71, 76, 94),
    1.65px 1.18px 0 rgb(68, 72, 90),
    1.98px 1.41px 0 rgb(64, 69, 86),
    2.31px 1.65px 0 rgb(60, 65, 81),
    2.64px 1.88px 0 rgb(57, 61, 77),
    2.97px 2.12px 0 rgb(54, 58, 72),
    3.30px 2.35px 0 rgb(50, 54, 68),
    3.63px 2.59px 0 rgb(46, 50, 64),
    3.97px 2.82px 0 rgb(43, 46, 59),
    4.30px 3.06px 0 rgb(40, 43, 55),
    4.63px 3.29px 0 rgb(36, 39, 51),
    4.96px 3.53px 0 rgb(32, 35, 46),
    5.29px 3.77px 0 rgb(29, 31, 42),
    5.62px 4.00px 0 rgb(26, 28, 37),
    5.95px 4.24px 0 rgb(22, 24, 33),
    13px 9px 40px rgba(10, 12, 28, .42);
}

/* Flat, and without a body pointing off to one side of a device that is no
   longer turned. */
@media (prefers-reduced-motion: reduce) {
  .device--tilt-today, .device--tilt-organise, .device--tilt-triage, .device--tilt-write, .device--tilt-focus {
    transform: none;
    box-shadow:
      inset 0 1.5px 1px rgba(255, 255, 255, .26),
      inset 0 -1.5px 1px rgba(0, 0, 0, .45),
      0 0 0 1px rgba(0, 0, 0, .3),
      var(--shadow-device);
  }
}

/* -------------------------------------------------- feature bands */
.feature {
  display: grid; gap: clamp(2rem, 5vw, 4rem); align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 860px) {
  .feature { grid-template-columns: 1fr 1fr; }
  .feature--flip .feature__art { order: -1; }

  /* Deliberately no sticky on the art. It was tried and taken out: holding the
     phone while the row scrolled read as scroll-jacking. The devices move on
     the way in and then travel with the page like everything else. */
}

/* Comes up into place as it arrives, a little after the copy.
   translate rather than transform, because transform already holds the 3D
   angle and this has to compose with it rather than replace it. */
.feature [class*='device--tilt'] {
  opacity: 0;
  translate: 0 48px;
  transition: opacity .75s ease .1s, translate .75s cubic-bezier(.2, .7, .3, 1) .1s;
}
/* Keyed off the device's OWN visibility, not its row's. The rows are not the
   same height: three of them carry prop clusters under the copy, and a taller
   row crosses the reveal threshold at a different moment relative to the phone
   inside it. That is why Triage and Write looked like they were not animating
   while the others were, despite identical CSS: the row had already tripped
   and the fade was over before the phone was worth looking at. */
.feature [class*='device--tilt'].is-shown {
  opacity: 1;
  translate: 0 0;
}
@media (prefers-reduced-motion: reduce) {
  .feature [class*='device--tilt'] { opacity: 1; translate: 0 0; transition: none; }
}
/* The tilted devices are rotated in 3D, so the box they actually occupy is
   wider than the image: on a phone that box reaches past the viewport edge and
   is the last source of horizontal scroll on the page. Clip, not hidden, so
   the vertical axis stays visible and no new scroller is created; the clip
   margin is the wrap's own gutter, so the cut lands exactly on the viewport
   edge and no shadow is trimmed before it needs to be. */
@media (max-width: 859px) {
  .feature { overflow-x: clip; overflow-clip-margin: 16px; }
}

.feature + .feature { margin-top: var(--band); }
.feature__text { max-width: 46ch; }
.feature__text h3 { margin-bottom: .45em; }
/* ---------------------------------------- Capture: the Add Things showcase
   Mirrors the in-app "Add Things" sheet: a grouped-background box holding a
   grid of cardSurface cells, each an 80pt asset over a 15pt medium label.
   Labels are the exact `TaskSection.sheetLabel` strings from TaskDetailView. */
.capture {
  position: relative;
  max-width: 860px; margin-inline: auto;
  /* Extra over the standard band: the reel is a full-bleed visual and needs
     more air under it than a block of copy would. The reel carries 48px of its
     own below the phones for the shadow, so this is trimmed to suit. */
  padding-bottom: calc(var(--band) * 1.1);
  text-align: center;
}
.capture h3 { font-size: clamp(1.9rem, 4.6vw, 2.9rem); font-weight: 600; }   /* hero scale, kept above the feature h3s */
/* Full-bleed rule closing the section, matching the band--edge line under the
   hero. Sits on a pseudo-element because .capture is width-constrained.
   Safe against a scrollbar because body sets overflow-x: hidden. */
.capture::after {
  content: ''; position: absolute; left: 50%; bottom: 0;
  width: 100vw; transform: translateX(-50%);
  border-bottom: 1px solid var(--divider);
}
.capture + .feature { margin-top: var(--band); }
.capture__lede { max-width: 48ch; margin-inline: auto; }

/* Sky gradient. Starts hard blue where the hero ends and dissolves into
   whatever is behind it, reaching nothing about two thirds down, so it is still
   faintly there behind the top of the Add Things card and gone by its bottom.

   It fades to rgba(...,0) of its own colour rather than to `transparent`: the
   keyword interpolates through transparent BLACK, which greys the middle of the
   fade. Fading to a zero-alpha blue also means it works on any ground, so dark
   mode needs no second definition. */
/* Deliberately not a .reveal. The reveal sets opacity 0 and translateY(18px)
   on whatever carries it, and here that would be the sky itself, so the
   gradient would fade and slide in rather than continuing the band above it.
   The bridge only reads as one move if this end of it is already there. */
.capture__sky { position: relative; }
.capture__sky::before {
  content: '';
  /* Up through the band's own top padding and its 1px edge, so the blue meets
     the section above with no strip of band background showing between. */
  position: absolute; top: calc(var(--band) * -1 - 1px); bottom: 0; left: 50%;
  width: 100vw; transform: translateX(-50%);
  background: linear-gradient(to bottom,
    var(--sky-blue) 0%,
    rgba(22, 150, 241, 0) 66%);
}
/* Lift the content above the absolutely-positioned gradient. */
.capture__sky > * { position: relative; }

.capture__intro { padding-block: clamp(2.5rem, 6vw, 4.5rem) clamp(2.25rem, 5vw, 3.5rem); }
.capture__intro h3,
.capture__intro .lede { text-shadow: 0 1px 3px rgba(6, 58, 110, .20); }
.capture__intro h3 { color: #fff; }
.capture__intro .section-label { color: rgba(255, 255, 255, .88); }
.capture__intro .lede { color: rgba(255, 255, 255, .94); }


/* "PRO feature" pill. Sits above the Add Things card to say plainly that
   colour is part of Pro, rather than letting the free copy imply otherwise. */
.pro-note {
  display: flex; align-items: center; gap: .75rem; text-align: left;
  max-width: 420px; margin: clamp(1.6rem, 3.5vw, 2.4rem) auto 0;
  padding: .5rem .65rem .5rem .55rem;
  background: var(--card-bg);
  border: 1px solid var(--card-stroke-solid);
  border-radius: 999px;
  box-shadow: var(--shadow-card);
  /* It is an <a> now, jumping to the Pro band. The global a:hover underline has
     to go: this is a card, and underlining the whole pill's text on hover
     looks like a mistake. The lift and the shadow carry the affordance
     instead, matching the Pro cards it links to. */
  text-decoration: none;
  transition: transform .18s cubic-bezier(.2, .7, .3, 1), box-shadow .18s ease;
}
.pro-note:hover,
.pro-note:focus-visible {
  text-decoration: none;
  transform: translateY(-2px);
  box-shadow: var(--shadow-card-hover);
}
.pro-note:active { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .pro-note { transition: box-shadow .18s ease; }
  .pro-note:hover, .pro-note:focus-visible { transform: none; }
}
.pro-note__icon {
  width: 40px; height: 40px; flex: none; border-radius: 13px;
  background: var(--card-bg-2);
  display: grid; place-items: center;
}
.pro-note__icon img { width: 23px; height: auto; }
.pro-note__text { display: flex; flex-direction: column; min-width: 0; margin-right: auto; }
.pro-note__text b { font-size: .9375rem; font-weight: 700; color: var(--text-1); line-height: 1.25; }
.pro-note__text i { font-style: normal; font-size: .8125rem; color: var(--text-2); line-height: 1.3; }
.pro-note__badge { width: 61px; height: 24px; flex: none; }

/* The same badge inline in running copy, wherever the free text promises
   something that is actually behind Pro. */
/* The global img rule is display:block; height:auto, which would put this on a
   line of its own and ignore the height. Both have to be overridden here. */
.pro-tag {
  display: inline-block;
  width: 44px; height: 17px;
  vertical-align: -2px; margin-left: .38em;
}
/* Keeps the badge welded to the words it qualifies, so it can never strand on
   a line of its own and leave the following comma orphaned. */
.pro-tagged { white-space: nowrap; }

.addthings {
  margin: clamp(2rem, 4vw, 3rem) auto 0;
  max-width: 720px;
  background: var(--card-bg-2);
  border: 1px solid var(--divider);
  border-radius: var(--r-large);
  padding: clamp(1.1rem, 3vw, 1.75rem);
  text-align: left;
}
.addthings__label { margin-bottom: .9rem; padding-inline: .5rem; }

.addthings__grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: .75rem;
  grid-template-columns: repeat(2, 1fr);   /* the app's own 2-column sheet */
}
@media (min-width: 640px) { .addthings__grid { grid-template-columns: repeat(5, 1fr); } }

.addthings__grid li {
  display: flex; flex-direction: column; align-items: center; gap: .6rem;
  padding: 1.1rem .5rem;
  background: linear-gradient(to bottom, var(--card-grad-a), var(--card-grad-b));
  border: 1px solid var(--card-stroke-solid);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
  text-align: center;
  transition: transform .22s cubic-bezier(.2, .7, .3, 1), box-shadow .22s ease;
}

/* A quiet lift on hover. Gated on a real pointer so a tap on a phone does not
   leave a card stuck in its hover state. Nothing here is a link. */
@media (hover: hover) and (pointer: fine) {
  .addthings__grid li:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-card-hover);
  }
}
.addthings__grid img { width: 56px; height: 56px; }   /* 240px source, crisp to @4x */
.addthings__grid span {
  font-size: .9375rem; font-weight: 500; line-height: 1.2;   /* 15pt medium */
  color: var(--text-1);
}

.capture__device { margin-top: clamp(2.5rem, 5vw, 3.5rem); }

/* -------------------------------------------------- cards */
.card {
  background: linear-gradient(to bottom, var(--card-grad-a), var(--card-grad-b));
  border: 1px solid var(--card-stroke);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
  padding: 1.5rem;
}
.grid-cards {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.grid-tiles {
  display: grid; gap: .75rem;
  grid-template-columns: repeat(auto-fit, minmax(165px, 1fr));
}

/* ---------------------------------------- day / night preview
   One device, two stacked screenshots crossfaded by a segmented control that
   mirrors the app's own. Starts on whichever the visitor's OS is set to, and
   keeps following the OS until they pick a side themselves. */
/* -------------------------------------------------- day / night band
   The band paints the sky for whichever theme is being previewed, so the page
   around the phone changes with it. Stops are sampled from Craig's references:
   each holds its top colour for the first 40% and turns over the rest, which
   is what keeps it reading as a sky rather than a two-colour wash.

   These are fixed per mode, not per page theme: the band is showing you the
   app's Light theme even when you are browsing in dark, so its own text has to
   be pinned to the sky it sits on rather than to the page's tokens. */
.daynight-band {
  transition: background 550ms ease, color 550ms ease;
}
.daynight-band[data-mode='light'] {
  --dn-ink: #1C1C1C; --dn-ink-2: #4A4E63; --dn-label: #6B6E8A;
  background: linear-gradient(to bottom,
    #E5ECF5 0%, #E5ECF5 40%, #DDEFFC 62%, #E5F0F5 75%, #F7F1E3 88%, #FFF2DC 100%);
}
.daynight-band[data-mode='dark'] {
  --dn-ink: #F0F0F5; --dn-ink-2: rgba(240, 240, 245, .82); --dn-label: #9DA0C4;
  background: linear-gradient(to bottom,
    #343757 0%, #343757 40%, #392550 62%, #3C1949 75%, #410A3C 88%, #440336 100%);
}
.daynight-band[data-mode='black'] {
  --dn-ink: #F2F2F2; --dn-ink-2: rgba(242, 242, 242, .78); --dn-label: #8E8E93;
  background: linear-gradient(to bottom,
    #000000 0%, #000000 40%, #0B061D 62%, #0D0A2D 75%, #061339 88%, #02173F 100%);
}
.daynight-band h2 { color: var(--dn-ink); }
.daynight-band .lede { color: var(--dn-ink-2); }
.daynight-band .section-label { color: var(--dn-label); }

/* The control keeps one appearance across all three skies, as in the
   references, so it never has to fight the background it is sitting on. */
.daynight-band .seg { background: rgba(233, 235, 243, .92); }
.daynight-band .seg__btn { color: #1C1C1C; }
.daynight-band .seg__btn[aria-pressed='true'] { background: #fff; }

.seg {
  display: none;                       /* JS drives it, so hide it without JS */
  margin: clamp(1.75rem, 4vw, 2.5rem) auto 0;
  padding: 4px;
  gap: 4px;
  background: var(--seg-track);
  border-radius: 999px;
}
html.js .seg { display: inline-flex; }

.seg__btn {
  appearance: none; border: 0; cursor: pointer;
  font: inherit; font-size: .9375rem; font-weight: 600;
  color: var(--text-1); opacity: .6;
  background: transparent;
  padding: .5rem 1.4rem;
  border-radius: 999px;
  transition: background .2s ease, opacity .2s ease, box-shadow .2s ease;
}
.seg__btn:hover { opacity: .85; }
.seg__btn[aria-pressed='true'] {
  background: var(--card-bg);
  opacity: 1;
  box-shadow: 0 2px 6px rgba(0, 0, 0, .12);
}

.daynight { margin-top: clamp(1.75rem, 4vw, 2.5rem); }
.daynight__stack {
  position: relative;
  aspect-ratio: 620 / 1348;            /* the screenshots' own proportions */
  border-radius: 37px;
  overflow: hidden;
}
.daynight__shot {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border-radius: 0;
  opacity: 0;
  transition: opacity .4s ease;
}
.daynight__shot.is-active { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .daynight__shot { transition: none; } }

/* ---------------------------------------- the unfold, scrubbed on scroll
   A canvas over a still. The still is the last frame, so with JS off, with
   reduced motion, or before the sequence has downloaded, the section still
   shows the thing it is describing: the device open flat. The canvas fades in
   over it only once every frame is decoded, and it is painted before the fade
   starts, so the swap never flashes the folded frame. */
/* The track is the scroll budget and the pin is the stage. Holding the device
   still at both ends needs more scroll than the section's own height, so the
   stage sticks to the middle of the viewport and the animation plays out
   against the track's extra height instead. It also means the device is fully
   on screen for the whole sequence rather than part-way in. */
.unfold-track {
  position: relative;
  height: calc(100vh + var(--unfold-scroll, 130vh));
}
.unfold-pin {
  position: sticky; top: 0;
  height: 100vh;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: clamp(1rem, 2.5vw, 1.5rem);
}
/* The Pro caveats, wherever a free-looking feature is actually gated. One type
   treatment so the two of them read as the same kind of note rather than two
   different asides. */
.pro-caveat {
  /* Sits at the foot of the block, after the art, with real space above it:
     it is a disclaimer rather than a third paragraph, and running it tight to
     the copy made it read as part of the pitch. */
  margin: 2.4rem 0 0;
  font-size: .875rem; line-height: 1.5; color: var(--text-2);
}
.pro-caveat a { color: var(--blue); text-decoration: underline; }
.pro-caveat a:hover { text-decoration: underline; }

/* Rides inside the pin rather than sitting after the track, so the caveat is
   next to the screen it is caveating the whole way through. */
.unfold__note {
  margin: 0; max-width: 52ch; text-align: center;
  font-size: .875rem; line-height: 1.5; color: var(--text-2);
}
.unfold {
  position: relative;
  width: min(100%, 860px);
  margin-inline: auto;
}
/* Without the scrub there is nothing to scroll through, so collapse the track
   rather than leaving two screens of empty band behind a single still. */
html:not(.js) .unfold-track,
html:not(.js) .unfold-pin { height: auto; position: static; }
html:not(.js) .unfold { margin-block: clamp(2.5rem, 5vw, 3.5rem) 0; }
@media (prefers-reduced-motion: reduce) {
  .unfold-track, .unfold-pin { height: auto; position: static; }
  .unfold { margin-block: clamp(2.5rem, 5vw, 3.5rem) 0; }
}
.unfold__still,
.unfold__canvas {
  display: block; width: 100%; height: auto;
  aspect-ratio: 1000 / 799;
}
.unfold__canvas {
  position: absolute; inset: 0;
  opacity: 0; transition: opacity .35s ease;
}
.unfold__still { transition: opacity .35s ease; }
.unfold.is-scrubbing .unfold__canvas { opacity: 1; }
.unfold.is-scrubbing .unfold__still { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .unfold__canvas, .unfold__still { transition: none; }
}

/* -------------------------------------------------- Pro band */
.pro {
  background: linear-gradient(155deg, #2B2450 0%, var(--purple-dark) 45%, #2E5FD0 100%);
  color: #fff;
}
.pro h2, .pro h3 { color: #fff; }
.pro .section-label { color: rgba(255, 255, 255, .7); }
.pro__grid {
  display: grid; gap: 1rem; margin-top: 2.5rem;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}




.pro__card p { color: rgba(255, 255, 255, .8); font-size: .95rem; margin: 0; }
.pro__note { color: rgba(255, 255, 255, .82); }

/* -------------------------------------------------- modal motion
   Both dialogs rise 15px as they fade in and settle back down as they go.

   A <dialog> leaves the top layer the instant close() runs, so an exit
   animation needs the discrete properties transitioned too: overlay and
   display. Without allow-discrete the element simply vanishes and only the
   opening half would ever be seen. @starting-style supplies the state to
   animate from, since there is no previous state on first paint.

   Browsers without allow-discrete ignore all of this and open and close
   instantly, which is a fair degradation. */
.at, .ps {
  opacity: 0;
  transform: translateY(15px);
  transition:
    opacity .26s ease,
    transform .26s cubic-bezier(.2, .7, .3, 1),
    overlay .26s ease allow-discrete,
    display .26s ease allow-discrete;
}
.at[open], .ps[open] { opacity: 1; transform: translateY(0); }
@starting-style {
  .at[open], .ps[open] { opacity: 0; transform: translateY(15px); }
}

.at::backdrop, .ps::backdrop {
  opacity: 0;
  transition: opacity .26s ease, overlay .26s ease allow-discrete, display .26s ease allow-discrete;
}
.at[open]::backdrop, .ps[open]::backdrop { opacity: 1; }
@starting-style {
  .at[open]::backdrop, .ps[open]::backdrop { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .at, .ps { transition: opacity .01s; transform: none; }
  .at[open], .ps[open] { transform: none; }
}

/* -------------------------------------------------- Pro sheets
   Each Pro card opens an explainer built to the app's own pattern: hero, badge,
   title, subtitle, then benefit rows. Six panels live in one dialog and are
   swapped rather than rebuilt, so Prev and Next cycle without reopening. */
.ps {
  width: min(550px, calc(100vw - 28px));
  max-height: min(800px, calc(100vh - 40px));
  padding: 0; border: 0; overflow: visible;
  border-radius: 26px;
  background: var(--card-bg);
  color: var(--text-1);
  box-shadow: 0 30px 70px rgba(19, 19, 22, .32);
}
.ps::backdrop { background: rgba(19, 19, 22, .42); backdrop-filter: blur(3px); }
.ps__scroll {
  /* 800 less the nav strip, so the sheet never grows past its ceiling. JS sets
     --ps-h to the TALLEST panel's natural height, capped by the same ceiling,
     so every sheet is the same height as you cycle rather than the dialog
     jumping between them. */
  height: var(--ps-h, auto);
  max-height: min(736px, calc(100vh - 104px));
  overflow-y: auto; overscroll-behavior: contain;
  border-radius: 26px 26px 0 0;
}
/* Scoped off [hidden], or display: flex would override it and every panel
   would show at once. */
.ps__panel:not([hidden]) {
  display: flex; flex-direction: column;
  min-height: 100%;
}
.ps__close {
  position: absolute; top: 14px; right: 14px; z-index: 3;
  width: 30px; height: 30px; padding: 0; border: 0; border-radius: 50%;
  background: rgba(255, 255, 255, .9); color: #4A4E63; cursor: pointer;
  display: grid; place-items: center;
  box-shadow: 0 2px 8px rgba(19, 19, 22, .2);
}
.ps__close svg { width: 13px; height: 13px; }
.ps__close:hover { background: #fff; }

/* Each sheet carries its own sky, and that sky dissolves into the modal's own
   background near its foot. Craig's references fade to solid by about 48% of
   the hero's height. The art itself sits ABOVE the fade, crisp and unfaded, so
   only the backdrop dissolves behind it.
   The fade goes to --card-bg, not white, so it resolves in dark mode too. */
.ps__hero {
  position: relative;
  /* 240px is the floor, not the height. When a sheet's content is shorter than
     the tallest one, the spare goes HERE rather than padding the rows out or
     stranding the footer: the art is what the reader came to look at. */
  flex: 1 1 auto;
  min-height: 240px;
  display: grid; place-items: center;
  /* The top padding is deliberate: several pieces of art carry almost no
     transparent margin of their own, so centring alone left them reading as
     clipped against the sheet's top edge. */
  padding: 16px 1.5rem 0;
  border-radius: 26px 26px 0 0;
  background: var(--hero-bg, linear-gradient(to bottom, #CBF0F9 0%, #E0F6FB 35%, #FBFEFF 80%));
  background-size: cover;
  background-position: center 40%;
}
/* Constrain by max-width AND max-height with auto dimensions, so each piece of
   art scales to fit the fixed hero without being squashed. Setting width and
   letting height follow would push the taller pieces straight out of it. */
.ps__hero img {
  position: relative; z-index: 2;      /* above the fade, never washed by it */
  width: auto; height: auto;
  max-width: var(--hero-w, 52%);
  /* A share of the hero rather than a fixed 184px, so a hero that grew gets
     bigger art instead of the same art floating in more sky. 82% of the 224px
     content box at the 240px floor is the 184px this replaces. */
  max-height: 82%;
}
.ps__hero::after {
  content: ''; position: absolute; inset: 0; z-index: 1;
  border-radius: inherit;
  background: linear-gradient(to bottom, transparent 46%, var(--card-bg) 97%);
}

/* Per-sheet sky and art size, both off the references. */
.ps__panel[data-sheet='folders'] .ps__hero {
  --hero-bg: url('../img/art/hero-beach.webp'); --hero-w: 70%;
}
.ps__panel[data-sheet='notes'] .ps__hero {
  --hero-bg: url('../img/art/hero-beach.webp'); --hero-w: 70%;
}
/* A single Sage notebook on the forest, rather than a photo of a notebook on a
   photo of a forest sitting on the forest. The background is untouched.
   34% lets the 184px height cap bind rather than the width, so the notebook
   renders about 148px wide, in line with the other single-object sheets. */
.ps__panel[data-sheet='backdrop'] .ps__hero {
  --hero-bg: url('../img/art/hero-forest.webp'); --hero-w: 34%;
}
.ps__panel[data-sheet='icons'] .ps__hero {
  --hero-bg: linear-gradient(100deg, #F1A2AB 0%, #F3C892 52%, #F2D791 100%);
  --hero-w: 70%;
}
.ps__panel[data-sheet='secrets'] .ps__hero {
  --hero-w: 40%;
}
/* Pink rather than the shared blue fallback, to sit with the heart. Set on
   the panel rather than by editing .ps__hero's fallback, which Secrets also
   falls back to and whose navy notebook wants the blue. */
.ps__panel[data-sheet='support'] .ps__hero {
  --hero-bg: linear-gradient(180deg, #F8CBD5 0%, #FBE0E9 35%, #FBFEFF 80%);
  --hero-w: 28%;
}
.ps__panel[data-sheet='stats'] .ps__hero {
  --hero-bg: linear-gradient(100deg, #C588E9 0%, #DFE1EA 48%, #75C7F2 100%);
  --hero-w: 42%;
}

/* The badge is tucked .2rem up into the hero, so it has to be lifted above the
   hero's fade. .ps__hero is position: relative and its ::after sits at z-index
   1, painting solid --card-bg at the foot, which sliced the top 3px clean off
   a static badge. */
.ps__badge {
  display: block; width: 61px; height: 24px; margin: -.2rem auto 0;
  position: relative; z-index: 3;
}
.ps__title {
  margin: .7rem 0 0; text-align: center;
  font-size: 1.65rem; font-weight: 600; letter-spacing: -.015em; color: var(--text-1);
}
.ps__sub {
  margin: .5rem auto 0; max-width: 30ch; text-align: center;
  font-size: .95rem; line-height: 1.4; color: var(--section-header);
}

.ps__rows { list-style: none; margin: 1.3rem 0 0; padding: 0 2rem 1.4rem; display: grid; gap: .55rem; }
.ps__rows li {
  display: flex; align-items: flex-start; gap: .8rem;
  padding: .85rem .9rem;
  background: var(--card-bg-2);
  border-radius: 16px;
}
.ps__icon { width: 44px; height: 44px; flex: none; display: block; }
.ps__icon img { width: 100%; height: 100%; border-radius: 13px; }
.ps__rowtext { display: flex; flex-direction: column; gap: .12rem; min-width: 0; }
/* 15pt semibold over 13pt regular, as the app sets these rows */
.ps__rowtext b { font-size: .9375rem; font-weight: 600; color: var(--text-1); line-height: 1.3; }
.ps__rowtext i { font-style: normal; font-size: .8125rem; color: var(--text-2); line-height: 1.35; }

.ps__nav {
  display: flex; justify-content: space-between; align-items: center;
  padding: .75rem 1rem calc(.75rem + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--divider);
}
.ps__nav button {
  display: inline-flex; align-items: center; gap: .4rem;
  border: 0; background: none; cursor: pointer;
  font: inherit; font-size: .9375rem; font-weight: 600; color: var(--blue);
  padding: .45rem .6rem; border-radius: 10px;
}
.ps__nav button svg { width: 7px; height: 12px; }
.ps__nav button:hover { background: color-mix(in srgb, var(--blue) 10%, transparent); }
.ps__nav button:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

/* ---- the cards that open them
   Sampled off Craig's design: the fill is about 11% white over the band and the
   very top edge about 30%, which is the glare. So it is a soft gradient over
   the top of the card plus a crisp inset rim, rather than one flat tint. */
.pro__card {
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  width: 100%; padding: 1.4rem 1.25rem 3.1rem;
  border-radius: 32px;
  /* All four numbers below are sampled off Craig's reference, scanning inward
     across the card's edge and down its face. The edge reads band 61 ->
     keyline 28 -> rim 142 -> fill 105, so there are three lines, not one: a
     dark keyline OUTSIDE a bright rim. The keyline was the piece missing
     entirely, and it is what gives the card its cut-out edge. */
  border: 1px solid rgba(255, 255, 255, .28);
  /* One ramp down the whole face, not a glare over the top fifth. Measured
     white-over-band runs .29 at a tenth down to .14 at the foot, a continuous
     fall the old two-layer fill could not describe. */
  background: linear-gradient(to bottom,
    rgba(255, 255, 255, .30) 0%,
    rgba(255, 255, 255, .22) 35%,
    rgba(255, 255, 255, .18) 65%,
    rgba(255, 255, 255, .14) 100%);
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, .5),          /* the keyline, measured at 52% black */
    0 5px 14px rgba(10, 12, 28, .18),     /* a resting cast, Craig's addition */
    inset 0 1px 0 rgba(255, 255, 255, .34);
  /* Deliberately NO backdrop-filter. It flickered on hover, and it was doing
     nothing to earn that: the only thing behind these cards is the band's own
     linear-gradient, and blurring a linear ramp returns the same linear ramp.
     So it bought no visible blur while forcing the browser to re-sample and
     re-composite the backdrop every frame of the lift, which is what the
     shimmer was. The glare, the inner rim and the cast all stay. */
  text-align: center; cursor: pointer; font: inherit; color: inherit;
  overflow: hidden;
  transition: transform .2s ease, box-shadow .2s ease;
}
/* Hover lifts and casts, and leaves the fill alone: the glare is what gives the
   card its shape, and brightening the whole surface flattens it. The inset rim
   is repeated because a second box-shadow would otherwise replace it. */
/* Hover keeps its geometry exactly, 14px down and 30px of blur. Only the alpha
   goes up, from .12 to .26, because the card now casts .18 at rest and a hover
   lighter than the resting state reads as the card sinking rather than lifting. */
.pro__card:hover {
  transform: translateY(-3px);
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, .5),
    0 14px 30px rgba(10, 12, 28, .26),
    inset 0 1px 0 rgba(255, 255, 255, .34);
}
.pro__card:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }

.pro__media { display: block; width: 100%; }
.pro__card-img {
  width: 100%; aspect-ratio: 16 / 11; object-fit: contain; object-position: center;
  margin-bottom: 1rem;
}
/* Bleed media runs to the card's own edges, under the rounded corner. */
.pro__card-img--bleed {
  width: calc(100% + 2.5rem); max-width: none;
  margin: -1.4rem -1.25rem 1rem;
  aspect-ratio: 16 / 11; object-fit: cover;
  border-radius: 31px 31px 0 0;   /* one less than the card, to sit inside its 1px border */
}
/* No gap: the title's own line-height carries the space, and .3rem on top of
   it pushed the two lines apart enough to read as separate items rather than
   a title and its subtitle. Scoped to the cards already, .pro__copy exists
   nowhere else on the site. */
.pro__copy { display: flex; flex-direction: column; }
.pro__copy b { font-size: 1.3rem; font-weight: 600; color: #fff; letter-spacing: -.01em; }
.pro__copy i { font-style: normal; font-size: .875rem; line-height: 1.4; color: rgba(255, 255, 255, .78); }

/* Inverted, off the reference: a near-solid light disc with a DARK glyph, not
   a translucent disc with a white one. Sampled at rgb(232,230,242) for the
   disc and rgb(70,70,70) for the plus. */
.pro__plus {
  position: absolute; right: 1.15rem; bottom: 1.15rem;
  width: 28px; height: 28px; border-radius: 50%;
  background: rgba(255, 255, 255, .9);
  color: #3F3F46; display: grid; place-items: center;
  transition: background .2s ease;
}
.pro__plus svg { width: 15px; height: 15px; }
.pro__card:hover .pro__plus { background: #fff; }

/* ---- the section header: heading left, the promise beside it */
.pro__head { display: grid; gap: 1rem 3rem; align-items: start; }
@media (min-width: 820px) {
  .pro__head { grid-template-columns: 1fr 1fr; }
  .pro__head .pro__note { margin-top: 2.6rem; }
}
.pro__head h2 { margin: .3rem 0 0; }

@media (max-height: 640px) {
  /* min-height, to match the floor above. The img cap is a percentage now, so
     it follows the shorter hero without needing its own number. */
  .ps__hero { min-height: 190px; }
}

@media (max-width: 460px) {
  .ps__hero { padding-inline: 1.1rem; }
  .ps__title { font-size: 1.45rem; }
  .ps__rows { padding-inline: .75rem; }
}

/* -------------------------------------------------- price / platform */
.platform {
  max-width: 320px; margin-inline: auto; text-align: center;
}
.platform img {
  display: block; width: 120px; height: 120px;
  border-radius: 20%; margin: 0 auto 1rem;
}
.platform__os { color: var(--text-2); font-size: .95rem; }
.badge-soon {
  display: inline-block; padding: .35rem .85rem; border-radius: 999px;
  background: var(--purple); color: #fff;
  font-size: .8125rem; font-weight: 700; letter-spacing: .4px; text-transform: uppercase;
}

/* -------------------------------------------------- signup */
.signup { display: flex; gap: .6rem; flex-wrap: wrap; justify-content: center; margin-top: 1.5rem; }
.signup input[type='email'] {
  flex: 1 1 260px; max-width: 340px;
  padding: .85rem 1.1rem;
  font: inherit; font-size: 1rem;
  color: var(--text-1);
  background: var(--card-bg);
  border: 1px solid var(--divider);
  border-radius: var(--r-button);
}
.signup input::placeholder { color: var(--text-3); }
.signup--onDark input[type='email'] { border-color: rgba(255, 255, 255, .3); }
.form-msg { margin-top: .9rem; font-weight: 600; min-height: 1.4em; }
.form-msg[data-state='error'] { color: var(--coral); }
.form-msg[data-state='ok'] { color: var(--teal); }

/* -------------------------------------------------- footer */
.footer { padding-block: 3rem; border-top: 1px solid var(--divider); }
.footer__row { display: flex; gap: 1.5rem; flex-wrap: wrap; align-items: center; justify-content: space-between; }
.footer__brand { display: flex; align-items: center; gap: .5rem; }
.footer__brand img { width: 24px; height: 24px; border-radius: 26%; }
.footer__brand span { font-weight: 800; font-size: 1.05rem; letter-spacing: -.02em; color: var(--text-1); }

@supports ((-webkit-mask-image: url('')) or (mask-image: url(''))) {
  .footer__brand span {
    display: block;
    width: 106px; height: 20px;
    text-indent: -200vw; overflow: hidden; white-space: nowrap;
    background-color: currentColor;
    -webkit-mask: url('../img/logo/wordmark-white.png') no-repeat center / contain;
    mask: url('../img/logo/wordmark-white.png') no-repeat center / contain;
  }
}
.footer a { color: var(--text-2); font-weight: 600; font-size: .95rem; }
.footer a:hover { color: var(--text-1); }
.footer__legal { color: var(--text-3); font-size: .85rem; margin-top: 1.5rem; }

/* Sits alone at the very end of the page, so it is centred rather than tucked
   into the link row. Same pill language as the nav CTA, at rest weight. */
.to-top {
  display: inline-flex; align-items: center; gap: .45rem;
  margin: 1.75rem auto 0;
  padding: .55rem 1.1rem .55rem .95rem;
  border: 1px solid var(--divider);
  border-radius: 999px;
  background: var(--card-bg);
  color: var(--text-2);
  font-weight: 600; font-size: .9rem;
  box-shadow: 0 1px 2px rgba(14, 16, 30, .05);
  transition: color .18s ease, border-color .18s ease,
              transform .18s cubic-bezier(.2,.7,.3,1), box-shadow .18s ease;
}
.footer .to-top { display: flex; width: max-content; }
.to-top svg { width: 15px; height: 15px; display: block; }
.to-top:hover {
  color: var(--text-1);
  text-decoration: none;
  border-color: color-mix(in srgb, var(--text-1) 22%, transparent);
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(14, 16, 30, .10);
}
.to-top:active { transform: translateY(0) scale(.98); }
@media (prefers-reduced-motion: reduce) {
  .to-top, .to-top:hover, .to-top:active { transition: none; transform: none; }
}

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

/* -------------------------------------------------- config marquee
   A slow edge-to-edge drift of real Task configurations, under the Add Things
   grid. Full-bleed out of .capture's 860px column using the same left/transform
   trick as the rule above it, which body's overflow-x: hidden keeps safe.

   The track only animates once JS has cloned enough copies to fill it, so
   without JS it degrades to a plain scrollable row rather than a loop with a
   visible gap in it. */
.marquee {
  position: relative; left: 50%; transform: translateX(-50%);
  width: 100vw; margin-top: clamp(2rem, 4vw, 3rem);
  /* The clip has to stay, or a track several thousand pixels wide escapes the
     viewport. So the shadow needs room inside it rather than outside. */
  padding-block: 20px 48px;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
          mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
}
.marquee__track { display: flex; width: max-content; }
.marquee__track.is-running { animation: marquee-drift var(--marquee-s, 42s) linear infinite; }
.marquee__item { flex: none; margin: 0 clamp(16px, 2.4vw, 34px) 0 0; }
/* The reel uses the site's own device frame so it matches every other phone on
   the page, rather than three differently-framed exports. */
.device--reel {
  width: clamp(168px, 22vw, 232px);
  margin-inline: 0;
  border-radius: 34px; padding: 7px;
  /* --shadow-device is built for a 300px device and throws 124px below it,
     which the reel's overflow clip cut clean off. Keep the bezel's inset rims,
     scale the cast shadow to the size these actually render at. */
  box-shadow:
    inset 0 1.5px 1px rgba(255, 255, 255, .26),
    inset 0 -1.5px 1px rgba(0, 0, 0, .45),
    0 0 0 1px rgba(0, 0, 0, .3),
    0 3px 8px rgba(14, 16, 30, .12),
    0 12px 28px rgba(14, 16, 30, .18);
}
.device--reel img { border-radius: 28px; }
@keyframes marquee-drift { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* Pause on hover, but only where hovering is a real thing. On a touch screen
   :hover sticks after a tap and would leave it stopped for good. */
@media (hover: hover) {
  .marquee:hover .marquee__track.is-running { animation-play-state: paused; }
}
@media (prefers-reduced-motion: reduce) {
  .marquee { overflow-x: auto; }
  .marquee__track.is-running { animation: none; }
}

/* -------------------------------------------------- QuickStats field
   A field of out-of-focus tiles running off both edges behind the section, so
   the twelve sharp ones read as a window onto something that carries on past
   them. Same column rhythm and row height as the real grid, so the blur sits
   on the same beat rather than looking like unrelated wallpaper.

   The blur is on the container, not the tiles, so neighbours bleed into each
   other instead of staying twelve separate soft rectangles. Sampled off the
   reference: the strongest blob there is only about a quarter of the way from
   the page ground to full colour, hence the low opacity. */
.qs-feature { position: relative; }
.qs-feature > .feature__text,
.qs-feature > .feature__art { position: relative; z-index: 1; }

/* The blur has to be clipped to the section or it bleeds into the bands above
   and below, which is worse the narrower the window gets, because the feature
   stacks and grows taller. The wrapper is exactly the section's height and
   full-bleed wide: overflow hides the overhang and the blur with it, and the
   mask softens what would otherwise be a flat cut across the blobs. */
.qs-fieldwrap {
  position: absolute; top: 0; bottom: 0; left: 50%;
  width: 100vw; transform: translateX(-50%);
  z-index: 0; pointer-events: none;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 9%, #000 91%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 9%, #000 91%, transparent 100%);
}
.qs-field {
  position: absolute; inset: -10% 0;
  display: grid; gap: .6rem;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  grid-auto-rows: 104px;
  filter: blur(34px);
  opacity: .32;
}
.qs-field i { display: block; border-radius: 16px; }
.qs-field .qf--blue    { background: linear-gradient(160deg, #10D3FE, #008DF1); }
.qs-field .qf--teal    { background: linear-gradient(160deg, #00D4B8, #01BABA); }
.qs-field .qf--purple  { background: linear-gradient(160deg, #BD74FF, #9A61ED); }
.qs-field .qf--pink    { background: linear-gradient(160deg, #F371C5, #D05EA8); }
.qs-field .qf--manilla { background: linear-gradient(160deg, #F9E1AA, #EAB578); }
.qs-field .qf--forest  { background: linear-gradient(160deg, #5FB87E, #2F7A4C); }
.qs-field .qf--sage    { background: linear-gradient(160deg, #B7C9A8, #93A982); }
.qs-field .qf--blush   { background: linear-gradient(160deg, #FAD2DA, #F1A9B8); }

/* Dark grounds need less of it, or the blobs turn into glare. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .qs-field { opacity: .20; }
}
:root[data-theme='dark'] .qs-field { opacity: .20; }

@media (prefers-reduced-motion: reduce) { .qs-field { opacity: .18; } }

/* -------------------------------------------------- QuickStats grid
   Six real tiles, read off a QuickStats screen rather than invented: Tasks
   Created, Top Stacks, Showing up, Notebooks, Overall and To do. Three across
   and two down on anything but a phone, which is how they sit in the app.
   Every colour here was sampled from that screen and then matched back to a
   FolderColor case, so the tokens are the app's own rather than near misses:
   the Notebooks tile is blurple (Lavender), not purple. */
.qs-grid {
  display: grid; gap: .6rem;
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 560px) { .qs-grid { grid-template-columns: repeat(3, 1fr); } }

.qs {
  display: flex; flex-direction: column; gap: .18rem;
  padding: .7rem .75rem .8rem;
  border-radius: 16px;
  min-height: 138px;        /* squarer than the old tiles: these carry rows and a gauge */
  text-align: left;
  overflow: hidden;
}

/* label and its badge share the top line */
.qs__top { display: flex; align-items: flex-start; justify-content: space-between; gap: .4rem; }
.qs__icon {
  flex: none; display: grid; place-items: center;
  width: 18px; height: 18px; border-radius: 6px;
  background: var(--blue); color: #fff;
}
.qs__icon svg { width: 11px; height: 11px; display: block; }
.qs__icon--grey { background: color-mix(in srgb, var(--text-2) 22%, transparent); color: var(--text-1); }
/* on a gradient tile the badge inverts: white chip, tile-coloured mark */
.qs__icon--onlight { background: rgba(255, 255, 255, .92); color: #9A61ED; }

/* Top Stacks: name on the left, count pill on the right, three deep */
.qs__rows { display: flex; flex-direction: column; gap: .22rem; margin-top: .4rem; }
.qs__row { display: flex; align-items: center; gap: .35rem; font-size: .75rem; font-weight: 600; }
.qs__row-emoji { flex: none; font-size: .8rem; line-height: 1; }
.qs__row-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qs__row-n {
  flex: none; min-width: 18px; padding: .05rem .3rem;
  border-radius: 999px; background: var(--blue); color: #fff;
  font-size: .6875rem; text-align: center;
}

/* Showing up: the count and its wording share a line, as they do in the app */
.qs__val--inline { display: flex; align-items: baseline; gap: .35rem; }
.qs__val--inline em {
  font-style: normal; font-size: .6875rem; font-weight: 600;
  line-height: 1.25; color: var(--text-2);
}
.qs__gauge { display: block; width: 72px; height: 40px; margin: auto auto -.15rem; }

/* the small aside under a figure, e.g. Nice work. */
.qs__note { font-size: .6875rem; font-weight: 600; opacity: .78; margin-top: .15rem; }
.qs__label { font-size: .6875rem; font-weight: 600; letter-spacing: .3px; opacity: .78; }
.qs__val { font-size: 1.3rem; font-weight: 600; line-height: 1.1; margin-top: .1rem; letter-spacing: -.01em; }
.qs__val--quote { font-size: .8125rem; font-weight: 600; line-height: 1.3; }
/* Sits straight under its figure, the way the app groups them. What goes to
   the bottom of a tile is the artwork (the day run, the gauge), not the words,
   and those claim the slack with their own auto margin. */
.qs__sub { font-size: .6875rem; line-height: 1.3; opacity: .72; }

/* plain card, takes the page's own surface */
.qs--flat {
  background: var(--card-bg);
  border: 1px solid var(--card-stroke-solid);
  box-shadow: var(--shadow-card);
  color: var(--text-1);
}
.qs--flat .qs__label { color: var(--section-header); opacity: 1; }
.qs--flat .qs__sub { color: var(--text-2); opacity: 1; }

/* gradient tiles, straight off FolderColor in Theme.swift */
.qs--blue,
.qs--purple,
.qs--blurple,
.qs--teal,
.qs--pink,
.qs--manilla,
.qs--forest { color: #fff; }
.qs--blue    { background: linear-gradient(160deg, #10D3FE, #008DF1); }
.qs--purple  { background: linear-gradient(160deg, #BD74FF, #9A61ED); }
/* FolderColor.blurple, shown to customers as Lavender */
.qs--blurple { background: linear-gradient(160deg, #9C8EFF, #675AE2); }
.qs--teal    { background: linear-gradient(160deg, #00D4B8, #01BABA); }
.qs--pink    { background: linear-gradient(160deg, #F371C5, #D05EA8); }
.qs--forest  { background: linear-gradient(160deg, #5FB87E, #2F7A4C); }
/* Manilla is a pale gradient, so it keeps dark ink rather than white. */
.qs--manilla { background: linear-gradient(160deg, #F9E1AA, #EAB578); color: #4A3410; }

/* Tasks Created carries a run of days: a dot for each quiet one, a bar for the
   two that were not. Same shape the app draws under that figure. */
.qs__spark {
  display: flex; align-items: flex-end; gap: 3px;
  height: 18px; margin-top: auto;
}
.qs__spark i {
  flex: none; width: 4px; height: 4px; border-radius: 50%;
  background: rgba(255, 255, 255, .5);
}
.qs__spark b { flex: none; width: 5px; height: 11px; border-radius: 2px; background: rgba(255, 255, 255, .8); }
.qs__spark b.is-tall { height: 17px; }

/* the little bar chart on the Work tile */
.qs__bars { display: flex; align-items: flex-end; gap: 2px; height: 16px; margin-top: .4rem; }
.qs__bars i {
  flex: 1; background: rgba(255, 255, 255, .55); border-radius: 1px;
  height: 35%;
}
.qs__bars i:nth-child(2) { height: 55%; }
.qs__bars i:nth-child(3) { height: 45%; }
.qs__bars i:nth-child(4) { height: 75%; }
.qs__bars i:nth-child(5) { height: 60%; }
.qs__bars i:nth-child(6) { height: 90%; }
.qs__bars i:nth-child(7) { height: 50%; }
.qs__bars i:nth-child(8) { height: 70%; }
.qs__bars i:nth-child(9) { height: 100%; }

/* the Pro badge standing in for a section label */
.section-label--pro img { display: inline-block; width: 54px; height: 21px; }

/* -------------------------------------------------- section props
   Small recreations of the app's own furniture sitting above three of the
   feature headings, to give each one something to look at before the copy.

   aria-hidden because they are scenery, not content: the words inside them are
   set dressing, and a screen reader announcing "Finish the App Store shots" as
   page copy would be nonsense. They also reuse the feature's own .reveal, and
   stagger off its .is-in, so the fade needs no extra JS. */
.props { display: flex; gap: .7rem; margin-top: 1.6rem; }

html.js .feature .prop {
  opacity: 0; transform: translateY(14px);
  transition: opacity .55s ease, transform .55s ease;
  transition-delay: var(--d, 0s);
}
html.js .feature.is-in .prop { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  html.js .feature .prop { opacity: 1; transform: none; transition: none; }
}

/* ---- Task cards */
.props--tasks { max-width: 430px; }
.tcard {
  position: relative; flex: 1 1 0; min-width: 0;
  background: var(--card-bg);
  border: 1px solid var(--card-stroke-solid);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
  padding: .85rem .9rem 1rem;
}
.tcard__top { display: flex; align-items: flex-start; justify-content: space-between; gap: .5rem; margin-bottom: .45rem; }
.tcard__date { color: var(--blue); font-size: .8125rem; font-weight: 600; }
.tcard__play {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  background: color-mix(in srgb, var(--blue) 16%, transparent);
  color: var(--blue); display: grid; place-items: center;
}
.tcard__play svg { width: 13px; height: 13px; }
.tcard__title {
  margin: 0 0 .35rem; font-size: .9375rem; font-weight: 700; line-height: 1.25;
  color: var(--text-1);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.tcard__note {
  margin: 0 0 .85rem; font-size: .8125rem; line-height: 1.35; color: var(--text-2);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.tcard__icons { display: flex; gap: .45rem; color: var(--blue); }
.tcard__icons svg { width: 15px; height: 15px; flex: none; }
.tcard__flag {
  position: absolute; right: .75rem; bottom: .7rem;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--imp-high); color: #fff;
  display: grid; place-items: center;
  font-size: .78rem; font-weight: 800; line-height: 1;
}

/* ---- Project and Notebook covers */
.props--covers { gap: 2rem; }
.cover { display: flex; flex-direction: column; flex: 0 1 104px; min-width: 0; }
.cover img {
  width: 100%; height: auto; border-radius: 3px;
  filter: drop-shadow(0 7px 16px rgba(20, 22, 40, .2));
}
/* 6px nudges the label off the cover's own transparent left margin, so the
   text lines up with the printed edge rather than the file's edge. */
.cover__name { margin-top: .55rem; font-size: .875rem; font-weight: 700; color: var(--text-1); padding-left: 6px; }
.cover__meta { font-size: .75rem; color: var(--text-2); padding-left: 6px; }

@media (max-width: 560px) {
  .props--tasks { max-width: none; }
  .tcard { padding: .75rem .8rem .9rem; }
  .props--covers { gap: .8rem; }
  .cover { flex-basis: 88px; }
}

/* -------------------------------------------------- Add Things modal
   Tapping a tile in the Add Things grid opens a two-column explainer: a rail of
   the ten things you can put on a Task, and a panel showing the real section as
   the app draws it.

   Every colour here is sampled from Craig's mockups rather than taken from the
   site tokens, because he asked for those exact values. That makes this one
   deliberately light-only: it does not follow the page into dark mode. */
.at {
  --at-bg: #FFFFFF;        /* pane */
  --at-rail: #F4F5F7;      /* rail ground */
  --at-sel: #E9EBF3;       /* selected tab */
  --at-ink: #131316;       /* titles, labels, body */
  --at-close: #686A7A;
  --at-card: #FDFDFD;
  --at-card-head: #FEFEFE;
  --at-pill: #EDEDEE;
  --at-muted: #6B6E8A;     /* section labels, secondary text */
  --at-coral: #FF0060;     /* Remove, Overdue */
  --at-blue: #008DF1;
  --at-blue-soft: #D5E8F5;
  --at-blue-soft-2: #CBE7FA;
  --at-pink-soft: #FECDDF;
  --at-line: #EFEFF2;      /* hairlines inside the card */
  --at-edge: #E9EBF3;      /* the division between rail and pane */
  --at-hover: #E9EBF380;
  --at-ring: #D3D4DA;      /* unticked checklist circle */
  --at-raised: #FFFFFF;    /* selected segment */
  --at-scroll: #97979766;
  --at-shadow: 0 30px 70px rgba(19, 19, 22, .28);

  width: min(860px, calc(100vw - 32px));
  max-height: min(600px, calc(100vh - 48px));
  padding: 0; border: 0; overflow: visible;
  border-radius: 22px;
  background: var(--at-bg);
  color: var(--at-ink);
  box-shadow: var(--at-shadow);
}
.at::backdrop { background: rgba(19, 19, 22, .38); backdrop-filter: blur(3px); }
.at[open] { display: block; }

.at__inner { display: grid; grid-template-columns: 205px 1fr; height: min(552px, calc(100vh - 48px)); border-radius: 22px; overflow: hidden; }

/* ---- rail */
.at__rail {
  background: var(--at-rail); padding: 14px 10px;
  border-right: 1px solid var(--at-edge);
  overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--at-scroll) transparent;
}
.at__rail::-webkit-scrollbar { width: 6px; }
.at__rail::-webkit-scrollbar-thumb { background: var(--at-scroll); border-radius: 999px; }
.at__tab {
  display: flex; align-items: center; gap: .7rem; width: 100%;
  padding: .5rem .6rem; margin-bottom: 2px;
  border: 0; border-radius: 12px; background: none; cursor: pointer;
  font: inherit; font-size: .95rem; font-weight: 600; color: var(--at-ink);
  text-align: left;
}
.at__tab img { width: 34px; height: 34px; flex: none; border-radius: 9px; }
.at__tab:hover { background: var(--at-hover); }
.at__tab[aria-selected="true"] { background: var(--at-sel); }
.at__tab:focus-visible { outline: 2px solid var(--at-blue); outline-offset: -2px; }

/* ---- pane */
.at__pane { background: var(--at-bg); padding: 30px 34px 34px; overflow-y: auto; overscroll-behavior: contain; }
.at__title { margin: 0 0 1.1rem; font-size: 1.5rem; font-weight: 600; letter-spacing: -.015em; color: var(--at-ink); }
.at__pane p { margin: 0 0 .9rem; font-size: 1rem; line-height: 1.55; color: var(--at-ink); max-width: 56ch; }
.at__pane p:last-child { margin-bottom: 0; }

.at__close {
  position: absolute; top: 14px; right: 14px; z-index: 2;
  width: 26px; height: 26px; padding: 0; border: 0; border-radius: 50%;
  background: var(--at-close); color: #fff; cursor: pointer;
  display: grid; place-items: center;
}
.at__close svg { width: 12px; height: 12px; }
.at__close:hover { background: #555765; }
.at__close:focus-visible { outline: 2px solid var(--at-blue); outline-offset: 2px; }

/* ---- the feature preview card: the app's own section, redrawn */
.fp {
  background: var(--at-card); border: 1px solid var(--at-line);
  border-radius: 14px; margin-bottom: 1.5rem; overflow: hidden;
  box-shadow: 0 1px 3px rgba(19, 19, 22, .05);
}
.fp__head {
  display: flex; align-items: center; gap: .55rem;
  padding: .6rem .75rem; background: var(--at-card-head);
  border-bottom: 1px solid var(--at-line);
}
.fp__head img { width: 22px; height: 22px; border-radius: 6px; flex: none; }
.fp__name { font-size: .75rem; font-weight: 700; letter-spacing: .5px; color: var(--at-muted); }
.fp__remove { margin-left: auto; font-size: .8125rem; font-weight: 600; color: var(--at-coral); }
.fp__body { padding: .2rem .75rem; }

.fp__row { display: flex; align-items: center; gap: .5rem; padding: .55rem 0; border-bottom: 1px solid var(--at-line); font-size: .875rem; }
.fp__row:last-child { border-bottom: 0; }
.fp__lab { color: var(--at-muted); font-size: .8125rem; margin-right: .1rem; }
.fp__pill { background: var(--at-pill); border-radius: 9px; padding: .3rem .6rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.fp__val { display: inline-flex; align-items: center; gap: .35rem; font-weight: 500; }
.fp__val svg { width: 7px; height: 11px; color: var(--at-muted); }
.fp__warn { display: inline-flex; align-items: center; gap: .3rem; margin-left: auto; color: var(--at-coral); font-weight: 600; font-size: .8125rem; }
.fp__warn svg { width: 14px; height: 14px; }

.fp__row--stack { align-items: flex-start; }
.fp__meta { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.fp__meta b { font-weight: 600; font-size: .875rem; }
.fp__meta i { font-style: normal; font-size: .8125rem; color: var(--at-muted); }

.fp__thumb { width: 34px; height: 34px; border-radius: 8px; flex: none; }
.fp__thumb--a { background: linear-gradient(140deg, #C24B33, #7E8B6A 60%, #4C5A46); }
.fp__thumb--b { background: linear-gradient(140deg, #E3E5EA, #C6CAD6); }
.fp__row--file .fp__meta { margin-right: auto; }
.fp__btn { background: var(--at-blue-soft); color: var(--at-blue); font-weight: 600; font-size: .8125rem; padding: .3rem .7rem; border-radius: 9px; }
.fp__minus { width: 22px; height: 22px; border-radius: 50%; background: var(--at-pink-soft); color: var(--at-coral); display: grid; place-items: center; font-weight: 700; font-size: .9rem; flex: none; }
.fp__add { color: var(--at-blue); font-weight: 600; }
.fp__plus { width: 20px; height: 20px; border-radius: 50%; background: var(--at-blue-soft); color: var(--at-blue); display: grid; place-items: center; font-weight: 700; font-size: .85rem; flex: none; }
.fp__play { margin-left: auto; width: 30px; height: 30px; border-radius: 50%; background: var(--at-blue-soft-2); color: var(--at-blue); display: grid; place-items: center; flex: none; }
.fp__play svg { width: 15px; height: 15px; }

.fp__tick { width: 19px; height: 19px; border-radius: 50%; border: 1.5px solid var(--at-ring); display: grid; place-items: center; font-size: .7rem; color: transparent; flex: none; }
.fp__tick--on { background: var(--at-blue); border-color: var(--at-blue); color: #fff; }
.fp__done { color: var(--at-muted); text-decoration: line-through; }

.fp__seg { display: flex; gap: 3px; background: var(--at-pill); border-radius: 10px; padding: 3px; margin: .6rem 0; font-size: .8125rem; font-weight: 600; }
.fp__seg span { flex: 1; text-align: center; padding: .35rem 0; border-radius: 8px; color: var(--at-muted); }
.fp__seg .on { background: var(--at-raised); color: var(--at-ink); box-shadow: 0 1px 3px rgba(19, 19, 22, .12); }


/* Dark. The light values above are Craig's exact mockup colours, so these are
   their counterparts from the site's own dark tokens: the rail recedes to
   --app-bg, the pane sits on --card-bg, and the card lifts to --card-bg-2, the
   same three-step stack the rest of the site uses. Coral and blue stay vivid,
   as they do in the app. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .at {
    --at-bg: #282B40;
    --at-rail: #1E2030;
    --at-sel: #2E3148;
    --at-ink: #F0F0F5;
    --at-close: #4E5270;
    --at-card: #2E3148;
    --at-card-head: #333756;
    --at-pill: #3C4060;
    --at-muted: #8487A8;
    --at-blue-soft: #1A2D40;
    --at-blue-soft-2: #1F3752;
    --at-pink-soft: #45203A;
    --at-line: #383B54;
    --at-edge: #383B54;
    --at-hover: #2E314880;
    --at-ring: #4A4E68;
    --at-raised: #4A4E68;
    --at-scroll: #8487A866;
    --at-shadow: 0 30px 70px rgba(0, 0, 0, .55);
  }
}
:root[data-theme='dark'] .at {
  --at-bg: #282B40;
  --at-rail: #1E2030;
  --at-sel: #2E3148;
  --at-ink: #F0F0F5;
  --at-close: #4E5270;
  --at-card: #2E3148;
  --at-card-head: #333756;
  --at-pill: #3C4060;
  --at-muted: #8487A8;
  --at-blue-soft: #1A2D40;
  --at-blue-soft-2: #1F3752;
  --at-pink-soft: #45203A;
  --at-line: #383B54;
  --at-edge: #383B54;
  --at-hover: #2E314880;
  --at-ring: #4A4E68;
  --at-raised: #4A4E68;
  --at-scroll: #8487A866;
  --at-shadow: 0 30px 70px rgba(0, 0, 0, .55);
}

/* ---- the grid tiles that open it */
.addthings__tile {
  display: flex; flex-direction: column; align-items: center; gap: .45rem;
  width: 100%; padding: .6rem .3rem; border: 0; border-radius: 16px;
  background: none; cursor: pointer; font: inherit; color: inherit;
}
.addthings__tile span { transition: color .18s ease; }
/* The tile sits inside a card that already lifts on hover, so the hover here is
   the label alone: blue and underlined, the way a link behaves. */
.addthings__tile:hover span,
.addthings__tile:focus-visible span {
  color: var(--blue);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.addthings__tile:focus-visible { outline: 2px solid var(--purple); outline-offset: 2px; }

/* ---- mobile: the rail narrows to icon over label, as in the mockup */
@media (max-width: 720px) {
  .at { width: calc(100vw - 20px); max-height: calc(100vh - 28px); border-radius: 20px; }
  .at__inner { grid-template-columns: 96px 1fr; height: min(560px, calc(100vh - 28px)); border-radius: 20px; }
  .at__rail { padding: 12px 8px; }
  .at__tab { flex-direction: column; gap: .3rem; text-align: center; font-size: .75rem; line-height: 1.2; padding: .55rem .2rem; }
  .at__tab img { width: 38px; height: 38px; }
  .at__pane { padding: 26px 18px 24px; }
  .at__title { font-size: 1.3rem; }
  .at__pane p { font-size: .9375rem; }
  .fp__row { flex-wrap: wrap; }
  .fp__warn { margin-left: 0; }
}

/* -------------------------------------------------- support page */
.support-layout { display: grid; gap: 2.5rem; grid-template-columns: 1fr; }
@media (min-width: 900px) {
  .support-layout { grid-template-columns: 220px 1fr; align-items: start; }
  .support-nav {
    position: sticky;
    /* Clear of the veil, not just the nav, or the section list sits inside the
       blurred strip. z-index keeps it above the veil (190) and below the
       pill (200) while it settles. */
    top: calc(var(--veil-h) + 24px);
    z-index: 191;
  }
}
/* Reading progress. A grey track beside the section list with a purple fill
   whose tip sits level with whichever section you are currently reading, so
   the bar and the labels say the same thing. JS writes --read, 0 to 1, as a
   fraction of the way through the five sections rather than of raw scroll,
   which is what keeps the tip lined up with the label. */
.support-nav { --read: 0; }
.support-nav ul {
  list-style: none; margin: 0; padding: 0 0 0 18px;
  position: relative;
}
.support-nav ul::before,
.support-nav ul::after {
  content: ''; position: absolute; left: 0; top: 0;
  width: 3px; border-radius: 999px;
}
.support-nav ul::before { height: 100%; background: var(--divider); }
.support-nav ul::after  { height: calc(var(--read) * 100%); background: var(--purple); }
.support-nav a.is-current { color: var(--text-1); }
@media (prefers-reduced-motion: no-preference) {
  .support-nav a { transition: color .2s ease; }
}

.support-nav li { margin-bottom: .35rem; }
.support-nav a { color: var(--text-2); font-weight: 600; font-size: .95rem; }
.support-nav a:hover { color: var(--text-1); text-decoration: none; }

/* Search field. The glyph is Magnifying-glass from Assets.xcassets, which is
   flat white with an alpha channel, so it goes in as a CSS mask painted with a
   palette colour rather than as an <img>. Same trick as the logo, and the same
   payoff: it follows light and dark on its own. */
.filter-field { position: relative; margin-bottom: 2rem; }
.filter-field::before {
  content: ''; position: absolute; pointer-events: none;
  left: 1.05rem; top: 50%; width: 17px; height: 17px; margin-top: -9px;
  background: var(--text-2);
  -webkit-mask: url('../img/icon/magnifying-glass.png') center / contain no-repeat;
          mask: url('../img/icon/magnifying-glass.png') center / contain no-repeat;
  transition: background .2s ease;
}
.filter-field:focus-within::before { background: var(--text-1); }

.filter {
  width: 100%; padding: .9rem 1.1rem .9rem 2.9rem; font: inherit; font-size: 1rem;
  color: var(--text-1); background: var(--card-bg);
  border: 1px solid var(--divider); border-radius: var(--r-button);
  box-shadow: none;
  transition: box-shadow .2s ease, border-color .2s ease;
}
.filter:hover, .filter:focus { box-shadow: var(--shadow-card-hover); }
.filter:focus { border-color: var(--text-3); }
.article { scroll-margin-top: calc(var(--veil-h) + 24px); margin-bottom: 1rem; }
.article h3 { margin-bottom: .6em; }
.article ul, .article ol { margin: 0 0 1.1em; padding-left: 1.3em; }
.article li { margin-bottom: .4em; }
.article li:last-child { margin-bottom: 0; }
.article blockquote {
  margin: 1.2em 0; padding: .9em 1.2em;
  border-left: 3px solid var(--purple);
  background: var(--card-bg-2); border-radius: 0 var(--r-small) var(--r-small) 0;
  color: var(--text-1);
}
.support-section { margin-bottom: 3.5rem; scroll-margin-top: calc(var(--veil-h) + 24px); }
.no-results { color: var(--text-2); font-style: italic; }
[hidden] { display: none !important; }

/* -------------------------------------------------- prose (privacy) */
.prose h2 { margin-top: 2.5em; font-size: clamp(1.4rem, 3vw, 1.9rem); }
.prose h2:first-of-type { margin-top: 0; }
.prose h3 { margin-top: 1.8em; font-size: 1.2rem; }
.prose ul { padding-left: 1.3em; margin-bottom: 1.1em; }
.prose li { margin-bottom: .5em; }
.prose table { width: 100%; border-collapse: collapse; margin: 1.5em 0; font-size: .95rem; }
.prose th, .prose td { text-align: left; padding: .7em .8em; border-bottom: 1px solid var(--divider); vertical-align: top; }
.prose th { color: var(--text-2); font-weight: 600; font-size: .8125rem; letter-spacing: .4px; text-transform: uppercase; }

.page-head { padding-block: calc(var(--nav-h) + clamp(2rem, 5vw, 3.5rem)) clamp(1.5rem, 4vw, 2.5rem); }
.page-head h1 { margin-bottom: .3em; }

/* -------------------------------------------------- reveal */
html.js .reveal { opacity: 0; transform: translateY(18px); transition: opacity .6s ease, transform .6s ease; }
html.js .reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { html.js .reveal { opacity: 1; transform: none; } }
