/* The landing page.

   **The layout is derived from named references, not composed.**
   `docs/design/pinterest-references.md` holds them and the pin ids; the three
   live competitors are measured in `docs/design/dating-landing-pages.md`.
   In short:

     Taylor Anne   the page is one edge-to-edge checkerboard of photographs
                   and flat colour tiles, grounded on tints of a single warm
                   brand colour.
     Kinetic       a card row staggers each card to a different vertical
                   offset.
     Hinge         the fold is a documentary photograph with the headline set
                   into it and the bar floating transparent over the top.
     Tinder        scale is the argument - the fold holds the headline and
                   almost nothing else, and controls are pills.

   **The first version of this redesign was black and white and was thrown
   away** (2026-08-31, the human: *"можна там більше кольорів, я не хочу
   чорно-білий сайт ... більше як для dating app, а не для фотогалереї"*). An
   edge-to-edge mosaic of monochrome photographs is a photographer's
   portfolio, which is what it read as. **The flat colour tiles are what stop
   it**: they break the run of pictures, they carry the copy, and they make
   the page a dating brand rather than a gallery.

   **There is no sports section, and that is a decision** (2026-08-31, the
   human: *"dont keep so big focus on sports on website ... it should be dating
   app, not workouts app"*). A strip of six training photographs sold a workout
   app. Sport survives on the page as one clause in the deck's tie-break, which
   is what it is in the domain - a Score term, not the product.

   **The band states a problem, and every section after it is an answer**
   (2026-08-31, the human: *"website should highlight value, proposition, not
   be documentation"*). `docs/design/gym-dating-demand.md` is the evidence the
   copy stands on.

   **Not one word of copy changed in the redesign** (2026-08-31, the human:
   *"нічого не змінює в контенті ... не змінює текст контент"*). Every string
   on the page is the one that was argued from the research; the mosaic
   rearranges where they sit, never what they say.

   **Anton is spent once, on the close.** Set on every section head as well it
   turns the page into a poster - a condensed black caps face repeated six
   times reads as shouting, and the references do not do this either. Sections
   take the app's own ramp at weight 700.

   **Two things were built here for their look and have been cut** (2026-08-31,
   the human: *"what is the purpose of this banner? ... dont build something
   without no reason"*). Both were the same error - reproducing a reference's
   shape without the job that shape was doing:

     * **A `PULL` marquee band under the fold.** Taylor Anne's marquee lists
       what she sells; Kinetic's oversized wordmark is part of the fold's own
       composition, not a strip under it. What was built was a band repeating a
       name the reader had just read in the header, sitting in the one position
       on the page that matters most - between the headline and the hook. The
       comment written to justify it admitted the fault out loud: that no words
       could be put in it because the copy was frozen. **A band that cannot be
       given anything to say does not need to exist.** The oversized wordmark
       is spent once, at the close, next to the ask.
     * **`01 / 02 / 03` in the corner of each copy tile**, taken from Liam
       Bennett as editorial furniture. The three statements are not a sequence
       and nothing is done in their order, so the numbers asserted a structure
       the product does not have.

   **The test every element on this page now has to pass: name what it sells,
   or what it helps the reader do.** The scroll cue is an affordance on a
   full-bleed fold. The band's corner light is what `CLAUDE.md` requires of a
   ground. The accent dot heads a promise. Decoration that only answers "the
   reference had one" does not ship. */

/* --- the display face --------------------------------------------------- */

.display {
  font-family: 'Anton', 'Arial Narrow', Impact, sans-serif;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: -.015em;
  line-height: .92;
  margin: 0;
  text-wrap: balance;
}

.lede {
  font-size: clamp(18px, 1.5vw, 21px);
  line-height: 1.5;
  color: var(--ink-2);
  text-wrap: pretty;
}

/* --- scroll progress ---------------------------------------------------- */

/* A hairline of accent across the top of the bar, driven by a scroll-linked
   animation where the browser has one and by the rAF loop where it does not.
   It is the one piece of chrome that reports position, which a page of
   full-bleed bands otherwise gives no clue about. */
.progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 3px;
  width: 100%;
  z-index: 60;
  background: var(--accent);
  transform: scaleX(var(--p, 0));
  transform-origin: 0 50%;
  pointer-events: none;
}

@supports (animation-timeline: scroll()) {
  .progress {
    animation: progress-grow linear;
    animation-timeline: scroll(root block);
  }
}

@keyframes progress-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* --- the fold ----------------------------------------------------------- */

.hero {
  position: relative;
  margin-top: -76px;           /* the bar floats over the picture */
  min-height: min(96svh, 940px);
  display: flex;
  align-items: flex-end;
  padding: 140px 0 clamp(44px, 5vw, 76px);
  overflow: hidden;
  background: var(--ink);
}

/* **The photograph is taller than its frame and rides up the page.** Hinge's
   fold is a still; the depth here comes from the picture moving at about a
   fifth of the scroll rate while the type moves at the page's, which is the
   cheapest parallax there is and the only one that survives a phone. The
   extra 22% of height is what the travel is spent out of, so no edge ever
   enters the frame. */
.hero-shot {
  position: absolute;
  inset: -11% 0;
  width: 100%;
  height: 122%;
  object-fit: cover;
  object-position: 74% 42%;
  transform: translate3d(0, var(--shift, 0px), 0);
  will-change: transform;
}

/* A scrim, not a veil. White type over a photograph needs a floor that does
   not depend on which pixel it lands on, so the bottom two thirds are taken
   to a measured black rather than tinted proportionally. Ink at the headline's
   band reads over `rgba(0,0,0,.62)` on a mid-grey floor. */
.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to top, rgba(0, 0, 0, .84) 0%, rgba(0, 0, 0, .64) 34%, rgba(0, 0, 0, .12) 66%, rgba(0, 0, 0, .34) 100%);
}

.hero .wrap { position: relative; z-index: 2; }

/* **The fold's type is white, and it was black until 2026-08-31.** `.display`
   and `.lede` both inherit `--ink` from the page, and neither the `h1` nor the
   lede was ever given the photograph's colour - the lede's rule was written
   `.hero .hero .lede`, a selector that matches nothing, so both lines rendered
   near-black over a dark gym and were invisible at 1512. */
/* **The fold's headline had no size at all until 2026-08-31** (the human:
   *"make this text bigger"*). `.display` sets the face, the weight, the case
   and the leading and never set `font-size`, so the `h1` fell through to the
   browser's default `2em` - 34px against the 120.96px Tinder runs at this
   viewport. The page's own reference file argues that *scale is the argument*
   on this fold and the fold was not making it.

   **8vw is Tinder's ratio, not a taste.** 120.96px measured at a 1512 viewport
   is 8% of the width per line (`docs/design/dating-landing-pages.md`), so the
   middle term is the measurement and the clamp only holds the ends: 124px
   stops it growing past the reference on a wide monitor, and 42px keeps two
   lines of Anton readable at 390 where 8vw would be 31. */
.hero h1 {
  color: #fff;
  font-size: clamp(42px, 8vw, 124px);
}

.hero .lede {
  margin: 26px 0 0;
  max-width: 46ch;
  color: rgba(255, 255, 255, .88);
}

/* **The headline arrives a line at a time from under its own edge**, which is
   the one motion a display face can take without looking like an animation:
   the type is clipped to its line box and slides up into it. Two lines of
   Anton dropping in sequence is the whole of the fold's entrance. */
.lines span {
  display: block;
  overflow: hidden;
  padding-bottom: .06em;        /* Anton's descender clips without this */
}

.lines span > i {
  display: block;
  font-style: normal;
  transform: translateY(105%);
  transition: transform 1s var(--ease);
  transition-delay: calc(var(--l, 0) * 110ms + 120ms);
}

.js .lines.in span > i { transform: none; }

.stores {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin: 32px 0 0;
}

.stores img { height: 52px; width: auto; }

/* The badges lift under the pointer. A store badge is the page's only real
   conversion target and it is a supplied graphic, so the affordance has to
   come from motion rather than from restyling somebody else's artwork. */
.stores a {
  display: block;
  border-radius: 10px;
  transition: transform .4s var(--ease), filter .4s var(--ease);
}

.stores a:hover { transform: translateY(-4px) scale(1.03); filter: brightness(1.06); }
.stores a:active { transform: translateY(-1px) scale(.99); }

/* A scroll hint at the foot of the fold - a hairline that fills, loops, and
   says the page continues past a full-bleed photograph. */
.cue {
  position: absolute;
  left: 50%;
  bottom: 22px;
  z-index: 2;
  width: 1px;
  height: 54px;
  overflow: hidden;
  background: rgba(255, 255, 255, .22);
}

.cue::after {
  content: '';
  position: absolute;
  inset: 0;
  background: #fff;
  animation: cue-run 2.4s var(--ease) infinite;
}

@keyframes cue-run {
  0%   { transform: translateY(-100%); }
  60%  { transform: translateY(100%); }
  100% { transform: translateY(100%); }
}

/* --- the band ----------------------------------------------------------- */

/* Bumble's colour block, full bleed and with no border on it - a boundary
   would make it a panel rather than a ground. */
.band {
  position: relative;
  background: var(--accent);
  color: #fff;
  padding: clamp(72px, 8vw, 124px) 0;
  overflow: hidden;
}

/* A single soft light in the band's corner, no edge on it, so a large flat
   field of one colour is not a flat field of one colour. */
.band::after {
  content: '';
  position: absolute;
  top: -30%;
  right: -10%;
  width: 60vw;
  height: 130%;
  background: radial-gradient(closest-side, rgba(255, 255, 255, .16), transparent 72%);
  pointer-events: none;
}

.band .wrap { position: relative; z-index: 2; }

.band p {
  margin: 0;
  font-size: clamp(28px, 3.6vw, 52px);
  line-height: 1.14;
  font-weight: 700;
  letter-spacing: -.03em;
  max-width: 16ch;
  text-wrap: balance;
}

/* Solid white, and the step down is size and weight rather than opacity.
   White is only 4.58:1 on the accent to begin with, so the alpha that clears
   4.5:1 is 0.99 - there is no room on this ground for a translucent grey, and
   `rgba(255,255,255,.78)` measured 2.94:1. */
.band .hit {
  margin-top: 20px;
  color: #fff;
  font-size: clamp(18px, 1.6vw, 23px);
  font-weight: 400;
  line-height: 1.42;
  max-width: 44ch;
}

/* The band's evidence line. **Solid white, like `.hit` and for the same
   reason**.

   **It carries no attribution, and that was the human's call** (2026-08-31:
   *"MyProtein, survey of 2,365 US adults - dont need this on the website"*).
   The source line was set at 13px under the figures. A landing page is not a
   paper and a footnote on one reads as hedging; the citation lives in
   `docs/design/gym-dating-demand.md`, which is where anybody who has to defend
   the number will look. */
.band .proof {
  margin-top: 34px;
  padding-top: 22px;
  border-top: 1px solid rgba(255, 255, 255, .34);
  color: #fff;
  font-size: clamp(15px, 1.15vw, 17px);
  font-weight: 600;
  line-height: 1.45;
  max-width: 42ch;
}

/* **The two figures count up when the band arrives**, because 85 against 29 is
   the argument of the whole page and a number that moves is read rather than
   skimmed. `tabular-nums` keeps the line from reflowing on every tick. */
.band .proof em {
  font-style: normal;
  font-variant-numeric: tabular-nums;
}

/* --- the mosaic --------------------------------------------------------- */

/* **Taylor Anne's checkerboard, in the accent's own tints.** Photographs and
   flat colour tiles share one grid and alternate; the tiles carry the product
   statements, so nothing on the page is decoration and no copy moved out of
   the argument to make room for a picture.

   Edge to edge, no gutter, no radius, no shadow. A rounded card with a drop
   says *component*; a bled photograph says *photograph*, and every reference
   on file puts its pictures to the edge. This is a deliberate departure from
   the app's "big rounded cards", which is an iOS register and not a web one -
   `docs/design/pinterest-references.md` records the cost. */
.mosaic {
  background: var(--paper);
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0;
}

.cell {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  display: flex;
}

/* **A wide tile is two columns across and one row tall, so its ratio is 2:1
   and not 1:1.** Left square it stood at twice the height of the cells beside
   it, the grid row took that height, and every photograph in the row sat
   short and top-aligned with a band of paper under it. The ratio is what
   keeps a checkerboard square. */
.cell-wide { grid-column: span 2; aspect-ratio: 2 / 1; }

/* **A photo cell holds an oversized picture and moves it.** The image is 124%
   of the cell so a parallax shift has somewhere to go; without the overshoot
   the translate would drag the tile's own edge into view.

   **The overshoot is taken from the bottom, not split evenly**, and that is
   not a taste (2026-08-31). Centred, it hid 12% off the top of every
   photograph - which is exactly where a face is, so on a phone the tiles were
   showing people from the shoulders down. `object-position` cannot fix it: the
   box is 4:5-ish and the sources are 4:5, so `cover` scales to height and
   there is no vertical slack for it to bite on. Biasing the inset is the only
   lever, and 6% off the top clears the heads while 18% at the foot still gives
   the parallax room to run. */
.cell-shot img {
  position: absolute;
  inset: -6% 0 -18% 0;
  width: 100%;
  height: 124%;
  object-fit: cover;
  transform: translate3d(0, var(--shift, 0px), 0) scale(1.02);
  will-change: transform;
}

/* A tile is a step of the accent, and which step it is comes from the class
   rather than from its position, so reordering the grid cannot silently
   change the rhythm. */
.cell-blush { background: var(--blush); }
.cell-peach { background: var(--peach); }
.cell-coral { background: var(--coral); }
.cell-ink   { background: var(--ink); color: #fff; }
.cell-act   { background: var(--accent); color: #fff; }

.cell-say {
  flex-direction: column;
  justify-content: center;
  padding: clamp(26px, 3.2vw, 54px);
}

.cell-say h2 {
  margin: 0;
  font-size: clamp(23px, 2.5vw, 40px);
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.06;
  text-wrap: balance;
}

.cell-say p {
  margin: 16px 0 0;
  font-size: clamp(15px, 1.15vw, 18px);
  line-height: 1.5;
  color: var(--ink-2);
  max-width: 34ch;
  text-wrap: pretty;
}

.cell-ink p, .cell-act p { color: rgba(255, 255, 255, .86); }

/* The tiles wipe in from their own bottom edge rather than fading. A fade on
   a full-bleed colour tile reads as a loading state; a wipe reads as the tile
   being placed. */
.js .cell {
  clip-path: inset(0 0 100% 0);
  transition: clip-path .95s var(--ease);
  transition-delay: calc(var(--i, 0) * 90ms);
}

.js .cell.in { clip-path: inset(0 0 0 0); }

/* **Two columns, and the wide tiles take the whole row.** `dense` is what
   stops a full-row tile from leaving a hole beside the single cell above it:
   without it the grid wraps and the gap stays. Reading order for a screen
   reader is the DOM's either way, and every copy tile still lands between the
   photographs it belongs to. */
@media (max-width: 1000px) {
  .mosaic { grid-template-columns: repeat(2, 1fr); grid-auto-flow: row dense; }
  .cell-wide { grid-column: span 2; aspect-ratio: 2 / 1; }
  .cell-say { padding: clamp(22px, 4vw, 40px); }
}

@media (max-width: 560px) {
  .mosaic { grid-template-columns: 1fr; }
  .cell, .cell-wide { grid-column: span 1; aspect-ratio: 4 / 5; }
  .cell-say { aspect-ratio: auto; min-height: 78vw; }
}

/* --- the promises ------------------------------------------------------- */

/* **Three, and it was four until 2026-08-31.** The one that left described
   photo deletion out of storage, which is a privacy policy's sentence and not
   a selling surface's - `docs/design/gym-dating-demand.md` records the cut.

   **Kinetic's staggered row.** Each card sits at a different vertical offset
   and takes a different parallax rate, which is what makes the movement read
   as depth rather than as three things sliding. The offsets are dropped
   below 900px, where a stagger is just a ragged column. */
.promises {
  background: var(--blush);
  padding: clamp(80px, 9vw, 140px) 0 clamp(112px, 12vw, 200px);
}

.promises h2 {
  margin: 0;
  font-size: clamp(31px, 4vw, 58px);
  font-weight: 700;
  letter-spacing: -.035em;
  line-height: 1.04;
  max-width: 17ch;
  text-wrap: balance;
}

.quad {
  list-style: none;
  margin: clamp(44px, 5vw, 76px) 0 0;
  padding: 0;
  display: grid;
  gap: clamp(16px, 1.6vw, 24px);
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  align-items: start;
}

.quad li {
  background: #fff;
  border-radius: 22px;
  padding: clamp(24px, 2.4vw, 36px);
  transform: translate3d(0, calc(var(--off, 0px) + var(--shift, 0px)), 0);
  will-change: transform;
}

.quad li:nth-child(2) { --off: 34px; }
.quad li:nth-child(3) { --off: 68px; }

.quad h3 {
  margin: 0 0 10px;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -.015em;
}

/* The accent dot each card is headed by - the one place on this ground the
   accent appears, and it marks the promise rather than decorating it. */
.quad h3::before {
  content: '';
  display: block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--accent);
  margin-bottom: 16px;
}

.quad p { margin: 0; color: var(--muted); font-size: 16px; }

@media (max-width: 900px) {
  .quad li { --off: 0px; }
  .promises { padding-bottom: clamp(80px, 9vw, 140px); }
}

/* --- the close ---------------------------------------------------------- */

/* Bumble's oversized logotype, used once and at the end. It is set as the
   section's own graphic and clipped by the section, which is why the wrap
   goes edge to edge here. */
.close {
  position: relative;
  background: var(--accent);
  color: #fff;
  padding: clamp(80px, 10vw, 140px) 0 0;
  overflow: hidden;
  text-align: center;
}

.close h2 {
  font-size: clamp(32px, 4.4vw, 62px);
  max-width: 20ch;
  margin-inline: auto;
}

.close .lede {
  margin: 24px auto 0;
  max-width: 46ch;
  color: rgba(255, 255, 255, .82);
}

.close .stores { justify-content: center; margin-top: 36px; }

.close-mark {
  margin: clamp(48px, 6vw, 80px) 0 -1.6%;
  padding: 0 var(--gutter);
}

/* A reverse lockup rather than an inverted one: the card takes white and the
   `p` takes the ground, so the counter is a knockout. Inverting the file
   instead turns the card white and the white `p` with it, which draws a plain
   white square. `#FD2F1A` on `#D93A26` was the other dead end - one colour
   arguing with itself. */
.close-mark img {
  width: 100%;
  height: auto;                /* the `height` attribute would squash it flat */
  max-width: 880px;
  margin-inline: auto;
}

/* --- the phone's dock --------------------------------------------------- */

.dock {
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: 12px;
  z-index: 50;
  display: none;
  transform: translateY(140%);
  transition: transform .34s var(--ease);
}

.dock .btn { width: 100%; box-shadow: var(--shadow-lift); }
.dock.show { transform: none; }

@media (max-width: 780px) {
  .dock { display: block; }
  body.has-dock { padding-bottom: 0; }
  .hero h1 { max-width: 12ch; }
  .stores img { height: 46px; }
  .cue { display: none; }
}

/* --- entrance ----------------------------------------------------------- */

.js .rise {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity .8s var(--ease), transform .8s var(--ease);
  transition-delay: calc(var(--i, 0) * 80ms);
}

.js .rise.in { opacity: 1; transform: none; }

/* **An opacity-only entrance, for anything whose transform is already spoken
   for.** The promise cards carry their stagger offset and their parallax in
   one `translate3d`, and `.rise.in`'s `transform: none` is the more specific
   selector - so a card wearing `.rise` had both silently cancelled and the
   three of them sat in a flat row. `.lift` fades and leaves the transform to
   its owner. */
.js .lift { opacity: 0; transition: opacity .8s var(--ease); transition-delay: calc(var(--i, 0) * 80ms); }
.js .lift.in { opacity: 1; }

/* **Everything above degrades to a static page under reduced motion**, and it
   has to be done here rather than in the script: the entrances are CSS, so a
   script that only stopped the parallax would leave a reader with the page
   permanently mid-transition. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  .js .rise { opacity: 1; transform: none; transition: none; }
  .js .lift { opacity: 1; transition: none; }
  .js .cell { clip-path: none; transition: none; }
  .lines span > i { transform: none; transition: none; }
  .cue::after { animation: none; }
  .hero-shot, .cell-shot img { transform: none; }
  .quad li { transform: translateY(var(--off, 0px)); }
  .progress { animation: none; }
}
