/* Rollfilm — rollfilm.org
   ---------------------------------------------------------------------------
   The page is built the way the app is: quiet, precise, and out of the way of
   the pictures. Big type at the top of the hierarchy, a lot of air, and one
   idea per band. Nothing decorative moves.

   What is left of the film language is deliberate and small: the mono edge
   voice used for every label, the frame number printed sideways in the margin
   beside each section, and the warm base colour of a developed negative used
   as the single accent. That is the whole costume. It should read as a maker's
   mark, not as a theme.

   The screenshots are all taken in the app's Graphite Dark. On a light band
   they are mounted like a window - hairline, radius, real shadow - so a dark
   rectangle on white reads as a screen rather than as a mistake. On the two
   dark bands they simply glow.

   No frameworks, no external assets, no webfonts. A page that promises no
   third parties cannot go and fetch a font from one; on Apple hardware the
   system stack is SF Pro anyway, which is the face the app itself uses. */

/* ---- Tokens ------------------------------------------------------------- */

:root {
  /* Surfaces and ink. Every component reads these rather than a literal, so a
     band can restate them and its contents adapt without a single override. */
  --page: #ffffff;
  --page-2: #f5f5f7;
  --surface: #ffffff;
  --fg: #1d1d1f;
  --fg-2: #6e6e73;
  --fg-3: #86868b;
  --rule: rgba(0, 0, 0, 0.13);
  --rule-soft: rgba(0, 0, 0, 0.07);
  --btn-bg: #1d1d1f;
  --btn-fg: #ffffff;
  --focus: #1d1d1f;

  /* The orange mask of a colour negative, held well back. It marks things, it
     never fills them. */
  --film: #a8511f;

  /* The mount a screenshot sits in on a light band. */
  --mount: 0 0 0 1px rgba(0, 0, 0, 0.06),
           0 2px 5px rgba(0, 0, 0, 0.04),
           0 14px 30px -10px rgba(0, 0, 0, 0.14),
           0 46px 84px -30px rgba(0, 0, 0, 0.22);

  /* Type. The steps are picked so the ramp still reads at 380px and at 2560,
     and the tracking tightens as the size grows, the way a good face wants. */
  --t-hero: clamp(2.35rem, 5vw, 4.4rem);
  --t-h2: clamp(1.95rem, 4vw, 3.15rem);
  --t-h3: clamp(1.3rem, 1.9vw, 1.7rem);
  --t-lede: clamp(1.1rem, 1.65vw, 1.45rem);
  --t-body: 1.0625rem;
  --t-sm: 0.9375rem;
  --t-xs: 0.8125rem;
  --t-mark: 0.6875rem;

  /* Rhythm. One vertical unit for every band, one gutter for every column. */
  --pad-y: clamp(72px, 9.2vw, 132px);
  --pad-y-tight: clamp(52px, 6.4vw, 88px);
  --gap: clamp(30px, 4.4vw, 64px);

  --w: 1040px;
  --w-wide: 1280px;
  --w-text: 700px;
  --gutter: clamp(22px, 4.4vw, 40px);

  --header-h: 52px;

  --r-lg: 22px;
  --r: 14px;
  --r-sm: 10px;
  --r-pill: 980px;

  /* Out fast, settle slow: the curve everything on the page decelerates on. */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-io: cubic-bezier(0.4, 0, 0.2, 1);

  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  color-scheme: light dark;
}

@media (prefers-color-scheme: dark) {
  :root {
    --page: #000000;
    --page-2: #0c0c0e;
    --surface: #141417;
    --fg: #f5f5f7;
    --fg-2: #a1a1a6;
    --fg-3: #86868b;
    --rule: rgba(255, 255, 255, 0.16);
    --rule-soft: rgba(255, 255, 255, 0.09);
    --btn-bg: #f5f5f7;
    --btn-fg: #141417;
    --focus: #f5f5f7;
    --film: #d98a55;
    --mount: 0 0 0 1px rgba(255, 255, 255, 0.09),
             0 30px 70px -30px rgba(0, 0, 0, 0.9);
  }
}

/* The two dark bands. They restate the palette rather than inheriting it, so
   they stay dark in both page modes - which is the point, because every
   screenshot on this site was taken in a dark app. */
.band-dark {
  --page: #08080a;
  --page-2: #101014;
  --surface: #17171b;
  --fg: #f5f5f7;
  --fg-2: #a1a1a6;
  --fg-3: #85858b;
  --rule: rgba(255, 255, 255, 0.16);
  --rule-soft: rgba(255, 255, 255, 0.08);
  --btn-bg: #f5f5f7;
  --btn-fg: #17171b;
  --focus: #f5f5f7;
  --film: #d98a55;
  --mount: 0 0 0 1px rgba(255, 255, 255, 0.1),
           0 40px 90px -40px rgba(0, 0, 0, 0.9);
  background: var(--page);
  color: var(--fg);
}

.band-tint {
  background: var(--page-2);
}

/* ---- Base --------------------------------------------------------------- */

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

html {
  scroll-behavior: smooth;
  /* an anchor must not land under the sticky bar */
  scroll-padding-top: calc(var(--header-h) + 16px);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--page);
  color: var(--fg);
  font-family: var(--sans);
  font-size: var(--t-body);
  line-height: 1.55;
  letter-spacing: -0.011em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

/* Every screenshot carries width/height so its space is reserved before it
   loads. Those attributes are also presentational hints, so without height:auto
   the picture keeps its full pixel height while max-width squeezes its width. */
img {
  max-width: 100%;
  height: auto;
  display: block;
}

a {
  color: inherit;
  text-decoration-color: color-mix(in srgb, currentColor 30%, transparent);
  text-underline-offset: 0.22em;
  text-decoration-thickness: 1px;
  transition: text-decoration-color 0.2s var(--ease-io), color 0.2s var(--ease-io);
}

a:hover {
  text-decoration-color: currentColor;
}

h1,
h2,
h3,
h4 {
  margin: 0;
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: -0.021em;
  text-wrap: balance;
}

h1 {
  font-size: var(--t-hero);
  letter-spacing: -0.026em;
  line-height: 1.04;
}

h2 {
  font-size: var(--t-h2);
  letter-spacing: -0.024em;
}

h3 {
  font-size: var(--t-h3);
  letter-spacing: -0.018em;
  line-height: 1.12;
}

p {
  margin: 0;
  text-wrap: pretty;
}

ul,
ol {
  margin: 0;
  padding: 0;
}

strong {
  font-weight: 600;
}

code {
  font-family: var(--mono);
  font-size: 0.92em;
}

.wrap {
  width: min(var(--w), 100% - var(--gutter) * 2);
  margin-inline: auto;
}

.wrap-wide {
  width: min(var(--w-wide), 100% - var(--gutter) * 2);
  margin-inline: auto;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: fixed;
  left: 12px;
  top: -80px;
  z-index: 200;
  padding: 11px 18px;
  border-radius: var(--r-pill);
  background: #1d1d1f;
  color: #fff;
  font-size: var(--t-sm);
  text-decoration: none;
  transition: top 0.24s var(--ease);
}

.skip-link:focus {
  top: 12px;
}

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: 4px;
}

::selection {
  background: color-mix(in srgb, var(--film) 28%, transparent);
}

/* ---- Grain -------------------------------------------------------------- */
/* One still layer of emulsion over the whole page, so no band is ever a flat
   fill. It does not move: a flickering overlay is a party trick, a fixed one is
   a surface, and a still layer costs nothing to composite.

   No blend mode, because overlay and soft-light both vanish against white and
   the light bands would lose the texture the dark ones keep. Plain grey noise
   at a few per cent lands on both: it lifts the blacks a little and settles on
   the whites a little, which is what emulsion does.

   It sits under the bar, so the glass up there stays clean glass. */

.grain {
  position: fixed;
  inset: 0;
  z-index: 90;
  pointer-events: none;
  opacity: 0.075;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='4' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='180' height='180' filter='url(%23g)'/></svg>");
  background-size: 180px 180px;
  background-repeat: repeat;
}

@media (prefers-color-scheme: dark) {
  .grain {
    opacity: 0.085;
  }
}

/* The page's second voice: labels, frame numbers, counters. Anything that is a
   marking rather than a sentence. */
.eyebrow {
  font-family: var(--mono);
  font-size: var(--t-mark);
  font-weight: 500;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.eyebrow-film {
  color: var(--film);
}

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

/* One bar, all the way down: frosted light grey glass over whatever happens to
   be passing beneath it. It does not change colour with the band underneath,
   so the chrome of the page stays put while the page moves through it. The
   glass is what varies: over the dark stage it settles to a mid grey, over the
   white bands it lifts to nearly white, and both are the same pane. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  --fg: #1d1d1f;
  --fg-2: #4b4b50;
  --fg-3: #6e6e73;
  --rule: rgba(0, 0, 0, 0.18);
  --focus: #1d1d1f;
  color: var(--fg);
  background: rgba(246, 246, 248, 0.72);
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  transition: box-shadow 0.3s var(--ease-io);
}

.site-header.is-stuck {
  box-shadow: 0 1px 12px -6px rgba(0, 0, 0, 0.3);
}

.nav {
  display: flex;
  align-items: center;
  gap: 20px;
  height: var(--header-h);
  width: min(var(--w-wide), 100% - var(--gutter) * 2);
  margin-inline: auto;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin-right: auto;
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  text-decoration: none;
}

.brand-mark {
  display: inline-flex;
  width: 24px;
  height: 24px;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: clamp(13px, 1.6vw, 24px);
  font-size: var(--t-xs);
  letter-spacing: -0.005em;
}

/* Never let a link give up width: the pill is the narrowest item and the first
   the browser would squeeze, and a squeezed pill puts its own label outside it. */
.nav-links a {
  position: relative;
  flex: none;
  padding: 4px 0;
  white-space: nowrap;
  text-decoration: none;
  color: color-mix(in srgb, currentColor 74%, transparent);
  transition: color 0.2s var(--ease-io);
}

.nav-links a:hover {
  color: currentColor;
}

/* The section you are looking at keeps a mark under it. */
.nav-links a.is-here {
  color: currentColor;
}

.nav-links a:not(.nav-cta)::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: currentColor;
  opacity: 0;
  transform: scaleX(0.4);
  transition: opacity 0.24s var(--ease), transform 0.24s var(--ease);
}

.nav-links a.is-here:not(.nav-cta)::after {
  opacity: 0.55;
  transform: none;
}

/* The glass is always light, so the pill on it is always dark. Scoped through
   .nav-links, because the plain-link rule above is one step more specific than
   a bare .nav-cta and would otherwise take the pill's padding away and leave
   its own label sticking out of the rounded ends. */
.nav-links a.nav-cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 16px;
  border-radius: var(--r-pill);
  background: #1d1d1f;
  color: #ffffff;
  font-weight: 500;
  transition: opacity 0.2s var(--ease-io);
}

.nav-links a.nav-cta:hover {
  opacity: 0.82;
}

/* The menu, on a screen too narrow for a row of links. A checkbox drives it, so
   it opens with JavaScript switched off and space-bar toggles it from the
   keyboard the way a disclosure should. */
.nav-check {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.nav-burger {
  display: none;
  width: 34px;
  height: 34px;
  margin-right: -8px;
  border-radius: 8px;
  cursor: pointer;
  position: relative;
}

.nav-burger span {
  position: absolute;
  left: 9px;
  width: 16px;
  height: 1.5px;
  border-radius: 2px;
  background: currentColor;
  transition: transform 0.32s var(--ease), opacity 0.2s var(--ease-io);
}

.nav-burger span:first-child { top: 14px; }
.nav-burger span:last-child { top: 19px; }

.nav-check:checked ~ .nav-burger span:first-child {
  transform: translateY(2.5px) rotate(45deg);
}

.nav-check:checked ~ .nav-burger span:last-child {
  transform: translateY(-2.5px) rotate(-45deg);
}

.nav-check:focus-visible ~ .nav-burger {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

@media (max-width: 900px) {
  .nav-burger {
    display: block;
  }

  /* A sheet, not a dropdown: it takes the screen under the bar so the menu is
     the only thing you are reading while it is open.

     It is painted rather than frosted. An ancestor with backdrop-filter becomes
     the backdrop root for everything inside it, and this panel hangs below the
     bar, where that root has nothing left to sample - so a blur here would
     filter nothing and leave the page legible straight through the menu. */
  .nav-links {
    position: fixed;
    left: 0;
    right: 0;
    top: var(--header-h);
    display: block;
    height: calc(100dvh - var(--header-h));
    max-height: 0;
    overflow: hidden;
    padding-inline: var(--gutter);
    background: #f2f2f4;
    font-size: 1.0625rem;
    transition: max-height 0.42s var(--ease);
  }

  .nav-check:checked ~ .nav-links {
    max-height: calc(100dvh - var(--header-h));
    overflow-y: auto;
  }

  .nav-links a {
    display: block;
    padding: 15px 0;
    border-bottom: 1px solid var(--rule-soft);
    color: currentColor;
  }

  .nav-links a:last-child {
    border-bottom: 0;
  }

  .nav-links a:not(.nav-cta)::after {
    display: none;
  }

  .nav-links a.nav-cta {
    width: fit-content;
    margin: 16px 0 24px;
    padding: 11px 22px;
    font-size: var(--t-body);
  }

  /* The legal pages carry one link home rather than a menu, and they have no
     burger to open one with, so that link never collapses. */
  .nav-links.is-plain {
    position: static;
    display: flex;
    max-height: none;
    overflow: visible;
    padding: 0;
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    font-size: var(--t-xs);
  }

  .nav-links.is-plain a {
    display: inline-block;
    padding: 4px 0;
    border: 0;
  }
}

/* ---- Buttons ------------------------------------------------------------ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  padding: 11px 23px;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  font-size: var(--t-body);
  font-weight: 400;
  letter-spacing: -0.01em;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: opacity 0.2s var(--ease-io), background 0.2s var(--ease-io),
              border-color 0.2s var(--ease-io), transform 0.2s var(--ease);
}

.btn-fill {
  background: var(--btn-bg);
  color: var(--btn-fg);
}

.btn-fill:hover {
  opacity: 0.86;
}

.btn-ghost {
  border-color: color-mix(in srgb, currentColor 26%, transparent);
  color: var(--fg);
}

.btn-ghost:hover {
  border-color: color-mix(in srgb, currentColor 55%, transparent);
  background: color-mix(in srgb, currentColor 5%, transparent);
}

.btn:active {
  transform: scale(0.985);
}

/* ---- Bands and section heads -------------------------------------------- */

.band {
  position: relative;
  padding-block: var(--pad-y);
}

/* The first band runs up under the bar instead of starting below it, so the
   glass has something behind it from the very first pixel rather than only
   once you have scrolled. The hero puts the height back as padding. */
.band-lead {
  margin-top: calc(var(--header-h) * -1);
}

.band-lead .hero {
  padding-top: calc(clamp(76px, 12vh, 136px) + var(--header-h));
}

/* The measure is set on the heading and the paragraph, never on the block that
   holds them: ch resolves against the element's own font-size, so a width put
   on the wrapper is counted in 17px characters and then applied to a 50px
   heading, which clamps it to roughly half the line it should get. */
.head {
  margin-bottom: clamp(38px, 5vw, 68px);
}

.head .eyebrow {
  display: block;
  margin-bottom: 15px;
}

.head h2 {
  max-width: 18ch;
}

.head p {
  margin-top: 18px;
  max-width: 44ch;
  font-size: var(--t-lede);
  line-height: 1.4;
  letter-spacing: -0.015em;
  color: var(--fg-2);
}

.head-center {
  text-align: center;
}

.head-center h2,
.head-center p {
  margin-inline: auto;
}

/* ---- Scroll reveal ------------------------------------------------------ */
/* Everything arrives the same way: up a little, and into focus. It is the only
   motion left on the page and it happens once.

   The hiding is scoped to .js, which a one-line script in the head puts on the
   document. Nothing can hide something it cannot then bring back, so with
   JavaScript off these rules never apply and the page is simply a page. */

.js .reveal {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
  transition-delay: var(--d, 0ms);
}

.js .reveal.is-in {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .js .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

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

/* No bottom padding: the gallery below it is on the same dark band and brings
   its own, so one gap separates them instead of two stacked ones. */
.hero {
  padding-top: clamp(76px, 12vh, 136px);
  padding-bottom: 0;
  text-align: center;
  overflow: hidden;
}

.hero h1 {
  max-width: 18ch;
  margin-inline: auto;
}

/* The second sentence is a deck, not a second headline: same weight and
   tracking, two thirds of the size, so the hero is one strong statement with a
   quieter line under it rather than four lines all shouting equally. */
.hero h1 .dim {
  display: block;
  margin-top: 0.14em;
  font-size: 0.62em;
  letter-spacing: -0.021em;
  color: var(--fg-2);
}

.hero .eyebrow {
  display: block;
  margin-bottom: clamp(20px, 3vw, 32px);
}

.hero-lede {
  max-width: 46ch;
  margin: clamp(20px, 2.6vw, 30px) auto 0;
  font-size: var(--t-lede);
  line-height: 1.42;
  letter-spacing: -0.015em;
  color: var(--fg-2);
}

.hero-lede a {
  color: var(--fg);
}

.cta-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 10px 22px;
  margin-top: clamp(26px, 3.4vw, 38px);
}

.hero-facts {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px 28px;
  list-style: none;
  margin-top: clamp(32px, 4vw, 46px);
}

.hero-facts li {
  font-family: var(--mono);
  font-size: var(--t-mark);
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--fg-3);
}

/* The hero print: the app window on the stage, with the light it throws behind
   it. The glow is one soft radial, sized off the picture, never animated. */
.hero-shot {
  position: relative;
  max-width: 1120px;
  margin: clamp(44px, 6vw, 80px) auto 0;
}

.hero-shot::before {
  content: "";
  position: absolute;
  inset: 8% -4% -14%;
  z-index: 0;
  border-radius: 50%;
  background: radial-gradient(
    ellipse at 50% 50%,
    rgba(255, 255, 255, 0.13),
    rgba(255, 255, 255, 0.04) 45%,
    transparent 72%
  );
  filter: blur(34px);
  pointer-events: none;
}

.hero-shot img {
  position: relative;
  z-index: 1;
  width: 100%;
  border-radius: clamp(10px, 1.2vw, 16px);
  box-shadow: var(--mount);
}

/* ---- Gallery ------------------------------------------------------------ */

/* The frame and its rail have to share a screen, so the head above them gives
   up some of the room a head usually gets. */
.gallery .head {
  margin-bottom: clamp(28px, 3.4vw, 46px);
}

/* One radio per shot drives both the big frame and the rail below it, so the
   gallery pages with JavaScript off and the arrow keys walk it from the
   keyboard for free. */
.gal-radio {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

/* One height for the frame, so the picture and the rail stay on the same
   screen: the next shot is always a click away, never a scroll. */
.gal {
  --shot-h: min(46vh, 520px);
}

.gal-frame {
  position: relative;
  max-width: calc(var(--shot-h) * 16 / 9);
  margin-inline: auto;
  border-radius: var(--r);
  background: var(--surface);
  box-shadow: var(--mount);
}

.gal-frame figure {
  margin: 0;
  display: none;
}

/* Every shot is shown at exactly one size whatever its own ratio: the stage is
   a 16:9 box and the picture fills it, anchored to the top so the part of the
   window where the app identifies itself is always kept. The frame never
   changes height as you page through, which is the whole point. */
.gal-stage {
  position: relative;
  aspect-ratio: 16 / 9;
  max-height: var(--shot-h);
  border-radius: var(--r) var(--r) 0 0;
  overflow: hidden;
}

.gal-stage img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
}

.gal-cap {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
  padding: 15px 18px;
  border-top: 1px solid var(--rule-soft);
  font-size: var(--t-sm);
  color: var(--fg-2);
}

.gal-cap b {
  font-family: var(--mono);
  font-size: var(--t-mark);
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--fg);
}

.gal-count {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  color: var(--film);
  white-space: nowrap;
}

.gal-count i {
  font-style: normal;
  opacity: 0.45;
  margin: 0 1px;
}

/* Paging. Each frame carries its own pair of labels pointing at the radio
   before and after it and wrapping at both ends, so the gallery still pages
   with JavaScript off. Narrow screens get a button over the picture; from
   1024px up they move out into the margin and touch nothing. */
.gal-nav {
  position: absolute;
  top: 0;
  bottom: 0;
  margin-block: auto;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--r-pill);
  background: rgba(8, 8, 10, 0.5);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  color: #f5f5f7;
  cursor: pointer;
  opacity: 0.75;
  transition: opacity 0.2s var(--ease-io), transform 0.3s var(--ease);
}

.gal-nav svg {
  width: 15px;
  height: 15px;
}

.gal-prev { left: 12px; }
.gal-next { right: 12px; }

.gal-nav:hover { opacity: 1; }
.gal-prev:hover { transform: translateX(-3px); }
.gal-next:hover { transform: translateX(3px); }

@media (min-width: 1024px) {
  .gal-frame {
    max-width: min(calc(var(--shot-h) * 16 / 9), 100% - 190px);
  }

  .gal-nav {
    width: 44px;
    height: 44px;
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    color: var(--fg);
    opacity: 0.42;
  }

  .gal-nav svg {
    width: 20px;
    height: 20px;
  }

  .gal-prev { left: -76px; }
  .gal-next { right: -76px; }

  .gal-nav:hover { opacity: 1; }
}

/* The rail. Fixed frames that scroll under the finger, faded out at both ends
   so it reads as a strip that continues rather than one that has been cut. */
.gal-rail {
  display: flex;
  gap: 10px;
  margin-top: 22px;
  padding: 4px 2px 6px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 40px, #000 calc(100% - 40px), transparent);
  mask-image: linear-gradient(to right, transparent, #000 40px, #000 calc(100% - 40px), transparent);
}

/* The native bar is hidden and the rail's own is drawn below instead. Twelve
   frames never fit on a screen, and a strip with nothing under it looks like a
   row that simply ends - but the platform bar cannot be relied on to say so:
   on macOS it is an overlay that appears only once you are already scrolling,
   which is exactly one moment too late. This one is always there, is the same
   in every browser, and can be dragged and clicked like the real thing. */
.gal-rail::-webkit-scrollbar {
  display: none;
}

.gal-bar {
  position: relative;
  width: min(440px, 62%);
  height: 5px;
  margin: 18px auto 0;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--fg) 13%, transparent);
  cursor: pointer;
  touch-action: none;
}

.gal-bar[hidden] {
  display: none;
}

.gal-bar i {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  display: block;
  min-width: 34px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--fg) 40%, transparent);
  transition: background 0.2s var(--ease-io);
}

.gal-bar:hover i,
.gal-bar.is-dragging i {
  background: color-mix(in srgb, var(--fg) 66%, transparent);
}

.gal-rail label {
  flex: 0 0 clamp(112px, 12vw, 150px);
  scroll-snap-align: center;
  cursor: pointer;
  opacity: 0.42;
  transition: opacity 0.28s var(--ease-io);
}

.gal-rail label:hover {
  opacity: 0.8;
}

.gal-rail img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  object-position: center top;
  border-radius: var(--r-sm);
  box-shadow: 0 0 0 1px var(--rule);
  transition: box-shadow 0.28s var(--ease-io);
}

.gal-rail span {
  display: block;
  margin-top: 9px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--fg-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Selected frame: show its figure, light up its thumbnail. One rule per shot,
   because CSS cannot index siblings by position. */
#shot-1:checked ~ .gal-frame figure:nth-of-type(1),
#shot-2:checked ~ .gal-frame figure:nth-of-type(2),
#shot-3:checked ~ .gal-frame figure:nth-of-type(3),
#shot-4:checked ~ .gal-frame figure:nth-of-type(4),
#shot-5:checked ~ .gal-frame figure:nth-of-type(5),
#shot-6:checked ~ .gal-frame figure:nth-of-type(6),
#shot-7:checked ~ .gal-frame figure:nth-of-type(7),
#shot-8:checked ~ .gal-frame figure:nth-of-type(8),
#shot-9:checked ~ .gal-frame figure:nth-of-type(9),
#shot-10:checked ~ .gal-frame figure:nth-of-type(10),
#shot-11:checked ~ .gal-frame figure:nth-of-type(11),
#shot-12:checked ~ .gal-frame figure:nth-of-type(12) {
  display: block;
  animation: shot-in 0.5s var(--ease) both;
}

#shot-1:checked ~ .gal-rail label:nth-of-type(1),
#shot-2:checked ~ .gal-rail label:nth-of-type(2),
#shot-3:checked ~ .gal-rail label:nth-of-type(3),
#shot-4:checked ~ .gal-rail label:nth-of-type(4),
#shot-5:checked ~ .gal-rail label:nth-of-type(5),
#shot-6:checked ~ .gal-rail label:nth-of-type(6),
#shot-7:checked ~ .gal-rail label:nth-of-type(7),
#shot-8:checked ~ .gal-rail label:nth-of-type(8),
#shot-9:checked ~ .gal-rail label:nth-of-type(9),
#shot-10:checked ~ .gal-rail label:nth-of-type(10),
#shot-11:checked ~ .gal-rail label:nth-of-type(11),
#shot-12:checked ~ .gal-rail label:nth-of-type(12) {
  opacity: 1;
}

#shot-1:checked ~ .gal-rail label:nth-of-type(1) img,
#shot-2:checked ~ .gal-rail label:nth-of-type(2) img,
#shot-3:checked ~ .gal-rail label:nth-of-type(3) img,
#shot-4:checked ~ .gal-rail label:nth-of-type(4) img,
#shot-5:checked ~ .gal-rail label:nth-of-type(5) img,
#shot-6:checked ~ .gal-rail label:nth-of-type(6) img,
#shot-7:checked ~ .gal-rail label:nth-of-type(7) img,
#shot-8:checked ~ .gal-rail label:nth-of-type(8) img,
#shot-9:checked ~ .gal-rail label:nth-of-type(9) img,
#shot-10:checked ~ .gal-rail label:nth-of-type(10) img,
#shot-11:checked ~ .gal-rail label:nth-of-type(11) img,
#shot-12:checked ~ .gal-rail label:nth-of-type(12) img {
  box-shadow: 0 0 0 2px var(--fg);
}

/* The radios carry the focus and they are invisible, so the ring has to be
   drawn on the thumbnail the focused radio belongs to - otherwise a keyboard
   user arrowing along the rail sees nothing move. */
#shot-1:focus-visible ~ .gal-rail label:nth-of-type(1) img,
#shot-2:focus-visible ~ .gal-rail label:nth-of-type(2) img,
#shot-3:focus-visible ~ .gal-rail label:nth-of-type(3) img,
#shot-4:focus-visible ~ .gal-rail label:nth-of-type(4) img,
#shot-5:focus-visible ~ .gal-rail label:nth-of-type(5) img,
#shot-6:focus-visible ~ .gal-rail label:nth-of-type(6) img,
#shot-7:focus-visible ~ .gal-rail label:nth-of-type(7) img,
#shot-8:focus-visible ~ .gal-rail label:nth-of-type(8) img,
#shot-9:focus-visible ~ .gal-rail label:nth-of-type(9) img,
#shot-10:focus-visible ~ .gal-rail label:nth-of-type(10) img,
#shot-11:focus-visible ~ .gal-rail label:nth-of-type(11) img,
#shot-12:focus-visible ~ .gal-rail label:nth-of-type(12) img {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
}

@keyframes shot-in {
  from {
    opacity: 0;
    transform: scale(1.008);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  #shot-1:checked ~ .gal-frame figure:nth-of-type(1),
  #shot-2:checked ~ .gal-frame figure:nth-of-type(2),
  #shot-3:checked ~ .gal-frame figure:nth-of-type(3),
  #shot-4:checked ~ .gal-frame figure:nth-of-type(4),
  #shot-5:checked ~ .gal-frame figure:nth-of-type(5),
  #shot-6:checked ~ .gal-frame figure:nth-of-type(6),
  #shot-7:checked ~ .gal-frame figure:nth-of-type(7),
  #shot-8:checked ~ .gal-frame figure:nth-of-type(8),
  #shot-9:checked ~ .gal-frame figure:nth-of-type(9),
  #shot-10:checked ~ .gal-frame figure:nth-of-type(10),
  #shot-11:checked ~ .gal-frame figure:nth-of-type(11),
  #shot-12:checked ~ .gal-frame figure:nth-of-type(12) {
    animation: none;
  }
}

/* ---- The path: alternating steps ---------------------------------------- */

.steps {
  display: grid;
  gap: clamp(72px, 9vw, 130px);
}

.step {
  display: grid;
  grid-template-columns: minmax(0, 0.86fr) minmax(0, 1.14fr);
  gap: var(--gap);
  align-items: center;
}

/* Odd steps put the picture on the left. Order is set on the children rather
   than with direction, so the reading order in the markup stays copy first. */
.step:nth-child(even) .step-copy {
  order: 2;
}

.step-no {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 18px;
  font-family: var(--mono);
  font-size: var(--t-mark);
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.step-no b {
  font-family: var(--sans);
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--film);
}

.step-lead {
  margin-top: 16px;
  max-width: 44ch;
  font-size: var(--t-lede);
  line-height: 1.42;
  letter-spacing: -0.014em;
  color: var(--fg-2);
}

.points {
  list-style: none;
  display: grid;
  gap: 11px;
  margin-top: 26px;
  max-width: 46ch;
  font-size: var(--t-sm);
  color: var(--fg-2);
}

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

.points li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 0.62em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--film);
  opacity: 0.85;
}

.points strong {
  color: var(--fg);
}

/* A screenshot on a light band, mounted like a screen rather than pasted on. */
.shot {
  border-radius: clamp(10px, 1vw, 14px);
  overflow: hidden;
  box-shadow: var(--mount);
}

.tag {
  display: inline-block;
  margin-left: 10px;
  padding: 3px 9px;
  border: 1px solid var(--rule);
  border-radius: var(--r-pill);
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-3);
  vertical-align: 0.32em;
}

@media (max-width: 860px) {
  .step {
    grid-template-columns: 1fr;
    gap: 30px;
  }

  .step:nth-child(even) .step-copy {
    order: 0;
  }
}

/* The closing panel of the path: three facts, no picture, so the sequence ends
   on a statement instead of trailing off. */
.keep {
  margin-top: clamp(72px, 9vw, 130px);
  padding: clamp(34px, 4.4vw, 58px);
  border-radius: var(--r-lg);
  background: var(--page-2);
}

.band-tint .keep {
  background: var(--page);
}

.keep h3 {
  max-width: 22ch;
}

.keep-lead {
  margin-top: 14px;
  max-width: 52ch;
  font-size: var(--t-lede);
  line-height: 1.42;
  letter-spacing: -0.014em;
  color: var(--fg-2);
}

.keep-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: clamp(24px, 3vw, 44px);
  margin-top: clamp(32px, 4vw, 48px);
  padding-top: clamp(28px, 3.4vw, 40px);
  border-top: 1px solid var(--rule-soft);
}

.keep-grid h4 {
  font-size: var(--t-body);
  font-weight: 600;
}

.keep-grid p {
  margin-top: 8px;
  font-size: var(--t-sm);
  color: var(--fg-2);
}

/* ---- Immich ------------------------------------------------------------- */

.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr);
  gap: var(--gap);
  align-items: center;
}

.split h2 {
  margin-top: 14px;
}

.split-text > p {
  margin-top: 20px;
  max-width: 44ch;
  font-size: var(--t-lede);
  line-height: 1.42;
  letter-spacing: -0.014em;
  color: var(--fg-2);
}

.checks {
  list-style: none;
  display: grid;
  gap: 13px;
  margin-top: 30px;
  max-width: 46ch;
  font-size: var(--t-sm);
}

.checks li {
  position: relative;
  padding-left: 27px;
  color: var(--fg-2);
}

.checks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.34em;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--film);
  opacity: 0.16;
}

.checks li::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 0.62em;
  width: 4px;
  height: 7px;
  border: solid var(--film);
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}

.sync {
  padding: clamp(26px, 3vw, 38px);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: 0 0 0 1px var(--rule-soft), 0 20px 44px -28px rgba(0, 0, 0, 0.35);
}

.band-tint .sync {
  background: var(--page);
}

.sync svg {
  width: 100%;
  height: auto;
  color: var(--fg);
}

.sync-label {
  font-family: var(--mono);
  font-size: 8.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  fill: var(--fg-3);
}

.sync-title {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: -0.01em;
  fill: var(--fg);
}

.sync-flow {
  stroke: var(--film);
  stroke-dasharray: 4 6;
  animation: sync-run 2.6s linear infinite;
}

.sync-head {
  stroke: var(--film);
}

@keyframes sync-run {
  to {
    stroke-dashoffset: -20;
  }
}

@media (max-width: 880px) {
  .split {
    grid-template-columns: 1fr;
  }
}

/* ---- Spec sheet --------------------------------------------------------- */

.specs {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-sm);
}

.specs tr {
  border-bottom: 1px solid var(--rule-soft);
}

.specs tr:first-child {
  border-top: 1px solid var(--rule-soft);
}

/* Label and value sit on their first baseline, not on the top of the box: the
   label is 11px mono and the value is 15px sans, so a top alignment leaves the
   label floating above its own row. */
.specs th {
  width: 210px;
  padding: 18px 24px 18px 0;
  text-align: left;
  vertical-align: baseline;
  font-family: var(--mono);
  font-size: var(--t-mark);
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.specs td {
  padding: 18px 0;
  vertical-align: baseline;
  color: var(--fg-2);
}

@media (max-width: 700px) {
  .specs th,
  .specs td {
    display: block;
    width: auto;
    padding: 0;
  }

  .specs th {
    padding-top: 18px;
  }

  .specs td {
    padding-bottom: 18px;
  }
}

/* ---- Download ----------------------------------------------------------- */

.dl-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 16px;
}

.dl-card {
  display: grid;
  align-content: start;
  gap: 5px;
  padding: 28px 26px 24px;
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: 0 0 0 1px var(--rule-soft);
  text-decoration: none;
  transition: box-shadow 0.24s var(--ease-io), transform 0.24s var(--ease);
}

.band-tint .dl-card {
  background: var(--page);
}

.dl-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 0 0 1px var(--rule), 0 18px 36px -22px rgba(0, 0, 0, 0.4);
}

.dl-icon {
  display: flex;
  align-items: center;
  height: 26px;
  margin-bottom: 16px;
  color: var(--fg-2);
  transition: color 0.24s var(--ease-io);
}

.dl-card:hover .dl-icon {
  color: var(--fg);
}

.dl-os {
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.dl-hint,
.dl-version {
  font-family: var(--mono);
  font-size: var(--t-mark);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.dl-version {
  margin-top: 12px;
  color: var(--film);
}

.dl-note-hint {
  margin-top: clamp(20px, 2.4vw, 28px);
  font-size: var(--t-sm);
  color: var(--fg-2);
  text-align: center;
  text-wrap: pretty;
}

/* A button that reads as a sentence, because it belongs inside one. */
.linkish {
  padding: 0;
  border: 0;
  background: none;
  color: var(--fg);
  font: inherit;
  cursor: pointer;
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
  text-underline-offset: 0.22em;
  transition: text-decoration-color 0.2s var(--ease-io);
}

.linkish:hover {
  text-decoration-color: currentColor;
}

/* Stacked, not columned: the macOS note carries three routes and the Windows
   one is a sentence, so side by side leaves most of a column empty. */
.notes {
  display: grid;
  gap: clamp(24px, 3vw, 38px);
  margin-top: clamp(34px, 4vw, 52px);
}

/* The steps for a platform you are not on are noise, so a note waits until a
   download names its platform. Scoped to .js, because nothing may hide what it
   cannot then bring back: with scripting off all three are simply there. */
.js .note[data-platform] {
  display: none;
}

.js .note[data-platform].is-shown {
  display: block;
  animation: note-in 0.5s var(--ease) both;
}

/* No note showing, no gap above the notes that are not there. */
.js .notes:not(:has(.is-shown)) {
  margin-top: 0;
}

@keyframes note-in {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.note {
  padding-left: 22px;
  border-left: 2px solid var(--rule);
  font-size: var(--t-sm);
  color: var(--fg-2);
  /* it gets scrolled to when it appears, and must clear the bar */
  scroll-margin-top: calc(var(--header-h) + 20px);
}

/* Paragraphs are reset to zero margin globally, so anything stacked inside a
   note needs its own gap or the sentences run straight into each other. */
.note > * + * {
  margin-top: 14px;
}

.note strong {
  color: var(--fg);
}

/* A command and the button that takes it. The block reserves room on the right
   so a wrapped line never runs under the button. */
.snip {
  position: relative;
}

.snip .code {
  padding-right: 80px;
}

.copy {
  position: absolute;
  top: 8px;
  right: 8px;
  padding: 6px 12px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.09);
  color: #f5f5f7;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
  transition: background 0.2s var(--ease-io), border-color 0.2s var(--ease-io),
              color 0.2s var(--ease-io);
}

.copy:hover {
  background: rgba(255, 255, 255, 0.18);
  border-color: rgba(255, 255, 255, 0.34);
}

.copy.is-done {
  background: #f5f5f7;
  border-color: #f5f5f7;
  color: #17171b;
}

.note ol {
  counter-reset: step;
  list-style: none;
  display: grid;
  gap: 11px;
}

.note ol li {
  position: relative;
  padding-left: 32px;
  counter-increment: step;
}

/* A command that belongs to one step sits inside it, indented past the badge
   with the rest of that step's text. */
.note ol li .snip {
  margin-top: 11px;
}

.note ol li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: 0.08em;
  display: grid;
  place-items: center;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--film) 15%, transparent);
  color: var(--film);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
}

.note .aside {
  font-size: var(--t-xs);
  color: var(--fg-3);
}

/* The download itself, at the foot of what it needs. */
.note-get {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 16px;
  padding-top: 4px;
}

.note-get span {
  font-family: var(--mono);
  font-size: var(--t-mark);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-3);
}

/* Faster for the people who want it, noise for everyone else. */
.note details {
  padding-top: 15px;
  border-top: 1px solid var(--rule-soft);
}

.note details summary {
  list-style: none;
  cursor: pointer;
  color: var(--fg);
  font-weight: 500;
}

.note details summary::-webkit-details-marker {
  display: none;
}

.note details summary::after {
  content: "›";
  display: inline-block;
  margin-left: 8px;
  transform: rotate(90deg);
  transition: transform 0.25s var(--ease);
}

.note details[open] summary::after {
  transform: rotate(-90deg);
}

.note details > * + * {
  margin-top: 14px;
}

/* A download URL has nowhere to break, so left to itself it runs off the end of
   the block and is simply cut: the horizontal scrollbar that would reveal the
   rest is an overlay on macOS and never appears until you are already
   scrolling. It wraps instead, and only where it has to. */
.code {
  display: block;
  padding: 13px 15px;
  border-radius: var(--r-sm);
  background: #08080a;
  color: #f5f5f7;
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.55;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  -webkit-user-select: all;
  user-select: all;
}

.fine {
  margin-top: clamp(28px, 3.4vw, 42px);
  font-size: var(--t-xs);
  color: var(--fg-3);
}

/* ---- FAQ ---------------------------------------------------------------- */

.faq {
  border-top: 1px solid var(--rule-soft);
}

.faq details {
  border-bottom: 1px solid var(--rule-soft);
}

.faq summary {
  list-style: none;
  position: relative;
  padding: 22px 44px 22px 0;
  font-size: 1.0625rem;
  font-weight: 500;
  letter-spacing: -0.014em;
  cursor: pointer;
  transition: color 0.2s var(--ease-io);
}

.faq summary::-webkit-details-marker {
  display: none;
}

.faq summary:hover {
  color: var(--fg-2);
}

/* The plus is drawn rather than typed, so the two strokes are the same weight
   and it can turn into a minus by rotating one of them out. */
.faq summary::before,
.faq summary::after {
  content: "";
  position: absolute;
  right: 6px;
  top: 50%;
  width: 13px;
  height: 1.5px;
  border-radius: 2px;
  background: var(--fg-3);
  transition: transform 0.32s var(--ease), opacity 0.24s var(--ease-io);
}

.faq summary::after {
  transform: rotate(90deg);
}

.faq details[open] summary::after {
  transform: rotate(0deg);
  opacity: 0;
}

.faq details p {
  padding: 0 44px 26px 0;
  max-width: 66ch;
  font-size: var(--t-sm);
  color: var(--fg-2);
}

.faq details[open] p {
  animation: faq-in 0.34s var(--ease) both;
}

@keyframes faq-in {
  from {
    opacity: 0;
    transform: translateY(-5px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .faq details[open] p {
    animation: none;
  }
}

/* ---- About -------------------------------------------------------------- */

.about {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.8fr);
  gap: var(--gap);
  align-items: start;
}

.about p {
  margin-top: 20px;
  max-width: 46ch;
  color: var(--fg-2);
}

.about-sign {
  margin-top: 30px !important;
  font-weight: 600;
  color: var(--fg) !important;
}

.about-quote {
  margin: 0;
  padding: 4px 0 4px 26px;
  border-left: 2px solid var(--film);
  font-size: clamp(1.2rem, 1.9vw, 1.55rem);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.34;
  text-wrap: balance;
}

.about-quote footer {
  margin-top: 18px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg-3);
}

@media (max-width: 880px) {
  .about {
    grid-template-columns: 1fr;
  }
}

/* ---- Donate ------------------------------------------------------------- */

.donate {
  text-align: center;
  padding-block: var(--pad-y-tight);
}

.donate p {
  margin: 18px auto clamp(28px, 3.4vw, 38px);
  max-width: 46ch;
  font-size: var(--t-lede);
  line-height: 1.42;
  letter-spacing: -0.014em;
  color: var(--fg-2);
}

.donate .eyebrow {
  display: block;
  margin-bottom: 15px;
}

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

.site-footer {
  padding-block: clamp(34px, 4vw, 52px);
  background: var(--page-2);
  font-size: var(--t-xs);
  color: var(--fg-3);
}

.footer-inner {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 30px;
  justify-content: space-between;
  align-items: baseline;
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
}

.site-footer a {
  text-decoration: none;
  color: var(--fg-2);
}

.site-footer a:hover {
  color: var(--fg);
  text-decoration: underline;
}

/* ---- Legal pages -------------------------------------------------------- */

.legal {
  padding-block: clamp(56px, 7vw, 96px);
}

.legal .wrap {
  width: min(var(--w-text), 100% - var(--gutter) * 2);
}

.legal h1 {
  font-size: clamp(2rem, 4vw, 2.8rem);
  margin-bottom: 28px;
}

.legal h2 {
  font-size: clamp(1.2rem, 2vw, 1.45rem);
  margin: 44px 0 12px;
}

.legal p,
.legal li {
  color: var(--fg-2);
  font-size: var(--t-sm);
}

.legal p {
  margin-top: 14px;
}

.legal ul {
  margin-top: 14px;
  padding-left: 20px;
}

.legal-divider {
  border: 0;
  border-top: 1px solid var(--rule-soft);
  margin: 56px 0;
}

.legal-updated,
.legal-back {
  font-family: var(--mono);
  font-size: var(--t-mark);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.legal-back {
  display: inline-block;
  margin-top: 40px;
  text-decoration: none;
}

/* ---- Reduced motion ----------------------------------------------------- */

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

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
