/* Anno site styles.
   Shared by index, privacy, support and 404 so the four pages cannot drift.

   Direction: the camera documented like equipment. A rigid grid on cool
   paper, extreme contrast between very large numerals and very small
   monospace labels, and the decades presented as a measured band rather than
   as decoration. The serif appears exactly twice, in the story and in the
   footer mark, so it reads as a signature on a technical document.

   Theme is locked to light. One theme, no section inverts. */

:root {
  color-scheme: light;

  --paper:   #EFEEEA;
  --paper2:  #E7E6E1;
  --slip:    #F8F7F3;   /* the tipped-in note */
  --ink:     #14140F;
  --ink2:    #55544C;
  /* The darkest this can be lightened and still clear WCAG AA (4.5:1)
     on paper. It carries the mono labels and the footer links, which
     are content, not decoration. */
  --ink3:    #6D6B62;
  --rule:    rgba(20,20,15,.17);
  --rule-2:  rgba(20,20,15,.09);

  /* One accent for the whole site. It is the product's blue, held back so it
     lands on paper rather than glowing. Used for state and emphasis only,
     never for decoration. */
  --accent:  #2B4CD1;

  --sans:  Helvetica, "Helvetica Neue", Arial, sans-serif;
  --mono:  "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --serif: "IBM Plex Serif", Georgia, "Times New Roman", serif;

  --ease: cubic-bezier(.16, 1, .3, 1);
  --gut: clamp(16px, 3vw, 40px);
  --max: 1440px;
}

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

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.5;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }
h1, h2, h3 { margin: 0; font-weight: 400; }
p { margin: 0; }
a { color: inherit; }

.grid { max-width: var(--max); margin: 0 auto; padding: 0 var(--gut); }

/* The one small-type style on the site. Labels, never eyebrows: they sit
   beside or beneath content, never above a headline as a category stamp. */
.lab {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink3);
}

.h2 { font-size: clamp(26px, 3.4vw, 48px); line-height: 1.04; letter-spacing: -0.035em; }
.body { font-size: 15.5px; line-height: 1.62; color: var(--ink2); max-width: 62ch; }

:where(a, button):focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.skip {
  position: absolute; left: 16px; top: -60px; z-index: 5;
  background: var(--ink); color: var(--paper);
  font-family: var(--mono); font-size: 12px; letter-spacing: .14em; text-transform: uppercase;
  padding: 12px 18px; text-decoration: none;
  transition: top .3s var(--ease);
}
.skip:focus { top: 16px; }

.cta {
  display: inline-block;
  background: var(--ink); color: var(--paper);
  font-family: var(--mono); font-size: 12px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
  padding: 16px 24px; text-decoration: none;
  transition: background .25s var(--ease), transform .25s var(--ease);
}
.cta:hover { background: #2C2C22; }
.cta:active { transform: translateY(1px); }

/* Blue has one job on this page: getting the app. The dark button is for
   moving around the page, the blue one is the App Store. Keeping the accent
   tied to a single meaning is why it can stay this quiet everywhere else. */
.cta-blue { background: var(--accent); color: #fff; }
.cta-blue:hover { background: #2440B8; }

/* Nothing to link to until the listing exists, so this is a state that looks
   like a button rather than a button that goes nowhere. Give it an href and
   change the tag to <a> the day the App Store URL lands. */
.cta-soon { cursor: default; }
.cta-soon:hover { background: var(--accent); }

.navcta { padding: 11px 16px; font-size: 11px; letter-spacing: .14em; }
.cta-big { padding: 20px 30px; font-size: 13px; }

/* A control with nowhere to go is a state, not a link. */
.state {
  display: inline-block;
  border: 1px solid var(--rule);
  padding: 15px 23px;
  font-family: var(--mono); font-size: 12px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink2);
}

/* ---------- nav ---------- */

.nav { border-bottom: 1px solid var(--ink); }
/* Brand, Decades, Motion, Privacy, Support, then the CTA. The document
   pages have fewer links and simply leave the trailing tracks empty. */
.nav .grid { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr 1fr auto; align-items: center; gap: 12px; padding-top: 14px; padding-bottom: 14px; }
.nav a { text-decoration: none; }
.nav .brand { font-family: var(--mono); font-weight: 600; font-size: 14px; letter-spacing: .24em; text-transform: uppercase; }
.nav .lab:hover, .nav .lab.here { color: var(--ink); }
/* The download button always ends the row, pinned right. Its track is sized to
   the button, so a menu that gains or loses a link can never leave it wrapped
   onto a row of its own. */
.nav .navcta { grid-column: -2 / -1; justify-self: end; white-space: nowrap; }

/* ---------- hero ---------- */

.hero { border-bottom: 1px solid var(--ink); }
.hero .grid { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 1fr); gap: clamp(24px, 4vw, 60px); padding-top: clamp(40px, 6vw, 84px); padding-bottom: clamp(32px, 4vw, 56px); }
.hero h1 { font-size: clamp(44px, 9.4vw, 136px); line-height: .88; letter-spacing: -0.045em; }
.hero .body { margin-top: 26px; max-width: 40ch; }
.hero .cta { margin-top: 26px; }
.specs { display: grid; gap: 15px; align-content: end; }
.specs div { border-top: 1px solid var(--rule-2); padding-top: 8px; }
.specs .v { font-family: var(--mono); font-size: 14px; margin-top: 3px; }

/* ---------- the decade scrub ----------
   Two states over one set of markup.

   Resting state (no JavaScript, an old browser, or reduced motion): the ten
   frames lay out as a plain grid with captions. That is a complete, honest
   section on its own.

   Running state (html.scrub-on, set by the script): the track grows tall, the
   stage sticks, the frames stack, and scroll position picks which is showing.
   Nothing is hidden behind the effect that is not visible without it. */
.decades { border-bottom: 1px solid var(--ink); }
.decades .head { padding-top: clamp(40px, 6vw, 80px); padding-bottom: 26px; }

.plate {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 1px;
  background: var(--rule-2);
  border-top: 1px solid var(--rule-2);
  border-bottom: 1px solid var(--rule-2);
  margin-bottom: clamp(40px, 6vw, 80px);
}
.plate figure { margin: 0; background: var(--paper); }
.plate img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
/* Stacked, not side by side: across five columns the stock name ran into
   the next cell's year. The year still starts on its own frame's edge. */
.plate figcaption { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; padding: 10px 14px 14px 0; }
/* Captions sit flush with the frame they describe, not inset from it. */
.plate figure { padding: 0; }
.plate figcaption .yr { font-family: var(--mono); font-size: 16px; font-weight: 500; }
.readout, .scale, .steps { display: none; }

html.scrub-on .scrub-track { position: relative; height: 420vh; }
html.scrub-on .scrub-stage {
  position: sticky; top: 0;
  height: 100dvh;
  display: flex; flex-direction: column; justify-content: center;
  padding-bottom: clamp(20px, 3vw, 40px);
}
/* .grid carries `margin: 0 auto`, and an auto cross-axis margin cancels a
   flex item's stretch: without this the column collapses to content width
   and takes the plate down with it. */
html.scrub-on .scrub-stage > .grid { width: 100%; }
html.scrub-on .plate {
  display: block; position: relative;
  width: 100%; aspect-ratio: 16 / 10;
  background: var(--paper2);
  border: 0; gap: 0; margin-bottom: 0;
  overflow: hidden;

  /* 16:10 off the full column width is taller than the viewport on a wide,
     short window: at 1360px of plate that is 850px, and the readout, the
     scale and the stage padding want ~194px more. The stage centres its
     content, so the excess spills evenly and the top half paints over the
     heading above. Capping the height keeps the whole column inside the
     sticky stage; the images are object-fit: cover, so they crop rather
     than distort when the ratio gives way. */
  max-height: calc(100dvh - 210px);
}
/* Stacked, the figures must not carry the grid's paper fill or the
   topmost one paints an opaque rectangle over the active frame. Only
   the images fade, so only the images may be opaque. */
html.scrub-on .plate figure { position: absolute; inset: 0; background: none; }
html.scrub-on .plate img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; aspect-ratio: auto;
  opacity: 0; transition: opacity .18s linear;
}
html.scrub-on .plate img.on { opacity: 1; }
html.scrub-on .plate figcaption { display: none; }

html.scrub-on .readout { display: flex; align-items: baseline; justify-content: space-between; gap: 20px; margin-top: 18px; }
.readout .yr { font-family: var(--mono); font-weight: 500; font-size: clamp(38px, 6vw, 84px); line-height: 1; letter-spacing: -0.05em; font-variant-numeric: tabular-nums; }
.readout .stock { font-family: var(--mono); font-size: 12px; letter-spacing: .18em; text-transform: uppercase; color: var(--ink2); }

/* The scale: ten marks, the current one inked. The same control as the app's
   dial, drawn flat on paper. */
html.scrub-on .scale { display: grid; grid-template-columns: repeat(10, 1fr); align-items: end; margin-top: 20px; height: 32px; }
.scale i { display: block; justify-self: center; width: 1px; height: 12px; background: var(--ink); opacity: .28; transition: height .2s var(--ease), opacity .2s var(--ease), background .2s var(--ease), width .2s var(--ease); }
.scale i.on { height: 30px; opacity: 1; background: var(--accent); width: 2px; }

/* The sentinels the observer watches. Never interactive, never visible. */
html.scrub-on .steps { display: grid; position: absolute; inset: 0; grid-auto-rows: 1fr; pointer-events: none; }


/* ---------- photo credit ----------
   Unsplash asks that the photographer and Unsplash both be linked. It rides
   the frame in the scrub, and drops to a line under the grid at rest. */
.credit {
  grid-column: 1 / -1;
  background: var(--paper);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .04em;
  color: var(--ink2);
  padding: 12px 0 2px;
}
.credit a { color: var(--ink); text-underline-offset: 2px; }
.credit a:hover { color: var(--accent); }

html.scrub-on .credit {
  position: absolute; left: 0; bottom: 0; z-index: 1;
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  backdrop-filter: blur(6px);
  padding: 9px 14px;
  font-size: 10.5px;
}

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

.motion { border-bottom: 1px solid var(--ink); padding: clamp(40px, 6vw, 80px) 0; }
.rateline { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; background: var(--rule-2); border-top: 1px solid var(--rule-2); border-bottom: 1px solid var(--rule-2); margin-top: 28px; }
.rateline > div { background: var(--paper); padding: 22px 16px; }
/* The outer edges carry no padding, so the first numeral starts on the
   same line as the headline and the last cell ends on the right edge. */
.rateline > div:first-child { padding-left: 0; }
.rateline > div:last-child { padding-right: 0; }
.rateline .n { font-family: var(--mono); font-weight: 500; font-size: clamp(38px, 6vw, 84px); line-height: .9; letter-spacing: -0.05em; font-variant-numeric: tabular-nums; }
.rateline .n small { font-size: .2em; letter-spacing: .12em; color: var(--ink3); margin-left: .45em; }
.rateline .lab { margin-top: 12px; display: block; }

.diagram { margin: clamp(36px, 5vw, 64px) 0 0; border: 1px solid var(--rule-2); background: var(--slip); padding: clamp(16px, 2.5vw, 28px); overflow-x: auto; }
.diagram svg { width: 100%; min-width: 560px; height: auto; display: block; }
/* Two beats: the shutter gathers, then the held frame settles and stays
   settled for most of the cycle. Driven by CSS so reduced motion can switch
   it off, and the resting state is the SETTLED state, because a still of
   this diagram has to carry the whole idea on its own. */
.diagram .beat { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .diagram .beat { animation: anno-settle 8s linear infinite; }
  .diagram .b1 { animation-delay: 0s }    .diagram .b2 { animation-delay: .18s }
  .diagram .b3 { animation-delay: .36s }  .diagram .b4 { animation-delay: 1.0s }
  .diagram .b5 { animation-delay: 1.18s } .diagram .b6 { animation-delay: 1.36s }
}
@keyframes anno-settle { 0% { opacity: 0 } 8% { opacity: 1 } 92% { opacity: 1 } 100% { opacity: 0 } }

/* ---------- the note, tipped in ---------- */

.notewrap { --pad: clamp(48px, 7vw, 100px);
            border-bottom: 1px solid var(--ink); padding: var(--pad) 0; }
/* The illustration runs off the left edge and the card sits over it, so the
   two overlap rather than sharing a tidy column each.

   Both share a single grid cell, which makes the section as tall as whichever
   is taller. Positioning the artwork absolutely instead made the section's
   height depend on the card alone, and since the artwork scales with the
   viewport, anything past roughly 1600px cropped the photographer's head off
   the top. */
.notewrap { position: relative; overflow: hidden; display: grid; }
.notewrap > .illo,
.notewrap > .grid { grid-area: 1 / 1; min-width: 0; }
.illo {
  width: 108%;
  /* Pulled down through the section's bottom padding so the artwork meets the
     rule instead of stopping short of it. The grid row lives inside the
     padding box, so align-self alone leaves a band of paper underneath. */
  margin: 0 0 calc(var(--pad) * -1) -32%;
  justify-self: start;
  align-self: end;
  pointer-events: none;
}
.illo img { width: 100%; height: auto; display: block; }
.notewrap > .grid { align-self: center; z-index: 1; }
.note { margin-left: auto; max-width: 636px; }
.note { background: var(--slip); border: 1px solid var(--rule-2); padding: clamp(28px, 4vw, 56px); }
.note p { font-size: 16.5px; line-height: 1.66; color: var(--ink2); margin-top: 1.05em; }
.note p:first-of-type { margin-top: 20px; }
.note em { font-style: italic; color: var(--ink); }
.note .q { font-family: var(--serif); font-style: italic; font-size: clamp(19px, 2vw, 24px); line-height: 1.38; color: var(--ink); }
.note .sign { font-family: var(--serif); font-style: italic; font-size: 19px; color: var(--ink); margin-top: 28px; }

/* ---------- travel ---------- */

.travel { border-bottom: 1px solid var(--ink); padding: clamp(40px, 6vw, 80px) 0; }
.packs { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; background: var(--rule-2); border-top: 1px solid var(--rule-2); border-bottom: 1px solid var(--rule-2); margin-top: 32px; }
.packs > div { background: var(--paper); padding: 22px 16px; }
.packs > div:first-child { padding-left: 0; }
.packs > div:last-child { padding-right: 0; }
.packs .n { font-family: var(--mono); font-weight: 500; font-size: clamp(38px, 6vw, 84px); line-height: .9; letter-spacing: -0.05em; font-variant-numeric: tabular-nums; }
.packs .lab { margin-top: 12px; display: block; }
@media (max-width: 720px) {
  .packs { grid-template-columns: repeat(2, 1fr); }
  .packs > div:nth-child(odd) { padding-left: 0; }
  .packs > div:nth-child(even) { padding-right: 0; }
}

/* ---------- claims ---------- */

.claims { border-bottom: 1px solid var(--ink); padding: clamp(40px, 6vw, 80px) 0; }
.claim { display: grid; grid-template-columns: minmax(0, 22ch) minmax(0, 1fr); gap: clamp(16px, 4vw, 56px); padding: 24px 0; border-top: 1px solid var(--rule-2); align-items: baseline; }
.claim:first-of-type { border-top: 0; }
.claim h3 { font-size: 17px; letter-spacing: -0.015em; }
.claim p { font-size: 15px; color: var(--ink2); max-width: 58ch; }

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

.close { border-bottom: 1px solid var(--ink); padding: clamp(56px, 8vw, 110px) 0; }
.close h2 { font-size: clamp(30px, 5.2vw, 74px); line-height: .98; letter-spacing: -0.04em; }
.close .row { display: flex; flex-wrap: wrap; align-items: center; gap: 18px; margin-top: 30px; }

/* ---------- the mark ---------- */

.markfoot { overflow: hidden; }
.markfoot .footrow { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; padding: 20px 0 14px; }
.markfoot .footrow a { text-decoration: none; }
.markfoot .footrow a:hover { color: var(--ink); }
.markfoot .mark {
  display: block; width: 100%;
  font-family: var(--sans); font-weight: 700;
  /* Sized from the content column, not the viewport. "Anno" in Helvetica
     Bold inks 2.272em wide at this tracking, so dividing the column by 2.272
     inks 2.272em wide, but its advance box is 2.335em once the tracking is
     counted, and it is the advance that has to fit or centring pushes the ink
     off one side. Dividing by 2.36 leaves the advance just inside the column,
     so the word centres on the page and still very nearly fills it.

     It must be min(100vw, --max) because the column stops growing at --max
     while the viewport does not: keying off 100vw alone made the word outgrow
     its container on anything wider and run off the right edge. */
  font-size: calc((min(100vw, var(--max)) - 2 * var(--gut)) / 2.36);
  text-align: center;
  /* Tracking is applied after the last glyph too, and the side bearings are
     not symmetric, so centring the advance box leaves the visible ink about
     0.049em right of true centre. Shifting the painted box rather than the
     content box, because the advance overflows the content box and centring
     clamps, which made padding corrections land at a fraction of their size.
     The value is measured off the render, not derived. */
  position: relative;
  left: -0.049em;
  line-height: 1; letter-spacing: -0.055em;
  white-space: nowrap; user-select: none;
  color: var(--ink); opacity: .085;
  /* Centred rather than pulled left, so the word sits in the middle of the
     screen at any width. */
  /* With line-height 1 the baseline sits about 0.8em down the line box, so
     pulling up 0.30em cuts roughly the bottom sixth of the letterforms. The
     word runs under the document rather than sitting on it. */
  margin-bottom: -0.30em;
}

/* ---------- scroll entry ----------
   Visible is the resting state. The hidden state is opted into from JS and
   dropped again if the observer never reports, so a page that fails to run
   the script is readable rather than blank. */
.rise { transition: opacity .8s var(--ease), transform .8s var(--ease); }
html.reveal .rise { opacity: 0; transform: translateY(18px); }
html.reveal .rise.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .rise { opacity: 1 !important; transform: none !important; transition: none; }
  .diagram .beat { animation: none; opacity: 1; }
  .cta, .scale i, .plate img { transition: none; }
}

/* ---------- inner pages ---------- */

.doc { padding: clamp(40px, 6vw, 80px) 0 clamp(60px, 9vw, 120px); }
.doc .grid { max-width: 820px; }
.doc h1 { font-size: clamp(34px, 6vw, 62px); line-height: .96; letter-spacing: -0.04em; }
.doc h2 { font-size: 17px; font-weight: 500; margin: 40px 0 10px; letter-spacing: -0.01em; }
.doc p, .doc li { font-size: 15.5px; line-height: 1.68; color: var(--ink2); }
.doc p { margin: 0 0 13px; }
.doc ul { margin: 0 0 13px; padding-left: 18px; }
.doc li { margin-bottom: 7px; }
.doc .updated { margin-top: 16px; }
.doc .intro { color: var(--ink); font-size: 17px; margin-top: 24px; }

/* ---------- responsive ---------- */

@media (max-width: 900px) {
  /* Nav stays on one line: the section anchors go, the App Store action stays. */
  .nav .grid { grid-template-columns: 1fr auto; }
  .nav .lab { display: none; }
  .hero .grid { grid-template-columns: 1fr; }
  .plate { grid-template-columns: repeat(2, 1fr); }
  .rateline { grid-template-columns: 1fr 1fr; }
  .markfoot .footrow { grid-template-columns: 1fr 1fr; }
  /* Stacked below 900px: artwork above, story under it, no overlap. */
  .notewrap { display: block; }
  .illo { width: 100%; margin: 0 0 26px; }
  .note { max-width: none; }
  .claim { grid-template-columns: 1fr; gap: 8px; }
  html.scrub-on .scrub-track { height: 340vh; }
}
@media (max-width: 560px) {
  .plate { grid-template-columns: 1fr; }
}
