/*
  Components -- CLAUDE.md §5.6. Each one built once, used everywhere.
  Accessibility rules that apply to every component here (§5.6):
  - visible focus ring: 2px solid var(--brand), 2px offset
  - minimum 44x44px hit area on phones for anything interactive
  - real <a>/<button> elements only, never a styled <div>
*/

/* ---------- Header / nav ---------- */

.site-header {
  border-bottom: 1px solid var(--border);
}

.site-header .container {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  padding-top: var(--space-4);
  padding-bottom: var(--space-4);
}
/* Phones: row one is the name and the toggle, row two is the nav. The toggle
   used to sit inside the nav and wrapped onto a third row of its own. */
.site-header .brand { margin-right: auto; }
.site-header .theme-toggle { order: 2; }
.site-header .site-nav { order: 3; flex-basis: 100%; }

@media (max-width: 767px) {
  /* Tighter gap so four nav items stay on one row at 360px -- at --space-5
     they come to roughly the full content width and risk wrapping to a
     third row, which is the problem this layout exists to fix. */
  .site-nav { gap: var(--space-4); }
  .site-header .container {
    padding-top: var(--space-3);
    padding-bottom: var(--space-3);
  }
}

@media (min-width: 768px) {
  /* One row again: name, nav, toggle. */
  .site-header .site-nav { order: 2; flex-basis: auto; }
  .site-header .theme-toggle { order: 3; }
}

.site-header .brand {
  font-weight: var(--fw-semibold);
  font-size: var(--fs-h3);
  color: var(--text);
  text-decoration: none;
}

.site-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  align-items: center;
}

.site-nav a {
  color: var(--text);
  text-decoration: none;
  font-weight: var(--fw-semibold);
  font-size: var(--fs-small);
  padding: var(--space-2) 0;
  border-bottom: 2px solid transparent;
}

.site-nav a:hover {
  color: var(--brand);
}

.site-nav a[aria-current="page"] {
  color: var(--brand);
  border-bottom-color: var(--brand);
}

/* Icon button. The accessible name comes from the visually hidden label
   inside it, which theme.js keeps in sync with the current theme.
   44x44 minimum is §5.6's hit-area rule, kept even though it's now an icon. */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  padding: 0;
  min-width: 44px;
  min-height: 44px;
  cursor: pointer;
}
.theme-toggle .theme-icon { display: block; }

.theme-toggle:hover {
  border-color: var(--brand);
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  text-decoration: none;
  cursor: pointer;
  border: 1.5px solid transparent;
  transition: background var(--transition-fast), border-color var(--transition-fast), transform var(--transition-fast);
}

.btn-primary {
  background: var(--brand);
  color: var(--bg);
  border-color: var(--brand);
}

.btn-primary:hover {
  transform: translateY(-1px);
  opacity: 0.92;
}

.btn-primary:active {
  transform: translateY(0);
}

.btn-secondary {
  background: transparent;
  color: var(--brand);
  border-color: var(--brand);
}

.btn-secondary:hover {
  background: var(--brand-tint);
  color: var(--brand-on-tint);
}

/* ---------- Status badge ---------- */
/* Word + colour, never colour alone (§5.3) -- the word is part of the markup, not decorative */

.badge {
  display: inline-block;
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  line-height: 1.4;
  padding: 2px var(--space-3);
  border-radius: var(--radius-pill);
  white-space: nowrap;
}

.badge-verified { background: var(--status-verified-bg); color: var(--status-verified-text); }
.badge-simulation { background: var(--status-sim-bg); color: var(--status-sim-text); }
.badge-progress { background: var(--status-progress-bg); color: var(--status-progress-text); }
.badge-client, .badge-personal { background: var(--brand-tint); color: var(--brand-on-tint); }

/* ---------- Tag ---------- */
/* Small label for audience (grc, m365, iam, ...) -- not a status, just a category */

.tag {
  display: inline-block;
  /* Mono marks these as metadata rather than prose -- same role as .eyebrow
     and the stat sources. Slightly smaller, since Plex Mono runs large. */
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  color: var(--text-muted);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 1px var(--space-2);
}

/* ---------- Case-study card ---------- */
/* Whole card is one link -- the <a> wraps everything, nothing nested is separately clickable */

.card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
}

@media (min-width: 768px) {
  .card-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1024px) {
  .card-grid { grid-template-columns: repeat(3, 1fr); }
}

.case-card {
  /* Flex column so the tag row can be pinned to the bottom -- otherwise cards
     with shorter summaries leave their tags floating mid-card and the row of
     cards reads ragged. */
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-top-width: 3px;
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
  padding: var(--space-5);
  text-decoration: none;
  color: var(--text);
  transition: transform var(--transition-fast), border-color var(--transition-fast), box-shadow var(--transition-fast);
}

/* Status as a top border. The badge word stays in the markup, so colour is
   never the only signal (§5.3). */
.card-badge-simulation { border-top-color: var(--status-sim-text); }
.card-badge-progress { border-top-color: var(--status-progress-text); }
.card-badge-verified { border-top-color: var(--status-verified-text); }
.card-badge-client, .card-badge-personal { border-top-color: var(--brand); }

/* Sides and bottom only -- a blanket border-color here would repaint the
   status top border and lose the signal exactly when the card is focused. */
.case-card:hover {
  transform: translateY(-2px);
  border-left-color: var(--brand);
  border-right-color: var(--brand);
  border-bottom-color: var(--brand);
  box-shadow: var(--shadow-raised);
}

.case-card h3 {
  margin-bottom: var(--space-2);
}

.case-card p {
  color: var(--text-muted);
  font-size: var(--fs-small);
  margin-bottom: var(--space-3);
}

.case-card .card-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  margin-top: auto; /* pins the row to the card's bottom edge */
  padding-top: var(--space-3);
}

/* ---------- At a glance box ---------- */
/* The status/verified row is never hidden, shrunk, or styled smaller than the rest */

.glance {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-5);
}

.glance-row {
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--border);
}

.glance-row:last-child {
  border-bottom: none;
}

.glance-row dt {
  font-weight: var(--fw-semibold);
  font-size: var(--fs-small);
  color: var(--text-muted);
}

.glance-row dd {
  margin: 0;
}

@media print {
  .glance { break-inside: avoid; }
}

/* ---------- Proof strip ---------- */

.proof-strip {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-5);
}

@media (max-width: 767px) {
  .proof-strip { grid-template-columns: 1fr; }
}

.proof-stat .stat-number {
  font-size: var(--fs-display);
  font-weight: var(--fw-semibold);
  color: var(--brand);
  line-height: 1.1;
}

.proof-stat .stat-label {
  margin-top: var(--space-2);
}

.proof-stat .stat-source {
  font-size: var(--fs-small);
  color: var(--text-muted);
  margin-top: var(--space-1);
}

/* ---------- Resume band (visual brief, PR 9) ---------- */
/* The page used to open with a bare row of download buttons on white. The
   band gives the downloads somewhere to sit and states the one thing worth
   knowing before reading: page and PDF come from the same file. The resume's
   own <header> still carries the h1, so there's no second one here. */

.resume-band {
  margin-top: calc(-1 * var(--space-7));
  padding: var(--space-7) 0;
  margin-bottom: var(--space-7);
}
.resume-band-inner { max-width: var(--measure); }
.resume-band .resume-band-lead {
  font-size: var(--fs-h3);
  line-height: 1.5;
  color: var(--band-muted);
}
.resume-band .resume-updated {
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  color: var(--band-muted);
  margin: var(--space-4) 0 0;
}
/* Same inversion the home hero uses -- a navy primary button disappears
   into a navy band. */
.resume-band .btn-primary {
  background: var(--band-text);
  color: var(--band-bg);
  border-color: var(--band-text);
}
.resume-band .btn-secondary {
  color: var(--band-text);
  border-color: var(--band-line);
  background: transparent;
}
.resume-band .btn-secondary:hover {
  background: rgba(255, 255, 255, 0.08);
  color: var(--band-text);
}
.resume-band .cta-row { margin-bottom: 0; }

/* ---------- Footer monogram (visual brief, PR 9) ---------- */

.footer-note { display: flex; align-items: center; gap: var(--space-3); }
.footer-mark {
  flex: none;
  color: var(--brand); /* the rect reads this via fill="currentColor" */
  border-radius: var(--radius);
}
/* Set through CSS, not presentation attributes: var() is a CSS value
   function and does not resolve inside fill="" or font-family="". */
.footer-mark text {
  fill: var(--bg);
  font-family: var(--font-sans);
}

/* ---------- Score tiles (visual brief, PR 8) ---------- */
/* Measured results, boxed. Distinct from .proof-strip, which is unboxed and
   overlaps the home band: these are standalone evidence, and each one carries
   its instrument and date underneath, so no number sits unattributed. */

.score-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--space-4);
  margin-bottom: var(--space-7);
}

.score-tile {
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: 3px solid var(--brand);
  border-radius: var(--radius);
  padding: var(--space-5);
}
/* Smaller than --fs-display: four of these in a row, and the home page's
   proof strip should stay the biggest numbers on the site. */
.score-tile .stat-number {
  font-size: var(--fs-h1);
  font-weight: var(--fw-semibold);
  color: var(--brand);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.score-tile .stat-label {
  margin-top: var(--space-2);
  font-weight: var(--fw-semibold);
}
.score-tile .stat-source {
  margin-top: var(--space-2);
  font-size: var(--fs-small);
  color: var(--text-muted);
}
.score-tile code {
  font-size: 0.9em;
  word-break: break-word;
}

/* ---------- Callout ---------- */
/* Honest notes, caveats -- a visually distinct aside, never styled as an error */

/* ---------- Shared primitives (visual brief, PR 1) ---------- */

/* A mono label above a heading. Plex Mono is reserved for metadata --
   eyebrows, tags, stat sources -- never for reading text. */
.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  margin-bottom: var(--space-2);
}
.band .eyebrow { color: var(--band-accent); }

/* Full-bleed dark section. Uses box-shadow + clip-path rather than 100vw,
   which overflows by the scrollbar's width and causes sideways scroll. */
.band {
  background-color: var(--band-bg);
  background-image: radial-gradient(var(--band-dot) 1px, transparent 1px);
  background-size: 22px 22px;
  box-shadow: 0 0 0 100vmax var(--band-bg);
  clip-path: inset(0 -100vmax);
  color: var(--band-text);
}
.band h1, .band h2, .band h3 { color: var(--band-text); }
.band p { color: var(--band-muted); }
.band a:not(.btn) { color: var(--band-accent); }
/* The default focus ring is --brand, which disappears on a dark band. */
.band a:focus-visible, .band button:focus-visible { outline-color: var(--band-text); }

/* Eyebrow + heading on the left, an optional "see all" link on the right. */
.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}
.section-head h2 { margin-bottom: 0; }
.section-head .eyebrow { margin-bottom: var(--space-1); flex-basis: 100%; }

/* ---------- Home hero band (visual brief, PR 3) ---------- */

/* Pulls up under the header so the band starts at the chrome, and carries
   extra bottom padding for the proof tiles to overlap into. */
.hero-band {
  margin-top: calc(-1 * var(--space-7));
  padding: var(--space-8) 0 calc(var(--space-8) + var(--space-7));
  margin-bottom: 0;
}

.hero-grid { display: grid; gap: var(--space-7); align-items: center; }

@media (min-width: 1024px) {
  /* The fix for the dead right half: content no longer stops at --measure
     with nothing beside it. */
  .hero-grid { grid-template-columns: 1.35fr 1fr; }
}

/* Display size only here, per the brief -- other pages keep --fs-h1. */
.hero-band .display-heading {
  font-size: clamp(2.25rem, 5.5vw, 3.5rem);
  letter-spacing: -0.02em;
  line-height: var(--lh-display);
}
.hero-band .subline { color: var(--band-muted); }
.hero-band .looking-for { border-left-color: var(--band-accent); }
.hero-band .looking-for-label { color: var(--band-accent); }

/* Buttons invert on the band: the navy primary would vanish into it. */
.hero-band .btn-primary {
  background: var(--band-text);
  color: var(--band-bg);
  border-color: var(--band-text);
}
.hero-band .btn-secondary {
  color: var(--band-text);
  border-color: var(--band-line);
  background: transparent;
}
.hero-band .btn-secondary:hover {
  background: rgba(255, 255, 255, 0.08);
  color: var(--band-text);
}

/* .cta-row's own margin-bottom is declared later in this file at equal
   specificity, so the .mb-0 utility loses to it. Set it here instead. */
.hero-band .cta-row { margin-bottom: 0; }

.hero-figure { margin: 0; }
.hero-figure img {
  display: block;
  width: 100%;
  max-width: 380px;
  height: auto;
  border-radius: var(--radius);
  box-shadow: var(--shadow-float);
}
.hero-figure figcaption {
  margin-top: var(--space-3);
  font-size: var(--fs-small);
  color: var(--band-muted);
  max-width: 380px;
}

/* Raised tiles overlapping the band's bottom edge. */
.proof-tiles { position: relative; margin-top: calc(-1 * var(--space-7)); }
.proof-tiles .proof-stat {
  background: var(--bg);
  border: 1px solid var(--border);
  border-top: 3px solid var(--brand);
  border-radius: var(--radius);
  padding: var(--space-5);
  box-shadow: var(--shadow-raised);
}
/* Smaller, non-wrapping stat: "63-70% -> 99%+" used to break onto a second
   line, making that column taller than the other two. */
.proof-tiles .stat-number {
  font-size: clamp(1.75rem, 3vw, 2.25rem);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.proof-tiles .stat-source { font-family: var(--font-mono); font-size: 0.8125rem; }

/* Client door: its own labelled band (CLAUDE.md §1), marked by a brand rule
   rather than an apologetic "Also:" in the heading. */
.client-band {
  border-left: 3px solid var(--brand);
  background: var(--surface);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: var(--space-5) var(--space-6);
}

/* ---------- Interior page band (visual brief, PR 6) ---------- */
/* The home page got its band in PR 3; interior pages opened with a bare h1 on
   white, which is most of why they read flat. Shallower than the home hero --
   this carries a title and one line, not a whole pitch. */

.page-band {
  margin-top: calc(-1 * var(--space-7));
  padding: var(--space-7) 0;
  margin-bottom: var(--space-7);
}
.page-band-inner { max-width: var(--measure); }

/* With a portrait, the band splits like the home hero. Without one it stays a
   single column, so pages that set no photo are unaffected. */
.page-band-grid { display: grid; gap: var(--space-6); align-items: center; }

@media (min-width: 1024px) {
  .page-band-grid.has-figure { grid-template-columns: 1.35fr auto; }
}

.band-figure { margin: 0; }
/* 190px -> ~300px tall, which is about the height of the band's three lines
   of text beside it. At 260px the portrait ran to 409px and drove the band to
   ~500px for three lines, leaving the text floating in dead space and pushing
   the page's actual content below the fold. */
.band-figure img {
  display: block;
  width: 100%;
  max-width: 190px;
  height: auto;
  border-radius: var(--radius);
  box-shadow: var(--shadow-float);
}
@media (max-width: 1023px) {
  /* Below the split it sits under the text, where even 190px reads large in
     the middle of a full-width band. */
  .band-figure img { max-width: 150px; }
}
.page-band h1 { margin-bottom: var(--space-4); }
/* The lead paragraph, sized up -- it's the page's one-line answer to
   "is this for me?", so it shouldn't be body-sized. */
.page-band p:not(.eyebrow) {
  font-size: var(--fs-h3);
  line-height: 1.5;
  color: var(--band-muted);
  margin-bottom: 0;
}

/* ---------- Two-column page (visual brief, PR 7) ---------- */
/* One section pulled out as an aside, for pages whose hard facts a reader
   wants to scan while reading the prose. Source order is prose-then-aside, so
   the reading order is identical with no CSS and for a screen reader; only the
   desktop grid puts the aside alongside. */

.page-cols {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6);
}

@media (min-width: 1024px) {
  .page-cols {
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
    gap: var(--space-8);
    align-items: start;
  }
  /* Follows the prose while it's being read, which is the point of pulling
     the facts out of the flow in the first place. */
  .page-cols .page-aside { position: sticky; top: var(--space-5); }
}

/* Inside a column, --wide is the column, not the container. */
.page-cols .page-body > h2 { max-width: none; }

.page-aside {
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: 3px solid var(--brand);
  border-radius: var(--radius);
  padding: var(--space-5);
}
/* The heading belongs to the box, so it drops the page's section hairline
   and top space -- it isn't one more section in the prose column. */
.page-aside h2 {
  font-size: var(--fs-h3);
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
  color: var(--brand);
}
/* The glance box is already the surface here, so it shouldn't double up. */
.page-aside .glance {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
}

/* ---------- Service cards (visual brief, PR 6) ---------- */
/* "What I build" was three bold-led paragraphs in a column. As cards they
   scan as three distinct offers, which is what a prospective client is
   actually comparing. Deliberately not .case-card: these link nowhere and
   carry no status, so they get a flat surface and no hover. */

.service-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
  margin-bottom: var(--space-7);
}

@media (min-width: 768px) {
  .service-grid { grid-template-columns: repeat(3, 1fr); }
}

.service-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: 3px solid var(--brand);
  border-radius: var(--radius);
  padding: var(--space-5);
}
.service-card h3 { margin-bottom: var(--space-3); }
.service-card p { margin-bottom: 0; }
/* The "not available yet" note on the M365 card. Emphasis inside a service
   card is always that kind of caveat, so it breaks onto its own line rather
   than hiding mid-sentence -- the honesty rule in CLAUDE.md §1. */
.service-card em {
  display: block;
  margin-top: var(--space-3);
  font-style: normal;
  font-size: var(--fs-small);
  color: var(--text-muted);
  border-top: 1px solid var(--border);
  padding-top: var(--space-3);
}

/* ---------- Numbered steps (visual brief, PR 6) ---------- */
/* Still a real <ol> -- the numerals are drawn by a counter, so screen readers
   and print keep the ordered-list semantics. §5.8's structure-is-information
   rule applies: these are numbered because the order genuinely matters. */

.steps {
  list-style: none;
  counter-reset: step;
  padding-left: 0;
  margin: 0 0 var(--space-7);
  max-width: var(--measure);
}
.steps > li {
  counter-increment: step;
  position: relative;
  padding-left: var(--space-7);
  padding-bottom: var(--space-5);
  margin-bottom: 0;
}
.steps > li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: 0;
  width: var(--space-6);
  height: var(--space-6);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-weight: var(--fw-semibold);
  color: var(--brand);
  background: var(--brand-tint);
  border-radius: var(--radius-pill);
}
/* The connecting rule between numerals, so the five steps read as one
   sequence rather than five separate notes. Not on the last one. */
.steps > li::after {
  content: "";
  position: absolute;
  left: calc(var(--space-6) / 2);
  top: var(--space-6);
  bottom: 0;
  width: 1px;
  background: var(--border);
}
.steps > li:last-child { padding-bottom: 0; }
.steps > li:last-child::after { display: none; }

/* ---------- Looking for (home hero) ---------- */
/* A recruiter's first question is "is this person even a match?". This sits in
   the hero so level, function and location land in the first few seconds,
   rather than below three case-study cards where it used to be. */

.looking-for {
  border-left: 3px solid var(--brand);
  padding: var(--space-3) 0 var(--space-3) var(--space-4);
  margin-bottom: var(--space-5);
  max-width: var(--measure);
}
.looking-for-label {
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--brand);
  margin-bottom: var(--space-1);
}

/* ---------- Next-step panel ---------- */
/* Ends every page with somewhere to go. Shares .panel's surface; only the
   separation from the body copy above it differs. */

.next-step { margin-top: var(--space-7); }

/* ---------- Case-study header band (visual brief, PR 5) ---------- */

.case-band {
  margin-top: calc(-1 * var(--space-7));
  padding: var(--space-7) 0;
  margin-bottom: var(--space-7);
}
.case-band-inner { max-width: var(--measure); }
.case-band .case-summary { color: var(--band-muted); font-size: var(--fs-h3); }
.case-band .tag {
  background: transparent;
  border-color: var(--band-line);
  color: var(--band-muted);
}

/* The "Verified" marker, used both on the band and in At a glance. */
.verified-pill {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 1px var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--status-verified-bg);
  color: var(--status-verified-text);
  margin-right: var(--space-2);
}
.case-band .case-verified {
  font-size: var(--fs-small);
  color: var(--band-muted);
}

/* Full verification text, in At a glance's Status row. Never shrunk (§5.6). */
.glance-verified {
  margin-top: var(--space-3);
  margin-bottom: 0;
  max-width: none;
}

/* ---------- One width system (visual brief, PR 5; extended PR 6) ---------- */
/* Prose reads at --measure; evidence-bearing blocks run to --wide. Two right
   edges instead of the previous zigzag between ~800px and ~1050px.
   Case studies and the interior static pages share this, so /about/ and
   /work-with-me/ line up with /work/<slug>/ rather than each drifting. */

.case-study-body > p,
.case-study-body > ul,
.case-study-body > ol,
.case-study-body > blockquote,
.page-body > p,
.page-body > ul,
.page-body > ol,
.page-body > blockquote { max-width: var(--measure); }

.case-study-body > .glance,
.case-study-body > figure,
.case-study-body > pre,
.case-study-body > .table-wrap,
.case-study-body > table,
.page-body > .glance,
.page-body > figure,
.page-body > pre,
.page-body > .table-wrap,
.page-body > table,
.page-body > .card-grid,
.page-body > .service-grid { max-width: var(--wide); }

/* Section rhythm. Headings carry no top margin globally, so eight sections of
   prose ran together as one slab -- the "flat" reading. A hairline above each
   h2 (not the first) gives the page visible structure without decoration. */
.case-study-body > h2,
.page-body > h2 {
  max-width: var(--wide);
  margin-top: var(--space-8);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
  color: var(--brand);
}
.case-study-body > h2:first-child,
.page-body > h2:first-child {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}
.case-study-body > h3,
.page-body > h3 {
  max-width: var(--measure);
  margin-top: var(--space-6);
}

/* The proof card-grid is the last thing in its section, so it needs the
   bottom space the service grid and steps already carry. */
.page-body > .card-grid { margin-bottom: var(--space-7); }

/* Tiles are evidence blocks, not prose -- they run to the full column. */
.page-body > .score-tiles { max-width: var(--wide); }

/* Figures: lift them off the page so a screenshot reads as evidence. */
.case-study-body figure img {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-raised);
}
.case-study-body figcaption {
  font-size: var(--fs-small);
  color: var(--text-muted);
  margin-top: var(--space-3);
  max-width: var(--measure);
}

.callout {
  background: var(--brand-tint);
  border-left: 3px solid var(--brand);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: var(--space-4) var(--space-5);
}

.callout p:last-child {
  margin-bottom: 0;
}

/* ---------- Table ---------- */
/* Zebra-free, hairline rules; scrolls sideways inside its own box on phones, never the whole page */

.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-small);
}

th, td {
  text-align: left;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

thead th {
  font-weight: var(--fw-semibold);
  color: var(--text-muted);
  background: var(--surface);
}

tbody tr:last-child td {
  border-bottom: none;
}

/* ---------- Figure ---------- */

figure {
  margin: var(--space-5) 0;
}

figure img, figure svg {
  width: 100%;
  height: auto;
  border-radius: var(--radius);
  border: 1px solid var(--border);
}

figcaption {
  font-size: var(--fs-small);
  color: var(--text-muted);
  margin-top: var(--space-2);
}

/* ---------- Code / path ---------- */

:not(pre) > code {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 1px 6px;
  word-break: break-word;
}

pre {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4);
  overflow-x: auto;
}

pre code {
  background: none;
  border: none;
  padding: 0;
}

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

.site-footer {
  border-top: 1px solid var(--border);
  margin-top: var(--space-8);
}

.site-footer .container {
  padding-top: var(--space-6);
  padding-bottom: var(--space-6);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  justify-content: space-between;
  align-items: center;
  font-size: var(--fs-small);
  color: var(--text-muted);
}

.site-footer .footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

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

.site-footer a:hover {
  color: var(--brand);
}

/* ---------- Skip link ---------- */
/* Required by §5.9's QA checklist; hidden until focused */

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--brand);
  color: var(--bg);
  padding: var(--space-3) var(--space-4);
  border-radius: 0 0 var(--radius) 0;
  z-index: 100;
}

.skip-link:focus {
  left: 0;
}

/* ---------- Page-level structural/utility classes ---------- */
/* §8: no style="" attributes anywhere on the main site, so the strict
   CSP (style-src 'self', no 'unsafe-inline') actually holds. Everything
   build.mjs needs for one-off page layout lives here instead. */

.text-muted { color: var(--text-muted); }

.display-heading { font-size: var(--fs-display); line-height: var(--lh-display); }

.subline { font-size: var(--fs-h3); color: var(--text-muted); max-width: var(--measure); }

.cta-row { display: flex; gap: var(--space-3); flex-wrap: wrap; margin: var(--space-5) 0 var(--space-7); }

.case-header { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }

.panel { background: var(--surface); border-radius: var(--radius); padding: var(--space-6); }

.glance dl { margin: 0; }

.page-main { padding-top: var(--space-7); padding-bottom: var(--space-8); }

.mt-0 { margin-top: 0; }
.mb-0 { margin-bottom: 0; }
.mb-6 { margin-bottom: var(--space-6); }
.mb-7 { margin-bottom: var(--space-7); }

/* ---------- Resume page (/resume/) ---------- */
/* Markup comes from src/resume.mjs -- the same markup the PDFs are printed
   from (tools/build-resumes.mjs), so the page and the PDFs can't drift. */

.resume-actions { display: flex; gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-6); }
.resume-actions p { margin: 0; align-self: center; color: var(--text-muted); font-size: var(--fs-small); }

.resume-header h1 { margin-bottom: var(--space-1); }
.resume-headline { font-weight: var(--fw-semibold); font-size: var(--fs-h3); margin-bottom: var(--space-2); }
.resume-contact { color: var(--text-muted); font-size: var(--fs-small); margin-bottom: var(--space-1); }
.resume-sep { color: var(--text-muted); }

.resume-section { margin-top: var(--space-6); }
.resume-section h2 {
  font-size: var(--fs-h3);
  color: var(--brand);
  border-bottom: 1px solid var(--border);
  padding-bottom: var(--space-2);
  margin-bottom: var(--space-3);
}

.resume-entry { margin-top: var(--space-4); }
.resume-row { margin: 0; }
.resume-title { display: inline; margin: 0; font-size: var(--fs-body); font-weight: var(--fw-semibold); line-height: var(--lh-body); }
.resume-entry-head .resume-title { color: var(--brand); }
.resume-role { margin-top: var(--space-3); }
.resume-date { color: var(--text-muted); font-size: var(--fs-small); white-space: nowrap; }
.resume-note { color: var(--text-muted); font-size: var(--fs-small); }

.resume ul { padding-left: 1.25em; margin: var(--space-2) 0 0; }
.resume li { max-width: var(--measure); margin-bottom: var(--space-1); }

.resume-next { margin-top: var(--space-7); }

/* ---------- Utilities ---------- */
/* Available to assistive tech, invisible on screen. Used for the theme
   toggle's label, which is what gives the icon button its accessible name. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Group count beside a heading on /work/ -- mono, like other metadata. */
.count {
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  font-weight: var(--fw-regular);
  color: var(--text-muted);
  vertical-align: middle;
}
