/* The opening film, and the page under it.

   One rule governs the whole of this file: the app is the only thing on
   screen that moves, and it moves once per idea. Everything else is type on
   black with room around it. There is no card, no gradient behind a heading,
   no icon standing in for a feature. What a person came here to see is a real
   screen out of the real app, so the page's whole job is to get out of its
   way and then point at one part of it at a time.

   The screens are photographs of the running app, taken from the simulator on
   13 Sep 2026. Nothing here is a drawing of the app. The page used to build a
   fake one out of divs, which meant the site could drift from the product
   without anybody noticing. */

:root {
  color-scheme: dark;

  --ground:  #000000;
  --raised:  #0b0b0d;
  --ink:     #f5f5f7;
  --muted:   #98989d;
  --faint:   #6e6e73;
  --rule:    #232326;
  --blue:    #0a84ff;
  --amber:   #ff9f0a;

  --measure: 62rem;
  --ease: cubic-bezier(.22, 1, .36, 1);
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text",
    "Segoe UI Variable Text", "Segoe UI", system-ui, Roboto, sans-serif;
  font-size: 17px;
  line-height: 1.52;
  letter-spacing: -.011em;
  -webkit-font-smoothing: antialiased;
  /* `clip`, and never `hidden`.

     This was `overflow-x: hidden`, and on an iPhone that one word takes the
     film apart. Hidden overflow makes an element a scroll container, and
     `position: sticky` resolves against its nearest scrolling ancestor: in
     Safari the stage stops sticking to the screen and travels with the track
     instead. The phone slides away after the first screen, the camera never
     appears to move again, and what is left is six screens of black to drag
     through before the footer. Reported from an iPhone 14 Plus as "da scroll,
     se le congela y le aparece al final de la pagina", which is exactly that,
     described from the outside.

     `clip` cuts the same overflow and does *not* make a scroll container, so
     sticky keeps resolving against the viewport. And it is guarding nothing
     today: measured at 390 points, on both pages, at every scroll position,
     the widest the document gets is exactly the viewport. It stays as a
     guard rail rather than a load-bearing rule. */
  overflow-x: clip;
}

img, video { display: block; max-width: 100%; }

a { color: var(--blue); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
  border-radius: 6px;
}

/* ── the bar ──────────────────────────────────────────────────────────────── */

.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  height: 56px;
  padding: 0 22px;
  background: rgb(0 0 0 / .55);
  backdrop-filter: saturate(180%) blur(18px);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
}

.nav .mark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -.02em;
  color: var(--ink);
}

.nav .mark img { width: 26px; height: 26px; border-radius: 7px; }

.nav nav { display: flex; gap: 26px; }

.nav nav a {
  font-size: 15px;
  color: var(--muted);
}

.nav nav a:hover { color: var(--ink); text-decoration: none; }

/* ── the film ─────────────────────────────────────────────────────────────── */

/* The track is what scrolls. The stage inside it stays still, and the phone
   inside the stage is moved by how far down the track you are, which is the
   whole of the mechanism. Six screens of track for five beats: enough that
   nothing snaps, and the beat you are on holds long enough to read. */
/* `svh`, not `vh`.

   `100vh` on a phone is the viewport with the browser's own bars *hidden*,
   which is taller than what you can actually see. The stage is that tall and
   the sentence is anchored to its bottom edge, so the bottom edge sat below
   the fold and what was left on screen was the type with a third of a screen
   of nothing under it. David photographed exactly that and asked whether the
   distance was deliberate, which is the question you get when it is not.

   `svh` is the viewport with the bars showing: the smallest it ever gets, and
   stable while scrolling, so the stage always fits and nothing is anchored
   off-screen. The track is stated in the same unit so the ratio of track to
   screen does not change with it. */
/* A screen of overlap at every hand-over (16 Sep 2026).

   Each track runs one screen past the point where the next scene takes the
   phone over, so at the hand-over both stages are pinned and show the same
   phone in the same place. Before, the next stage became visible on exactly
   the scroll position where this one began to leave, and the swap waited for
   a script to notice. On an iPhone, scrolling with momentum, the script
   noticed a frame late: for that frame the phone rode up with its stage, and
   David saw a little jump, as if it had moved. Now a late frame shows the
   same picture it replaces. The poses do not change, because each script
   measures its travel to where the next scene begins, not to where its own
   stage lets go. */
.film { position: relative; height: 720svh; }

.stage {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: hidden;
  perspective: 2200px;
  perspective-origin: 62% 46%;
}

/* No lighting behind the device at all.

   Six attempts went behind this phone: a lamp, a masked cone, blurred
   ellipses, solid cards, glass panes, and finally nine glass panes with the
   app's own mark cut through each one. The last was the best of them and it
   was still wrong, for a reason none of the others made obvious. Every one of
   them competed with the screenshot. The screenshot is the product; a fan of
   colour behind it is a poster about a product. David called it: the indigo
   end read as a purple glow, and the page is better with the air.

   So the stage is black and the device is the only thing lit. Nothing here
   replaces it, on purpose. */

.rig {
  position: absolute;
  top: 50%;
  /* Off centre on purpose. The sentences live in the left third and the camera
     works in the right two thirds, so a close-up never lands on the words. */
  left: 62%;
  width: var(--pw);
  height: var(--ph);
  margin: calc(var(--ph) / -2) 0 0 calc(var(--pw) / -2);
  /* Deliberately flat. `preserve-3d` here put the rounded, clipped screen into
     a 3D context and the whole device stopped painting the moment the camera
     turned it: a black stage with the element still in the right place. The
     rig is one plane being turned, so it never needed depth of its own. */
  transform-style: flat;
  will-change: transform;
}

/* Apple's own product bezel, from Apple Design Resources under the App Store
   Marketing Artwork licence, rather than the three gradients that used to
   pretend to be titanium here. The picture is 1350 by 2760 with a hole of
   1206 by 2622 at 72, 69: exactly a screenshot off an iPhone 16 Pro, and
   centred in the picture, so the camera below needs no offset of its own. */
.phone {
  position: relative;
  width: 100%;
  height: 100%;
}

.glass {
  position: absolute;
  left: 5.3333%;
  top: 2.5%;
  width: 89.3333%;
  height: 95%;
  object-fit: cover;
  border-radius: 8.4%/3.9%;
  background: #000;
}

.bezel {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* The clip sits over the still of the same screen. It is never swapped for the
   still halfway through: the plane would jump to wherever the clip happened to
   be and jump back on the way out, which is what David saw as two steps
   backwards. It comes up at the start of the beat, parked on its first frame,
   and from then on the scroll is what moves it. */
video.glass { opacity: 0; }

.island {
  position: absolute;
  display: none;
}

/* ── what is said over it ─────────────────────────────────────────────────── */

.lines {
  position: absolute;
  inset: 0;
  display: grid;
  align-content: center;
  padding: 0 clamp(22px, 6vw, 92px);
  pointer-events: none;
}

.line {
  grid-area: 1 / 1;
  /* Measured against the stage, not in `ch`. A `ch` on this element resolves
     against *its* font size, which is the body's seventeen points, so the
     heading inside was being squeezed into a column about a hundred and fifty
     pixels wide and came out one word per line. */
  max-width: min(44vw, 620px);
  margin: 0;
  opacity: 0;
  transform: translateY(16px);
  will-change: opacity, transform;
}

.line h1,
.line h2 {
  margin: 0;
  font-size: clamp(44px, 7.6vw, 112px);
  line-height: .98;
  letter-spacing: -.04em;
  font-weight: 700;
  text-wrap: balance;
}

.line p {
  margin: 26px 0 0;
  max-width: 26ch;
  font-size: clamp(18px, 1.9vw, 27px);
  line-height: 1.35;
  letter-spacing: -.018em;
  color: var(--muted);
}

.line .cta { margin-top: 30px; pointer-events: auto; }

.button {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 17px 34px;
  border-radius: 999px;
  background: var(--blue);
  color: #fff;
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -.01em;
}

.button:hover { background: #339cff; text-decoration: none; }

.button.quiet {
  background: transparent;
  color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--rule);
}

.button.quiet:hover { background: #141416; }

.line .icon {
  width: 74px;
  height: 74px;
  border-radius: 18px;
  margin-bottom: 24px;
}

/* ── the page under the film ──────────────────────────────────────────────── */

main { position: relative; z-index: 2; background: var(--ground); }

section { padding: clamp(72px, 11vw, 140px) 22px; }

.wrap { max-width: var(--measure); margin: 0 auto; }

.eyebrow {
  margin: 0 0 14px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--faint);
}

h2 {
  margin: 0 0 18px;
  font-size: clamp(30px, 4.4vw, 48px);
  line-height: 1.08;
  letter-spacing: -.028em;
  font-weight: 700;
  text-wrap: balance;
}

h3 {
  margin: 0 0 8px;
  font-size: 20px;
  line-height: 1.25;
  letter-spacing: -.018em;
  font-weight: 600;
}

.lede {
  margin: 0 0 40px;
  max-width: 46ch;
  font-size: clamp(19px, 2.2vw, 23px);
  line-height: 1.42;
  letter-spacing: -.018em;
  color: var(--muted);
  text-wrap: pretty;
}

p { margin: 0 0 16px; }
p:last-child { margin-bottom: 0; }

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 40px 44px;
}

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

/* Two screens side by side, at the size a screen wants to be read at. */
.pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: clamp(34px, 5vw, 64px);
  align-items: start;
}

.plate {
  border-radius: 30px;
  overflow: hidden;
  background: #08080a;
  box-shadow: 0 0 0 1px var(--rule), 0 30px 80px rgb(0 0 0 / .6);
}

.plate img { width: 100%; height: auto; }

.plate + figcaption,
figure > figcaption {
  margin: 16px 2px 0;
  color: var(--muted);
  font-size: 15px;
  line-height: 1.45;
}

figure { margin: 0; }

.rule { height: 1px; background: var(--rule); border: 0; margin: 0; }

.notes {
  display: grid;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--muted);
}

.notes li { padding-left: 22px; position: relative; }

.notes li::before {
  content: "";
  position: absolute;
  left: 4px;
  top: .62em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--faint);
}

footer {
  border-top: 1px solid var(--rule);
  padding: 44px 22px 64px;
  color: var(--faint);
  font-size: 14.5px;
}

.foot-inner {
  max-width: var(--measure);
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 24px 44px;
  justify-content: space-between;
  align-items: flex-start;
}

.foot-inner p { margin: 0; line-height: 1.7; }
.foot-inner strong { color: var(--muted); font-weight: 600; }
.foot-links { display: flex; flex-wrap: wrap; gap: 8px 22px; }
.foot-links a { color: var(--muted); }

/* ── entrances for the page under the film, kept to one step ──────────────── */

.js .rise { opacity: 0; transform: translateY(14px); }

.js .rise.seen {
  opacity: 1;
  transform: none;
  transition: opacity .6s var(--ease), transform .6s var(--ease);
}

/* ── from zero ────────────────────────────────────────────────────────────── */

/* The second scene reuses the stage, rig, phone and lines above. It is its own
   track, a little shorter, and its phone is the recording itself, so the video
   is visible from the first frame rather than faded up over a still. The
   stage sits flat: this camera goes in and out and never turns the device. */
.film.births { height: 700svh; }
.births .stage { perspective: none; }
.births video.glass { background: #f2f2f7; }
/* This scene's sentences are longer than the film's, and its camera goes in on
   cards across the whole screen, so on a desk they are set a size down to stay
   clear of the phone. */
@media (min-width: 60.0625rem) {
  .births .line h2 { font-size: clamp(44px, 6vw, 88px); }
}
/* A clip comes up over the one below it in a fifth of a second rather than in
   one frame. Its first frame is the last frame underneath, so the fade shows
   nothing but covers the moment a phone takes to paint it. */
.births video.glass { transition: opacity .2s var(--ease); }
.births video.glass[data-birth="money"] { transition: none; }

/* One phone, handed over without a cut.

   The second track starts a screen early, over the last screen of the first,
   and its stage stays invisible until it is stuck. At that instant the first
   stage begins to leave, and the second, opaque and holding a phone of exactly
   the same size in exactly the same place, covers it. Two phones on screen at
   once, and a small phone alone in black, were what David saw before. */
.film.births { margin-top: -200svh; position: relative; z-index: 1; }
.births .stage { visibility: hidden; }
.births .stage.stuck { visibility: visible; background: var(--ground); }

/* The screen off, and the screen coming on. Both sit exactly over the glass. */
.phone .off,
.phone .boot {
  position: absolute;
  left: 5.3333%;
  top: 2.5%;
  width: 89.3333%;
  height: 95%;
  border-radius: 8.4%/3.9%;
  background: #000;
  pointer-events: none;
}

.phone .off { opacity: 0; }

.boot svg {
  position: absolute;
  left: 50%;
  top: 46%;
  /* 128 points of a 402 point screen, the size the app draws it on Home. */
  width: 32%;
  transform: translate(-50%, -50%);
  overflow: visible;
}

/* The app's own motion for this mark, AheadWalletMark in Design.swift:
   nothing is drawn for 0.16 s, then the front card is simply there and the
   two behind it rise from exactly behind it, the far one 0.07 s after the
   near one, on `.spring(duration: 0.62, bounce: 0.42)`. The curve below is
   that spring sampled, damping ratio 0.58 and a period of 0.62 s, so the
   overshoot of about a tenth is the app's and not a guess. The drops are the
   app's too: 150 and 290 in the drawing's units, which hide each card
   completely behind the front one. */
.boot .card { opacity: 0; }
.boot .mid { transform: translateY(150px); }
.boot .back { transform: translateY(290px); }

.boot.booting .card { animation: boot-armed 0s linear .16s forwards; }
.boot.booting .mid { animation: boot-armed 0s linear .16s forwards, boot-rise 1.2s var(--wallet-spring) .16s forwards; }
.boot.booting .back { animation: boot-armed 0s linear .16s forwards, boot-rise 1.2s var(--wallet-spring) .23s forwards; }
.boot.booting svg { animation: boot-mark-out .3s ease-in 1.55s forwards; }
.boot.booting { animation: boot-on .45s ease 1.85s forwards; }

.boot {
  --wallet-spring: linear(0, .0422, .148, .2898, .4454, .5983, .7371, .8551, .949, 1.0187,
    1.0659, 1.0937, 1.1056, 1.1055, 1.097, 1.0834, 1.0672, 1.0506, 1.0349, 1.0212,
    1.0099, 1.0012, .9951, .9912, .9891, .9886, .9891, .9903, .992, .9938, .9955,
    .9971, .9984, .9995, 1.0002, 1.0008, 1.0011, 1.0012, 1.0012, 1.0011, 1);
}

@keyframes boot-armed { to { opacity: 1; } }
@keyframes boot-rise { to { transform: translateY(0); } }
@keyframes boot-mark-out { to { opacity: 0; } }
@keyframes boot-on { to { opacity: 0; } }
.births video.glass[data-birth="money"] { opacity: 1; }

/* ── narrow ───────────────────────────────────────────────────────────────── */

@media (max-width: 60rem) {
  /* The camera work is simpler on a phone, as it has to be: less room to move
     through, and a hand holding the thing you are showing a picture of. The
     type goes under the device instead of beside it. */
  .stage { perspective-origin: 50% 40%; }

  /* The device moves up out of the way and the type takes the bottom third.
     Side by side needs width this layout does not have, and type over a screen
     is unreadable however it is dimmed. */
  .rig { top: 29%; left: 50%; }

  .lines {
    align-content: end;
    padding-bottom: clamp(24px, 5vh, 52px);
  }

  /* A close-up fills a narrow screen, so the sentence has to stand on the
     picture. One gradient up from the floor, which is what a photograph with a
     caption has always done, rather than a panel with an edge. */
  .lines::before {
    content: "";
    position: absolute;
    inset: 42% 0 0;
    background: linear-gradient(to bottom, transparent, rgb(0 0 0 / .82) 34%, #000 62%);
    pointer-events: none;
  }

  .line { max-width: 100%; }
  .line h1, .line h2 { font-size: clamp(34px, 8.6vw, 52px); }
  .line p { max-width: 34ch; }
}

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

  /* No film. The track collapses to one screen, the phone sits still at the
     size it ends the film at, and every line is simply on the page. */
  .film { height: auto; }
  .stage { position: static; height: auto; padding: 96px 0 40px; overflow: visible; }
  .rig { position: static; margin: 0 auto; transform: none !important; }
  .lines { position: static; padding: 44px 22px 0; gap: 40px; }
  .line { opacity: 1 !important; transform: none !important; max-width: 34ch; margin: 0 auto; }
  .js .rise, .js .rise.seen { opacity: 1; transform: none; transition: none; }
  .film.births { margin-top: 0; }
  .births .stage { visibility: visible; }
  .phone .off, .phone .boot { display: none; }
}

/* The bar stops blurring on a phone.

   A sticky bar with `backdrop-filter: blur(20px)` across the full width has to
   re-blur whatever is behind it on every frame of a scroll, and what is behind
   it here is a playing video and screenshots the height of the screen. On a
   desktop that is free. On a phone it is the most expensive thing on the page,
   and it stutters exactly where the content behind it is heaviest, which is
   what "se frisa en algunos lugares" describes. The bar is forty-eight points
   tall: made opaque it looks the same and costs nothing.

   Keyed on a coarse pointer rather than on width, because the cost belongs to
   the device and not to how wide the window happens to be. */
@media (hover: none) and (pointer: coarse) {
  .nav {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: rgb(0 0 0 / .96);
  }
}
/* Apple's credit line. Its marketing guidelines ask for one in every
   communication that names its trademarks, and this site names several. */
.marks {
  max-width: var(--measure, 980px);
  margin: 18px auto 0;
  font-size: 11px;
  line-height: 1.5;
  opacity: .8;
}


/* ── in your colour ───────────────────────────────────────────────────────── */

/* The third track. It takes the phone over from the births scene exactly the
   way that scene takes it from the film: a screen early, invisible until it
   sticks, then opaque over the one above. Short on purpose, a little over
   three screens, so the row is always moving under the finger. */
.film.accents { height: 540svh; margin-top: -200svh; position: relative; z-index: 2; }
.accents .stage { visibility: hidden; perspective: none; }
.accents .stage.stuck { visibility: visible; background: var(--ground); }

.accents .phone { z-index: 1; }

/* A screen per accent, stacked; the one whose pane has gone behind shows. */
.accents .shot { opacity: 0; transition: opacity .32s var(--ease); }
.accents .shot.on { opacity: 1; }

/* The panes sit behind the phone, in the same box as its screen, and the
   script moves them out to the right in a row and back behind it. */
.accents .panes { position: absolute; inset: 0; }
/* No light behind the glass any more: David wanted the colour in the glass
   itself, a blue pane rather than a clear pane lit blue from behind. */
.accents .glows { display: none; }

.accents .pane {
  position: absolute;
  left: 5.3333%;
  top: 2.5%;
  width: 89.3333%;
  height: 95%;
  border-radius: 8.4%/3.9%;
  overflow: hidden;
  /* The app itself in each colour (David, 16 Sep 2026). Colour on its own,
     flat or as glass, never felt premium; the same Home the phone is showing,
     once per accent, fanned out behind it, says what the choice does. The
     screens are the ones the phone takes, so they come from cache. */
  background-color: #f2f2f7;
  background-size: cover;
  background-position: center top;
  box-shadow: 0 28px 70px rgb(0 0 0 / .55);
  will-change: transform;
}
/* Further back is a little smaller and a little darker, so the row reads as
   depth rather than as five tiles in a line. The darkening is a layer and not
   a filter, which a phone would have to recompute on every frame of scroll. */
.accents .pane::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgb(0 0 0 / var(--dim, 0));
  pointer-events: none;
}
.accents .panes { isolation: isolate; }
.accents .pane:nth-child(1) { background-image: url(/screens/accent-blue-0917.webp);   scale: .99; --dim: .02; z-index: 5; }
.accents .pane:nth-child(2) { background-image: url(/screens/accent-orange-0917.webp); scale: .97; --dim: .07; z-index: 4; }
.accents .pane:nth-child(3) { background-image: url(/screens/accent-green-0917.webp);  scale: .95; --dim: .12; z-index: 3; }
.accents .pane:nth-child(4) { background-image: url(/screens/accent-pink-0917.webp);   scale: .93; --dim: .17; z-index: 2; }
.accents .pane:nth-child(5) { background-image: url(/screens/accent-purple-0917.webp); scale: .91; --dim: .22; z-index: 1; }

@media (prefers-reduced-motion: reduce) {
  .film.accents { margin-top: 0; }
  .accents .stage { visibility: visible; }
  .accents .panes, .accents .glows { display: none; }
}

/* Nornas's blue on the last word of the opening line, as on the overview. */
.line .hl-blue { color: #0a84ff; }
