/* Flock marketing site.
 *
 * One green screen: the wordmark, the flock loop under it at the middle of the
 * page, Apple's App Store badge below that, and a footer line. There is no second
 * section and nothing to scroll to — if you are adding one, this file is the
 * wrong shape for it.
 *
 * Colour tokens are lifted from the iOS design system
 * (Flock-iOS/Packages/FlockDesign/Sources/FlockDesign/ThemePalette.swift) so the
 * site and the app agree. `--green` is the launch screen's background — the
 * flock loop over it reproduces the app's launch animation exactly, which is the
 * point of using it.
 */

:root {
  --green: #3A7D5C;                        /* launchBackground light */
  --on-green: #FFFFFF;
  /* ⚠️ NOTHING ON THIS SITE USES `--on-green-dim`, `--on-green-rule` OR
     `--on-green-fill` as of v8. Named rather than counted, because they are not
     contiguous: `--on-green-hairline` is declared between them and IS still in use,
     by `.foot__rule`. Deleting it would take the footer's dividers with it.

     The three dressed the "Coming Soon" pill, which became a real App Store link
     when the app shipped, and is now Apple's own badge artwork (`.cta__badge`
     below — nothing here styles it). They are kept for two reasons: the
     note below is the canonical statement of the contrast rule on this background
     and is cited by name from privacy.html, terms.html, 404.html and
     Flock-API/src/pageShell.ts; and that shell mirrors these values in its own
     `--disabled-fill` / `--disabled-ink`, which still dress the invite page's
     disabled pill — a rendering only its preview script can now reach, but one the
     shell still carries. Prune them if that stops being true — but move the note
     somewhere before you do.

     DISABLED CONTROLS ONLY — not for text. Over `--green` in light mode this
     composites to #C8DBD1, which is 3.4:1: below WCAG AA's 4.5:1 for body copy.
     Raising the alpha does not rescue it, because the light green is the
     constraint rather than the alpha — even pure white on #3A7D5C is only
     4.92:1, and 0.9 alpha is 4.33:1. So anything that has to be *read* uses
     `--on-green`, and this token is reserved for a disabled control, which
     WCAG 1.4.3 exempts as an inactive component. (Dark mode is comfortable
     either way: 5.67:1 at this alpha.) Keeping the split this way means the
     contrast rule stays "dimmed is only ever a disabled control" as the page
     grows, rather than needing a per-element audit. */
  --on-green-dim: rgba(255, 255, 255, 0.72);
  --on-green-rule: rgba(255, 255, 255, 0.38);
  /* A STANDALONE line, as opposed to `--on-green-rule` above, which is a border
     drawn around something. The distinction is not stylistic: the pill's border
     encloses a fill and a label, which carry the shape even where the border
     itself is faint, so 0.38 there is supported by everything it surrounds. The
     footer's divider is 1px of nothing else — it has no fill, no text, and no
     enclosed area, so the same alpha reads as an artefact rather than a mark.
     At 0.38 it measures 2.00:1 over the light green, under WCAG 1.4.11's 3:1
     for non-text content; 0.68 gives 3.20:1 light and 5.25:1 dark.
     `aria-hidden` and "it's decorative" are both arguable exemptions from
     1.4.11, so treat the number as a legibility floor rather than a compliance
     one — it is still the reason not to pick the alpha by eye.
     Deliberately NOT `--on-green-dim` (0.72, and close enough to be tempting):
     that token's whole point is that its scope is "disabled controls only", and
     borrowing it for a hairline is exactly the erosion the note above guards
     against. */
  --on-green-hairline: rgba(255, 255, 255, 0.68);
  --on-green-fill: rgba(255, 255, 255, 0.10);

  /* The app's display face is SF Rounded — `Design.UI.roundedTitleFont` builds it
     from `fontDescriptor.withDesign(.rounded)`. `ui-rounded` is how CSS asks for
     that same face, so the web wordmark and the one on the phone are the same
     typeface rather than a lookalike.
     NOTE: Helvetica Rounded is listed but will never be hit on an Apple device —
     neither macOS nor iOS ships it. It (and Arial Rounded MT Bold, which they do
     ship) are here for non-Apple platforms, where `ui-rounded` resolves to
     nothing rounded at all and the stack degrades to plain system-ui. */
  --font-rounded: ui-rounded, "SF Pro Rounded", "Helvetica Rounded",
                  "Arial Rounded MT Bold", system-ui, -apple-system, sans-serif;

  /* The flock's width, FIXED — it deliberately does not grow with the viewport.
     0.5 × screen width is `LaunchFlockView.bandWidthFraction` on iOS, so on a
     phone this is the same size band as the splash screen.

     `min()` takes the SMALLER operand, so the 220px is what wins on viewports
     WIDER than 440px — it is a ceiling, not a floor, and the vw branch is the
     one that applies on phones. 220 is chosen as exactly 50vw of the widest
     iPhone in portrait (440pt, the 16 Pro Max), so every current phone takes
     the vw branch and the iOS parity is exact; landscape and desktop are held
     flat. At 200px the cap instead bit from 402pt upward, leaving the 16 Pro
     Max 9% narrow — the same class of silent mismatch as measuring the band
     against `.hero`'s padded box. A phone wider than 440pt would start being
     capped, which is the number to revisit.

     `vw`, NOT `%`. A percentage resolves against this element's containing
     block — `.hero`'s *content* box, i.e. viewport minus its 1.5rem side
     padding and minus any safe-area inset — so it came out ~12% narrower than
     the app (172.5px vs 196.5pt on a 393pt iPhone). `bandWidthFraction` is a
     fraction of the whole screen, and `vw` is the unit that means that. Keep it
     independent of `.hero`'s padding or the invariant quietly stops holding.

     The loop's 20:9 is also the band's 160:72 aspect, so height follows from
     width and needs no rule. */
  --flock-width: min(220px, 50vw);
  --wordmark-size: 3rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --green: #24503B;                      /* launchBackground dark */
  }
}

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

/* The green goes on <html> as well as <body> so it also paints the rubber-band
   overscroll area in mobile Safari — a background only on <body> stops at the
   document box and the bounce exposes the browser's own backdrop. Together with
   `viewport-fit=cover` and the `theme-color` metas (both in index.html) that is
   the full set: overscroll, safe-area insets, and toolbar tint respectively. */
html {
  background: var(--green);
  -webkit-text-size-adjust: 100%;

  /* No rubber-band bounce. This kills the overscroll *glow/bounce past the
     edges* without disabling scrolling itself, so the page still scrolls
     normally in the one case that needs it — a viewport too short for the
     content, e.g. a phone in landscape. That is why this is the right lever and
     `overflow: hidden` / `position: fixed` on <body> is not: those would also
     strand a landscape visitor on content they cannot reach.
     Note it disables pull-to-refresh too, which on a static page costs nothing.
     Unsupported before iOS 16, where the bounce simply remains — and lands on
     green either way, because <html> is painted above. */
  overscroll-behavior: none;
}

body {
  margin: 0;
  background: var(--green);
  color: var(--on-green);
  font: 400 17px/1.6 var(--font-rounded);
  -webkit-font-smoothing: antialiased;

  /* `svh` is the *small* viewport height — the page is sized as if Safari's
     toolbars are showing, so it never grows or jumps as they collapse on scroll.
     `vh` first as the fallback for anything without svh support. */
  min-height: 100vh;
  min-height: 100svh;

  /* Horizontal safe-area padding matters in landscape on a notched phone, where
     the inset is on the side rather than the top. */
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);

  /* Equal `1fr` above and below the hero centres it at exactly 50% of the page
     whatever the wordmark and video measure — that is the whole reason for the
     empty first row. Row 3 (`.below`) takes the rest and places the pill and the
     footer inside it. The `fr` rows collapse before the `auto` ones are squeezed,
     so a short viewport degrades to a normal stacked page instead of overlapping. */
  display: grid;
  grid-template-rows: 1fr auto 1fr;
}

/* ── Hero: wordmark over the flock, dead centre of the page ─────────────── */

.hero {
  grid-row: 2;
  display: grid;
  justify-items: center;
  padding: 0 1.5rem;
  /* Also painted green (identical to the page) purely so the video below has a
     guaranteed opaque box to blend against — see `mix-blend-mode` on .hero__flock. */
  background: var(--green);
}

.wordmark {
  margin: 0 0 0.5rem;
  font-family: var(--font-rounded);
  /* Fixed, not `clamp()`d against the viewport: the wordmark is sized to sit
     over a fixed-width flock, so scaling one without the other breaks the pair. */
  font-size: var(--wordmark-size);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--on-green);
}

.hero__flock,
.hero__still {
  display: block;
  width: var(--flock-width);
  height: auto;
  /* Black backing, and it is NOT decorative: `screen` treats black as a no-op
     (result = 1-(1-a)(1-b)), so a black layer contributes exactly nothing to the
     page. What it buys is the failure mode — any instant this element paints
     itself rather than a frame (a `load()` that empties it, a layer allocated
     before the poster decodes) it composites as black-under-screen, i.e.
     invisibly, instead of as WHITE, which under `screen` is opaque white over
     everything behind it. The pixels are identical in the normal case; only the
     transient differs. */
  background: #000;
  /* The encode is opaque white-on-black. `screen` keeps the white and drops the
     black to fully transparent against any backdrop: result = 1-(1-a)(1-b), so
     black contributes nothing. Do NOT swap this for an alpha video — one opaque
     H.264 decodes everywhere, and this way the bird colour is a CSS variable. */
  mix-blend-mode: screen;
}

/* The still is the DEFAULT; the video is revealed only once script has opted into
   motion. Reduced motion is handled by never setting the video's `src` (see
   index.html), not here: `display: none` does not cancel a media load, so a
   CSS-only rule would download the whole 2.4 MiB and then hide it — the opposite
   of the intent.

   The swap happens as soon as script opts in, NOT once the video reports
   `playing`. Waiting for `playing` would deadlock on iOS Safari: WebKit won't
   start a video that isn't in the render tree, so a `display: none` element
   waiting to become visible never fires the event that would reveal it. The
   video's own `poster` covers the gap until the first frame decodes, and also
   covers a refused autoplay — so there's no bare-background state to guard. */
.hero__flock { display: none; }
.hero__still { display: block; }

.motion .hero__flock { display: block; }
.motion .hero__still { display: none; }

/* ── Below the flock: the pill, then the footer ─────────────────────────── */

/* Equal `1fr` above and below the pill again — here that centres it between the
   bottom of the flock and the top of the footer, i.e. halfway down the lower
   half of the page. The footer is the last row, so it sits on the bottom edge. */
.below {
  grid-row: 3;
  display: grid;
  grid-template-rows: 1fr auto 1fr auto;
  justify-items: center;
}

/* The same 1.5rem gutter `.hero` and `.foot` hold. The badge is a fixed ~120px
   wide and carries 10px of its own padding either side, so with this gutter the
   row needs ~188px before anything is squeezed — narrower than any viewport that
   exists, which is the point: it is here as the horizontal half of Apple's
   clear-space rule (see `.cta__badge`), not as a layout fix. That is a reason to
   keep it which survives the badge never needing it. */
.cta {
  grid-row: 2;
  margin: 0;
  padding: 0 1.5rem;
}

/* The App Store badge — Apple's artwork, so this rule's job is to place it and
   then get out of the way. Every earlier version of this element was ours to
   style (a disabled "Coming Soon" pill, then a solid white one); this one is not.
   The App Store Marketing Guidelines say to use only the badge artwork provided
   and not to modify it, which rules out the styled text button that was here.

   `display: block` on BOTH the link and the image, and the link is the half that
   is easy to miss. The image being block kills the descender gap *inside* the
   link; the link being block is what keeps it off the line box in `<p class="cta">`
   outside it. An `inline-block` whose only child is `display: block` has no in-flow
   line boxes, so its baseline becomes its bottom margin edge — it sits ON the
   paragraph's text baseline, and the strut (17px at line-height 1.6) hangs its
   descent underneath. The clear space then measures 10px at the top and ~17px at
   the bottom, and the badge rides above the centre of its grid row.

   Nothing about that is visible in the markup, and the previous `.cta__button`
   didn't have it: that element contained text, so its baseline was the text's own
   and its 0.7rem bottom padding already cleared the strut. The badge has no text
   to sit on. The invite page's copy escapes it by accident — its `.badge` is a
   grid item, which blockifies — so this is the one place the rule has to be
   written down.

   The height is the artwork's own 40px, which is also Apple's stated onscreen
   minimum, so it is a floor and not a starting point. `width: auto` keeps the
   119.66:40 ratio off the rounding in the `width`/`height` attributes, which are
   there for the aspect-ratio box before the SVG loads and are necessarily
   integers. Setting both in CSS is how the badge silently gets distorted.

   No `background`, `border`, `border-radius` or `filter`: the badge brings its own
   white fill and outline, and each of those would be a modification.

   The WHITE lockup in both schemes, so this is one `<img>` rather than a
   `<picture>`. Both were tried on the green — light and dark — and white reads
   better on it, so the field changing colour doesn't force a swap. The invite
   page's badge does swap, because it sits on the card rather than the green. */
.cta__badge {
  display: block;
  /* Apple asks for clear space of at least a quarter the badge height — 10px —
     on every side. The grid rows either side give far more; this makes the
     horizontal minimum explicit rather than incidental, and pairs with `.cta`'s
     gutter. Block display is what makes all four of these measure equally — see
     above. */
  padding: 10px;
}

.cta__badge img {
  display: block;
  height: 40px;
  width: auto;
}

/* The ring sits on the padding's outer edge, so it lands on the green rather than
   on the badge — which keeps it visible against the white lockup AND means the
   focus state is drawn around the artwork instead of over it. `outline` never
   composites into the image, so this is not a modification of the badge. */
.cta__badge:focus-visible {
  outline: 2px solid var(--on-green);
  outline-offset: 0;
  border-radius: 12px;
}

.foot {
  grid-row: 4;
  /* Full white, not the dim token — this is body copy at 0.8rem, too small for
     the large-text allowance, and the dim token is 3.4:1 here. See the token. */
  color: var(--on-green);
  font-size: 0.8rem;
  text-align: center;
  /* The inset keeps the line clear of the home indicator; the 1.25rem is the
     floor for devices that have no inset to report. */
  padding: 1.25rem 1.5rem calc(1.25rem + env(safe-area-inset-bottom));
}

.foot p { margin: 0; }

/* Contact, a vertical divider, then the copyright — one centred line.
   Flex rather than a separator character (`|`, `·`) between two text nodes: the
   divider is then a real hairline that sits at a fixed height regardless of the
   face's glyph metrics, and the space either side of it is one `gap` rather than
   two spaces whose width is the font's business.

   `wrap` is not decoration. A flex item defaults to `min-width: auto`, so
   without it these three don't shrink below min-content *or* wrap — they
   overflow, and a horizontally scrollable page is a worse outcome than a
   two-line footer on a page whose whole premise is that there is nothing to
   scroll to. It doesn't bite at normal sizes (min-content is ~155px against the
   ~272px a 320px viewport leaves), only under large accessibility text — which
   is WCAG 1.4.10 and exactly the case a fixed single line is wrong for. When it
   does wrap the hairline dangles at the end of the first line; the tighter
   row-gap keeps that from reading as a gap in the middle of a sentence.

   `em`, NOT `rem`: this sizes against `.foot`'s own font-size, so the space
   either side of the divider stays proportional to the divider's `1em` height
   and to the text it separates. With `rem` the two are pinned to different
   things and drift apart the moment the footer's size changes. 1.1em ≈ the
   0.9rem this started as, at the footer's 0.8rem. */
.foot__line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.25em 1.1em;
}

/* Empty, and `aria-hidden` in the markup — it is punctuation rather than
   content, so nothing should announce it. `1em` ties its height to the footer's
   own font size, and `flex: none` stops a narrow viewport shrinking a 1px item
   to nothing.
   Its own token rather than `--on-green-rule` (which dressed the disabled pill
   this page used to carry, and is now unused — see its note): a border around a
   filled, labelled shape can be fainter than a line standing on its own. See
   `--on-green-hairline` for the measured ratios. */
.foot__rule {
  flex: none;
  width: 1px;
  height: 1em;
  background: var(--on-green-hairline);
}

/* ── Links on the green ─────────────────────────────────────────────────── */

/* The footer's Contact link, and 404.html's way back — that page is the same
   green field and loads this same stylesheet (there is no second one), though
   its own layout lives in that file. Full white rather than the dim token, for
   the contrast reason in the token's note above. */
.foot a,
.notfound a {
  color: var(--on-green);
  text-underline-offset: 0.2em;
}
