/* Integra base styles — primitives only. Page layout lives in per-page sheets. */
*, *::before, *::after { box-sizing: border-box; }

/* The hidden attribute always wins — author display rules (e.g. .btn's
   inline-block) must never resurrect an element JS has hidden. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; scrollbar-color: var(--graphite) var(--paper); }

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.6;
  font-weight: 400;
  color: var(--ink);
  background: var(--paper);
  text-align: left;
  -webkit-font-smoothing: antialiased;
}

/* --accent-ink, not --paper: --blurple is a fixed fill in both themes, so the
   copy sitting on it must be fixed too (4.42:1; --paper would read 3.94:1 dark). */
::selection { background: var(--blurple); color: var(--accent-ink); }
::-moz-selection { background: var(--blurple); color: var(--accent-ink); }

/* Page-load fade — CSS only; no fill mode, so the page always ends fully visible */
@media (prefers-reduced-motion: no-preference) {
  body { animation: integra-fade var(--t-fast) var(--ease); }
}
@keyframes integra-fade { from { opacity: 0.001; } to { opacity: 1; } }

/* ---- Type roles (from brand guide §06) ---- */

/* DISPLAY · Geist Regular 32–52px · tracking −1% · lh 1.05–1.15 */
.display, h1 {
  font-weight: 400;
  font-size: clamp(32px, 5vw, 52px);
  letter-spacing: -0.01em;
  line-height: 1.1;
  margin: 0 0 var(--s5);
}

/* HEADING · Geist Semibold 18–24px · tracking −0.5% · lh 1.25 */
h2, h3 {
  font-weight: 600;
  font-size: clamp(18px, 2.2vw, 24px);
  letter-spacing: -0.005em;
  line-height: 1.25;
  margin: 0 0 var(--s4);
}
h3 { font-size: 17px; }

h1, h2 { text-wrap: balance; }

/* BODY · Geist Regular 15–17px · lh 1.55–1.65 · measure ≤75ch */
p { margin: 0 0 var(--s4); max-width: var(--measure); }

/* LABEL / EYEBROW · Geist Mono Medium 12px · +13% tracking · uppercase */
.label {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--graphite);
}

/* DATA · Geist Mono, tabular figures. EVERY number, ID, timestamp wears this. */
.data, .mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum';
}

.muted { color: var(--graphite); }
.small { font-size: 13px; }

/* ---- Links & buttons ---- */
a { color: var(--blurple-text); text-decoration: none; transition: color var(--t-fast) var(--ease), text-decoration-color var(--t-fast) var(--ease); }
a:hover { color: var(--blurple-text-hover); }

/* Content links: underline slides in on hover. Buttons and image-wrapping links opt out. */
a:not(.btn) {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  text-decoration-color: transparent;
}
a:not(.btn):hover { text-decoration-color: currentColor; }
a:not(.btn):has(> img), a:not(.btn):has(> svg) { text-decoration: none; }

/* Primary button — the loudest fill on the page, so it inverts with the theme
   (--btn-fill: Ink on light, #E6E6EB on dark) instead of following --ink, which
   becomes a text colour in dark. 16.38:1 light / 14.59:1 dark. Hover moves the
   fill toward the copy colour in both themes: lighter on light, brighter on dark. */
.btn {
  display: inline-block;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 15px;
  line-height: 1;
  padding: 14px 24px;
  border: 1px solid var(--btn-fill);
  border-radius: var(--radius);
  background: var(--btn-fill);
  color: var(--btn-ink);
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.btn:hover { background: var(--btn-fill-hover); border-color: var(--btn-fill-hover); color: var(--btn-ink); }

.btn-accent { background: var(--blurple); border-color: var(--blurple); color: var(--accent-ink); }
.btn-accent:hover { background: var(--haze); border-color: var(--haze); color: var(--accent-ink); }

.btn-ghost { background: transparent; color: var(--ink); border-color: var(--rule); }
.btn-ghost:hover { background: transparent; border-color: var(--graphite); color: var(--ink); }

.btn:disabled { opacity: 0.45; cursor: default; }

/* ---- Theme toggle ---- */
/* Chrome, not a feature: a 16px line glyph in graphite, no fill, no label.
   Hidden by default and revealed only by html.theme-js — the inline <head>
   snippet that sets data-theme also sets that class, so with JS disabled the
   page still themes itself from the system preference and no dead control
   ships. The two glyphs both live in the markup; --show-on-* picks one per
   theme, so the icon never flashes and never needs JS to swap.
   js/theme.js owns aria-pressed and aria-label. */
.theme-toggle {
  appearance: none;
  display: none;
  align-items: center;
  justify-content: center;
  flex: none;
  /* 32px: comfortably over the 24px target minimum, and the smallest size that
     does not grow the compact page headers more than it has to (the lockup
     beside it is 28px). */
  width: 32px;
  height: 32px;
  padding: 0;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius);
  color: var(--graphite);
  cursor: pointer;
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
html.theme-js .theme-toggle { display: inline-flex; }
.theme-toggle:hover { color: var(--ink); border-color: var(--rule); }
.theme-toggle svg { display: block; width: 16px; height: 16px; }
/* The glyph names the theme the click moves TO: moon on light, sun on dark. */
.theme-toggle .icon-moon { display: var(--show-on-light); }
.theme-toggle .icon-sun { display: var(--show-on-dark); }

/* ---- Forms ---- */
input, select, textarea {
  font-family: var(--font-sans);
  font-size: 15px;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 12px 14px;
  width: 100%;
  transition: border-color var(--t-fast) var(--ease);
}
input:hover:not(:focus), select:hover:not(:focus), textarea:hover:not(:focus) { border-color: var(--graphite); }
input:focus:not(:focus-visible), select:focus:not(:focus-visible), textarea:focus:not(:focus-visible) { border-color: var(--blurple); outline: none; }
input:focus-visible, select:focus-visible, textarea:focus-visible {
  border-color: var(--blurple);
  outline: 2px solid var(--blurple);
  outline-offset: 2px;
}

label { display: block; margin-bottom: var(--s2); }

/* ---- Structure: hairlines, not cards ---- */
hr, .rule-line {
  border: 0;
  border-top: 1px solid var(--rule);
  margin: var(--s6) 0;
}

table { border-collapse: collapse; width: 100%; }
th, td { text-align: left; padding: var(--s3) var(--s4) var(--s3) 0; border-bottom: 1px solid var(--rule); }
th { font-family: var(--font-mono); font-weight: 500; font-size: 12px; letter-spacing: 0.13em; text-transform: uppercase; color: var(--graphite); }

/* ---- Status dots (dashboard only) ---- */
.status-dot { display: inline-block; width: 10px; height: 10px; border: 1px solid var(--graphite); border-radius: 50%; vertical-align: middle; }
.status-live { background: var(--live); }
.status-attention { background: var(--attention); }
.status-fault { background: var(--fault); }

/* ---- Accessibility ---- */
:focus-visible { outline: 2px solid var(--blurple); outline-offset: 2px; }

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

/* ---- Theme-swapped marks ---- */
/* Dark surfaces need the Paper mark. Both variants ship in the markup and CSS
   shows one per theme: no JS, so no swap flash on first paint. Two classes
   (not `img.logo-light`) so the rule outranks the `.footer-inner img` style
   page sheets set. CSS cannot rewrite alt text per theme, so BOTH images are
   decorative — the wrapping link's aria-label carries the accessible name. */
.logo-swap.logo-light { display: var(--show-on-light); }
.logo-swap.logo-dark { display: var(--show-on-dark); }

/* ---- Utility ---- */
.container { max-width: var(--page-max); margin: 0 auto; padding: 0 var(--page-pad); }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  clip: rect(0 0 0 0); overflow: hidden; white-space: nowrap;
}
