/* The headline face.
 *
 * `font-display: swap` and not `block`: the headline is the first thing on the
 * page and holding it invisible for up to three seconds to avoid a reflow is a
 * worse trade than the reflow. The fallback is metrically close enough that the
 * swap is a change of texture rather than of layout.
 *
 * Two subsets. The site is English, but `latin-ext` carries the accented names
 * that appear in the copy — Portugues (Brasil) in the language figure — and
 * without it those fall back mid-word.
 */
@font-face {
  font-family: "Bricolage Grotesque";
  src: url("../fonts/bricolage-latin.woff2") format("woff2");
  font-weight: 400 800;
  font-stretch: 75% 100%;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Bricolage Grotesque";
  src: url("../fonts/bricolage-latin-ext.woff2") format("woff2");
  font-weight: 400 800;
  font-stretch: 75% 100%;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* The shell: reset, type, layout primitives, masthead, buttons, footer.
 *
 * Everything here is expressed in `tokens.css` values. There is no second
 * palette and no second spacing ladder — if a rule below needs a number that is
 * not a token, that is a gap in the design system and it is reported rather
 * than patched here.
 *
 * Load order is `tokens.css` then this. Page-level CSS comes after and may
 * compose these classes; it may not redefine them.
 */

/* ------------------------------------------------------------------ *
 * Four values the token file does not carry yet.
 *
 * `tokens.css` ships two easings and no durations, and nothing that knows how
 * tall the sticky bar is. Both are needed in more than one place here, and the
 * alternative — repeating `220ms` in nine rules — is how a site ends up with
 * three different "fast". They live on :root so page CSS can read them; if the
 * owner wants them in the system, they belong in `tokens.css` unchanged.
 * ------------------------------------------------------------------ */
:root {
  --t-fast: 120ms;
  --t-mid: 220ms;
  --t-slow: 480ms;
  /* The bar's height, so anchor targets can clear it and the hero can start
   * underneath it rather than after it. */
  --masthead-h: 60px;
}

/* ================================================================== *
 * 1. Reset
 * ================================================================== */

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

* {
  margin: 0;
}

html {
  /* Landscape Safari inflates body text on rotate without this. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* Anchor links land below the sticky bar instead of behind it. */
  scroll-padding-top: calc(var(--masthead-h) + var(--s-l));
}

/* Smooth scrolling is motion, so it is opt-out by construction rather than
 * declared and then undone further down the file. */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  font-family: var(--face);
  font-size: var(--step-0);
  line-height: 1.6;
  color: var(--ink-body);
  background-color: var(--paper);
  /* The token palette was sampled from an AppKit app, where text is drawn
   * antialiased. Without this the same hex reads a weight heavier in a browser
   * and the site stops matching the product. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Warm paper wants old-style figures nowhere, but it does want the ligature
   * and kerning defaults the system face ships with. */
  font-kerning: normal;
  min-height: 100vh;
}

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

/* Only the intrinsic box is released. Media that carries width/height
 * attributes keeps its aspect ratio and reserves its space before it loads —
 * which is the whole reason those attributes are mandatory on this site. */
img,
video {
  height: auto;
}

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
  background: none;
}

button {
  border: 0;
  padding: 0;
  cursor: pointer;
}

p,
li,
h1,
h2,
h3,
h4,
h5,
h6 {
  /* A 40-character German compound at 320px otherwise pushes the whole page
   * sideways. */
  overflow-wrap: break-word;
}

ul,
ol {
  padding: 0;
  list-style: none;
}

a {
  color: inherit;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}

hr {
  border: 0;
  border-top: 1px solid var(--paper-edge);
}

/* `hidden` has to beat every `display` this file sets, or the mobile panel is
 * visible while it is still marked closed to a screen reader. */
[hidden] {
  display: none !important;
}

::selection {
  background-color: var(--accent-quiet);
  color: var(--ink);
}

/* ================================================================== *
 * 2. Type
 *
 * Tracking tightens as the size grows. Optical sizing is not a nicety here:
 * the system face is drawn for 13pt UI, and at --step-5 its default spacing
 * reads as a browser default rather than as a designed headline.
 * ================================================================== */

.display,
.h1,
.h2,
.h3,
.h4 {
  font-family: var(--display);
  color: var(--ink);
  font-weight: 600;
  /* Headlines break on sense rather than on the last word fitting. */
  text-wrap: balance;
}

.display {
  font-size: var(--step-5);
  line-height: 1.02;
  letter-spacing: -0.035em;
}

.h1 {
  font-size: var(--step-4);
  line-height: 1.05;
  letter-spacing: -0.03em;
}

.h2 {
  font-size: var(--step-3);
  line-height: 1.08;
  letter-spacing: -0.025em;
}

.h3 {
  font-size: var(--step-2);
  line-height: 1.15;
  letter-spacing: -0.02em;
}

.h4 {
  font-size: var(--step-1);
  line-height: 1.25;
  letter-spacing: -0.012em;
  font-weight: 600;
}

/* The one place tracking goes the other way. Uppercase at --step--1 sets far
 * too tight by default, because the face's spacing assumes lowercase
 * sidebearings. */
.eyebrow {
  font-size: var(--step--1);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-quiet);
}

/* The paragraph directly under a headline. Larger than body, quieter than ink,
 * and never wider than the measure. */
.lede {
  font-size: var(--step-1);
  line-height: 1.5;
  letter-spacing: -0.01em;
  color: var(--ink-body);
  max-width: 46ch;
}

.quiet {
  color: var(--ink-quiet);
}

/* --ink-faint is 2.9:1 on paper. It is legal for a rule, a disabled glyph or a
 * number the size of a headline, and it is never legal for running text. */
.faint {
  color: var(--ink-faint);
}

.mono {
  font-family: var(--mono);
  font-size: 0.92em;
  letter-spacing: 0;
  /* Figures that change under animation must not change the width of their
   * own box. */
  font-variant-numeric: tabular-nums;
}

.prose {
  max-width: var(--measure);
  /* Ragged edges beat rivers of white in a justified column at this measure. */
  text-wrap: pretty;
}

.prose > * + * {
  margin-top: var(--s-m);
}

.prose a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--ink-faint);
  transition: text-decoration-color var(--t-fast) var(--ease);
}

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

/* ================================================================== *
 * 3. Layout primitives
 * ================================================================== */

/* The content column. The gutter grows with the viewport rather than stepping
 * at breakpoints, so the page never has one width where the text is jammed
 * against the edge. Floor is 16px, which is the smallest gutter that still
 * reads as a margin on a 320px phone. */
.page {
  width: 100%;
  max-width: var(--page);
  margin-inline: auto;
  padding-inline: clamp(var(--s-m), 5vw, var(--s-3xl));
}

.narrow {
  max-width: var(--page-narrow);
  margin-inline: auto;
}

/* Vertical rhythm. One section is one idea, and the space between two ideas is
 * the largest space on the page — that is the whole difference between this and
 * a template. */
.section {
  padding-block: clamp(var(--s-3xl), 9vw, var(--s-5xl));
}

.section--tight {
  padding-block: clamp(var(--s-2xl), 6vw, var(--s-3xl));
}

.section--loose {
  padding-block: clamp(var(--s-4xl), 13vw, calc(var(--s-5xl) + var(--s-3xl)));
}

.section + .section {
  padding-block-start: 0;
}

.section--ruled {
  border-top: 1px solid var(--paper-edge);
}

/* Every vertical gap in the site goes through this, so a page never needs a
 * one-off margin. The fallback is the common case; anything else sets --gap on
 * the element itself. */
.stack {
  display: grid;
  gap: var(--gap, var(--s-m));
  align-content: start;
}

/* The horizontal counterpart, used by button pairs and meta lines. It wraps,
 * because two buttons side by side is 320px of nothing at 320px wide. */
.cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--gap, var(--s-s));
}

/* Reachable by keyboard, invisible until then. */
.visually-hidden:not(:focus):not(:focus-within) {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  z-index: 100;
  top: var(--s-xs);
  left: var(--s-xs);
  padding: var(--s-xs) var(--s-m);
  border-radius: var(--radius-s);
  background-color: var(--ink);
  color: var(--paper);
  font-size: var(--step--1);
  font-weight: 600;
  text-decoration: none;
  transform: translateY(-200%);
}

.skip-link:focus {
  transform: none;
}

/* ================================================================== *
 * 4. Focus
 *
 * One ring for the whole site. --accent is the system's state colour and focus
 * is a state, so this is the accent doing its job rather than an exception to
 * it. `outline` and not `box-shadow`: an outline follows the shape through a
 * line wrap and survives Windows High Contrast, and a shadow does neither.
 * ================================================================== */

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

/* ================================================================== *
 * 5. Buttons
 *
 * Two, and only two. A third weight is how a page loses track of what its one
 * action is.
 * ================================================================== */

.btn-primary,
.btn-quiet {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-xs);
  padding: var(--s-s) var(--s-l);
  border-radius: var(--radius-m);
  font-size: var(--step-0);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.008em;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  /* Two thumbs' worth on a phone, and it keeps the two buttons the same height
   * when one wraps and the other does not. */
  min-height: 44px;
  transition:
    background-color var(--t-fast) var(--ease),
    border-color var(--t-fast) var(--ease),
    color var(--t-fast) var(--ease),
    box-shadow var(--t-fast) var(--ease),
    transform var(--t-fast) var(--ease);
}

.btn-primary {
  background-color: var(--ink);
  color: var(--paper);
  /* A border of the same colour as the fill, so the two buttons are the same
   * height to the pixel without either needing a hand-tuned padding. */
  border: 1px solid var(--ink);
}

.btn-primary:hover {
  background-color: var(--ink-body);
  border-color: var(--ink-body);
  box-shadow: var(--lift-1);
}

.btn-quiet {
  background-color: transparent;
  color: var(--ink);
  border: 1px solid var(--paper-edge);
}

.btn-quiet:hover {
  border-color: var(--ink-faint);
  background-color: var(--paper-raised);
}

/* A press is a press: the button goes down by one pixel and gives up its
 * shadow. Anything that grows on click reads as a toy. */
.btn-primary:active,
.btn-quiet:active {
  transform: translateY(1px);
  box-shadow: none;
}

.btn-primary:focus-visible,
.btn-quiet:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* The masthead's copy of the action, one step down so it never out-shouts the
 * hero's. */
.btn-sm {
  padding: var(--s-xs) var(--s-m);
  min-height: 36px;
  font-size: var(--step--1);
  border-radius: var(--radius-s);
}

/* ================================================================== *
 * 6. Masthead
 *
 * Markup contract, so the page and the JS agree:
 *
 *   <header class="masthead">
 *     <div class="masthead__bar page"> wordmark, nav, toggle </div>
 *     <div class="masthead__panel" id="menu" hidden> … </div>
 *   </header>
 *
 * The scrolled state is a `data-scrolled` attribute. Both the header itself and
 * the root element are accepted, because the JS that sets it is not this
 * agent's and either target is a reasonable choice.
 * ================================================================== */

.masthead {
  position: sticky;
  top: 0;
  z-index: 40;
  background-color: transparent;
  /* Declared transparent rather than absent: a border that appears on scroll
   * would otherwise push the whole page down by one pixel at the moment it
   * appears, which is the most visible layout shift a site can have. */
  border-bottom: 1px solid transparent;
  transition:
    background-color var(--t-mid) var(--ease),
    border-color var(--t-mid) var(--ease);
}

/* No blur and no translucency. A frosted bar over warm paper turns the paper
 * grey, and it is the single most recognisable tell of a generated page. */
.masthead[data-scrolled],
:root[data-scrolled] .masthead {
  background-color: var(--paper);
  border-bottom-color: var(--paper-edge);
}

.masthead__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-m);
  min-height: var(--masthead-h);
}

.wordmark {
  /* Flex, because the mark and the word are one object. Left as the default
   * inline flow they wrapped onto two lines at every width — the icon sat on
   * top of the name and the bar grew to fit it. */
  display: inline-flex;
  align-items: center;
  gap: var(--s-xs);
  /* A step up from the nav links. The wordmark was the same size as the four
   * links beside it, which made the masthead read as five links rather than as
   * a name and its navigation. */
  font-size: var(--step-1);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
  /* The one place a link may sit tight against the edge of the bar. */
  margin-inline-end: auto;
}

/* The shipped icon. `flex: none` so it keeps its size when the bar is tight;
 * a squashed app icon is worse than no icon. */
.wordmark__mark {
  display: block;
  flex: none;
  width: 27px;
  height: 27px;
}

/* The right-hand group. Its own flex row rather than three loose children of
 * the bar, so the gap between the sign-in link and the button is set once and
 * does not inherit the bar's larger one. */
.masthead__actions {
  display: flex;
  align-items: center;
  gap: var(--s-m);
  flex: none;
}

.masthead__nav {
  display: flex;
  align-items: center;
  gap: var(--s-l);
}

.navlink {
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--ink-quiet);
  text-decoration: none;
  transition: color var(--t-fast) var(--ease);
}

.navlink:hover {
  color: var(--ink);
}

/* The page the visitor is on, said in ink weight rather than in the accent —
 * the accent means state, and "you are here" is navigation. */
.navlink[aria-current="page"] {
  color: var(--ink);
}

/* ---- The disclosure ---- */

.masthead__toggle {
  display: none;
  align-items: center;
  gap: var(--s-2xs);
  padding: var(--s-xs) var(--s-s);
  min-height: 40px;
  min-width: 44px;
  justify-content: center;
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius-s);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--ink);
  background-color: transparent;
  transition:
    border-color var(--t-fast) var(--ease),
    background-color var(--t-fast) var(--ease);
}

.masthead__toggle:hover {
  border-color: var(--ink-faint);
  background-color: var(--paper-raised);
}

.masthead__toggle:active {
  transform: translateY(1px);
}

/* Two bars that become an X. Drawn in CSS rather than fetched, because the
 * whole site is one folder of files that opens by double-clicking. */
.masthead__toggle-glyph {
  position: relative;
  display: block;
  width: 16px;
  height: 10px;
  flex: none;
}

.masthead__toggle-glyph::before,
.masthead__toggle-glyph::after {
  content: "";
  position: absolute;
  left: 0;
  width: 100%;
  height: 1.5px;
  border-radius: 2px;
  background-color: currentColor;
  transition: transform var(--t-mid) var(--ease);
}

.masthead__toggle-glyph::before {
  top: 0;
}

.masthead__toggle-glyph::after {
  bottom: 0;
}

.masthead__toggle[aria-expanded="true"] .masthead__toggle-glyph::before {
  transform: translateY(4.25px) rotate(45deg);
}

.masthead__toggle[aria-expanded="true"] .masthead__toggle-glyph::after {
  transform: translateY(-4.25px) rotate(-45deg);
}

/* ---- The panel ---- */

.masthead__panel {
  display: grid;
  gap: var(--s-2xs);
  padding: var(--s-s) clamp(var(--s-m), 5vw, var(--s-3xl)) var(--s-l);
  background-color: var(--paper);
  border-top: 1px solid var(--paper-edge);
  box-shadow: var(--lift-2);
  /* An entrance, not a state: the panel is `hidden` when closed, so there is
   * nothing to transition from and a keyframe is the honest mechanism. */
  animation: panel-in var(--t-mid) var(--ease-out) both;
}

@keyframes panel-in {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
}

.masthead__panel a {
  display: block;
  padding: var(--s-s) 0;
  font-size: var(--step-1);
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--paper-edge);
}

.masthead__panel a:last-of-type {
  border-bottom: 0;
}

.masthead__panel .btn-primary {
  margin-top: var(--s-s);
  /* Full width here and only here: it is the last thing in a closed column, so
   * a shrink-to-fit button would leave a ragged bottom edge. */
  width: 100%;
  border-bottom: 0;
  color: var(--paper);
  font-size: var(--step-0);
}

/* 720px is where four nav links plus the wordmark plus the action stop fitting
 * on one line at the smallest gutter. It is measured, not chosen. */
/* 880, not 720. The wordmark grew a step and the bar stopped fitting at 768:
 * a name, four links, a theme control, a sign-in and a filled button need more
 * room than they did, and the disclosure has to take over before they run out
 * rather than after. Measured — 768 overflowed by 57px at the old breakpoint. */
@media (max-width: 880px) {
  .masthead__nav {
    display: none;
  }

  .masthead__toggle {
    display: inline-flex;
  }
}

@media (min-width: 881px) {
  /* The panel exists in the markup at every width; above the breakpoint the
   * real nav is showing and the panel must never appear, even if a stale
   * `hidden` was removed by a resize. */
  .masthead__panel {
    display: none;
  }
}

/* The narrow end, where the bar actually runs out of room.
 *
 * Measured at 320px: the wordmark, the sign-in link, a filled Download button
 * and the disclosure together came to 333px and pushed the whole document 13px
 * sideways. Both of the pushed-out items are already in the panel behind the
 * disclosure, so hiding them costs a visitor nothing and is the only fix that
 * does not shrink the tap target below 44px. */
@media (max-width: 400px) {
  .masthead__signin,
  .masthead__bar > .masthead__actions {
    display: none;
  }
}

/* ================================================================== *
 * 6b. Code that is wider than the phone
 *
 * A `<code>` block holding a note's YAML front matter is `white-space: pre`
 * and cannot wrap — it measured 332px inside a 320px viewport and took the
 * page with it. It scrolls inside its own box instead: the sample is there to
 * be recognised, not read word for word, and wrapping a UUID mid-line would
 * make it unrecognisable as a file.
 * ================================================================== */

pre,
.scroll-x {
  max-width: 100%;
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

/* `pre` is the container; a bare `code` inside a paragraph still must not
 * push the page, so it is allowed to break rather than to scroll. */
:not(pre) > code {
  overflow-wrap: anywhere;
}

/* ================================================================== *
 * 7. Reveal
 *
 * Opacity and 12px. Anything further — a scale, a blur, a stagger of 400ms —
 * is the page performing rather than the content arriving.
 *
 * The hidden state is applied only where scripting exists, so a page whose JS
 * failed to load shows all of its text instead of a blank column.
 * ================================================================== */

@media (scripting: enabled) {
  .reveal {
    opacity: 0;
    transform: translateY(12px);
    transition:
      opacity var(--t-slow) var(--ease-out),
      transform var(--t-slow) var(--ease-out);
    /* The transform is the only reason a compositor layer is worth asking for
     * here; it is released the moment the element has arrived. */
    will-change: opacity, transform;
  }

  .reveal.is-in {
    opacity: 1;
    transform: none;
    will-change: auto;
  }
}

/* ================================================================== *
 * 8. Footer
 * ================================================================== */

.site-footer {
  border-top: 1px solid var(--paper-edge);
  padding-block: var(--s-3xl) var(--s-2xl);
  font-size: var(--step--1);
  color: var(--ink-quiet);
}

/* auto-fit rather than a fixed column count: the columns collapse one at a
 * time on the way down to 320px instead of all four at one breakpoint. */
.site-footer__columns {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--s-2xl) var(--s-l);
}

.site-footer h2 {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: var(--s-s);
}

.site-footer li + li {
  margin-top: var(--s-xs);
}

.site-footer a {
  /* --ink-quiet is 4.9:1 on paper, so these pass as body text at this size.
   * --ink-faint would not, which is why it is only on the headings above. */
  color: var(--ink-quiet);
  text-decoration: none;
  transition: color var(--t-fast) var(--ease);
}

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

.site-footer__base {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-s) var(--s-l);
  margin-top: var(--s-3xl);
  padding-top: var(--s-l);
  border-top: 1px solid var(--paper-edge);
  color: var(--ink-faint);
}

/* ================================================================== *
 * 9. Reduced motion
 *
 * One block, at the end, catching everything above it. Nothing in this file
 * animates a value that carries meaning, so removing all of it costs the
 * visitor nothing.
 * ================================================================== */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  /* Not merely a faster fade: the element is simply present, so a visitor whose
   * scroll observer never fires still reads the page. */
  .reveal,
  .reveal.is-in {
    opacity: 1;
    transform: none;
  }

  .btn-primary:active,
  .btn-quiet:active,
  .masthead__toggle:active {
    transform: none;
  }
}

/* ================================================================== *
 * 9. The theme control
 *
 * One button, three states. The glyph is a sun that loses its rays and gains a
 * bite as it moves system → light → dark, so the state is legible without a
 * word beside it and the control stays the width of a square.
 * ================================================================== */

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  background: none;
  color: var(--ink-quiet);
  cursor: pointer;
  transition:
    color var(--t-fast) var(--ease),
    background-color var(--t-fast) var(--ease),
    border-color var(--t-fast) var(--ease);
}

.theme-toggle:hover {
  color: var(--ink);
  background: var(--paper-sunk);
}

.theme-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* System: the plain sun, quiet, because no choice has been made. */
.theme-toggle[data-theme-state="system"] {
  color: var(--ink-faint);
}

/* Light: the same sun, lit. */
.theme-toggle[data-theme-state="light"] {
  color: var(--ink);
}

/* Dark: the rays go and the disc is offset into a crescent. Redrawing the path
 * would need a second SVG; a stroke-dasharray on the rays and a translate on
 * the circle is the same picture with no extra markup. */
.theme-toggle[data-theme-state="dark"] {
  color: var(--ink);
}

.theme-toggle[data-theme-state="dark"] .theme-toggle__rays {
  opacity: 0;
}

.theme-toggle[data-theme-state="dark"] circle {
  fill: currentColor;
  r: 4.6px;
}

@media (prefers-reduced-motion: no-preference) {
  .theme-toggle__rays,
  .theme-toggle circle {
    transition:
      opacity var(--t-mid) var(--ease),
      r var(--t-mid) var(--ease);
  }
}


/* ================================================================== *
 * 10. Air above an eyebrow
 *
 * A section's small uppercase label sat directly under the rule that opens the
 * section, with only the section's own padding between them — and at
 * `--step--1` with +0.09em tracking it reads as stuck to the line above it.
 *
 * The fix is on the eyebrow rather than on the section, because not every
 * section starts with one: adding the space to `.section` would push the ones
 * that open with a heading down by the same amount for no reason.
 * ================================================================== */

.eyebrow:first-child,
.section > .page > .eyebrow:first-child,
.illus-open > .eyebrow:first-child {
  margin-block-start: var(--s-l);
}

/* Except at the very top of a page, where there is nothing above to clear and
 * the masthead already provides the distance. */
main > :first-child .eyebrow:first-child {
  margin-block-start: 0;
}

/* ---- The language switcher -------------------------------------------------
 *
 * In the footer rather than the masthead, deliberately. A language picker at
 * the top competes with the navigation for a decision almost nobody makes
 * twice, and a visitor who lands on the wrong language scrolls to the bottom
 * looking for it — which is where every site they already use keeps it.
 *
 * Each name is written in its own language and never translated: somebody
 * looking for their own scans for the word they would write, and "German" is
 * invisible to a German speaker reading a Portuguese page.
 */
.langpick {
  display: inline-flex;
  align-items: center;
  gap: var(--s-xs);
}

.langpick__label {
  color: var(--ink-quiet);
  font-size: var(--step--1);
}

/* Styled, but still a `<select>`. `appearance: none` is deliberately *not*
 * used: the native control already looks like a thing you open, on every
 * platform, and reproducing that with a background-image chevron is how you end
 * up with a control that looks right until the stylesheet is late. */
.langpick__select {
  font: inherit;
  font-size: var(--step--1);
  color: var(--ink-body);
  background: var(--paper-raised);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius-s, 6px);
  padding: var(--s-2xs) var(--s-xs);
  cursor: pointer;
}

.langpick__select:hover {
  border-color: var(--ink-faint);
  color: var(--ink);
}

.langpick__fallback {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--s-xs) var(--s-m);
  font-size: var(--step--1);
}

/* The returning-visitor language hint.
 *
 * A line, not a modal, and not a redirect. Somebody who followed a link to a
 * particular language must not be moved somewhere else — that breaks the back
 * button and makes the site feel like it is arguing with them.
 */
.langhint {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-s);
  padding: var(--s-xs) var(--s-m);
  background: var(--paper-sunk);
  border-block-end: 1px solid var(--paper-edge);
  font-size: var(--step--1);
}

.langhint a {
  color: var(--ink);
}

.langhint__close {
  background: none;
  border: 0;
  color: var(--ink-quiet);
  cursor: pointer;
  font-size: 1.1em;
  line-height: 1;
  padding: 0 var(--s-2xs);
}

.langhint__close:hover {
  color: var(--ink);
}

/* ================================================================== *
 * Pre-launch
 *
 * There is no download and no sign-in yet. Every "Download for macOS"
 * button points at `/#download` or `/download`, neither of which exists,
 * and every "Sign in" link reaches a form with no accounts behind it —
 * so the site's primary call to action, on every page, was a control
 * that did nothing. A dead button costs more than a missing one: it is
 * the moment a visitor decides the product is not real.
 *
 * Hidden here, in one block, rather than deleted from fourteen pages
 * and four assembled sections. Restoring them is deleting this block —
 * which is the whole point of putting it in one place, and the reason
 * the selectors below are written against what the markup already says
 * rather than against a class added for the occasion.
 *
 * `.hero__meta` goes too: it carries `{{SIZE}}`, and the size of a
 * download that does not exist is not a number anybody can supply.
 *
 * DELETE THIS BLOCK when the first build ships.
 * ================================================================== */

a[href="/#download"],
a[href="/download"],
a[href="/login.html"],
.masthead__signin,
.hero__meta {
  display: none !important;
}
