/* ---------------------------------------------------------------
   Shared design system.

   Palette is cold blue-grey neutrals with a single hazard-amber
   accent, drawn from roadside/commercial service vocabulary rather
   than a generic portfolio look. Monospace carries labels, IDs and
   figures — on pages about measurement, the mono is the register,
   not decoration.

   Themes are token-level: components style through custom
   properties only, never inside the media query, so the viewer's
   explicit toggle (data-theme) can override the OS preference in
   both directions.
   --------------------------------------------------------------- */

:root {
  --ground: #F5F6F7;
  --ground-raised: #FFFFFF;
  --ground-sunk: #EBEDEF;
  --ink: #14181D;
  --steel: #5A6472;
  --steel-dim: #838C99;
  --amber: #B36A0C;
  --signal-red: #B03A32;
  --signal-green: #3D6B4A;
  --rule: #DCDFE4;
  --rule-strong: #C3C8CF;

  /* Tab labels sit brighter than body text so the bar reads as
     controls. In light mode "brighter" means darker against the
     pale ground — the contrast direction inverts, the intent does not. */
  --nav-ink: #2B3138;
  --nav-ink-hi: #000000;

  --measure: 66ch;
  --rail: 8rem;
  --gap: 2.5rem;

  --font-display: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-body: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", "Segoe UI Mono", Menlo, Consolas, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --ground: #12151A; --ground-raised: #171B21; --ground-sunk: #0D1014;
    --ink: #E6E9ED; --steel: #98A2B0; --steel-dim: #6E7887;
    --amber: #D98218; --signal-red: #D4574D; --signal-green: #6FA37F;
    --rule: #262C35; --rule-strong: #333B46;
    --nav-ink: #F2F5F8; --nav-ink-hi: #FFFFFF;
  }
}

:root[data-theme="dark"] {
  --ground: #12151A; --ground-raised: #171B21; --ground-sunk: #0D1014;
  --ink: #E6E9ED; --steel: #98A2B0; --steel-dim: #6E7887;
  --amber: #D98218; --signal-red: #D4574D; --signal-green: #6FA37F;
  --rule: #262C35; --rule-strong: #333B46;
  --nav-ink: #F2F5F8; --nav-ink-hi: #FFFFFF;
}

:root[data-theme="light"] {
  --ground: #F5F6F7; --ground-raised: #FFFFFF; --ground-sunk: #EBEDEF;
  --ink: #14181D; --steel: #5A6472; --steel-dim: #838C99;
  --amber: #B36A0C; --signal-red: #B03A32; --signal-green: #3D6B4A;
  --rule: #DCDFE4; --rule-strong: #C3C8CF;
  --nav-ink: #2B3138; --nav-ink-hi: #000000;
}

* { box-sizing: border-box; }

body {
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  margin: 0;
  padding: 0 1.5rem 5rem;
}

.wrap { max-width: 58rem; margin: 0 auto; }

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

.label {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--steel-dim);
  font-weight: 500;
}

h1, h2, h3 { font-family: var(--font-display); text-wrap: balance; margin: 0; }
h1 { font-size: clamp(2.3rem, 5.5vw, 3.6rem); font-weight: 800; letter-spacing: -0.035em; line-height: 1.04; }
h2 { font-size: clamp(1.4rem, 2.8vw, 1.75rem); font-weight: 750; letter-spacing: -0.022em; line-height: 1.18; }
h3 { font-size: 1rem; font-weight: 700; letter-spacing: -0.01em; line-height: 1.3; }

/* Italics in the display face lean into the following character at
   this weight; the small letter-spacing bump buys back the gap. */
h1 em, h2 em { font-style: italic; letter-spacing: 0.005em; }

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

a {
  color: var(--ink);
  text-decoration-color: var(--amber);
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1.5px;
}
a:hover { color: var(--amber); }
a:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; border-radius: 2px; }

code {
  font-family: var(--font-mono);
  font-size: 0.875em;
  background: var(--ground-sunk);
  border: 1px solid var(--rule);
  padding: 0.08em 0.32em;
  border-radius: 2px;
  word-break: break-word;
}

/* Placeholder text the author still needs to fill in. */
.ph { color: var(--amber); border-bottom: 1px dashed currentColor; }

/* ---------- masthead ---------- */

.masthead { padding: 3.5rem 0 0; border-bottom: 1px solid var(--rule); }

.stamp {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 0.6rem 1.25rem; padding-bottom: 1.75rem;
}

/* Subtitle riding directly under an h1 — h1 has margin:0, so the gap has to
   come from here. Tighter than .deck's, since this reads as part of the title. */
h1 + .label { display: block; margin-top: 0.9rem; color: var(--steel); }

.deck {
  font-size: clamp(1.03rem, 1.8vw, 1.15rem);
  color: var(--steel);
  max-width: 54ch;
  margin: 1.5rem 0 0;
  line-height: 1.55;
}

.byline { display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem; margin: 2rem 0 1.75rem; }

/* ---------- spec strip ---------- */

.spec-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  border-bottom: 1px solid var(--rule);
}
.spec { padding: 1.35rem 1.25rem 1.35rem 0; display: flex; flex-direction: column; gap: 0.28rem; }
.spec-val {
  font-family: var(--font-mono); font-size: 1.4rem; font-weight: 600;
  letter-spacing: -0.03em; font-variant-numeric: tabular-nums; line-height: 1;
}
.spec-note { font-size: 0.8125rem; color: var(--steel-dim); line-height: 1.35; }

/* ---------- sections with spec rail ---------- */

section {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  gap: 0 var(--gap);
  padding: var(--gap) 0;
  border-bottom: 1px solid var(--rule);
}
.rail { display: flex; flex-direction: column; gap: 0.35rem; padding-top: 0.3rem; }
.rail-num { font-family: var(--font-mono); font-size: 0.6875rem; letter-spacing: 0.14em; color: var(--amber); font-weight: 600; }
.body-col { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }
.body-col > h2 { margin-bottom: 0.15rem; }

@media (max-width: 46rem) {
  section { grid-template-columns: 1fr; gap: 0.85rem; }
  .rail { flex-direction: row; align-items: baseline; gap: 0.7rem; padding-top: 0; }
}

/* ---------- source cards ---------- */

.sources {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 1px; background: var(--rule-strong);
  border: 1px solid var(--rule-strong); border-radius: 3px; overflow: hidden;
}
.source { background: var(--ground-raised); padding: 1rem; display: flex; flex-direction: column; gap: 0.45rem; }
.source-name { font-family: var(--font-mono); font-size: 0.8125rem; font-weight: 600; }
.source-sees { font-size: 0.875rem; color: var(--steel); }
.source-blind { font-size: 0.8125rem; color: var(--signal-red); display: flex; gap: 0.45rem; }
.source-blind::before { content: "\2715"; font-size: 0.75rem; line-height: 1.5; }

/* ---------- inspection tag ---------- */

.tag { border: 1px solid var(--rule-strong); background: var(--ground-raised); border-radius: 3px; overflow: hidden; }
.tag-head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 0.5rem 1rem; padding: 0.65rem 1rem;
  background: var(--ground-sunk); border-bottom: 1px solid var(--rule-strong);
}
.tag-id {
  font-family: var(--font-mono); font-size: 0.6875rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--steel); font-weight: 600;
}
.chip {
  font-family: var(--font-mono); font-size: 0.625rem; letter-spacing: 0.1em;
  text-transform: uppercase; font-weight: 600; padding: 0.2rem 0.5rem;
  border-radius: 2px; border: 1px solid currentColor; white-space: nowrap;
}
.chip-risk { color: var(--signal-red); }
.chip-hold { color: var(--amber); }

.tag-body { padding: 1.15rem 1rem; display: flex; flex-direction: column; gap: 0.95rem; }
.tag-body p { max-width: 60ch; }

.beat { display: grid; grid-template-columns: 5.5rem minmax(0, 1fr); gap: 0.9rem; align-items: baseline; }
.beat > .label { padding-top: 0.15em; }
@media (max-width: 40rem) { .beat { grid-template-columns: 1fr; gap: 0.25rem; } }

pre {
  margin: 0; background: var(--ground-sunk); border: 1px solid var(--rule);
  border-radius: 3px; padding: 0.85rem 1rem; overflow-x: auto;
  font-size: 0.8125rem; line-height: 1.55;
}
pre code { background: none; border: none; padding: 0; font-size: inherit; word-break: normal; }
.cmt { color: var(--steel-dim); }
.kw { color: var(--amber); }

/* Subheads inside a prose column. Body text and h3 are both 1rem, so
   the amber rule does the separating that size alone can't. */
.body-col > h3 {
  margin-top: 1.25rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--rule);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--amber);
}
.body-col > h3:first-of-type { margin-top: 0.5rem; }

/* ---------- platform strip ---------- */

/* Third-party product shots, not this project's data — held to a uniform
   tile so they read as identification rather than as evidence.
   Breaks out of the 58rem wrapper toward the viewport edges (capped),
   since dashboard screenshots need the width to stay legible. */
.platforms {
  margin: 0 auto var(--gap) 50%;
  width: min(100vw - 3rem, 78rem);
  transform: translateX(-50%);
  display: flex; flex-direction: column; gap: 0.6rem;
}
.platform-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1px; background: var(--rule-strong);
  border: 1px solid var(--rule-strong); border-radius: 3px; overflow: hidden;
}
.platform { background: var(--ground-raised); display: flex; flex-direction: column; }
/* cover, not contain: these are wide dashboard captures and letterboxing
   them into a short tile shrank the UI past the point of being readable.
   Anchored top-left so each crop keeps the chart/table it's identified by. */
.platform img {
  display: block; width: 100%; height: 11rem;
  object-fit: cover; object-position: left top;
  background: #FFFFFF;
  border-bottom: 1px solid var(--rule);
}
.platform-cap {
  padding: 0.7rem 0.85rem;
  font-size: 0.8125rem; line-height: 1.5; color: var(--steel);
  display: flex; flex-direction: column; gap: 0.2rem;
}
.platform-cap strong {
  font-family: var(--font-mono); font-size: 0.75rem;
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--ink); font-weight: 600;
}
/* Tied to the row rather than floating under it: the lead clause carries
   the argument, the provenance note trails in the dimmer colour. */
.platforms figcaption {
  font-size: 0.8125rem; line-height: 1.5;
  color: var(--steel); max-width: 68ch;
  padding-left: 0.85rem; border-left: 2px solid var(--rule-strong);
}
.provenance { color: var(--steel-dim); }

/* A real artifact from the work, unlike the platform strip above — shown
   whole rather than cropped, since the columns are the evidence. */
/* Breaks out of the text column: at column width this 1915px capture
   renders near 40%, which puts the row text below reading size. */
/* Prose beside a figure. The text keeps the larger share so it never
   narrows into a ragged column; below 52rem they stack, since two
   cramped columns read worse than one of each. */
.split {
  display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: var(--gap); align-items: start;
}
.split > p { max-width: none; }

/* Multi-paragraph prose in a split needs a wrapper to stay one grid cell.
   Paragraph spacing on this site comes from .body-col's flex gap, which only
   reaches direct children — so the wrapper has to re-establish it or the
   paragraphs run together. */
.split-prose {
  display: flex; flex-direction: column; gap: 1rem; min-width: 0;
}
.split-prose > p { max-width: none; }

/* Gives the figure the larger share, for screenshots whose detail matters
   more than the paragraph beside them. */
.split-figure { grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr); }

@media (max-width: 52rem) {
  .split { grid-template-columns: 1fr; gap: 1.1rem; }
}

/* Supporting evidence, not a hero image — full detail is one click away,
   so it doesn't need to be legible inline. */
.shot {
  margin: 0;
  display: flex; flex-direction: column; gap: 0.5rem;
}
/* Optical alignment: the paragraph's first baseline sits below its box
   top, so a flush image reads as riding high next to it. */
.split .shot { padding-top: 0.3rem; }

/* Two figures side by side. Their sources are 1.63 and 2.44 aspect, so
   natural height would leave one visibly taller; a fixed frame with a
   top-anchored crop keeps the pair even and the chart titles visible. */
.shot-pair {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1.1rem; margin-top: 0.35rem; align-items: start;
}
/* contain, not cover: cropping either chart cuts its axis labels, and the
   full-size link means nothing is lost by letterboxing them here. */
.shot-pair .shot img {
  height: 13rem; object-fit: contain; object-position: center;
  padding: 0.5rem;
}

/* Homepage app shots: two UI screenshots side by side, framed portrait so
   they read as app windows rather than wide banners. */
.shot-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1.1rem; align-items: start;
  /* Slimmer than the 58rem wrapper — narrowing the columns is half of what
     makes the frames read as tall. Viewport cap avoids h-scroll. */
  width: min(48rem, 100vw - 2rem);
  margin: 0 auto var(--gap);
}
/* cover, not contain: these are list views, so a top-anchored crop keeps
   the header and first rows — the legible part — and drops the tail. The
   full image is one click away, so nothing is actually lost. */
.shot-row .shot img {
  height: 17rem; object-fit: cover; object-position: top center;
}

/* Supporting shot: the customer-facing site is a step on the way to the
   app, not the point, so it's held to a fraction of the app shots' width.
   Centered under them, it reads as a footnote rather than a third column. */
.shot-minor {
  max-width: 22rem; margin: 0 auto var(--gap);
  align-items: center; text-align: center;
}

/* Lead figure: sits between the masthead and the spec strip, so it takes
   the wrapper's full width rather than a text measure. */
.shot-lead { margin: 0 0 var(--gap); }
.shot img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--rule-strong); border-radius: 3px;
  background: #FFFFFF;
}
.shot a { display: block; text-decoration: none; }
.shot a:hover img { border-color: var(--amber); }
.shot a:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }
.shot figcaption {
  font-size: 0.8125rem; line-height: 1.5; color: var(--steel);
  max-width: 68ch;
}
/* Only on figures whose detail is genuinely too small inline — repeating
   it under every screenshot on a page turns it into noise. */
.shot-zoom figcaption::after {
  content: " Click to view full size.";
  color: var(--steel-dim);
}

@media (max-width: 46rem) {
  .platform img { height: 9rem; }
}

/* ---------- sequence ---------- */

.arc { border: 1px solid var(--rule-strong); border-radius: 3px; overflow: hidden; }
.arc-step {
  display: grid; grid-template-columns: 2.25rem minmax(0, 1fr);
  gap: 0.9rem; padding: 0.85rem 1rem;
  background: var(--ground-raised); border-bottom: 1px solid var(--rule);
}
.arc-step:last-child { border-bottom: none; }
.arc-n { font-family: var(--font-mono); font-size: 0.75rem; color: var(--amber); font-weight: 600; padding-top: 0.15em; }
.arc-text { display: flex; flex-direction: column; gap: 0.2rem; }
.arc-text strong { font-size: 0.9375rem; }
.arc-text span { font-size: 0.875rem; color: var(--steel); }

/* ---------- lists ---------- */

ul.plain { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.6rem; max-width: var(--measure); }
ul.plain li { padding-left: 1.1rem; position: relative; }
ul.plain li::before { content: ""; position: absolute; left: 0; top: 0.68em; width: 5px; height: 1px; background: var(--amber); }

/* ---------- pull quote ---------- */

.pull {
  border-left: 2px solid var(--amber);
  padding: 0.1rem 0 0.1rem 1.2rem;
  font-size: 1.05rem; line-height: 1.5; max-width: 52ch;
}

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

.note {
  border: 1px solid var(--rule-strong);
  border-left: 2px solid var(--steel-dim);
  background: var(--ground-raised);
  padding: 0.9rem 1rem; border-radius: 3px;
  display: flex; flex-direction: column; gap: 0.4rem; max-width: 60ch;
}
.note p { font-size: 0.9375rem; }

/* ---------- table ---------- */

.table-scroll { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; font-size: 0.875rem; min-width: 30rem; }
th, td { text-align: left; padding: 0.6rem 1rem 0.6rem 0; border-bottom: 1px solid var(--rule); vertical-align: top; }
th {
  font-family: var(--font-mono); font-size: 0.6875rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--steel-dim); font-weight: 500;
  border-bottom-color: var(--rule-strong);
}
td.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---------- index cards ---------- */

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 1rem; }
/* Interactive, so it carries more weight than the static containers it
   sits among (.note, .tag): a raised surface, a shadow, and an amber
   edge that reads as a target rather than a panel. */
.card {
  position: relative; isolation: isolate;
  border: 1px solid var(--rule-strong); background: var(--ground-raised);
  border-radius: 5px; padding: 1.1rem 1.1rem 1.2rem;
  display: flex; flex-direction: column; gap: 0.5rem;
  text-decoration: none; color: inherit;
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.05), 0 1px 1px rgb(0 0 0 / 0.03);
}
/* Amber rule along the top edge — visible at rest, so the card reads as
   actionable before any pointer arrives (and on touch, where hover never does). */
.card::before {
  content: ""; position: absolute; inset: -1px -1px auto -1px; height: 3px;
  background: var(--amber); opacity: 0.55;
  border-radius: 5px 5px 0 0;
}
.card:hover {
  border-color: var(--amber); color: inherit;
  background: var(--ground-raised);
  box-shadow: 0 6px 16px rgb(0 0 0 / 0.10), 0 2px 5px rgb(0 0 0 / 0.06);
}
.card:hover::before { opacity: 1; }
.card:active { box-shadow: 0 1px 2px rgb(0 0 0 / 0.06); }
.card:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }
.card h3 { margin: 0; }
.card:hover h3 { color: var(--amber); }
.card p { font-size: 0.9rem; color: var(--steel); max-width: none; }

/* Persistent affordance: without it the cards read as static blocks,
   since the border only reacted on hover and touch has no hover. */
/* Rendered as a button, not a text link: at a glance the eye needs a
   shape it recognises as pressable, which underlined amber text is not. */
.card-cta {
  margin-top: auto; align-self: flex-start;
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--amber); border-radius: 3px;
  font-family: var(--font-mono); font-size: 0.75rem;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--amber); font-weight: 600;
}
.card-cta::after { content: "\2192"; font-size: 0.875rem; }
/* Fills on hover so the whole card has one obvious focal point. */
.card:hover .card-cta {
  background: var(--amber);
  color: var(--ground-raised);
}
@media (prefers-reduced-motion: no-preference) {
  .card-cta { transition: background 120ms ease, color 120ms ease; }
  .card-cta::after { transition: transform 120ms ease; }
  .card:hover .card-cta::after { transform: translateX(3px); }
}

/* The card CTA shape reused outside a card — as a standalone button. Needs its
   own hover, since there's no parent .card to trigger the fill, and
   margin-top:auto is meaningless without the flex parent. Deliberately a
   separate class rather than a bare `a.card-cta`, so it can't reach inside a
   .card and collapse the bottom-alignment that keeps CTAs in a row level. */
.cta-standalone {
  margin-top: 0;
  text-decoration: none;
}
.cta-standalone:hover {
  background: var(--amber);
  color: var(--ground-raised);
}

/* Tab bar. Sticky so the switch between case studies is always in
   reach; the active tab is carried by an amber underline riding the
   container's bottom rule, not by color alone. */
.sitenav {
  position: sticky; top: 0; z-index: 10;
  display: flex; flex-wrap: wrap; gap: 0.35rem;
  margin-bottom: 0.5rem; padding: 0.75rem 0 0;
  border-bottom: 1px solid var(--rule-strong);
  /* Bleed into the body gutter so scrolled content can't show
     through beside the bar. */
  margin-left: -1.5rem; margin-right: -1.5rem;
  padding-left: 1.5rem; padding-right: 1.5rem;
  background: var(--ground);
}

.sitenav a {
  position: relative;
  padding: 0.6rem 1.1rem;
  margin-bottom: -1px;
  border: 1px solid transparent;
  border-bottom: 2px solid transparent;
  border-radius: 3px 3px 0 0;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--nav-ink);
  font-weight: 600;
  white-space: nowrap;
}

.sitenav a:hover {
  color: var(--nav-ink-hi);
  background: var(--ground-sunk);
  border-bottom-color: var(--rule-strong);
}

.sitenav a[aria-current="page"] {
  color: var(--amber);
  background: var(--ground-raised);
  border-color: var(--rule);
  border-bottom-color: var(--amber);
}

.sitenav a:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

@media (max-width: 30rem) {
  .sitenav a { flex: 1 1 auto; text-align: center; padding: 0.6rem 0.5rem; }
}

footer { padding-top: var(--gap); display: flex; flex-direction: column; gap: 0.75rem; }
.footer-note { font-size: 0.875rem; color: var(--steel-dim); max-width: 60ch; }

@media (prefers-reduced-motion: no-preference) {
  a, .card { transition: color 120ms ease, border-color 120ms ease, background 120ms ease, transform 140ms ease, box-shadow 140ms ease; }
  .card:hover { transform: translateY(-3px); }
  .card:active { transform: translateY(-1px); }
}
