/* ---------------------------------------------------------------
   campaignlanding — the default theme.

   Every colour is a token, and nothing below reaches past one, so a
   campaign retheme is a short block of custom properties in its own
   stylesheet rather than a fork of this file.

   The theme as it ships: a cool, violet-biased ground against a warm
   accent, with --verified marking figures a reader can check and
   --alarm marking figures that should worry them. Monospace for
   display and data, humanist sans for reading, on the argument that a
   page making numeric claims should look like it means them.
   --------------------------------------------------------------- */
:root {
  /* Dark is the default rather than the alternative. A browser that reports no
     colour preference — Facebook's in-app browser among them — was being handed the
     light palette, which is not what this page is meant to look like. */
  color-scheme: dark light;

  --ground:        #101219;
  --surface:       #181B23;
  --sunken:        #1E222C;
  --ink:           #E9EBF1;
  --ink-2:         #A2A8BC;
  --ink-3:         #767C90;
  --rule:          #2B2F3B;
  --accent:        #F0A32C;
  --accent-soft:   #3A2C13;
  --verified:      #46A891;
  --alarm:         #E4776A;
  --focus:         #7CA0F0;
  --btn-ink:       #1A1204;
  /* The headline's one-line width in ems: how wide it runs per 1px of font size,
     including any mark beside it. The hero sizes its type from this so the
     headline shrinks to fit rather than wrapping, which means the value has to be
     measured for your own words, in the browser, per language — headlines in two
     languages are never the same length. Set them in your own stylesheet:

       :root          { --h1-ratio: 24.0; }
       html[lang="es"]{ --h1-ratio: 29.15; }

     Measure a touch high rather than exact, so the line lands just inside the
     column's right edge; a font that renders a hair wider elsewhere would wrap.
     These defaults are a starting point, not a fit. */
  --h1-ratio: 24.0;
  /* Same idea for the eyebrow above the headline, measured the same way. */
  --eyebrow-ratio: 27.0;

  --font-display: ui-monospace, "SFMono-Regular", "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  /* Prose fills the column out to the same right edge as the hero image. This is
     a long line by typographic standards (~130 characters), so body line-height is
     raised to compensate. */
  --measure: 65rem;
  /* Same as the hero wrap, so a section's right edge lands exactly on the right
     edge of the hero image. */
  --wide: 68rem;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --ground:   #ECEDF1;
    --surface:  #FFFFFF;
    --sunken:     #E2E4EA;
    --ink:      #15171E;
    --ink-2:    #4C5164;
    --ink-3:    #767C90;
    --rule:     #D2D5DE;
    --accent:      #C77503;
    --accent-soft: #F6E4C4;
    --verified:    #1C6555;
    --alarm: #A93227;
    --focus:    #2C5CC5;
    --btn-ink:  #FFF8EC;
  }
}

:root[data-theme="light"] {
  --ground:        #ECEDF1;
  --surface:       #FFFFFF;
  --sunken:        #E2E4EA;
  --ink:           #15171E;
  --ink-2:         #4C5164;
  --ink-3:         #767C90;
  --rule:          #D2D5DE;
  --accent:        #C77503;
  --accent-soft:   #F6E4C4;
  --verified:      #1C6555;
  --alarm:         #A93227;
  --focus:         #2C5CC5;
  --btn-ink:       #FFF8EC;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
}

.wrap {
  max-width: var(--wide);
  margin: 0 auto;
  padding: 0 1.5rem;
}

.col { max-width: var(--measure); }

/* ---------- type ---------- */

h1, h2, h3 { font-family: var(--font-display); font-weight: 600; text-wrap: balance; margin: 0; }

/* text-wrap: balance optimises for even line lengths, which on a two-sentence
   heading strands the first word of the second sentence at the end of line one.
   Marking each clause inline-block leaves the boundary between them as the only
   comfortable break, so the heading splits where a reader would pause. A clause
   still wraps internally if it alone is wider than the line. */
.clause { display: inline-block; }

h1 {
  /* Narrow screens: wrap, and keep the display size. Fitting 39 characters on one
     line at 390px would mean a 15px headline, smaller than body text. */
  font-size: clamp(2rem, 7vw, 2.85rem);
  line-height: 1.04;
  letter-spacing: -0.035em;
}

/* Wide enough for one line at a display size: the headline measures --h1-ratio px
   of width per 1px of font-size in this face, measured in the browser, so the size
   comes from the space actually available rather than a guess. */
@media (min-width: 62rem) {
  h1 {
    white-space: nowrap;
    font-size: min(2.85rem, calc((min(68rem, 100vw) - 3rem) / var(--h1-ratio)));
  }
}

h2 {
  font-size: clamp(1.35rem, 3vw, 1.85rem);
  line-height: 1.15;
  letter-spacing: -0.025em;
}

h3 {
  font-size: 1.02rem;
  letter-spacing: -0.01em;
}

p { margin: 0; }

a { color: inherit; text-decoration-color: var(--ink-3); text-underline-offset: 3px; }
a:hover { text-decoration-color: var(--accent); }

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

.eyebrow {
  font-family: var(--font-display);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--ink-3);
  margin: 0;
}

.lede { font-size: 1.15rem; color: var(--ink-2); }

.fine { font-size: 0.86rem; color: var(--ink-3); line-height: 1.55; }

strong { font-weight: 650; color: var(--ink); }

.num {
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

/* ---------- structure ---------- */

section { border-top: 1px solid var(--rule); }
section > .wrap { padding-top: 4.5rem; padding-bottom: 4.5rem; }

.stack { display: flex; flex-direction: column; gap: 1.25rem; }
.stack-lg { display: flex; flex-direction: column; gap: 2.5rem; }
.stack-sm { display: flex; flex-direction: column; gap: 0.6rem; }

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

.hero { border-top: none; background: var(--surface); }
/* Wider than the reading column: the hero carries a second column for the image,
   while every other section stays at 60rem for line length. */
.hero > .wrap {
  /* 44rem of text + 2.75rem gap + 21rem of image = 67.75rem; wider than that
     would only open dead space between the two columns. */
  max-width: 68rem;
  padding-top: 5rem;
  padding-bottom: 4rem;
  display: flex;
  flex-direction: column;
  gap: 1.75rem;
}

.hero-head,
.hero-in,
.hero-more { display: flex; flex-direction: column; gap: 1.75rem; max-width: 44rem; }

/* ---------- language switch ---------- */

.langbar {
  display: flex;
  justify-content: flex-end;
  gap: 0.55rem;
  align-items: baseline;
  font-family: var(--font-display);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
}
.langbar a, .langbar span { text-decoration: none; padding: 0.15rem 0.1rem; }
.langbar a { color: var(--ink-3); }
.langbar a:hover { color: var(--accent); }
/* aria-current marks the page you are on; it is not a link. */
.langbar [aria-current] { color: var(--ink); font-weight: 600; }
.langbar .sep { color: var(--rule); letter-spacing: 0; }

.sunline {
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  font-family: var(--font-display);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-3);
}
.sunline::before {
  content: "";
  width: 2.5rem;
  height: 2px;
  background: var(--accent);
  transform: translateY(-0.25em);
  flex: none;
}

/* ---------- buttons ---------- */

.cta-row { display: flex; flex-wrap: wrap; gap: 0.9rem; align-items: center; }

.btn {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 0.92rem;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.01em;
  padding: 0.85rem 1.4rem;
  background: var(--accent);
  color: var(--btn-ink);
  border: 1px solid var(--accent);
  text-decoration: none;
  transition: transform 120ms ease, filter 120ms ease;
}
/* ---------- signup button with the join count as subtext ----------
   The label and the count stack, and the arrow stays vertically centred against
   both rather than against the label alone. The count is hidden above the rail's
   breakpoint, where the edge tab carries the number instead, so on desktop this
   button looks exactly as it did. */

/* The signup button is two lines tall once the count sits inside it, so its
   neighbours match its height instead of floating at their own. Stretching needs
   the buttons to be flex containers, otherwise the taller box would leave its
   label pinned to the top. No effect above the rail's breakpoint, where the count
   is hidden and every button is one line anyway. */
.hero .cta-row { align-items: stretch; }
.hero .cta-row > .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.btn-with-sub {
  display: inline-flex;
  align-items: center;
  gap: 0.8rem;
}
.btn-stack {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.05rem;
}
.btn-arrow { flex: none; }

/* On the button, the count borrows the button's own ink rather than the page's
   muted grey, which would be close to unreadable on the accent fill. */
.btn-with-sub .joininline {
  align-self: flex-start;
  font-size: 0.68rem;
  letter-spacing: 0.01em;
  color: inherit;
  opacity: 0.75;
  white-space: nowrap;
}
.btn-with-sub .joininline b { color: inherit; font-weight: 700; }

.btn:hover { filter: brightness(1.08); transform: translateY(-1px); }

.btn-quiet {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--rule);
}
.btn-quiet:hover { border-color: var(--accent); filter: none; }

/* ---------- data blocks ---------- */

.meter { display: flex; flex-direction: column; gap: 1.5rem; }

.bar-row { display: grid; grid-template-columns: 1fr; gap: 0.35rem; }

.bar-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  font-size: 0.9rem;
  color: var(--ink-2);
}

.bar-val { font-family: var(--font-display); font-variant-numeric: tabular-nums; color: var(--ink); font-weight: 600; }

.bar-track { height: 0.7rem; background: var(--sunken); overflow: hidden; }

.bar-fill {
  height: 100%;
  width: 0;
  background: var(--ink-3);
  transition: width 900ms cubic-bezier(0.22, 0.8, 0.3, 1);
}
.bar-fill.is-alert { background: var(--alarm); }
.bar-fill.is-sun { background: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .tab { transition: none; }
  .bar-fill { transition: none; }
  .btn { transition: none; }
}

.ledger { width: 100%; border-collapse: collapse; font-size: 0.95rem; }
.ledger th, .ledger td { text-align: left; padding: 0.65rem 0.9rem 0.65rem 0; border-bottom: 1px solid var(--rule); }
.ledger th {
  font-family: var(--font-display);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink-3);
  font-weight: 500;
}
.ledger td.n { text-align: right; font-family: var(--font-display); font-variant-numeric: tabular-nums; padding-right: 0; white-space: nowrap; }
.ledger tr.total td { border-bottom: none; border-top: 2px solid var(--ink); font-weight: 700; padding-top: 0.8rem; }
.ledger .muted { color: var(--ink-3); }
.scroll-x { overflow-x: auto; }

/* ---------- tier cards ---------- */

/* Stacked on phones, three across once there is room for three columns of text. */
.tiers { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); }
@media (min-width: 52rem) { .tiers { grid-template-columns: repeat(3, 1fr); } }

.tier { background: var(--surface); padding: 1.5rem; display: flex; flex-direction: column; gap: 0.5rem; }
.tier-price { font-family: var(--font-display); font-size: 1.5rem; font-weight: 600; letter-spacing: -0.03em; }
.tier-price .per { font-size: 0.8rem; color: var(--ink-3); font-weight: 400; letter-spacing: 0; }
.tier-name {
  font-family: var(--font-display);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-3);
}
.tier p { font-size: 0.93rem; color: var(--ink-2); }
/* The highlight belongs to whichever tier is selected, not to the free one
   permanently. Free simply starts selected, in the markup, so it looks right before
   any script runs. */
.tier.is-selected { background: var(--accent-soft); }
.tier.is-selected .tier-name { color: var(--accent); }

/* Only once the script has made them selectable: no pointer cursor on cards that
   cannot be clicked. */
.tiers.is-live .tier { cursor: pointer; transition: background-color 160ms ease; }
.tiers.is-live .tier:hover:not(.is-selected) {
  background: color-mix(in srgb, var(--accent-soft) 45%, var(--surface));
}
.tiers.is-live .tier:focus-visible { outline: 2px solid var(--focus); outline-offset: -3px; }
@media (prefers-reduced-motion: reduce) { .tiers.is-live .tier { transition: none; } }

/* ---------- asks ---------- */

.asks { display: flex; flex-direction: column; gap: 0; border-top: 1px solid var(--rule); }
.ask {
  display: grid;
  grid-template-columns: 2.5rem 1fr;
  gap: 1rem;
  padding: 1.35rem 0;
  border-bottom: 1px solid var(--rule);
  align-items: start;
}
.ask-n { font-family: var(--font-display); font-size: 0.8rem; color: var(--accent); font-variant-numeric: tabular-nums; padding-top: 0.28rem; }
.ask p { font-size: 0.95rem; color: var(--ink-2); margin-top: 0.15rem; }

/* ---------- callout ---------- */

.callout {
  border-left: 3px solid var(--accent);
  padding: 0.35rem 0 0.35rem 1.4rem;
  font-size: 1.05rem;
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
}

.quote { border-left: 3px solid var(--rule); padding: 0.35rem 0 0.35rem 1.4rem; }
.quote p { font-size: 1.02rem; }
.quote cite { display: block; margin-top: 0.6rem; font-style: normal; font-size: 0.84rem; color: var(--ink-3); }

.honest { background: var(--surface); }

.tag {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  padding: 0.2rem 0.5rem;
  border: 1px solid currentColor;
  color: var(--verified);
}
.tag.is-none { color: var(--alarm); }

.ul { margin: 0; padding-left: 1.15rem; display: flex; flex-direction: column; gap: 0.5rem; }
.ul li { color: var(--ink-2); }
.ul li strong { color: var(--ink); }

/* ---------- services ---------- */

.svc { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); }
@media (min-width: 40rem) { .svc { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 58rem) { .svc { grid-template-columns: repeat(3, 1fr); } }

.svc-item { background: var(--surface); padding: 1.1rem 1.2rem; display: flex; flex-direction: column; gap: 0.2rem; }
.svc-item b { font-family: var(--font-display); font-size: 0.95rem; font-weight: 600; letter-spacing: -0.01em; }
.svc-item span { font-size: 0.85rem; color: var(--ink-3); }

/* ---------- roadmap ---------- */

.acts { display: flex; flex-direction: column; gap: 3rem; }

.act-head {
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  padding-bottom: 0.9rem;
  border-bottom: 2px solid var(--ink);
  margin-bottom: 0.25rem;
}
.act-head h3 { font-size: 1.15rem; }
.act-head .act-when {
  margin-left: auto;
  font-family: var(--font-display);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink-3);
  white-space: nowrap;
}

.phase {
  display: grid;
  grid-template-columns: 2.75rem 1fr;
  gap: 1rem;
  padding: 1.2rem 0;
  border-bottom: 1px solid var(--rule);
  align-items: start;
}
.phase-n {
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-size: 0.82rem;
  color: var(--ink-3);
  padding-top: 0.3rem;
}
.phase-body { display: flex; flex-direction: column; gap: 0.3rem; }
.phase-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.7rem; }
.phase-title h4 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.phase p { font-size: 0.93rem; color: var(--ink-2); }

.chip {
  font-family: var(--font-display);
  font-size: 0.63rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  padding: 0.15rem 0.45rem;
  border: 1px solid var(--rule);
  color: var(--ink-3);
  white-space: nowrap;
}
.chip.now { color: var(--accent); border-color: var(--accent); }
.chip.next { color: var(--verified); border-color: var(--verified); }

/* ---------- signup form ---------- */

.form-card {
  background: var(--surface);
  border: 1px solid var(--rule);
  padding: 1.75rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.field { display: flex; flex-direction: column; gap: 0.4rem; }

.field label, .checks legend, .repeat legend {
  font-family: var(--font-display);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  color: var(--ink-3);
}

.field input[type="text"], .field textarea {
  font: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: var(--ground);
  border: 1px solid var(--rule);
  padding: 0.7rem 0.8rem;
  width: 100%;
}
.field input[type="text"]:focus, .field textarea:focus { border-color: var(--accent); }
.field textarea { resize: vertical; min-height: 4.5rem; }
.field .hint { font-size: 0.82rem; color: var(--ink-3); }

.checks { border: 0; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.55rem; }
.checks legend { padding: 0; margin-bottom: 0.15rem; }
.check { display: flex; gap: 0.6rem; align-items: flex-start; font-size: 0.93rem; color: var(--ink-2); cursor: pointer; }
.check input { margin-top: 0.35rem; accent-color: var(--accent); flex: none; }

/* Rows a reader can add to, one per thing they have several of: each a boxed
   name with its own checkboxes, and a button that adds another. */
.repeat { border: 0; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.75rem; }
.repeat legend { padding: 0; margin-bottom: 0.15rem; }
.repeat-row {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 0.9rem;
  border: 1px solid var(--rule);
  background: var(--sunken);
}
.repeat-row .field input[type="text"] { background: var(--surface); }
.repeat-row [data-repeat-remove] { align-self: flex-end; }
[data-repeat-add] { align-self: flex-start; }
/* Both are buttons, and a .btn sets its own display, which beats the hidden
   attribute's. Without this the add button shows with JavaScript off, where it
   can do nothing, and the remove button shows on rows that can't be removed. */
[data-repeat-add][hidden], [data-repeat-remove][hidden] { display: none; }

.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form-msg { font-size: 0.95rem; padding: 0.8rem 1rem; border: 1px solid var(--rule); display: none; }
.form-msg.show { display: block; }
.form-msg.ok { border-color: var(--verified); color: var(--verified); }
.form-msg.err { border-color: var(--alarm); color: var(--alarm); }

.form-card button { cursor: pointer; }

/* ---------- signup modal ---------- */

.modal { display: none; position: fixed; inset: 0; z-index: 100; }
/* :target is the no-JS path; .is-open is the scripted one. Single rule for both
   so nothing later has to override an earlier display value.

   .is-closed exists because clearing the hash cannot close a :target-opened
   modal. Measured on the live page: after history.replaceState removed the
   fragment, the element still matched :target and still computed to display:grid,
   because Chrome does not re-evaluate :target for History API URL changes. The
   script adds this class on close and removes it on open, so the scripted state
   always wins over the URL. With no script it is never added and :target behaves
   exactly as before. */
.modal:target:not(.is-closed),
.modal.is-open { display: grid; place-items: center; padding: 1rem; }

.modal-scrim {
  position: absolute;
  inset: 0;
  background: rgba(10, 12, 18, 0.55);
  backdrop-filter: blur(2px);
  display: block;
}

.modal-card {
  position: relative;
  z-index: 1;
  margin: auto;
  max-width: 34rem;
  max-height: calc(100dvh - 2rem);
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--rule);
  box-shadow: 0 18px 50px rgba(10, 12, 18, 0.28);
  padding: 2rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.modal-card .form-card { border: 0; padding: 0; background: transparent; }

.modal-close {
  position: absolute;
  top: 0.65rem;
  right: 0.9rem;
  font-family: var(--font-display);
  font-size: 1.5rem;
  line-height: 1;
  color: var(--ink-3);
  text-decoration: none;
  padding: 0.25rem 0.4rem;
}
.modal-close:hover { color: var(--accent); }

body.modal-open { overflow: hidden; }

@media (prefers-reduced-motion: no-preference) {
  .modal:target .modal-card, .modal.is-open .modal-card { animation: modal-in 160ms ease-out; }
  @keyframes modal-in {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: none; }
  }
}

footer { border-top: 1px solid var(--rule); background: var(--sunken); }
footer > .wrap { padding-top: 3rem; padding-bottom: 4rem; }
.srcs { display: flex; flex-wrap: wrap; gap: 0.4rem 1rem; font-size: 0.84rem; }

/* ---------- reason tabs ----------
   Progressive enhancement: with no JS every panel is visible and stacked, and
   the tablist is hidden because it would do nothing. JS adds .js-tabs, which
   collapses them into real ARIA tabs. The content is always in the DOM. */

.tabs { display: flex; flex-direction: column; gap: 2.5rem; }

/* Offset the sticky tablist by whatever the section nav occupies at the top.
   Narrow screens have the nav as a sticky strip; from 84rem it becomes a fixed
   left rail and stops taking vertical space. JS refines this by measurement. */
:root { --stick-top: 2.6rem; }
@media (min-width: 84rem) { :root { --stick-top: 0px; } }

.tablist {
  display: flex;
  gap: 0;
  overflow-x: auto;
  border-bottom: 1px solid var(--rule);
  /* four labels do not fit on a phone; scroll rather than wrap or shrink */
  scrollbar-width: none;
  /* Sticks under the section nav so a tab is reachable from anywhere in a panel.
     Opaque, or the panel text scrolls visibly underneath it. */
  position: sticky;
  top: var(--stick-top);
  z-index: 40;
  background: var(--ground);
}
.tablist::-webkit-scrollbar { display: none; }

.tabs:not(.js-tabs) .tablist { display: none; }

/* A mark per reason, so the four read as distinct things rather than as four
   words of the same length. Sized in em, so it grows with the label when the bar
   enlarges. */
.tab { display: inline-flex; align-items: center; gap: 0.5em; }
.tab-icon {
  width: 1.25em;
  height: 1.25em;
  flex: none;
  fill: none;
  /* The accent rather than currentColor: the label shifts between --ink and --ink-3
     with selection, and the mark should not dim along with it. */
  stroke: var(--accent);
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.tab {
  flex: none;
  appearance: none;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  padding: 0.75rem 1.1rem;
  font-family: var(--font-display);
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  color: var(--ink-3);
  cursor: pointer;
  white-space: nowrap;
}
.tab:first-child { padding-left: 0; }
.tab:hover { color: var(--ink); }
.tab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--accent); }

.panel:focus-visible { outline: none; }

/* The panels also carry .stack-lg, whose `display: flex` beats the UA's
   `[hidden] { display: none }`. Without this the hidden attribute toggles
   correctly and changes nothing on screen. Specificity 0-1-1 beats 0-1-0. */
.panel[hidden] { display: none; }

/* Stacked (no-JS) panels need their own separators; tabbed ones must not have
   them, so the rule is scoped to the un-enhanced container. */
.tabs:not(.js-tabs) .panel + .panel {
  border-top: 1px solid var(--rule);
  padding-top: 2.5rem;
}

/* ---------- section nav ----------
   One nav element, two presentations. Narrow screens get a sticky scrolling
   strip; wide screens get a fixed rail on the left edge, which only appears once
   the viewport is wide enough to hold it beside the 60rem column. Plain anchors
   throughout, so it works with scripting off — JS only marks the current entry. */

.sectionnav {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  gap: 0;
  overflow-x: auto;
  scrollbar-width: none;
  background: var(--surface);
  border-bottom: 1px solid var(--rule);
}
.sectionnav::-webkit-scrollbar { display: none; }

.sectionnav a {
  flex: none;
  padding: 0.7rem 0.9rem;
  font-family: var(--font-display);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  color: var(--ink-3);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
}
.sectionnav a:hover { color: var(--ink); }
.sectionnav a[aria-current] { color: var(--ink); border-bottom-color: var(--accent); }

/* The sticky strip would otherwise sit on top of the heading you jumped to. */
section[id], header[id] { scroll-margin-top: 3.25rem; }

@media (min-width: 84rem) {
  .sectionnav {
    position: fixed;
    top: 50%;
    left: 2rem;
    transform: translateY(-50%);
    flex-direction: column;
    gap: 0.15rem;
    overflow: visible;
    background: none;
    border-bottom: 0;
    max-width: 10rem;
  }
  .sectionnav a {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    /* Was 0.3rem, which gave 28.6px boxes separated by 2.4px. The boundary with the
       entry above then sat about 10px over the label's glyphs, so a click aimed just
       high of a word selected the section above it. */
    padding: 0.55rem 0;
    border-bottom: 0;
    margin-bottom: 0;
    white-space: normal;
  }
  /* A tick per entry — the same gauge language as the water bars. The flag is not
     a destination, so it gets no tick. */
  .sectionnav a:not(.nav-flag):not(.nav-brand):not(.nav-icon)::before {
    content: "";
    flex: none;
    width: 0.85rem;
    height: 2px;
    background: var(--rule);
    transition: width 140ms ease, background-color 140ms ease;
  }
  .sectionnav a:not(.nav-icon):hover::before { background: var(--ink-3); }
  .sectionnav a[aria-current] { border-bottom: 0; }
  .sectionnav a[aria-current]::before { width: 1.7rem; background: var(--accent); }

  /* ---------- the sticky reason tabs grow while you are inside the section ----------
     Habitual downward scrolling carries a reader straight past three of the four
     reasons, so once the bar pins itself to the top it enlarges to say there is
     more here, and returns to its normal size before the section ends so the
     change is noticed a second time on the way out.

     Only the type grows, never the bar. The bar reserves the taller height at all
     times and aligns its tabs to the bottom: it is sticky and in flow, so a height
     that changed mid-scroll would shove the panel text below it by that amount,
     right where the reader is looking. Bottom alignment also keeps the active
     tab's underline sitting on the bar's own rule rather than floating above it.

     Measured before choosing 1.5: the tabs span 443px of a 1040px column, so the
     enlarged row needs about 664px and still fits with room to spare. */
  .tablist {
    min-height: 3.55rem;
    align-items: flex-end;
  }
  .tab {
    transition: font-size 280ms ease, padding 280ms ease;
  }
  .tablist.is-big .tab {
    font-size: 1.17rem;
    padding-right: 1.6rem;
  }

  section[id], header[id] { scroll-margin-top: 1.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .sectionnav a:not(.nav-flag):not(.nav-brand):not(.nav-icon)::before { transition: none; }
}

/* ---------- join count ----------
   One number, two presentations, and the styles are public because the element
   is. Below the rail's breakpoint it rides along in the button row as plain
   text: a fixed edge tag has no side margin to sit in at those widths and would
   cover the prose. At the rail's width there is a column of empty margin for it,
   and it becomes a stacked tag with the number carrying the emphasis.

   `is-new` is only ever emitted for a signed-in administrator, so the accent
   state cannot appear for a visitor. */

.joininline {
  align-self: center;
  font-family: var(--font-display);
  /* Sits beside the signup button, so it has to fit in what the button leaves of
     a phone's line. Smaller than body text and never broken mid-phrase: if the
     line cannot hold it, the whole phrase moves rather than splitting across the
     button's right edge. */
  font-size: 0.76rem;
  white-space: nowrap;
  color: var(--ink-3);
  text-decoration: none;
}
.joininline b { font-weight: 700; color: var(--ink-2); }
a.joininline:hover { color: var(--ink-2); }
a.joininline:hover b { color: var(--accent); }
a.joininline.is-new, a.joininline.is-new b { color: var(--accent); }

/* Hidden until there is margin to hold it. The one exception is a signed-in
   administrator: the count inside the button cannot be a link, since an anchor
   nested in an anchor is invalid, so at phone widths this tab is their only route
   to the list. A visitor never receives the class. */
.jointag { display: none; }
.jointag.is-admin { display: flex; }

@media (min-width: 84rem) {
  .jointag {
    position: fixed;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
    z-index: 90;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.12rem;
    padding: 0.7rem 0.55rem;
    background: var(--surface);
    color: var(--ink-3);
    border: 1px solid var(--rule);
    border-right: 0;
    border-radius: 0.4rem 0 0 0.4rem;
    font-family: var(--font-display);
    font-size: 0.56rem;
    line-height: 1.1;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    text-decoration: none;
    transition: border-color 140ms ease, color 140ms ease;
  }
  .jt-n {
    font-size: 1.2rem;
    font-weight: 700;
    letter-spacing: 0;
    color: var(--ink);
  }
  .jointag:hover { border-color: var(--accent); }
  .jointag:hover .jt-n { color: var(--accent); }
  .jointag.is-new { border-color: var(--accent); color: var(--accent); }
  .jointag.is-new .jt-n { color: var(--accent); }
}

@media print { .jointag { display: none; } }

/* ---------- last resorts for very narrow phones ----------
   The bar has to hold five labels, the home icon and both flags on one row. Two
   tiers of relief, in the order that costs the least meaning:

   First the flags give up their padding and a little size. That is invisible to a
   reader and buys about 20px.

   Only if that is not enough does "Why" become "?", because a question mark is a
   worse label than a word and should be the thing we reach for last. The word is
   still announced: the visible mark is aria-hidden and the full label stays in the
   accessible name through the hidden span beside it. */

@media (max-width: 26.5rem) {
  .sectionnav a.nav-flag,
  .sectionnav span.nav-flag {
    font-size: 1.28rem;
    padding: 0.5rem;
    margin: -0.5rem 0;
  }
  .sectionnav .nav-lang {
    gap: 0.15rem;
    padding-left: 0.35rem;
    padding-right: 0.1rem;
  }
  .sectionnav .nav-icon { width: 2.2rem; margin-left: 0.05rem; }
}

/* 23rem, not 25: measured with the squeeze tier applied, a 393px Pixel 4 has about
   25px of slack in Spanish and keeps the word. Only 360px-and-under phones, where
   Spanish overruns by ~8px, fall back to the mark. */
@media (max-width: 23rem) {
  /* Only .nav-swap, never .nav-short as a class: the other four labels have no
     replacement, and hiding them left a "?" followed by four empty tabs. */
  .sectionnav .nav-swap { display: none; }
  .sectionnav .nav-min { display: inline; }
}

/* Sits at the foot of the form it describes, so the claim is next to the field
   it is a claim about rather than beside the button that opens it. */
.form-provenance { margin-top: -0.35rem; }

/* ---------- citations ----------
   Wikipedia-style numbered superscripts pointing at the reference list in the
   footer. :target highlights the entry you jumped to, so a click on [2] shows
   which line answered it. */

.ref { font-size: 0.62em; line-height: 0; vertical-align: super; white-space: nowrap; }
.ref a {
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  text-decoration: none;
  padding: 0 0.08em;
}
.ref a:hover { text-decoration: underline; }

.refs {
  margin: 0;
  padding-left: 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  font-size: 0.84rem;
  line-height: 1.5;
}
.refs li { color: var(--ink-3); scroll-margin-top: 4.5rem; }
.refs li::marker { font-family: var(--font-display); font-variant-numeric: tabular-nums; }
.refs li:target { color: var(--ink); box-shadow: -0.5rem 0 0 var(--accent-soft), 0.5rem 0 0 var(--accent-soft); background: var(--accent-soft); }
.refs cite { font-style: normal; color: var(--ink-2); }

/* Service names in the grid are links to the projects themselves. */
.svc-item a { text-decoration-color: var(--rule); }
.svc-item a:hover { text-decoration-color: var(--accent); }

/* ---------- language switcher flags ----------
   The flag is decorative and the word is the label. On narrow screens the word
   is visually hidden rather than removed, so the link is still announced as
   "English" / "Español" to a screen reader and still reachable by voice control. */

.langbar a, .langbar [aria-current] { display: inline-flex; align-items: baseline; gap: 0.4rem; padding: 0.45rem 0.35rem; }
.langbar .flag {
  /* A little larger than the label beside it, but nowhere near the rail flags:
     0.72rem x 1.6 is about 18px against the rail's 25.6px. */
  font-size: 1.6em;
  letter-spacing: 0;
  /* Windows Chrome renders regional-indicator pairs as letters (US / MX) rather
     than flags. That still reads correctly, so no fallback is forced. */
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
}

@media (max-width: 34rem) {
  .langbar .lang-word {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .langbar .flag { font-size: 1.25em; }
}

/* The "Why" label belongs to the tab bar, so pull it out of the section's
   2.5rem rhythm and let it sit just above the bar. It cannot live inside .tabs:
   the tablist is sticky, and wrapping it would shrink its containing block and
   kill the stickiness. */

/* ---------- mobile hero spacing ----------
   5rem of hero padding is generous on a phone, especially under the sticky
   section nav. Tighten it, and give the language switcher equal space above and
   below so it reads as centred in the band between the top and the first line
   of text rather than crowding either edge. */

@media (max-width: 52rem) {
  /* The switcher is pinned in the sticky bar now, so the hero does not reserve
     vertical space for it. */
  .hero > .wrap { padding-top: 1.6rem; padding-bottom: 2.75rem; }
}

@media (max-width: 52rem) {
  /* No room for the marigold rule alongside the text on a phone, and the three
     parts should stay on one line, so the size follows the space available. */
  .sunline::before { display: none; }
  .sunline {
    gap: 0;
    white-space: nowrap;
    font-size: min(0.78rem, calc((100vw - 3.2rem) / var(--eyebrow-ratio)));
  }
}

/* ---------- narrow-screen nav and tabs: squish, don't scroll away ----------
   Exactly one of the two labels is in the DOM's accessibility tree at a time,
   so a screen reader never hears both. */

.nav-short { display: none; }
.nav-min { display: none; }

@media (max-width: 52rem) {
  .nav-long { display: none; }
  .nav-short { display: inline; }

  /* The third call to action takes a line of its own, so "Get updates" and "Why"
     stay together on the first one. Left to wrap naturally the three would break
     wherever the widths happened to land. */
  .hero .cta-row > .btn:last-child { flex-basis: 100%; }

  /* Measured: the Spanish pair needs 356.1px against 347px of line on a 353px
     phone, so it broke mid-pair while English fit with 78px to spare. Trimming
     the horizontal padding here returns 19.2px across the two buttons, which
     clears it with room rather than by a pixel. */
  .hero .cta-row > .btn { padding-left: 1.1rem; padding-right: 1.1rem; }


  /* Fill the bar rather than clustering left: size to content, then grow into
     whatever is left over, so the labels spread across the available width.
     `auto` rather than `0` so WHY does not get as much room as WHY HERE.

     Wrap to a second row instead of crowding. `min-width: 0` plus shrink let each
     label be squeezed narrower than its own text, which clipped all five Spanish
     labels at 353px ("HOJA DE RUTA" wanted 82px and was given 61px) rather than
     scrolling or wrapping. `flex-shrink: 0` with the intrinsic minimum restored
     means a label is never narrower than its text, so a row that cannot hold them
     all breaks instead. The bar keeps its single row wherever they do fit, and
     initStickTop() measures the real height, so the sticky tab bar below follows
     a two-row bar without a hardcoded offset. */
  .sectionnav { padding-right: 0.35rem; flex-wrap: wrap; }
  .sectionnav a {
    flex: 1 0 auto;
    min-width: auto;
    text-align: center;
    padding: 0.65rem 0.3rem;
    font-size: 0.62rem;
    letter-spacing: 0.05em;
  }

  /* Two rows of two when the labels do not fit side by side, never a lopsided
     3 + 1. auto-fit produced exactly that at 430-480px, so the column count is
     explicit: two here, four once there is room for all of them (below). The
     longest Spanish label, "MEDIO AMBIENTE", needs about 6.7rem plus padding, and
     a 320px phone gives each of two columns 8.5rem. */
  .tablist {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    overflow: visible;
  }
  .tab {
    min-width: 0;
    text-align: center;
    padding: 0.7rem 0.35rem;
    font-size: 0.66rem;
    letter-spacing: 0.05em;
    /* No -1px overlap when there is a row above the container's border. */
    margin-bottom: 0;
  }
  .tab:first-child { padding-left: 0.35rem; }

  /* The hero switcher is hidden here: the nav bar carries the flag instead, as a
     real flex item rather than a fixed overlay. A fixed element stays pinned while
     the sticky bar rubber-bands during overscroll, which visibly separated the two
     borders. One bar, one border, nothing to desync. */
  .hero .langbar { display: none; }

  /* Scoped under .sectionnav on purpose: the base `.nav-lang { display: none }`
     lives further down the sheet, and a media query adds no specificity, so an
     unscoped rule here would lose on source order and the control would vanish. */
  /* display: contents so the dock's children become items of the bar itself.
     That is what lets the home icon sit at the far left while the flags stay at
     the far right, from one piece of markup rather than two copies. */
  .sectionnav .nav-top,
  .sectionnav .nav-dock {
    display: contents;
  }

  /* A bar of its own at the foot of the window. The top bar is already full at
     these widths, and these two are the least urgent things on it, so they move
     to where a thumb is rather than competing for the row that holds navigation.
     The safe-area inset keeps them clear of a home indicator. */
  .sectionnav .nav-social {
    display: flex;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 60;
    justify-content: center;
    gap: 2rem;
    padding: 0.3rem 0;
    padding-bottom: max(0.3rem, env(safe-area-inset-bottom));
    background: var(--surface);
    border-top: 1px solid var(--rule);
  }
  .sectionnav .nav-fb,
  .sectionnav .nav-copy { display: inline-flex; }

  /* So the last line of the page is not sitting under that bar. */
  body { padding-bottom: 3.4rem; }
  .sectionnav .nav-icon { order: 0; margin-left: 0.15rem; }
  /* The star stays at the left of the top bar; the link icon has moved to the
     footer below, so it is no longer hidden here. */
  .sectionnav .nav-home { display: inline-flex; }
  .sectionnav .nav-lang {
    order: 3;
    margin-left: auto;
    padding-left: 0.7rem;
    padding-right: 0.25rem;
    /* Absorbs the flags' negative vertical margin. That margin buys a large tap
       target without making the bar taller, which works while the flags share a
       row with the labels, because the labels set the row height. Once the bar
       wraps the flags onto a row of their own they set it themselves, and the box
       hung 6.8px below the bar with the selected outline landing 0.2px off the
       bottom border, so the box read as breaking the border. This padding makes
       the row tall enough to contain the flags either way; it costs one pixel of
       bar height in the unwrapped case. */
    padding-block: 0.5rem;
  }
  /* Phase chips go above the title, not below it. On one line the chip reads as a
     right-hand annotation to the heading; wrapped underneath it reads as a label
     for the paragraph that follows, which is the wrong thing. column-reverse puts
     it back on top without reordering the markup, so it is still announced after
     the heading it qualifies. Applied to every chip, not just the coloured Now and
     Next: a mix of positions would look like a mistake. */
  .phase-title {
    flex-direction: column-reverse;
    align-items: flex-start;
    gap: 0.4rem;
  }

  /* Alternating fills so each label's extent is readable. Once the labels grow to
     fill the row there is only 0.3rem between one word and the next, and they ran
     together. --sunken is the existing subtle alternate surface, so this holds up in
     light, dark and the un-stamped default without a new token. */
  .sectionnav a:not(.nav-flag):not(.nav-brand):nth-of-type(odd) { background: var(--sunken); }

  .sectionnav .nav-brand { display: none; }
  .sectionnav .nav-lang { display: flex; align-items: center; gap: 0.3rem; opacity: 1; transform: none; pointer-events: auto; }
  .sectionnav a:not(.nav-flag):not(.nav-brand):not(.nav-icon) { order: 1; }
  /* No box on the current language here. The two flags read as a switcher on their
     own, and the page's own text says which language you are in. The rail keeps its
     box, where the flags sit beside the mark with no surrounding text to say it. */
  .sectionnav .nav-flag.is-current { cursor: default; }
  .sectionnav a.nav-flag,
  .sectionnav span.nav-flag {
    display: inline-block;
    font-size: 1.45rem;
    line-height: 1;
    text-decoration: none;
    letter-spacing: 0;
    padding: 0.55rem;
    margin: -0.55rem 0;
  }
}

/* ---------- hero satellite figure ----------
   Hidden entirely below 62rem: on a phone it would push the headline and the
   buttons off the first screen for no argumentative gain. Above that the hero
   becomes two columns and the image sits to the right of the title, running down
   past the action buttons. The figure comes last in the DOM so the headline is
   read first; grid puts it back on the right. */

.hero-figure { display: none; margin: 0; }

/* A character at the head of the headline rather than a block beside it: sized in
   em so it tracks the type, with the space after it doing the work a space does. */
.hero-mark {
  display: inline-block;
  width: 1.59em;
  height: 1.59em;
  /* The drawing carries its own margin: its leftmost ink sits inside the box, so
     the box is pulled left by that much to line the star up with the text column
     rather than with empty space. The right margin replaces the literal space that
     used to follow it, which was wider than the mark needs. */
  margin-left: -0.11em;
  margin-right: 0.22em;
  /* middle, not a hand-tuned offset: the mark is symmetrical and has no baseline
     of its own, and an em offset tuned by eye stops being centred the moment the
     size changes. */
  vertical-align: middle;
}

/* Hidden where the sticky bar carries the mark, which on a phone is the whole time:
   the same star twice is once too many, and the bar has the better claim since it is
   always on screen and the mark is its only identity there. Qualified rather than
   left to source order, which is what let the first attempt at this lose to the rule
   above it. */
@media (max-width: 52rem) {
  .hero h1 .hero-mark { display: none; }
}

@media (min-width: 62rem) {
  .hero > .wrap {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 21rem;
    column-gap: 2.75rem;
    row-gap: 1.75rem;
    align-items: start;
  }
  .langbar     { grid-row: 1; grid-column: 1 / -1; }
  /* Full width so the headline has the whole hero to fit on one line — the 44rem
     reading cap applies to the paragraphs, not to the headline, or the text
     overflows its box and spills across the image column. */
  .hero-head   { grid-row: 2; grid-column: 1 / -1; max-width: none; }
  .hero-in     { grid-row: 3; grid-column: 1; }
  .hero-more   { grid-row: 4; grid-column: 1; }
  /* Spans every row of hero text and centres in it. Sized by the column width at
     its own aspect ratio — no stretch, so no crop and no distortion.
     `span 2` rather than `2 / -1`: -1 resolves against the EXPLICIT grid, and
     there is no grid-template-rows here, so -1 collapsed to line 1 and put the
     figure above the text instead of beside it. */
  /* Bounded by the lede at the top and the closing paragraph at the bottom — rows
     3 and 4 — and centred in that area. Deliberately not row 2: the headline sits
     above it. `span 2` not `/ -1`, since -1 resolves against the explicit grid and
     there is no grid-template-rows here. */
  .hero-figure { grid-row: 3 / span 2; grid-column: 2; display: block; align-self: center; }
  .hero-figure img {
    display: block;
    width: 100%;
    height: auto;
    border: 1px solid var(--rule);
  }
}

/* Minimal source credit under the hero image. NASA asks to be acknowledged; the
   full line lives in the footer, this is just the visible pointer. */
.hero-credit {
  margin-top: 0.45rem;
  font-family: var(--font-display);
  font-size: 0.6rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-3);
  opacity: 0.8;
}
.hero-credit a { color: inherit; text-decoration-color: var(--rule); }
.hero-credit a:hover { color: var(--accent); opacity: 1; }

/* ---------- keep the centred column clear of the fixed left rail ----------
   The rail sits at left:2rem and is at most 10rem wide, so it occupies up to
   12rem. Content is centred, so reserving 13.5rem on both sides puts the content
   edge at 13.5rem — clear of the rail at every width, with no magic breakpoints.
   Without this the wider hero overlapped the rail between 84rem and ~97rem. */

@media (min-width: 84rem) {
  :root { --wide: min(68rem, calc(100% - 27rem)); }
  .hero > .wrap { max-width: min(68rem, calc(100% - 27rem)); }
  h1 { font-size: min(2.85rem, calc((min(68rem, 100vw - 27rem) - 3rem) / var(--h1-ratio))); }
}

/* The Spanish headline is 48 characters against 39 in English, so it needs more
   width per point of font-size to stay on one line inside the same column. */

/* ---------- rail brand and docked language flag ----------
   Desktop only. The flag sits on the same line as the mark, to its right, and
   fades in once the hero's own switcher has scrolled away. It is positioned
   absolutely so appearing and disappearing never shifts the mark or the links. */

.nav-top, .nav-brand, .nav-lang { display: none; }

/* ---------- nav icons ----------
   Same muted ink as the labels beside them, so they read as part of the bar
   rather than as a second call to action. Stroke follows currentColor, which is
   what makes the copied state a one-property change. */
.nav-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.6rem;
  height: 2.6rem;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0.3rem;
  color: var(--ink-3);
  cursor: pointer;
  transition: color 140ms ease;
}
.nav-icon svg {
  width: 1.35rem;
  height: 1.35rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* The mark replaces the drawn house. In the bar it has no ground of its own: it
   sits among muted outline glyphs, and a solid tile there would shout. Opacity
   does the work that colour does for its neighbours, since a raster cannot take
   currentColor, and it lifts to full on hover the way they lighten to --ink. */
/* Plain images rather than CSS masks. The mask never painted, not even with the
   mask removed and a solid colour set, so the colour is baked into two drawings
   instead: the full one, and a clean one that drops the satellite nodes hanging
   outside the star's outline. */
.nav-logo { width: 84%; height: 84%; display: block; }
.nav-logo-clean { display: none; }

/* A filled brand glyph, unlike the stroked icons beside it: with fill:none and a
   stroke it rendered as a heavy double outline, which is what made it look huge. */
.nav-fb svg { fill: currentColor; stroke: none; }

.nav-icon:hover { color: var(--ink); }
.nav-icon.is-copied { color: var(--verified); }
@media (prefers-reduced-motion: reduce) { .nav-icon { transition: none; } }

/* Which of the two shows: at the top of the page there is nothing to go back to,
   so that slot offers the link instead. Once the dock appears on scroll it
   becomes the way home. */
/* No more swapping between the two. The star belongs to the row that fades in on
   scroll, and the link and Facebook icons are always there in the row below it. */
.nav-copy, .nav-fb { display: inline-flex; }

@media (min-width: 84rem) {
  /* Sized against the flags beside it: their glyph reads large inside a 45px box,
     and a thin-stroked icon at the same box size reads smaller than it measures. */
  .sectionnav .nav-icon { width: 2.75rem; height: 2.75rem; }
  /* The rail is quiet and close-set, so the satellite web reads as noise there.
     The phone bar keeps the full drawing, where it has room to be seen. */
  .nav-logo-full { display: none; }
  .nav-logo-clean { display: block; }
  .sectionnav .nav-icon svg { width: 1.55rem; height: 1.55rem; stroke-width: 1.75; }

  .nav-top {
    display: flex;
    flex-direction: column;
    /* Left, in line with the labels below, rather than centred in a column whose
       width is set by the longest label. */
    align-items: flex-start;
    gap: 0.5rem;
    position: relative;
    padding-bottom: 0.9rem;
  }


  .nav-lang { display: flex; align-items: center; gap: 0.35rem; }

  /* The mark and the flags share a row that is only relevant once you have left the
     top of the page: the hero carries its own language switch, and "back to top"
     means nothing while you are already there. It fades rather than appears, and it
     keeps its space either way, so the row below never moves. */
  .nav-dock {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    opacity: 0;
    transform: translateX(-3px);
    pointer-events: none;
    transition: opacity 180ms ease, transform 180ms ease;
  }
  .sectionnav.has-lang .nav-dock {
    opacity: 1;
    transform: none;
    pointer-events: auto;
  }

  /* Always present, below the dock: the page's two outward links. The gap matches
     the dock's exactly, which is what puts the link icon under the first flag: both
     rows start with a 44px item, so an equal gap makes the second column line up.
     At 0.35rem it sat 6.4px to the left, which is precisely the difference. */
  .nav-social { display: flex; align-items: center; gap: 0.75rem; }
  /* Element-qualified on purpose: `.sectionnav a` is (0,2,0) and beat a bare
     `.nav-flag` (0,1,0), so the link kept the nav label's 0.68rem while the span
     took the flag size. This is (0,2,1) and wins outright. */
  .sectionnav a.nav-flag,
  .sectionnav span.nav-flag {
    display: inline-block;
    font-size: 1.6rem;
    line-height: 1;
    text-decoration: none;
    letter-spacing: 0;
    /* Padding takes the hit area past 45px. The negative margin is vertical only:
       applying it horizontally pulled the flag back into the mark and cancelled
       the gap, which is what made it look crowded. */
    padding: 0.6rem;
    margin: -0.6rem 0;
    transition: filter 140ms ease, opacity 140ms ease;
  }
  /* Both flags are always shown so the control reads as a language switcher. The
     one you are already reading is full strength; the other is muted until hover. */
  /* Both flags stay full colour; the one you are reading gets a hairline box.
     Drawn with outline and a negative offset so it hugs the glyph rather than the
     45px hit area, and so it costs no layout. */
  /* A circle, not a box. Drawn as a pseudo-element rather than by giving the
     outline a border-radius: rounded outlines are recent enough that Safari only
     picked them up in 16.4, and a square ring on an older browser would look like
     a bug rather than a fallback. The flag box is square (about 45px), so a 50%
     radius on an inset ring is a true circle. */
  .sectionnav .nav-flag.is-current {
    position: relative;
    cursor: default;
  }
  .sectionnav .nav-flag.is-current::after {
    content: "";
    position: absolute;
    /* 0.33rem, not 0.42: the ring is inset from a ~45px box, so shrinking the inset
       by 0.09rem on each side grows the circle from about 29px across to about 32,
       which is the 10% Ben asked for. */
    inset: 0.33rem;
    border: 1px solid var(--accent);
    border-radius: 50%;
    pointer-events: none;
  }
  .sectionnav a.nav-flag:hover { filter: brightness(1.08); }
}

@media (prefers-reduced-motion: reduce) {
  .nav-dock { transition: none; }
  .nav-flag { transition: none; }
}

/* Wide enough for all four labels on one line, but still the narrow-screen bar. */
@media (min-width: 38rem) and (max-width: 52rem) {
  .tablist { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}


/* ---------- phone: a footer bar ----------
   Appended at the end of the sheet on purpose: these override rules earlier in the
   phone block, and equal-specificity rules losing on position has caused several
   bugs in this file.

   Two fixed elements, not one, because CSS cannot move the flags into .nav-social.
   .nav-social paints the bar the full width and the flags sit over its right end. The
   trick to making that look like one bar is that both share a box model exactly: the
   same bottom padding, the same item size, so their contents land on one line. The
   flags also give up the negative vertical margins they use in the top bar, which is
   what was pushing them out of alignment here. */
@media (max-width: 52rem) {
  .sectionnav .nav-social,
  .sectionnav .nav-lang {
    position: fixed;
    bottom: 0;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding-top: 0.3rem;
    padding-bottom: max(0.3rem, env(safe-area-inset-bottom));
  }
  .sectionnav .nav-social {
    left: 0;
    right: 0;
    z-index: 60;
    justify-content: flex-start;
    padding-left: 0.85rem;
    padding-right: 0.85rem;
    background: var(--surface);
    border-top: 1px solid var(--rule);
  }
  .sectionnav .nav-lang {
    left: auto;
    right: 0.85rem;
    z-index: 61;
    order: 0;
    margin: 0;
    padding-left: 0;
    padding-right: 0;
    opacity: 1;
    transform: none;
    pointer-events: auto;
  }

  /* One size for everything in the bar, icons and flags alike.

     flex: none is the load-bearing line. The top bar gives every anchor
     flex: 1 0 auto so the labels spread across it, and the Facebook link is an
     anchor, so in the footer it grew to fill the row and swallowed the width
     setting under it: in a flex container, grow beats width. */
  .sectionnav .nav-social .nav-icon,
  .sectionnav .nav-lang .nav-flag {
    flex: none;
    width: 2.5rem;
    height: 2.5rem;
    margin: 0;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
  }
  .sectionnav .nav-lang .nav-flag { font-size: 1.35rem; }
  .sectionnav .nav-social .nav-icon svg { width: 1.3rem; height: 1.3rem; }

  /* The top bar keeps its own, slightly larger mark and its bigger labels. */
  .sectionnav a:not(.nav-flag):not(.nav-icon) {
    font-size: 0.7rem;
    letter-spacing: 0.07em;
    padding: 0.7rem 0.36rem;
  }
  .sectionnav .nav-home { width: 2.7rem; height: 2.7rem; }
  .sectionnav .nav-home svg { width: 1.35rem; height: 1.35rem; }

  body { padding-bottom: 3.5rem; }
}

/* The rail's quiet confirmation that a section link copied itself. Nothing marks the
   behaviour beforehand; this only appears after the fact. */
.sectionnav a.is-copied { color: var(--verified); }
.tab.is-copied { color: var(--verified); }
.tab.is-copied .tab-icon { stroke: var(--verified); }
.sectionnav a.is-copied::before { background: var(--verified); }

/* The copy confirmation. Positioned by script beside whatever was copied; the styling
   is the page's own small-caps label treatment so it reads as part of the furniture
   rather than as browser chrome. */
.copied-toast {
  position: fixed;
  z-index: 120;
  pointer-events: none;
  padding: 0.32rem 0.55rem;
  background: var(--surface);
  color: var(--verified);
  border: 1px solid var(--verified);
  border-radius: 0.3rem;
  font-family: var(--font-display);
  font-size: 0.6rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(2px);
  transition: opacity 140ms ease, transform 140ms ease;
}
.copied-toast.is-on { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .copied-toast { transition: none; } }

/* ---------- phone: the top bar is not a scroll container ----------
   Reported: in Facebook's in-app browser on Android the top of the sticky bar is
   clipped while scrolling.

   .sectionnav is sticky and also carries overflow-x: auto, from when the labels
   were expected to scroll sideways. Setting one axis to auto makes the other
   compute to auto as well, so the bar became its own vertical scrollport: any
   content even slightly taller than the bar can sit scrolled out of view at the
   top, and in-app browsers, which resize the viewport as their chrome collapses,
   are where that shows up.

   The labels have fitted on one row since they were shortened, so nothing needs to
   scroll here. flex-wrap stays as the fallback for a device narrow enough to defeat
   even the short forms: wrapping to two rows is a better failure than clipping. */
@media (max-width: 52rem) {
  .sectionnav {
    overflow: visible;
  }
}

/* ---------- phone: buttons size to the longest label ----------
   The Spanish "Ver el código y la investigación →" is the longest, and at a fixed
   0.92rem it wrapped its arrow onto a second line on an S24+. Ben's call: shrink
   the type on every button rather than let one of them wrap, so the row stays a row
   and the buttons stay a set.

   min() rather than clamp(): the cap is the desktop size, and below the width where
   3.1vw reaches it the label simply tracks the window. Measured at 3.1vw the longest
   Spanish button clears its column at 320px, which is the narrowest phone worth
   supporting. */
@media (max-width: 52rem) {
  .btn { font-size: min(0.92rem, 3.1vw); }
  .btn-with-sub .joininline { font-size: min(0.68rem, 2.4vw); }
}

/* Section headings copy their own link (script.js: initTitleCopy). No cursor change:
   like the rail, it is undiscoverable on purpose and confirms after the fact. */
.copy-title.is-copied { color: var(--verified); }

/* ---------- language menu: for more languages than flags have room for ----------
   Two flags fit beside the mark in the rail and at the end of the phone bar; six
   don't, and a flag is the wrong label for a language that many countries speak.
   So a site with more languages puts one <details class="lang-menu"> in .nav-lang
   instead of the flags: a chip with the current language's short code, and the
   list of languages by their own names. Native <details>, so it opens and closes
   with JavaScript off. The options keep the .nav-flag class, which every section-
   link rule in this sheet already excludes, and the scroll-spy only reads #links.

   Last in the sheet, and four classes deep, on purpose: it has to beat the flag
   sizing in both the rail block (.sectionnav a.nav-flag) and the phone bar
   (.sectionnav .nav-lang .nav-flag), which equal specificity would lose to on
   position. */
.sectionnav .nav-lang .lang-menu { position: relative; }
.sectionnav .nav-lang .lang-menu > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  min-width: 2.75rem;
  height: 2.5rem;
  padding: 0 0.6rem;
  font-family: var(--font-display);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  color: var(--ink-2);
  background: var(--surface);
  border: 1px solid var(--rule);
  user-select: none;
}
.sectionnav .nav-lang .lang-menu > summary::-webkit-details-marker { display: none; }
.sectionnav .nav-lang .lang-menu > summary::after { content: "\25BE"; font-size: 0.85em; }
.sectionnav .nav-lang .lang-menu > summary:hover,
.sectionnav .nav-lang .lang-menu[open] > summary { color: var(--ink); border-color: var(--accent); }
.sectionnav .nav-lang .lang-menu ul {
  position: absolute;
  z-index: 80;
  top: calc(100% + 0.35rem);
  left: 0;
  min-width: 12rem;
  margin: 0;
  padding: 0.3rem 0;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--rule);
  box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.25);
}
.sectionnav .nav-lang .lang-menu .nav-flag {
  display: block;
  width: auto;
  height: auto;
  margin: 0;
  padding: 0.6rem 0.95rem;
  font-family: var(--font-body);
  font-size: 0.95rem;
  line-height: 1.3;
  letter-spacing: 0;
  text-transform: none;
  white-space: nowrap;
  /* The phone bar centres its labels; a list reads down its left edge. */
  text-align: start;
  color: var(--ink-2);
  text-decoration: none;
  filter: none;
}
.sectionnav .nav-lang .lang-menu a.nav-flag:hover { color: var(--accent); background: var(--sunken); }
.sectionnav .nav-lang .lang-menu .nav-flag.is-current { color: var(--ink); font-weight: 600; }
.sectionnav .nav-lang .lang-menu .nav-flag.is-current::after { content: none; }

/* On a phone the menu lives in the footer bar, so it opens upward from the right. */
@media (max-width: 52rem) {
  .sectionnav .nav-lang .lang-menu ul {
    top: auto;
    bottom: calc(100% + 0.4rem);
    left: auto;
    right: 0;
  }
}

/* ---------- Japanese and Chinese ----------
   No spaces between words, so a browser left to itself breaks headings anywhere,
   including in the middle of a word. auto-phrase breaks Japanese between phrases
   where the browser supports it (Chromium 119+) and is ignored elsewhere. Strict
   line breaking keeps small kana and closing punctuation off the start of a line.
   Uppercase and wide letter-spacing do nothing useful for these scripts, so the
   eyebrow and label styles drop the spacing. */
:lang(ja) h1, :lang(ja) h2, :lang(ja) h3, :lang(ja) .lede { word-break: auto-phrase; }
:lang(ja), :lang(zh-Hant) { line-break: strict; }
:lang(ja) .eyebrow, :lang(ja) .sunline, :lang(zh-Hant) .eyebrow, :lang(zh-Hant) .sunline,
:lang(ja) .field label, :lang(zh-Hant) .field label,
:lang(ja) .checks legend, :lang(zh-Hant) .checks legend,
:lang(ja) .repeat legend, :lang(zh-Hant) .repeat legend { letter-spacing: 0.04em; }
