/* base.css — document, type defaults, chrome.
   Governed by docs/BUILD-CONTRACT.md. No hex literals: every colour is a token. */

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

html {
  /* 0.9rem root so the scale lands on 12.6 / 14.4 / 16.2 / 45 / 90 */
  font-size: calc(0.9 * 16px);
  scroll-behavior: smooth;
}
/* THE SCALE MOVES UP WITH THE WINDOW, as one piece. §3's ratios are what
   matter — 12.6 / 14.4 / 16.2 / 45 / 90 — and they survive intact because
   only the root changes; every size is rem-derived and moves together, so the
   "max 4 distinct font sizes per viewport" count is unaffected.
   14.4px body reads correctly at 1440 and reads like fine print at 2300, which
   is half of why the page looked like it was waiting for content. Charles's
   own site sets ~17px body at 1798 and that is the calibration he handed me. */
@media (min-width: 1900px) { html { font-size: calc(1.00 * 16px); } }
@media (min-width: 2200px) { html { font-size: calc(1.10 * 16px); } }
@media (min-width: 2560px) { html { font-size: calc(1.18 * 16px); } }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

html, body {
  width: 100%;
  margin: 0;
  /* clip, not hidden — position: sticky still works under clip */
  overflow-x: clip;
  /* the ground is the PURPLE anchor, not the teal-black --pitch */
  background: var(--ground);
}

body {
  font-family: var(--body);
  font-size: 1rem;                 /* 14.4px */
  line-height: var(--lh-body);     /* exactly 2.0 */
  letter-spacing: var(--tr-body);
  color: var(--chalk);
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }

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

.display {
  font-family: var(--display);
  font-variation-settings: "opsz" 96, "wght" 380, "SOFT" 22, "WONK" 1;
  font-weight: 400;
  line-height: var(--lh-display);
  letter-spacing: var(--tr-display);
  margin: 0;
}

.util {
  font-family: var(--mono);
  font-size: .7875rem;             /* 12.6px */
  line-height: var(--lh-utility);
  letter-spacing: var(--tr-utility);
  text-transform: uppercase;
  color: var(--pewter);
  margin: 0;
}

.lede {
  font-size: 1.125rem;             /* 16.2px */
  line-height: 1.8;
  letter-spacing: var(--tr-mid);
  color: var(--chalk);
}

p { margin: 0; max-width: var(--measure); }

/* ---- links: no underline by default; drawn from a rule ---------------- */

a { color: inherit; text-decoration: none; }

a:not(.chrome__brand):not(.threshold__door) {
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0 var(--rule);
  transition: background-size var(--t-base) var(--ease), color var(--t-base) var(--ease);
}
a:not(.chrome__brand):not(.threshold__door):hover { background-size: 100% var(--rule); }

/* A link must look like a link at rest.
   The previous version of this rule LOST TO SPECIFICITY: the :not() selector
   above scores 0,2,1 and beat this at 0,1,1, so computed background-size was
   0px 1px and three of four exit routes rendered identical to plain text.
   Matching the :not() shape puts them on equal footing, and the later position
   wins. */
a:not(.chrome__brand):not(.threshold__door).entry__link,
/* The Lens & Lu credit is an outbound link and the ONLY one on the page that
   leaves the site, so it needs the affordance more than the others, not less.
   It lives in .closing__tel rather than .closing__links, so the selector here
   missed it and it rendered as plain text — the same defect the comment above
   this block was written about, recurring in a different cell. */
.closing__tel a:not(.chrome__brand):not(.threshold__door),
.closing__links a:not(.chrome__brand):not(.threshold__door) {
  background-size: 100% var(--rule);
  opacity: .78;
  transition: opacity var(--t-base) var(--ease), background-size var(--t-base) var(--ease);
}
.closing__links a:not(.chrome__brand):hover,
.closing__tel a:not(.chrome__brand):hover,
a.entry__link:not(.chrome__brand):hover { opacity: 1; }

:focus-visible {
  outline: 2px solid var(--chalk);
  outline-offset: 3px;
  /* two-tone: a dark outer ring so the indicator survives on light grounds too */
  box-shadow: 0 0 0 5px var(--ground);
}

/* ---- skip link -------------------------------------------------------- */

.skip {
  position: fixed;
  z-index: 100;
  top: var(--s-4);
  left: var(--s-4);
  padding: var(--s-3) var(--s-4);
  background: var(--ground);
  color: var(--chalk);
  font-family: var(--mono);
  font-size: .7875rem;
  transform: translateY(-160%);
  transition: transform var(--t-base) var(--ease);
}
.skip:focus-visible { transform: translateY(0); }

/* The nav lives in assets/css/nav.css — it is shared by every page, and a
   shared component cannot drift into three versions the way it just did. */

/* ---- stations: the shared frame --------------------------------------- */

.station {
  position: relative;
  z-index: 1;
  /* Named so a full-bleed child can cancel it exactly. A plate that inherits
     this sits INSIDE the margin and stops being full-bleed — which is what
     put dark bars down both sides of the hero at 2529px wide. */
  --station-pad: var(--margin);
  padding-inline: var(--station-pad);
  padding-block: var(--s-10);
  /* the fixed marks own the bottom band; body text never enters it */
  padding-bottom: calc(var(--s-10) + var(--s-7));
  scroll-margin-top: var(--s-8);
}

/* Reveal targets rest VISIBLE. If GSAP never loads, nothing is stranded. */
.no-gsap [data-reveal] { opacity: 1 !important; transform: none !important; }
