/* Event Runner — the GitHub Pages surface: the landing page (docs/index.html)
   and every generated documentation page (docs/docs/**, via
   scripts/build-pages.cjs). docs/docs.css adds only the docs shell on top.

   The rules this file is held to are in docs/interface-guidelines.md, "The
   project site" — including why the devices below take the shapes they do. */

/* Self-hosted, latin subsets, no font-CDN request from any page this site
   serves. Each weight below is one a rule in this file asks for. */

@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-weight: 700 800;
  font-display: swap;
  src: url("fonts/bricolage-grotesque-latin.woff2") format("woff2");
}

@font-face {
  font-family: "Source Sans 3";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/source-sans-3-latin.woff2") format("woff2");
}

@font-face {
  font-family: "Source Sans 3";
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/source-sans-3-italic-latin.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/ibm-plex-mono-400-latin.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("fonts/ibm-plex-mono-500-latin.woff2") format("woff2");
}

/* ---------- Tokens ----------

   The scale and the palette are generated: docs/tokens.css, written by
   scripts/build-pages.cjs from design/tokens/ through the same generator
   that writes apps/web/src/generated/theme.css. Loaded before this file,
   and `build-pages --check` fails on a stale copy.

   What stays here is what is this site's own: the three faces it serves,
   the device contracts it composes, and its measure. Never write a color
   into a declaration below — only `rgb(var(--…-rgb))`. */

:root {
  /* Font roles, never family names (brief §3.2). These are NOT the preset's
     type map: this site serves its own three faces from docs/fonts, so the
     roles are named here rather than generated. `data` is the voice beside
     the text; `mono` is a value a reader would copy or compare. */
  --font-heading: "Bricolage Grotesque", "Arial Black", "Avenir Next Condensed", sans-serif;
  --font-body: "Source Sans 3", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --font-data: "Source Sans 3", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;

  /* Tier 3 — the device contracts this site composes (brief §3.1). Same
     names as the product; the values are this surface's own. */
  --nameplate-rule-width: var(--rule-nameplate-width);
  --nameplate-gap: var(--space-sm);
  --section-rule-width: var(--rule-strong-width);
  --section-rule-gap: var(--space-lg);
  --folio-rule-width: var(--rule-hairline-width);
  --folio-font: var(--font-data);
  /* The folio floor. `--text-folio` bottoms out at 11px, which is right for a
     running head a reader glances past and too small for the folios this site
     actually uses: the docket's group heads, a table's column heads, the
     status words, the "Previous"/"Next" on the pager. Every one of those is a
     functional label somebody has to read, so the floor is caption size
     (interface guidelines, Typography; brief §2.4 on tiny subtext). The
     tracking, the leading, and the case are the folio's own. */
  --folio-size: var(--text-caption);
  --folio-tracking: var(--text-folio-tracking);
  --folio-gap: var(--space-xs);
  --page-section-gap: var(--space-2xl);
  --page-row-pad-block: var(--space-md);

  /* The measure. Long-form text holds 60–75 characters per line (interface
     guidelines, Typography); `--measure` is the prose column and `--max` the
     page. */
  --measure: 44rem;
  --max: 68rem;
}

/* ---------- Base ---------- */

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

html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Wayfinding is instant (brief §2.2): an in-page jump from the site nav is
     navigation, so it never animates. No `scroll-behavior: smooth` here. */
}

body {
  margin: 0;
  min-height: 100vh;
  background: rgb(var(--color-surface-rgb));
  color: rgb(var(--color-text-primary-rgb));
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: var(--text-body-leading);
  letter-spacing: var(--text-body-tracking);
}

/* ---------- Type ---------- */

h1,
h2,
h3 {
  font-family: var(--font-heading);
  text-wrap: balance;
  /* A heading can carry an identifier nothing will break for it —
     AUTO_DEPLOY_ENVIRONMENTS in the deploy runbook is 24 characters with no
     opportunity in them. Without this the whole page scrolls sideways on a
     phone (interface guidelines, Typography). */
  overflow-wrap: break-word;
}

a {
  color: rgb(var(--color-accent-rgb));
  text-underline-offset: 0.18em;
  text-underline-position: from-font;
  text-decoration-skip-ink: auto;
}

:focus-visible {
  outline: var(--rule-strong-width) solid rgb(var(--color-accent-rgb));
  outline-offset: 3px;
  border-radius: var(--radius-base);
}

.skip-link {
  position: absolute;
  inset-inline-start: var(--space-md);
  inset-block-start: -3rem;
  z-index: 10;
  padding: var(--space-xs) var(--space-sm);
  border: var(--rule-hairline-width) solid rgb(var(--color-text-primary-rgb));
  border-radius: var(--radius-base);
  background: rgb(var(--color-text-primary-rgb));
  color: rgb(var(--color-surface-rgb));
  text-decoration: none;
  font-weight: var(--weight-semibold);
}

.skip-link:focus-visible {
  inset-block-start: var(--space-md);
}

.wrap {
  width: min(var(--max), calc(100% - 2.5rem));
  margin-inline: auto;
}

/* ---------- Editorial devices ----------

   Rules are drawn with `border-block-*` rather than a height, so the width
   token is the property it was written for and a hairline stays a hairline
   on a fractional-pixel display. This mirrors apps/web/src/index.css. */

/* Folio: a small-caps plain-text label that sits ON a rule. */
.folio {
  font-family: var(--folio-font);
  font-size: var(--folio-size);
  font-weight: var(--weight-medium);
  line-height: var(--text-folio-leading);
  letter-spacing: var(--folio-tracking);
  text-transform: uppercase;
  color: rgb(var(--folio-text-rgb));
}

/* The label, then the rule it sits on, running out to the trailing edge. */
.folio-row {
  display: flex;
  align-items: center;
  gap: var(--folio-gap);
  margin: 0;
}

/* Decorative, so the markup keeps it out of the accessibility tree. */
.folio-row__rule {
  flex: 1;
  align-self: center;
  border-block-start: var(--folio-rule-width) solid rgb(var(--folio-rule-rgb));
}

.folio-row > .folio {
  white-space: nowrap;
}

/* Section boundary. Same shape as the product's SectionHead.

   ONE RULE PER BOUNDARY, and the boundary states its own weight: a major one
   opens on the strong rule, a secondary one on this heading and the hairline
   beside it. Never both at once. */
.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-3xs) var(--space-md);
  margin-block: var(--space-sm) var(--space-lg);
}

.section-head h2 {
  margin: 0;
  font-size: var(--text-h2);
  font-weight: var(--weight-semibold);
  line-height: var(--text-h2-leading);
  letter-spacing: var(--text-h2-tracking);
}

.section-head__rule {
  flex: 1 1 var(--space-2xl);
  align-self: center;
  border-block-start: var(--folio-rule-width) solid rgb(var(--folio-rule-rgb));
}

.section-major > .section-head > .section-head__rule {
  border-block-start: 0;
}

/* ---------- Masthead ----------

   The rule-bounded title block that names the site, at the compact size so
   the headline below it leads. The line under the name is masthead metadata,
   which the eyebrow ban does not reach — and it sits below the name. */

.site-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-lg);
  padding-block: var(--nameplate-gap);
  border-block-start: var(--nameplate-rule-width) solid rgb(var(--nameplate-rule-rgb));
  border-block-end: var(--rule-hairline-width) solid rgb(var(--rule-hairline-rgb));
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  color: rgb(var(--nameplate-text-rgb));
  text-decoration: none;
}

/* The mark: a stem and the system's three rule weights, at mark size. It
   takes the masthead's ink, and it sits beside the name, never behind it. */
.brand-mark {
  flex: none;
  width: calc(var(--text-h3) * 1.3);
  height: calc(var(--text-h3) * 1.3);
  fill: currentColor;
}

.brand-words {
  display: block;
}

.brand strong {
  display: block;
  font-family: var(--font-heading);
  font-size: var(--text-h3);
  font-weight: var(--weight-bold);
  line-height: var(--text-h3-leading);
  letter-spacing: var(--text-h3-tracking);
}

.brand-line {
  display: block;
  font-family: var(--font-data);
  font-size: var(--text-caption);
  line-height: var(--text-caption-leading);
  color: rgb(var(--color-text-secondary-rgb));
}

@media (hover: hover) {
  .brand:hover strong {
    text-decoration: underline;
    text-underline-offset: 0.2em;
  }
}

.site-header nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-lg);
}

.site-header nav a {
  color: rgb(var(--color-text-secondary-rgb));
  font-family: var(--font-data);
  font-size: var(--text-caption);
  font-weight: var(--weight-semibold);
  text-decoration: none;
  /* The padding, not the word, is the hit area: 12px each side of a caption
     line box clears 44px on the block axis, which is the touch bar
     (interface guidelines, Accessibility). The floor is the 24px absolute
     minimum from the same line, so a caption that shrinks cannot take the
     target below it. */
  padding-block: var(--space-sm);
  min-height: 24px;
}

@media (hover: hover) {
  .site-header nav a:hover {
    color: rgb(var(--color-text-primary-rgb));
    text-decoration: underline;
    text-underline-offset: 0.3em;
  }
}

/* ---------- Hero ---------- */

.hero {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(16rem, 0.85fr);
  align-items: start;
  gap: var(--space-2xl);
  padding-block: var(--space-2xl);
}

.thesis h1 {
  margin: 0 0 var(--space-md);
  font-size: var(--text-h1);
  font-weight: var(--weight-bold);
  line-height: var(--text-h1-leading);
  letter-spacing: var(--text-h1-tracking);
}

.lede {
  margin: 0;
  max-width: var(--measure);
  font-size: var(--text-lead);
  line-height: var(--text-lead-leading);
  letter-spacing: var(--text-lead-tracking);
  color: rgb(var(--color-text-secondary-rgb));
  text-wrap: pretty;
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-block-start: var(--space-lg);
}

/* A ground, a hairline, and a word, on a near-square corner. */
.btn {
  display: inline-block;
  padding: var(--space-sm) var(--space-md);
  border: var(--rule-hairline-width) solid rgb(var(--color-text-primary-rgb));
  border-radius: var(--radius-base);
  background: rgb(var(--color-text-primary-rgb));
  color: rgb(var(--color-surface-rgb));
  font-family: var(--font-data);
  font-size: var(--text-caption);
  font-weight: var(--weight-semibold);
  text-decoration: none;
  /* Hover changes color instantly: a frequent interaction is not animated
     (interface guidelines, Animation). */
}

.btn-ghost {
  background: transparent;
  border-color: rgb(var(--color-border-control-rgb));
  color: rgb(var(--color-text-primary-rgb));
}

/* The only motion on this site. Under `prefers-reduced-motion: reduce` the
   button is truly static, not faster. */
@media (prefers-reduced-motion: no-preference) {
  .btn {
    transition: scale var(--motion-slow) var(--motion-ease);
  }

  .btn:active {
    scale: 0.97;
  }
}

@media (hover: hover) {
  .btn:hover {
    background: rgb(var(--color-accent-rgb));
    border-color: rgb(var(--color-accent-rgb));
    color: rgb(var(--color-surface-rgb));
  }

  .btn-ghost:hover {
    background: transparent;
    border-color: rgb(var(--color-text-primary-rgb));
    color: rgb(var(--color-accent-rgb));
  }
}

/* ---------- Build status ----------

   A folio on a hairline names it; hairlines separate the rows under it. */

.status-panel {
  margin: 0;
}

.status-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.status-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
  padding-block: var(--space-sm);
  border-block-start: var(--rule-hairline-width) solid rgb(var(--rule-hairline-rgb));
  font-size: var(--text-caption);
  line-height: var(--text-caption-leading);
}

.status-row-name {
  font-weight: var(--weight-semibold);
  overflow-wrap: break-word;
}

/* ---------- Status labels ----------

   The word is the status and the colour is the second signal, so these are
   plain small-caps text (interface guidelines, Accessibility). */

.status-label {
  flex: none;
  font-family: var(--font-data);
  font-size: var(--folio-size);
  font-weight: var(--weight-medium);
  letter-spacing: var(--folio-tracking);
  text-transform: uppercase;
  white-space: nowrap;
}

.status-label-done {
  color: rgb(var(--semantic-success-rgb));
}

.status-label-progress {
  color: rgb(var(--semantic-warning-rgb));
}

/* ---------- Sections ---------- */

/* A secondary boundary: no rule above it. The heading, the hairline running
   out beside it, and the space do the work. */
.section {
  margin-block-start: var(--space-lg);
  padding-block-end: var(--space-xl);
  scroll-margin-block-start: var(--space-lg);
}

/* A major boundary: the strong rule, and more air above it than a secondary
   one gets — the two signals agree instead of competing. Three of them down
   this page, at the three places the subject actually changes. */
.section-major {
  margin-block-start: var(--space-2xl);
  padding-block-start: var(--space-md);
  border-block-start: var(--section-rule-width) solid rgb(var(--section-rule-rgb));
}

/* `.note` sets its own leading gap, so it is excluded rather than reset. */
.section > p:not(.note) {
  max-width: var(--measure);
  margin: 0 0 var(--space-md);
  text-wrap: pretty;
}

.section h3 {
  font-weight: var(--weight-bold);
}

/* Three entries, not three cards. One hairline bounds the row; the column
   gap and the heading face separate the three inside it, so there is no
   second rule doing a job the gap already does. When the row stacks there is
   no gap left to do it, and the hairline comes back between the items. */
.feature-list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0 var(--space-lg);
  margin-block-start: var(--space-lg);
  border-block-start: var(--rule-hairline-width) solid rgb(var(--rule-hairline-rgb));
}

.feature {
  padding-block: var(--page-row-pad-block);
}

.feature h3 {
  margin: 0 0 var(--space-2xs);
  font-size: var(--text-h3);
  line-height: var(--text-h3-leading);
  letter-spacing: var(--text-h3-tracking);
}

.feature p {
  margin: 0;
  color: rgb(var(--color-text-secondary-rgb));
  font-size: var(--text-caption);
  line-height: var(--text-caption-leading);
  text-wrap: pretty;
}

.note {
  max-width: var(--measure);
  margin-block-start: var(--space-lg);
  color: rgb(var(--color-text-secondary-rgb));
  font-size: var(--text-caption);
  line-height: var(--text-caption-leading);
  overflow-wrap: break-word;
  text-wrap: pretty;
}

/* ---------- Milestones ----------

   Ruled rows. The number is a folio in the margin BESIDE the heading, never
   stacked above it — at any viewport. */

.milestones {
  list-style: none;
  margin: var(--space-lg) 0 0;
  padding: 0;
}

.milestone {
  display: grid;
  grid-template-columns: 7rem minmax(0, 1fr) auto;
  align-items: baseline;
  gap: var(--space-sm) var(--space-md);
  padding-block: var(--page-row-pad-block);
  border-block-start: var(--rule-hairline-width) solid rgb(var(--rule-hairline-rgb));
}

.milestone-id {
  font-family: var(--folio-font);
  font-size: var(--folio-size);
  font-weight: var(--weight-medium);
  letter-spacing: var(--folio-tracking);
  text-transform: uppercase;
  color: rgb(var(--folio-text-rgb));
}

.milestone-body h3 {
  margin: 0 0 var(--space-3xs);
  font-size: var(--text-h3);
  line-height: var(--text-h3-leading);
  letter-spacing: var(--text-h3-tracking);
}

.milestone-body p {
  margin: 0;
  color: rgb(var(--color-text-secondary-rgb));
  font-size: var(--text-caption);
  line-height: var(--text-caption-leading);
  text-wrap: pretty;
}

/* ---------- Footer ---------- */

.site-footer {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-lg);
  padding-block: var(--space-md) var(--space-xl);
  border-block-start: var(--rule-hairline-width) solid rgb(var(--rule-hairline-rgb));
  font-family: var(--font-data);
  font-size: var(--text-caption);
  line-height: var(--text-caption-leading);
  color: rgb(var(--color-text-secondary-rgb));
}

.site-footer a {
  color: rgb(var(--color-text-secondary-rgb));
}

@media (hover: hover) {
  .site-footer a:hover {
    color: rgb(var(--color-text-primary-rgb));
  }
}

/* ---------- Responsive ----------

   One column below 56rem, which is also where a 200% zoom on a 1440px screen
   lands. */

@media (max-width: 56rem) {
  .hero {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-xl);
    padding-block: var(--space-xl);
  }

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

  .feature-list {
    grid-template-columns: minmax(0, 1fr);
  }

  .feature + .feature {
    border-block-start: var(--rule-hairline-width) solid rgb(var(--rule-hairline-rgb));
  }
}

@media (max-width: 34rem) {
  .site-header {
    align-items: flex-start;
  }

  /* The number and the status word drop BELOW the milestone, never above the
     heading: an eyebrow is an eyebrow at every viewport (brief §2.4). */
  .milestone {
    grid-template-columns: auto minmax(0, 1fr);
    row-gap: var(--space-xs);
  }

  .milestone-body {
    grid-column: 1 / -1;
    order: 1;
  }

  .milestone-id {
    order: 2;
  }

  .milestone .status-label {
    order: 3;
    justify-self: end;
  }
}
