/* ══════════════════════════════════════════════════════════════════════
   A single project, read top to bottom.

   The dashboard is one screen and never scrolls. This is its opposite: a
   document, and it should admit that instead of pretending to be a card.
   So the chrome is shared — the same bar, the same glass, the same caps
   labels — and the body is built for reading. Tokens come from
   tokens.css; nothing here invents a colour, a size or a duration.
   ══════════════════════════════════════════════════════════════════════ */

* { box-sizing: border-box; }
/* `hidden` means hidden. Without this it is a 0,1,0 rule losing to every
   component that sets its own display, which is how a hidden button went
   on being drawn: `.solid[hidden]` and `.solid.ico` are both 0,2,0 and
   the later one won. One rule, at the top, that nothing has to remember. */
[hidden] { display: none !important; }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  /* The same two washes as the dashboard, fixed, so scrolling the page
     moves the text across the light rather than dragging the light with
     it. */
  background-color: var(--bg);
  background-image: var(--wash);
  background-attachment: fixed;
  color: var(--fg);
  font-family: var(--sans);
  font-size: var(--t-md);
  font-weight: var(--w-reg);
  line-height: var(--lh-body);
  letter-spacing: -0.011em;
  -webkit-font-smoothing: antialiased;
}

/* ── The slot above the bar ─────────────────────────────────────────────
   The bar floats ten pixels clear of the top, and on a document that
   scrolls, lines of text slid up through that slot and were legible in
   it. So the slot gets a lid. It repaints the page's own ground —
   colour and washes, same fixed attachment — which means it lines up
   with the real background by construction rather than by a value
   someone matched by eye and will not match again. */
body::before {
  content: "";
  position: fixed; top: 0; left: 0; right: 0;
  height: var(--gap);
  z-index: calc(var(--z-bar) - 1);
  pointer-events: none;
  background-color: var(--bg);
  background-image: var(--wash);
  background-attachment: fixed;
}

.defs { position: absolute; width: 0; height: 0; }
.defs symbol { fill: none; stroke: currentColor; stroke-width: 1.35; stroke-linecap: round; stroke-linejoin: round; }
svg use { pointer-events: none; }
.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.sp { flex: 1 1 auto; }

.grain {
  position: fixed; inset: 0; width: 100%; height: 100%;
  pointer-events: none; opacity: .03;
  z-index: var(--z-grain); mix-blend-mode: overlay;
}

/* ── The bar ────────────────────────────────────────────────────────────
   The same object as on the dashboard, doing one more job: on a page
   this long the way back has to stay reachable, so it sticks, and the
   hairline along its bottom edge reports how far down the case you are.
   That line is the only thing on the page that uses the accent. */
.bar {
  position: sticky; top: var(--gap); z-index: var(--z-bar);
  display: flex; align-items: center; gap: var(--sp-12);
  max-width: 1180px;
  margin: var(--gap) auto 0;
  /* The padding is not chosen for the bar. At 24 it matches the
     document's own gutter, so the back button's left edge lands on the
     chapter rail's hairline and the spine of the page appears to drop
     out of the button. The clock sits the same 24 in from the other
     side. (Both are one pixel out from the document, because the bar
     has a border and the document does not; a pixel narrower than the
     hairline itself is not a misalignment anyone can see.) */
  padding: var(--sp-8) var(--sp-24);
  border: 1px solid var(--line);
  /* The card radius on a forty-four pixel bar is a third of its height.
     Stepped down to match the dashboard's bar. */
  border-radius: var(--r-12);
  background: color-mix(in srgb, var(--panel) 62%, transparent);
  -webkit-backdrop-filter: blur(26px);
  backdrop-filter: blur(26px);
  box-shadow: var(--s-inset), 0 10px 28px rgba(0, 0, 0, .5);
  overflow: hidden;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .bar { background: var(--panel); }
}
.bar-name {
  font-size: var(--t-md); font-weight: var(--w-med);
  letter-spacing: var(--ls-tight);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bar-item { color: var(--muted); font-size: var(--t-xs); white-space: nowrap; }

.bar-prog {
  position: absolute; left: 0; bottom: 0; height: 1px;
  width: var(--p, 0%);
  background: var(--accent);
  transition: width var(--d-fast) linear;
}

/* ── A caps label ───────────────────────────────────────────────────── */
.lbl {
  font-size: var(--t-xs); font-weight: var(--w-med);
  text-transform: uppercase; letter-spacing: var(--ls-caps);
  color: var(--faint);
  display: inline-flex; align-items: center; gap: var(--sp-6);
  white-space: nowrap;
}

/* ── Buttons ────────────────────────────────────────────────────────────
   Lifted whole from the dashboard, including the rule that nothing
   moves: glass answers by clearing and catching light along its top
   edge. Only the arrow travels, because an arrow means direction. */
.solid {
  appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: var(--sp-8);
  padding: var(--sp-8) var(--sp-12); border-radius: var(--r-8);
  background: var(--s-rest);
  border: 1px solid var(--e-rest);
  box-shadow: var(--s-inset);
  -webkit-backdrop-filter: var(--s-blur); backdrop-filter: var(--s-blur);
  color: var(--fg); text-decoration: none;
  font-family: inherit;
  font-size: var(--t-sm); font-weight: var(--w-med); line-height: var(--lh-flat);
  transition: background var(--d-base) var(--ease),
              border-color var(--d-base) var(--ease),
              box-shadow var(--d-base) var(--ease),
              backdrop-filter var(--d-base) var(--ease),
              -webkit-backdrop-filter var(--d-base) var(--ease),
              color var(--d-base) var(--ease);
}
.solid.sm { padding: var(--sp-6) var(--sp-10); }

.solid svg { width: 14px; height: 14px; flex: none; transition: transform var(--d-base) var(--ease); }
.solid svg.flip { transform: scaleX(-1); }
@media (hover: hover) {
  .solid:hover {
    background: var(--s-hover); border-color: var(--e-hover);
    box-shadow: var(--s-inset-hi);
    -webkit-backdrop-filter: var(--s-blur-hi); backdrop-filter: var(--s-blur-hi);
  }
  .solid:hover svg { transform: translateX(3px); }
  .solid:hover svg.flip { transform: scaleX(-1) translateX(3px); }
}
.solid:active {
  background: var(--s-press); border-color: var(--e-press);
  box-shadow: var(--s-sunk);
  -webkit-backdrop-filter: var(--s-blur); backdrop-filter: var(--s-blur);
}
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-4); }

/* ── The document ───────────────────────────────────────────────────────
   A rail of chapters and one column of reading. The measure is 660px
   because the copy is long and technical, and 620 was tight once the
   figures started carrying captions of their own. */
.doc {
  display: grid;
  grid-template-columns: 168px minmax(0, 660px);
  gap: var(--sp-44);
  max-width: 1180px;
  margin: 0 auto;
  padding: var(--sp-44) var(--sp-24) 30vh;
  align-items: start;
}
/* --bar-h is measured and set by case.js; the fallback is only what the
   page shows in the instant before the first frame. */
.doc-side { position: sticky; top: calc(var(--gap) + var(--bar-h, 44px) + var(--sp-24)); }
.doc-main { min-width: 0; }

/* ── The chapter rail ───────────────────────────────────────────────────
   A hairline with the chapters hung off it, so the page has a spine.
   The active chapter is marked on the line itself rather than by going
   brighter alone: on a rail this quiet, colour alone was not findable. */
.doc-toc {
  position: relative;
  display: flex; flex-direction: column;
  gap: var(--sp-2);
  padding-left: var(--sp-14);
  counter-reset: toc;
}
.doc-toc::before {
  content: ""; position: absolute; left: 0; top: var(--sp-6); bottom: var(--sp-6);
  width: 1px; background: var(--line);
}
.doc-toc a {
  position: relative;
  padding: var(--sp-6) var(--sp-8);
  margin-left: calc(-1 * var(--sp-8));
  border-radius: var(--r-8);
  color: var(--faint); text-decoration: none;
  font-size: var(--t-xs); font-weight: var(--w-med);
  text-transform: uppercase; letter-spacing: var(--ls-caps);
  transition: color var(--d-base) var(--ease), background var(--d-base) var(--ease);
}
.doc-toc a::before {
  content: ""; position: absolute;
  left: calc(-1 * var(--sp-6)); top: 50%; margin-top: -1px;
  width: 1px; height: 2px;
  background: var(--fg); opacity: 0;
  transition: opacity var(--d-base) var(--ease), height var(--d-base) var(--ease);
}
@media (hover: hover) { .doc-toc a:hover { color: var(--muted); background: var(--s-rest); } }
.doc-toc a.active { color: var(--fg); }
.doc-toc a.active::before { opacity: 1; height: 14px; margin-top: -7px; }

/* ── The title ──────────────────────────────────────────────────────── */
.intro { margin: 0 0 var(--sp-36); }
.intro .name {
  margin: 0;
  font-size: var(--t-xl); font-weight: var(--w-med);
  line-height: var(--lh-tight); letter-spacing: var(--ls-tight);
}
.intro .role {
  margin: var(--sp-10) 0 0;
  font-family: var(--mono);
  font-size: var(--t-xs); font-weight: var(--w-reg);
  text-transform: uppercase; letter-spacing: var(--ls-caps);
  color: var(--faint);
}

/* ── Chapters ───────────────────────────────────────────────────────────
   Numbered, because a case study is a sequence and the reader should be
   able to tell where in it they are without counting. The rule above
   each one is what separates chapters; the type alone was doing that
   job with margin, and margin is not a signal. */
.doc-bio { counter-reset: chap fig; }
.section-label {
  counter-increment: chap;
  display: flex; align-items: baseline; gap: var(--sp-12);
  margin: var(--sp-44) 0 var(--sp-16);
  padding-top: var(--sp-18);
  border-top: 1px solid var(--hair);
  font-size: var(--t-lg); font-weight: var(--w-med);
  line-height: var(--lh-tight); letter-spacing: var(--ls-tight);
  color: var(--fg);
  scroll-margin-top: calc(var(--gap) + 84px);
}
.section-label::before {
  content: counter(chap, decimal-leading-zero);
  flex: none;
  font-family: var(--mono);
  font-size: var(--t-xs); font-weight: var(--w-med);
  letter-spacing: var(--ls-caps);
  color: var(--faint);
}
/* The first chapter needs no rule: there is nothing above it to divide. */
.doc-bio > .section-label:first-child { margin-top: 0; padding-top: 0; border-top: 0; }

/* ── Prose ──────────────────────────────────────────────────────────────
   Body copy runs in --muted and the page keeps --fg for the things that
   structure it. Twenty paragraphs of full white on black is a wall. */
.doc-bio p:not([class]) { margin: 0 0 var(--sp-14); color: var(--muted); }
.doc-bio p:not([class]):last-child { margin-bottom: 0; }
.doc-bio p a,
.doc-bio li a {
  color: var(--fg);
  text-decoration: underline;
  text-decoration-color: var(--edge);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: text-decoration-color var(--d-base) var(--ease);
}
.doc-bio p a:hover,
.doc-bio li a:hover { text-decoration-color: var(--fg); }
.bio ul.plain { margin: var(--sp-12) 0; padding-left: var(--sp-18); color: var(--muted); }
.bio ul.plain li { margin: var(--sp-4) 0; }

/* ── Figures ────────────────────────────────────────────────────────────
   Media is allowed out past the reading measure, but only to the right
   and only into space the window actually has: the rail holds the left
   edge, so growing that way would push the figure under it. At narrow
   widths the bleed is zero and nothing overflows. */
.doc-bio { --bleed: clamp(0px, calc((100vw - 1180px) / 2), 140px); }
.media, .site-frame { margin-right: calc(-1 * var(--bleed)); }

.media { margin-top: var(--sp-24); margin-bottom: var(--sp-24); }
.media img,
.media video {
  display: block; width: 100%; height: auto;
  border-radius: var(--r-12);
  border: 1px solid var(--line);
  background: var(--card);
  box-shadow: inset 0 1px 0 var(--lift), 0 1px 22px rgba(0, 0, 0, .45);
}
.media figcaption,
.cap {
  counter-increment: fig;
  display: flex; align-items: baseline; gap: var(--sp-8);
  margin: var(--sp-10) 0 0;
  font-family: var(--mono);
  font-size: var(--t-xs); font-weight: var(--w-reg);
  text-transform: uppercase; letter-spacing: var(--ls-caps);
  color: var(--faint);
}
.media figcaption::before,
.cap::before {
  content: "Fig " counter(fig, decimal-leading-zero);
  flex: none;
  color: var(--muted);
}
/* A narrow figure is held to the left edge of the measure rather than
   centred in it: centred, it floated free of the column it belongs to
   and every caption started in a different place. */
.media.narrow { max-width: 380px; margin-right: 0; }

/* ── Framed previews ────────────────────────────────────────────────── */
.site-frame {
  margin-top: var(--sp-24); margin-bottom: var(--sp-24);
  border-radius: var(--r-12);
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--card);
  box-shadow: inset 0 1px 0 var(--lift), 0 1px 22px rgba(0, 0, 0, .45);
}
.site-frame-bar {
  display: flex; align-items: center; gap: var(--sp-6);
  height: 30px; padding: 0 var(--sp-12);
  background: var(--panel-up);
  border-bottom: 1px solid var(--hair);
}
.site-frame-bar span { width: 7px; height: 7px; border-radius: var(--r-full); background: var(--e-hover); }
.site-frame-view { --h: 440px; position: relative; height: var(--h); overflow: hidden; }
.site-frame-scroll {
  display: block; width: 100%; height: auto;
  animation: siteScroll 30s var(--ease-io) infinite alternate;
}
@keyframes siteScroll {
  0%, 5% { transform: translateY(0); }
  95%, 100% { transform: translateY(calc(-100% + var(--h))); }
}
@media (prefers-reduced-motion: reduce) {
  .site-frame-view { overflow: auto; -webkit-overflow-scrolling: touch; }
  .site-frame-scroll { animation: none; }
}

/* ── Narrow ─────────────────────────────────────────────────────────────
   The rail cannot be a spine on a phone, so it becomes a scrolling strip
   under the bar, and the chapter marks turn off with it. */
@media (max-width: 900px) {
  .doc { display: block; padding: var(--sp-20) var(--sp-16) 20vh; }
  .doc-side {
    /* Flush under the bar, with nothing between them: any gap here is a
       slot that scrolling text shows through. */
    position: sticky; top: calc(var(--gap) + var(--bar-h, 44px)); z-index: calc(var(--z-bar) - 1);
    margin: 0 calc(-1 * var(--sp-16)) var(--sp-24);
    padding: var(--sp-8) var(--sp-16);
    background: color-mix(in srgb, var(--bg) 78%, transparent);
    -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px);
    border-bottom: 1px solid var(--hair);
  }
  .doc-toc {
    flex-flow: row nowrap; gap: var(--sp-2);
    padding-left: 0;
    overflow-x: auto; scrollbar-width: none; white-space: nowrap;
  }
  .doc-toc::-webkit-scrollbar { display: none; }
  .doc-toc::before { display: none; }
  .doc-toc a { flex: none; margin-left: 0; }
  .doc-toc a::before { display: none; }
  .bar { margin-left: var(--gap); margin-right: var(--gap); }
  .bar .sp, .bar #clock { display: none; }
  .site-frame-view { --h: 300px; }
  .section-label { margin-top: var(--sp-36); }
}
@media (max-width: 420px) {
  .bar .lbl { display: none; }
}
