:root {
  --paper: #F0F0F0;
  --ink: #0A0A0A;
  --rule: rgba(10, 10, 10, 0.14);
  --rule-soft: rgba(10, 10, 10, 0.08);
  /* small standalone labels only — never a name, a heading, or text sitting
     inside a coloured panel. 5.0:1 on the paper, so it clears AA. */
  --text-soft: #666666;
  --grid: 112px;
  --pad: 5vw;
  --font: "Stack Sans Text", sans-serif;
  /* secondary colour — 11.6:1 on the paper, 13.2:1 with white on top */
  --accent: #062D69;
  --ease: cubic-bezier(.16, 1, .3, 1);
}

* { box-sizing: border-box; }

html { -webkit-font-smoothing: antialiased; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-optical-sizing: auto;
  font-style: normal;
  font-size: 16px;
  line-height: 1.7;
}

.mono {
  font-family: var(--font);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-soft);
}

/* ---------- hero ---------- */

.hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* symmetric top/bottom so the centred block lands on the viewport's
     midpoint, not the midpoint of an off-centre content box */
  padding: 34px var(--pad);
}

/* ---------- intro: portrait beside the name and about ---------- */

.intro {
  display: flex;
  align-items: flex-start;
  gap: clamp(20px, 2.6vw, 40px);
  width: 100%;
  max-width: 740px;
  /* the hero centres this vertically; auto side margins centre it across */
  margin: 0 auto;
}

.portrait {
  flex-shrink: 0;
  /* 10% up on each stop from 112/13/172 */
  width: clamp(123px, 14.3vw, 189px);
  height: auto;
  display: block;
  border-radius: 16px;
}

.intro-text { min-width: 0; }

.tagline {
  margin: 0;
  font-size: clamp(17px, 1.5vw, 20px);
  font-weight: 700;
  letter-spacing: -0.005em;
  line-height: 1.3;
  color: var(--accent);
}

/* the short dash under the tagline */
.tagline-rule {
  display: block;
  width: 74px;
  height: 3px;
  border-radius: 2px;
  background: var(--accent);
  margin: 14px 0 22px;
}

.about-copy {
  margin: 0;
  font-size: clamp(14px, 1.15vw, 16.5px);
  line-height: 1.75;
}

/* ---- hero buttons ---- */

.hero-cta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 22px;
  margin-top: 26px;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* "Let's talk" is a <button> so it opens the dialog, and buttons carry a
     default `border: 2px outset` bevel — that was the shine around it */
  border: none;
  appearance: none;
  -webkit-appearance: none;
  background: none;
  cursor: pointer;
  font-family: var(--font);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.18s ease, color 0.18s ease, gap 0.18s ease;
}

.btn--solid {
  padding: 13px 26px;
  border-radius: 99px;
  background: var(--accent);
  color: #fff;
}

.btn--solid:hover { background: var(--ink); }

.btn--text { color: var(--text-soft); }

.btn--text:hover { color: var(--accent); gap: 12px; }

.btn svg {
  width: 15px;
  height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .btn, .btn--text:hover { transition: none; }
}

/* ---------- rotating header line ---------- */

/* pinned to the top and out of the flex flow — in flow it consumed height
   at the top of the column, which pushed the centred intro below the
   viewport's midpoint */
.quips {
  position: absolute;
  top: 34px;
  left: var(--pad);
  right: var(--pad);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
}

.quip-next {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 1px solid var(--ink);
  border-radius: 99px;
  background: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.18s ease;
}

.quip-next svg {
  width: 15px;
  height: 15px;
  fill: none;
  stroke: var(--ink);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke 0.18s ease;
}

.quip-next:hover { background: var(--accent); border-color: var(--accent); }

.quip-next:hover svg { stroke: #fff; }

.quip-next:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

.quip {
  margin: 0;
  font-size: clamp(13px, 1.05vw, 16px);
  line-height: 1.5;
  color: var(--text-soft);
  transition: opacity 0.26s ease;
}

.quip.is-out { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .quip { transition: none; }
}

/* ---------- document ---------- */

.doc {
  max-width: 1040px;
  margin: 0 auto;
  /* 24px, not the 120px this carried before. That figure was breathing room
     at the bottom of the page, when .doc was the last thing on it. The footer
     now follows, so the same padding became an inter-element gap and stacked
     with the last section's own 40px to put 160px of nothing above the footer
     rule. 24 leaves 64px there, which the footer's own 52px top padding then
     completes. Case pages are unaffected — .case--wide zeroes this. */
  padding: 0 var(--pad) 24px;
}

/* Sections size to their content. There used to be a min-height of 100vh
   here, on the idea that each section covers a screen — but paired with
   justify-content: flex-start it could never centre anything, so its only
   effect was trailing dead space in whichever section was short of a full
   screen. Projects is that section: 332px of content in an 860px box left
   448px of nothing under it. That was invisible while Projects sat last on
   the page and became a visible hole the moment Writing moved below it.

   Removing it costs nothing on the sections that were already taller than
   the viewport, and on a tall screen it stops them all leaving a gap. The
   rhythm between sections is now just padding x 2, the same 80px that
   Experience and Projects already sat at. */
section {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  padding: 40px 0;
}

.sec-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 34px;
}

/* the section heading itself — .mono is shared with the small right-hand
   labels and the row/role meta, so target only the first child here */
.sec-head > .mono:first-child {
  font-size: 30px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
  text-transform: none;
  color: var(--accent);
}

/* the line under a section heading */
.sec-note {
  max-width: 640px;
  margin: -22px 0 30px;
  font-size: 14.5px;
  line-height: 1.65;
  color: var(--text-soft);
}

.lede {
  max-width: 660px;
  font-size: 19px;
  line-height: 1.68;
  margin: 0 0 22px;
}

.lede.small { font-size: 16px; color: var(--text-soft); }

/* dated rows */

.row {
  display: grid;
  grid-template-columns: 74px 92px 1fr;
  gap: 22px;
  padding: 13px 0;
  border-bottom: 1px solid var(--rule-soft);
  align-items: baseline;
}

.row:first-of-type { border-top: 1px solid var(--rule); }

.row .when,
.row .kind {
  font-family: var(--font);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-soft);
}

.row .what { font-size: 15px; }

.row .what b { font-weight: 700; }

.row .what span { color: var(--text-soft); }

/* ---------- experience cards ---------- */

/* three equal full-width rows */
.work-grid {
  display: grid;
  gap: 14px;
}

.work-card {
  display: grid;
  grid-template-columns: 1fr 1fr;
  /* minmax(0,1fr) caps the row to the card — without it the panel padding
     plus the mark's intrinsic size pushes the row past the card height and
     the mark gets clipped by overflow:hidden */
  grid-template-rows: minmax(0, 1fr);
  gap: 18px;
  /* min-height, not height: Garden carries a description and stats and needs
     to grow; the other two settle at the minimum */
  min-height: 220px;
  /* the brand panel is an inset rounded box, not a full-bleed half, so the
     card carries the margin that separates it from its own edge */
  padding: 18px;
  border-radius: 22px;
  overflow: hidden;
  background: #FFFFFF;
  color: var(--ink);
  text-decoration: none;
  transition: transform 0.35s var(--ease), box-shadow 0.35s var(--ease);
  box-shadow:
    0 14px 32px rgba(10, 10, 10, 0.08),
    0 2px 6px rgba(10, 10, 10, 0.05);
}

/* alternating sides — Garden right, Horses Mouth left, Enginr right */
.work-card--flip .work-brand { grid-column: 2; grid-row: 1; }

.work-card--flip .work-copy { grid-column: 1; grid-row: 1; }

a.work-card:hover {
  transform: translateY(-3px);
  box-shadow:
    0 22px 44px rgba(10, 10, 10, 0.12),
    0 3px 9px rgba(10, 10, 10, 0.07);
}

a.work-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

/* ---- cursor-tracking badge, one per card ---- */

/* every card anchors its own badge; --dead additionally has no page to open */
.work-card { position: relative; }

.work-card--dead { cursor: default; }

/* Two elements on purpose. The outer one is moved by JS on every mousemove
   and must not carry a transition, or the badge lags behind the pointer.
   The inner one owns the fade and scale, which do want easing. */
.cursor-badge {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 3;
  pointer-events: none;
  will-change: transform;
  /* How far the badge rides above the pointer, measured from the pointer to
     the badge's top edge. Declared here rather than baked into the margin
     because the script reads it to keep the badge from being clipped by the
     card's overflow:hidden near the top edge. */
  --lift: 58px;
}

.cursor-badge-face {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  /* sits above the pointer, centred on it horizontally */
  margin: calc(-1 * var(--lift)) 0 0 -22px;
  font-size: 22px;
  line-height: 1;
  background: #FFFFFF;
  border: 1px solid var(--rule);
  border-radius: 10px;
  box-shadow: 0 6px 18px rgba(10, 10, 10, 0.14);
  opacity: 0;
  transform: scale(0.6);
  transform-origin: 50% 100%; /* grows upward out of the cursor */
  transition: opacity 0.2s ease, transform 0.28s var(--ease);
}

.work-card--dead:hover .cursor-badge-face { opacity: 1; transform: scale(1); }

/* ---- brand panel: a rounded box inset from the card edge ---- */

.work-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  border-radius: 14px;
  /* breathing room inside the box so the mark never touches its edge */
  padding: 30px 36px;
}

.work-brand img,
.work-brand video {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  object-fit: contain;
  display: block;
  /* flex items default to min-width/min-height:auto, which is the image's
     intrinsic size — that stops max-width:100% from shrinking it, so the
     400px poster overflowed its box at full size */
  min-width: 0;
  min-height: 0;
}

/* panel colours sampled from the logo files themselves, so each mark's own
   background blends into the panel instead of showing as a square */
.work-brand--garden { background: #F5A9D3; }

/* The supplied Horses Mouth JPEG had its purple gradient baked in, which
   showed as a square. assets/logos/horses-mouth.png is that file with the
   background keyed out to real transparency, so the panel is free to carry
   the brand gradient itself. The loader tries .png before .jpg, so the PNG
   wins automatically; the original .jpeg is left in place untouched. */
.work-brand--hm { background: linear-gradient(135deg, #6F3CBD, #7267C6); }

.work-brand--enginr { background: #000000; }

/* ---- copy ---- */

.work-role {
  margin: 0 0 6px;
  font-family: var(--font);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--accent);
}

/* "(sunsetted)" rides with the name but shouldn't compete with it */
.work-sunset {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--text-soft);
  white-space: nowrap;
}

.work-desc {
  margin: 10px 0 0;
  max-width: 42ch;
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-soft);
}

/* the phrases that carry the substance — lifted out of the soft grey to
   near-black so they read first */
.work-desc strong {
  font-weight: 600;
  color: var(--ink);
}

/* stats row: the figures carry the accent, the labels stay quiet */
/* Each stat stacks its figure over its label, as in the reference card. As
   one flex row the three ran wider than the copy column and wrapped 2+1 once
   the figures grew; columns give each an equal share and keep them on one
   line. */
.work-stats {
  display: grid;
  grid-template-columns: repeat(3, auto);
  justify-content: start;
  gap: 0 30px;
  /* fixed 35px instead of margin-top:auto — auto pushed the row to the card
     floor, leaving a 70px gulf under the description */
  margin: 35px 0 0;
  font-size: 11.5px;
  line-height: 1.35;
  color: var(--text-soft);
}

.work-stats span {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.work-stats b {
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: var(--accent);
  margin-bottom: 3px;
}

/* name and role sit top-left on every card */
.work-copy {
  position: relative;
  padding: 4px 14px 8px;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  min-width: 0;
}

.work-name {
  margin: 0 0 5px;
  font-size: 21px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
}

.work-meta {
  margin: 0;
  font-family: var(--font);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-soft);
}

@media (prefers-reduced-motion: reduce) {
  .work-card, a.work-card:hover { transition: none; transform: none; }
}

.contact a {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid rgba(6, 45, 105, 0.35);
}

.contact a:hover { border-bottom-color: var(--accent); }

/* ---------- writing tiles ---------- */

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 30px;
}

.filter {
  font-family: var(--font);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-soft);
  background: none;
  border: 1px solid var(--rule);
  border-radius: 99px;
  padding: 7px 15px;
  cursor: pointer;
  transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}

.filter:hover { color: var(--ink); border-color: var(--ink); }

.filter[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.filter:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* .tiles is just the mount now — it holds either a single grid of six
   (collapsed) or a horizontally sliding track of pages (expanded) */
.tiles-page {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 26px 22px;
  /* Inset by padding, NOT max-width. The page must stay a full 100% of the
     track, because the track slides by translateX(-100%) of its own width —
     a narrower page desyncs those steps and the next page bleeds in.
     4px each side leaves 904 for the grid: 3 x 286 + 2 x 22 — about 10%
     wider than the previous 260. */
  padding: 0 4px;
}

/* the viewport clips; the track slides. Pages are full-width so the section
   never grows taller than one page, however many posts there are. */
.tiles-viewport {
  /* must clip cleanly — a negative margin here widens the clip box and lets
     the neighbouring pages bleed in at the edges */
  overflow: hidden;
  /* small vertical padding so the hover lift isn't clipped */
  padding: 4px 0 8px;
}

.tiles-track {
  display: flex;
  align-items: start;
  transition: transform 0.55s var(--ease);
}

.tiles-page { flex: 0 0 100%; }

/* ---- carousel controls ---- */

.car-nav {
  display: flex;
  align-items: center;
  gap: 14px;
}

.car-btn {
  width: 38px;
  height: 38px;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 99px;
  background: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.18s ease, border-color 0.18s ease, opacity 0.18s ease;
}

.car-btn svg {
  width: 15px;
  height: 15px;
  fill: none;
  stroke: var(--ink);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke 0.18s ease;
}

.car-btn:hover:not(:disabled) { background: var(--accent); border-color: var(--accent); }

.car-btn:hover:not(:disabled) svg { stroke: #fff; }

.car-btn:disabled { opacity: 0.3; cursor: default; }

.car-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.car-count {
  font-family: var(--font);
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--text-soft);
  min-width: 46px;
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .tiles-track { transition: none; }
}

/* soft floating card: image panel on top with the title and CTA laid over
   it, an info block beneath */
.tile {
  position: relative;
  display: flex;
  flex-direction: column;
  color: var(--ink);
  text-decoration: none;
  background: #FFFFFF;
  border-radius: 26px;
  padding: 10px 10px 4px;
  /* no drop shadow — the white card reads on the grey paper on its own */
  transition: transform 0.35s var(--ease);
}

a.tile:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; }

.tile:hover { transform: translateY(-3px); }

/* 5:4 rather than square. A tile that is both wider and shorter cannot hold
   a square photo, so the box goes landscape and crops ~20% of the source.
   object-position:top below takes all of that off the BOTTOM, which the
   title scrim already covers, keeping the headline at the top intact. */
.tile-media {
  position: relative;
  aspect-ratio: 5 / 4;
  border-radius: 18px;
  overflow: hidden;
  background: rgba(10, 10, 10, 0.05);
}

.tile-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* crop from the bottom only — the headline lives at the top */
  object-position: top;
}

.tile-media.is-empty {
  display: flex;
  align-items: center;
  justify-content: center;
}

.tile-fallback {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-soft);
}

/* These covers are light, text-heavy infographics, not the dark photo the
   reference card uses — white type needs a much heavier scrim here or it
   disappears into a white chart. */
.tile-media-body {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 46px 14px 14px;
  background: linear-gradient(
    to top,
    rgba(0, 0, 0, 0.94) 0%,
    rgba(0, 0, 0, 0.86) 26%,
    rgba(0, 0, 0, 0.6) 55%,
    rgba(0, 0, 0, 0.2) 80%,
    rgba(0, 0, 0, 0) 100%
  );
}

/* three lines across the full width — the space the CTA pill used to hold */
.tile-title {
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  color: #fff;
  font-size: 14.5px;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.01em;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}

/* ---- info block ---- */

.tile-info {
  padding: 12px 8px 10px;
}

/* category on the left, "Read" at the right edge of the tile */
.tile-catrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.tile-read {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  white-space: nowrap;
  /* grows from its right edge so it never pushes the category around */
  transform-origin: right center;
  transition: transform 0.2s var(--ease), gap 0.2s var(--ease);
}

.tile-read svg {
  width: 11px;
  height: 11px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

a.tile:hover .tile-read {
  transform: scale(1.14);
  gap: 8px;
}

.tile-cat {
  display: block;
  font-size: 13.5px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--accent);
}

.tile-blurb {
  margin: 3px 0 0;
  font-size: 11px;
  line-height: 1.5;
  color: var(--text-soft);
}

.tile-hr {
  height: 1px;
  margin: 12px 0 11px;
  background: var(--rule-soft);
}

/* below the line: date hard left, type hard right */
.tile-stats {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
}

.tile-stat b {
  display: block;
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

/* the type sits on the right and reads right-aligned with it. it is far
   longer than a date, so it wraps rather than pushing the row wider. */
.tile-stat--type {
  min-width: 0;
  text-align: right;
}

.tile-stat--type b {
  white-space: normal;
  line-height: 1.25;
}

.tile-stat span {
  display: block;
  margin-top: 2px;
  font-size: 9.5px;
  letter-spacing: 0.04em;
  color: var(--text-soft);
}

/* a staged post with no url — nothing to click */
.tile.is-pending { box-shadow: none; outline: 1px dashed var(--rule); }

@media (prefers-reduced-motion: reduce) {
  .tile, .tile:hover { transition: none; transform: none; }
  .tile-read, a.tile:hover .tile-read { transition: none; transform: none; }
}

.tiles-empty {
  margin: 0;
  font-size: 15px;
  color: var(--text-soft);
}

/* ---------- read more ---------- */

.more {
  display: flex;
  justify-content: center;
  margin-top: 15px;
}

/* no button rendered when a view holds 3 or fewer — don't leave the gap */
.more:empty { display: none; }

/* ---------- projects ---------- */

/* Compact cards, not full-width rows. Three across at 1040px puts each at
   roughly 337px — wide enough for a title and one line, short enough that
   the card reads as an item rather than a banner. */
.proj-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  align-items: stretch;
}

.proj-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  padding: 18px 18px 15px;
  background: #FFFFFF;
  border: none;
  border-radius: 18px;
  text-align: left;
  font-family: var(--font);
  color: var(--ink);
  cursor: pointer;
  transition: transform 0.3s var(--ease);
}

.proj-card:hover { transform: translateY(-3px); }
.proj-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.proj-kind {
  font-size: 10.5px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-soft);
}

.proj-name {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.25;
}

.proj-blurb {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-soft);
}

/* pushed to the bottom so every card's affordance sits on the same line
   regardless of how long its blurb runs */
.proj-open {
  margin-top: auto;
  padding-top: 12px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--accent);
}

.proj-open svg {
  width: 11px;
  height: 11px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  transition: transform 0.25s var(--ease);
}

.proj-card:hover .proj-open svg { transform: translate(2px, -2px); }

/* A slot with no write-up yet. Dashed and unclickable, same language as the
   pending writing tiles — visibly a placeholder, not a broken card. */
.proj-card.is-empty {
  background: none;
  border: 1px dashed var(--rule);
  cursor: default;
  min-height: 118px;
  justify-content: center;
}

.proj-card.is-empty:hover { transform: none; }
.proj-card.is-empty .proj-name { font-weight: 500; color: var(--text-soft); font-size: 14px; }

/* ---- the project window (reuses .macwin) ---- */

/* the sidebar entries are section switches here, not links */
button.macwin-sideitem {
  width: 100%;
  font-family: var(--font);
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
}

button.macwin-sideitem:hover { background: rgba(10, 10, 10, 0.06); }
button.macwin-sideitem:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Wider than the contact window — this one carries screenshots and several
   paragraphs, not four links. Qualified with the element name on purpose:
   .macwin is defined further down this file, so a bare .macwin--proj would
   tie on specificity and lose on source order. */
dialog.macwin--proj { width: min(760px, calc(100vw - 40px)); }

.macwin-body--proj { min-height: 250px; }

/* the write-up can run long, so the main pane scrolls while the title bar
   and the section list stay put */
.macwin-body--proj .macwin-main {
  max-height: min(66vh, 560px);
  overflow-y: auto;
}

.macwin-shots { display: flex; flex-direction: column; gap: 16px; margin-top: 18px; }

/* a shot that leads the write-up sits flush under the title */
.macwin-shots:first-child { margin-top: 4px; }

.macwin-shot { margin: 0; }

/* shrink-wrapped and centred, so the frame hugs a portrait screenshot
   instead of boxing it in a full-width letterbox */
.macwin-shot a {
  display: block;
  width: fit-content;
  max-width: 100%;
  margin: 0 auto;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--rule-soft);
  line-height: 0;
}

/* Natural aspect within bounds rather than a fixed box. These screenshots
   run from 4:3 landscape to portrait, and forcing them all into one
   letterbox shrank the portrait ones until the text was unreadable —
   which defeats the point of showing them. Full size is one click away. */
.macwin-shot img {
  display: block;
  max-width: 100%;
  max-height: 560px;
  width: auto;
  height: auto;
}

.macwin-shot a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.macwin-shot figcaption {
  margin-top: 8px;
  text-align: left;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-soft);
}

.macwin-para { margin: 0 0 12px; font-size: 14px; line-height: 1.65; color: var(--text-soft); }
.macwin-para:last-child { margin-bottom: 0; }
.macwin-para strong { color: var(--ink); font-weight: 600; }

/* no sidebar to sit beside when a project has a single section */
.macwin-body--proj.is-solo { grid-template-columns: 1fr; }

/* the author display:flex above beats the hidden attribute's UA
   display:none, so the sidebar needs telling twice */
.macwin-side[hidden] { display: none; }

.macwin-sub {
  margin: 18px 0 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
}

.macwin-sub:first-child { margin-top: 0; }

.macwin-list {
  margin: 0 0 14px;
  padding-left: 18px;
  list-style: disc;
}

.macwin-list li {
  margin-bottom: 6px;
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-soft);
}

.macwin-list li::marker { color: var(--rule); }
.macwin-list li strong { color: var(--ink); font-weight: 600; }
.macwin-list li:last-child { margin-bottom: 0; }

/* who the tool was built for, and what the ownership was */
.macwin-org {
  display: flex;
  align-items: center;
  gap: 11px;
  margin-top: 22px;
  padding-top: 16px;
  border-top: 1px solid var(--rule-soft);
}

.macwin-orglogo {
  width: 46px;
  height: 26px;
  object-fit: cover;
  border-radius: 5px;
  flex: none;
}

.macwin-orgtext {
  display: flex;
  flex-direction: column;
  gap: 1px;
  font-size: 12.5px;
  color: var(--text-soft);
  min-width: 0;
}

.macwin-orgtext b { color: var(--ink); font-weight: 600; }

.macwin-role { font-size: 12px; line-height: 1.5; }

.macwin-chips { display: flex; flex-wrap: wrap; gap: 6px; }

.macwin-chip {
  font-size: 10.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-soft);
  background: rgba(10, 10, 10, 0.05);
  border-radius: 99px;
  padding: 5px 11px;
}

/* ---------- mobile ---------- */

@media (max-width: 900px) {
  :root { --pad: 24px; }

  .proj-grid { grid-template-columns: 1fr 1fr; }

  .hero {
    min-height: 0;
    align-items: stretch;
    padding: 0 0 48px;
  }

  .intro {
    margin: 0 auto;
    padding: 30px var(--pad) 0;
    gap: 18px;
  }

  /* back into the flow on mobile — the hero is not full-height here, so
     there is nothing to centre against and pinning would overlap the intro */
  .quips {
    position: static;
    padding: 26px var(--pad) 0;
    align-items: flex-start;
    gap: 12px;
    margin-bottom: 8px;
  }

  .quip-next { width: 32px; height: 32px; }

  .row { grid-template-columns: 62px 1fr; gap: 12px; }
  .row .kind { display: none; }
  .lede { font-size: 17px; }

  .tiles-page { grid-template-columns: 1fr 1fr; gap: 26px 16px; padding: 0 8px; }
  .tile-title { font-size: 15px; }
  .tile-blurb { font-size: 13px; }

  /* one column — the lead card stops spanning rows */
  .work-grid { grid-template-columns: 1fr; gap: 14px; }
  .work-card--lead { grid-row: auto; }
  .work-card { padding: 22px 20px 18px; border-radius: 20px; }
  .work-card--lead .work-name { font-size: 24px; }
}

@media (max-width: 540px) {
  .tiles-page { grid-template-columns: 1fr; gap: 30px; padding: 0; }
  .filters { gap: 6px; }
  .filter { padding: 6px 12px; }
  .proj-grid { grid-template-columns: 1fr; }
  .macwin-body--proj { min-height: 0; }
}

/* ---------- work subpages (work/*.html) ---------- */

.case { padding-top: 60px; }

.case-back {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-soft);
  text-decoration: none;
  transition: color 0.18s ease;
}

.case-back svg {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.case-back:hover { color: var(--accent); }

/* The back link and the jump-to-the-other-case link share one row, so the
   pair reads as a navigation strip across the top rather than two loose
   links. space-between rather than a float, because the right-hand label is a
   different length on each of the two pages and neither should be able to
   shift the other. The next link is the same component with the arrow on the
   trailing side — that is markup order, so it needs no rule of its own. */
.case-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.case-head { padding: 46px 0 34px; }

.case-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 120px;
  height: 120px;
  border-radius: 18px;
  padding: 22px;
  margin-bottom: 26px;
}

.case-mark img {
  max-width: 100%;
  max-height: 100%;
  min-width: 0;
  min-height: 0;
  width: auto;
  object-fit: contain;
  display: block;
}

.case-name {
  margin: 0 0 6px;
  font-size: clamp(30px, 4vw, 46px);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.05;
}

.case-meta {
  margin: 0;
  font-family: var(--font);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-soft);
}

.case-body { padding-bottom: 120px; }

/* subpages are single-column documents, not full-viewport sections */
.case section { min-height: 0; display: block; padding: 0; }

/* ---------- contact window (Finder-style dialog) ---------- */

.macwin {
  width: min(560px, calc(100vw - 40px));
  padding: 0;
  border: none;
  border-radius: 14px;
  overflow: hidden;
  background: #F4F4F5;
  color: var(--ink);
  box-shadow:
    0 40px 80px rgba(10, 10, 10, 0.28),
    0 2px 10px rgba(10, 10, 10, 0.10);
}

.macwin::backdrop {
  background: rgba(10, 10, 10, 0.34);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}

/* ---- title bar ---- */

.macwin-bar {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  height: 40px;
  padding: 0 14px;
  background: #EBEBEC;
  border-bottom: 1px solid rgba(10, 10, 10, 0.08);
}

.macwin-dots { display: flex; gap: 8px; }

.macwin-dot {
  width: 12px;
  height: 12px;
  padding: 0;
  border: none;
  border-radius: 99px;
  display: block;
}

.macwin-dot--red { background: #FF5F57; cursor: pointer; }
.macwin-dot--amber { background: #FEBC2E; }
.macwin-dot--green { background: #28C840; }

.macwin-dot--red:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.macwin-bartitle {
  position: absolute;
  left: 0;
  right: 0;
  text-align: center;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-soft);
  pointer-events: none;
}

/* ---- body ---- */

.macwin-body { display: grid; grid-template-columns: 150px 1fr; }

.macwin-side {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 14px 10px;
  background: #EFEFF0;
  border-right: 1px solid rgba(10, 10, 10, 0.07);
}

.macwin-sideitem {
  padding: 6px 10px;
  border-radius: 6px;
  font-size: 13px;
  color: var(--ink);
  text-decoration: none;
}

a.macwin-sideitem:hover { background: rgba(10, 10, 10, 0.06); }

.macwin-sideitem.is-active { color: var(--accent); font-weight: 600; }

.macwin-main { padding: 24px 26px 28px; }

.macwin-eyebrow {
  margin: 0 0 2px;
  font-size: 12px;
  color: var(--text-soft);
}

.macwin-h {
  margin: 0 0 8px;
  font-size: 27px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
}

.macwin-note {
  margin: 0 0 20px;
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-soft);
}

.macwin-links { display: flex; flex-direction: column; gap: 8px; }

.macwin-link {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border-radius: 10px;
  background: #FFFFFF;
  text-decoration: none;
  color: var(--ink);
  transition: background 0.18s ease;
}

.macwin-link:hover { background: #F7F7F8; }

.macwin-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.macwin-icon {
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  border-radius: 8px;
  background: var(--accent);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
}

.macwin-icon svg { width: 19px; height: 19px; display: block; }

/* LinkedIn's own blue, since this is their mark rather than our icon */
.macwin-icon--li { background: #0A66C2; }

.macwin-icon--li svg { width: 21px; height: 21px; fill: #fff; stroke: none; }

.macwin-link b { font-size: 14px; font-weight: 600; }

@media (max-width: 560px) {
  .macwin-body { grid-template-columns: 1fr; }
  .macwin-side { flex-direction: row; flex-wrap: wrap; border-right: none;
                 border-bottom: 1px solid rgba(10, 10, 10, 0.07); }
  /* width:100% would force one section switch per line once the sidebar
     turns into a horizontal strip */
  button.macwin-sideitem { width: auto; }
}

/* Compact header, for case pages whose content is a panel rather than a
   scroll. The default .case-head is sized for a long-form document where a
   120px mark and a 46px title are a welcome pause before the reading starts.
   Here they push the pillar rail 670px down an 860px laptop viewport, which
   defeats the point of a layout chosen so the reader never scrolls. */

.case--tight { padding-top: 24px; }

.case--tight .case-head { padding: 18px 0 14px; }

.case--tight .case-mark {
  width: 54px;
  height: 54px;
  border-radius: 11px;
  padding: 9px;
  margin-bottom: 12px;
}

.case--tight .case-name { font-size: clamp(26px, 3vw, 34px); }

/* wider measure and a step down in size, so the standfirst is two lines
   rather than four — 60px of vertical space back */
.case--tight .lede {
  max-width: 780px;
  font-size: 17px;
  line-height: 1.6;
  margin-bottom: 26px;
}

.case--tight .case-body { padding-bottom: 80px; }

@media (max-width: 860px) {
  .case--tight { padding-top: 24px; }
  .case--tight .case-head--inline { padding: 16px 0 40px; }
  .case--tight .case-mark { width: 54px; height: 54px; margin-bottom: 12px; }
}

/* ---------- case page: wide pillar layout ---------- */

/* The pre-sketch design — label gutter, big pane headline, stat band,
   numbered figures — run at full width with the figures beside the text
   rather than under it. Two changes from the original: the container stops
   throwing away the sides, and the pane splits into two columns. Together
   they take the page from ~1536px to roughly one screen. */

/* .doc caps at 1040px with 5vw padding, so on a 1440 screen ~400px of side
   space sat empty while the pane below scrolled. Cap removed, 32px gutter. */
.case--wide { max-width: none; padding-left: 100px; padding-right: 100px; padding-bottom: 0; }
.case--wide .case-body { padding-bottom: 20px; }

/* Header on one line: name first, mark to its right. Stacked, the mark cost
   ~80px of vertical before the reader reached anything. */
/* .case--tight .case-head is two classes, so a bare .case-head--inline
   loses the cascade — this has to be scoped the same way to win. */
.case--tight .case-head--inline { padding: 14px 0 55px; }

.case-head--inline .case-id { display: flex; align-items: center; gap: 15px; }

.case-head--inline .case-name { margin: 0; font-size: clamp(26px, 3vw, 36px); }

.case-head--inline .case-mark {
  width: 44px;
  height: 44px;
  border-radius: 10px;
  padding: 8px;
  margin: 0;
  flex: 0 0 auto;
}

.case-head--inline .case-meta { margin: 7px 0 0; }

/* the standfirst is header furniture, so it can take a wider measure than a
   reading paragraph would */
.case-head--inline .lede {
  margin: 13px 0 0;
  max-width: 118ch;
  font-size: 16px;
  line-height: 1.55;
}

/* the hard break after "asset" is a wide-screen nicety — under 1100px the
   sentence wraps on its own and forcing it again leaves a ragged short line */
.case-head--inline .lede br { display: none; }

@media (min-width: 1100px) {
  .case-head--inline .lede br { display: inline; }
}

.pillars { display: grid; grid-template-columns: 218px minmax(0, 1fr); }

.pillar-rail {
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--rule-soft);
  padding: 0;
}

.pillar-tab {
  display: flex;
  flex-direction: column;
  gap: 3px;
  width: 100%;
  padding: 13px 20px 13px 17px;
  border: none;
  border-left: 2px solid transparent;
  background: none;
  font-family: var(--font);
  text-align: left;
  cursor: pointer;
  transition: background 0.16s ease, border-color 0.16s ease;
}

.pillar-tab + .pillar-tab { border-top: 1px solid var(--rule-soft); }
.pillar-tab:hover { background: rgba(10, 10, 10, 0.03); }
.pillar-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* white, not a tint — on #F0F0F0 paper white reads as lifted */
.pillar-tab.is-active { border-left-color: var(--accent); background: #FFFFFF; }

.pillar-tab-n {
  font-size: 21px;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
  color: rgba(6, 45, 105, 0.26);
  transition: color 0.16s ease;
}

.pillar-tab.is-active .pillar-tab-n { color: var(--accent); }

.pillar-tab-t {
  font-size: 14.5px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.pillar-tab.is-active .pillar-tab-t { color: var(--accent); }
.pillar-tab-m { font-size: 11.5px; line-height: 1.45; color: var(--text-soft); }

/* 13px matches the first rail item's own padding, so the first line of copy
   and the top of the first tile sit level with the 01 beside them. */
.pillar-panel { padding: 13px 0 0 32px; }
.pillar-pane[hidden] { display: none; }

/* Copy beside the media, which is what makes the pane only as tall as its
   taller column. Two text columns were tried and pushed the tiles below the
   fold of the pane; this is the arrangement that holds. */
.pane-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 400px;
  gap: 28px;
}

.pane-copy { display: flex; flex-direction: column; gap: 10px; }

/* Full column now that the label gutter is gone — ~124px more per line, so
   every paragraph loses a line. The measure caps at 84ch so it stays readable
   on a very wide screen. */
.pane-text { margin: 0; max-width: 84ch; font-size: 16px; line-height: 1.55; }
.pane-text strong { font-weight: 600; }

/* the .work-stats idiom from the landing card — accent numeral over a soft
   label — banded in hairlines */
.pane-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 16px 28px;
  margin: 4px 0 0;
  padding: 0;
}

.pane-stat { display: flex; flex-direction: column; min-width: 0; }

.pane-stat b {
  font-size: clamp(24px, 2.6vw, 30px);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.05;
  color: var(--accent);
  margin-bottom: 4px;
}

.pane-stat.is-empty b { color: rgba(6, 45, 105, 0.28); }
.pane-stat span { font-size: 11.5px; line-height: 1.4; color: var(--text-soft); }

/* align-items: start so a short tile sits at the top of its row instead of
   stretching. Tiles no longer share one height — each is shaped like the
   media inside it — so rows are ragged by design. */
.pane-figs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 15px; align-content: start; align-items: start; }

.pane-fig { margin: 0; min-width: 0; }

/* Direct child only. As `.pane-fig a` this also matched links inside the
   figcaption, rendering a "See the post" link as a 109px grey media box —
   and it outranked .pane-link, so the link cards would not flex either. The
   media anchor is always a direct child; caption links never are. */

/* Tile shape. Showing all of an image and leaving no dead space in its box
   are the same requirement only when the box is the shape of the image — so
   each tile takes the shape of what is inside it, and there are four shapes
   rather than one. The media here runs from a 5.1:1 SERP strip to a 0.56:1
   phone reel; one shape could not serve both without either cropping most of
   the strip away or standing the reel in a field of background.

   aspect-ratio is safe now. The earlier attempt at it broke because the
   figures sat in a fluid column, so a wider window made the page taller. The
   column is a fixed 372px, so cell width — and therefore height — no longer
   moves with the viewport. */
.pane-fig > a,
.pane-fig .pane-well {
  display: block;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--rule-soft);
  border-radius: 10px;
  overflow: hidden;
  background: rgba(10, 10, 10, 0.022);
}

/* Six shapes, each within a few percent of the media assigned to it. The
   comment on each is the range of source ratios it covers; the widest gap
   between any file and its box is 6%, so what looks like padding is padding. */
.pane-fig--sq   > a { aspect-ratio: 1 / 1; }    /* 1.00–1.01 */
.pane-fig--box  > a { aspect-ratio: 4 / 3; }    /* 1.30–1.42 */
.pane-fig--land > a { aspect-ratio: 3 / 2; }    /* 1.48      */
.pane-fig--por  > a { aspect-ratio: 7 / 8; }    /* 0.87      */
.pane-fig--tall > a { aspect-ratio: 9 / 16; }   /* 0.56 phone */

/* Wider than the tile is, so they take the whole figures column rather than
   half of it — 400px instead of 179px, which is what makes a SERP legible. */
.pane-fig--wide,
.pane-fig--strip,
.pane-fig--slide { grid-column: 1 / -1; }
.pane-fig--wide  > a { aspect-ratio: 5 / 2; }   /* 2.47 */
.pane-fig--strip > a { aspect-ratio: 5 / 1; }   /* 5.09 */
/* 16:9 at nearly full column width. Deck slides are 1.79–1.82 like the
   YouTube thumbnails, but a slide is a page of text — at half width it is a
   swatch, at this size the headings and the structure read. Capped at 380
   rather than the full 400 because two of them plus captions at 400 put the
   pane 10px past the fold, and 20px of width is not worth a scrollbar. */
.pane-fig--slide > a { aspect-ratio: 16 / 9; max-width: 380px; }

/* contain, not cover. Cover filled the tile by cropping, which on a 5:1 SERP
   strip meant showing a slice of its middle. Because each box is already the
   shape of its media, contain crops nothing and mattes almost nothing. */
.pane-fig img { display: block; width: 100%; height: 100%; object-fit: contain; }
.pane-fig a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.pane-well { display: flex; align-items: center; justify-content: center; padding: 10px; }

/* Video shares the image cell's box. Native controls would eat a third of a
   110px-tall cell, so the whole cell is a button: click to play, click to
   pause. It is a <button> rather than a div so it works from the keyboard. */
.pane-vid {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  padding: 0;
  border: 1px solid var(--rule-soft);
  border-radius: 10px;
  overflow: hidden;
  background: var(--ink);
  cursor: pointer;
}

/* The shape classes drive the video box too, so a clip and a screenshot in
   the same grid follow the same rule. Specificity (0,2,0) beats .pane-vid. */
.pane-fig--sq   .pane-vid { aspect-ratio: 1 / 1; }
.pane-fig--box  .pane-vid { aspect-ratio: 4 / 3; }
.pane-fig--land .pane-vid { aspect-ratio: 3 / 2; }
.pane-fig--tall .pane-vid { aspect-ratio: 9 / 16; }

/* contain for the same reason as the images: every clip now sits in a box of
   its own ratio, so the whole frame shows and there is nothing to letterbox. */
.pane-vid video { display: block; width: 100%; height: 100%; object-fit: contain; }

.pane-vid:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* play cue — a filled triangle, hidden while the clip runs */
.pane-vid-cue {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 30px;
  height: 30px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.88);
  transition: opacity 0.18s ease;
  pointer-events: none;
}

.pane-vid-cue::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 52%;
  transform: translate(-50%, -50%);
  border-left: 9px solid var(--ink);
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
}

.pane-vid.is-playing .pane-vid-cue { opacity: 0; }

.pane-well span {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(10, 10, 10, 0.32);
  text-align: center;
}

.pane-fig figcaption { margin-top: 7px; font-size: 11.5px; line-height: 1.4; color: var(--text-soft); }

@media (max-width: 1180px) {
  /* below this the figures column can no longer hold two cells beside a
     reading column — stack it and accept the scroll */
  .pane-grid { grid-template-columns: minmax(0, 1fr); gap: 24px; }
  .pane-figs { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (max-width: 860px) {
  .case--wide { padding-left: 22px; padding-right: 22px; }
  .case-head--inline .case-name { font-size: clamp(24px, 6vw, 28px); }
  .pillars { grid-template-columns: 1fr; }
  .pillar-rail { flex-direction: row; border-right: none; border-bottom: 1px solid var(--rule-soft); padding: 0; }
  .pillar-tab {
    flex: 1 1 0;
    min-width: 0;
    border-left: none;
    border-bottom: 2px solid transparent;
    padding: 10px 11px;
  }
  .pillar-tab + .pillar-tab { border-top: none; border-left: 1px solid var(--rule-soft); }
  .pillar-tab.is-active { border-left-color: transparent; border-bottom-color: var(--accent); }
  .pillar-tab-t { font-size: 12.5px; }
  .pillar-tab-m { display: none; }
  .pillar-panel { padding: 22px 0 0; }
  .pane-figs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* The figures column had ~260px of vertical slack while the text column set
   the pane height, so the stat band moved over here. Numbers now sit under
   the evidence they came from, and the left column lost 86px. */
.pane-side { display: flex; flex-direction: column; }

.pane-side .pane-stats { margin-top: 16px; }

/* auto-fit, not a hard 3. With two stats a fixed three-column grid left them
   99px wide with an empty third column; auto-fit gives each ~160px. */
.pane-side .pane-stats {
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 14px 18px;
  margin-top: 14px;
  padding-top: 13px;
}

.pane-side .pane-stat b { font-size: clamp(21px, 2.1vw, 26px); }
.pane-side .pane-stat span { font-size: 11px; }

@media (max-width: 1180px) {
}

/* Between 1180 and 1360 the text column narrows enough that every paragraph
   gains a line, and the pane outgrows the viewport. The figures column has
   vertical slack to spare, so give the reading column more of the width and
   trim the label gutter rather than let the page start scrolling. */
/* Bounded at the bottom on purpose. This block sits after the 1180px and
   860px queries, so an unbounded max-width:1360px would win on a phone and
   put the pane back into two columns at 395px wide. */
/* Fixed px, not 1.5fr/1fr. A fractional media column is fluid, and the tiles
   are sized by aspect-ratio now — so on a 1280px screen the fraction resolved
   to 334px and the tiles came out smaller here than on a wide one. A fixed
   column keeps tile size the same across the band; only the text reflows. */
@media (min-width: 1181px) and (max-width: 1360px) {
  .pane-grid { grid-template-columns: minmax(0, 1fr) 356px; gap: 26px; }
  .pane-side .pane-stats { gap: 12px 14px; }
}

/* The two Reddit SERP strips are 5.1:1 and 2.5:1. They used to be cropped
   into a half-width tile and anchored left to save the headline; they now get
   --strip and --wide instead, so the whole strip shows at full column width
   and there is nothing left to anchor. */

/* A pane with no figures — the reflective closer on the Horse's Mouth page.
   Without this the copy would sit in a 1fr column beside an empty 333px gap.
   Measure capped so a full-width pane does not run to 120 characters. */
.pane-grid--solo { grid-template-columns: minmax(0, 1fr); }
.pane-grid--solo .pane-text { max-width: 76ch; }

/* A placeholder label longer than its box used to spill past the rounded
   corner. Clamped, so an over-long well label truncates instead of leaking. */
.pane-well span {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Two-by-two, at a capped tile width, for panes whose media is all
   phone-shaped. Four 9:16 reels at the full 193px column is 780px of height
   and puts the pane 400px past the fold; capping the tile at 132px keeps the
   2x2 arrangement and fits, without cropping a single frame. The column is
   wider than the two tiles need, so the block sits left and the remainder is
   empty — the alternative was trimming the reels, and the overlay text is the
   content of a reel. */
/* Reels at the full column width — 193px a side rather than 132 — at the
   height they already had. That combination is only possible by cropping:
   a 0.56 frame in a 0.92 box loses 39% of its height, top and bottom evenly.
   It is the one place on either page where a tile crops its media, and it is
   deliberate. Width was the requirement and height was the constraint, and
   nothing else in the pane had space left to give: the stat band sits back
   under the tiles, so the figures grid gets 482px and no more. */
.pane-figs--reels .pane-fig--tall .pane-vid { aspect-ratio: 11 / 12; }
.pane-figs--reels .pane-vid video { object-fit: cover; object-position: center; }

/* One line, not two. The second caption line cost 32px across two rows, which
   at this width is 13px of tile height — the number and the handle fit on one
   line once the tile is 193px wide, so it buys back what it used to spend. */
.pane-figs--reels figcaption { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* A media cell that is a link rather than an image. Both instances were
   removed — a tile whose only content is a word and an arrow reads as an
   empty tile next to real screenshots, so those two URLs moved into the body
   copy instead. Kept because it is the right component for a link-only proof
   and the Horse's Mouth evidence is mostly link-only. */
.pane-fig a.pane-link {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: space-between;
  gap: 6px;
  aspect-ratio: 3 / 2;
  padding: 12px 13px;
  border: 1px solid var(--rule);
  border-radius: 10px;
  background: rgba(10, 10, 10, 0.022);
  text-decoration: none;
  transition: border-color 0.16s ease, background 0.16s ease;
}

.pane-fig a.pane-link:hover { border-color: var(--accent); background: #FFFFFF; }
.pane-fig a.pane-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.pane-link-n {
  font-size: clamp(17px, 1.9vw, 21px);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.05;
  color: var(--accent);
}

.pane-link-t { font-size: 11.5px; line-height: 1.4; color: var(--text-soft); }

.pane-link-go {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
}

/* A solo pane still has a stat band, and .pane-side becomes a second row
   rather than a column. Left uncapped the two stats would each take half of
   a 1000px row; this keeps them at the width they have everywhere else. */
.pane-grid--solo .pane-side { margin-top: 4px; }
.pane-grid--solo .pane-stats { max-width: 420px; margin-top: 0; }

/* A caption that carries a verification link. The Horse's Mouth reels are
   local files, so the live Instagram post is what proves the impression count
   — it belongs in the caption, not as a separate tile. */
.pane-fig figcaption a {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid rgba(6, 45, 105, 0.3);
}

.pane-fig figcaption a:hover { border-bottom-color: var(--accent); }

/* ---------- footer ---------- */

/* Same measure and gutters as .doc, so the footer's left edge lines up with
   every section heading above it instead of being a full-bleed band. */
.foot {
  max-width: 1040px;
  margin: 0 auto;
  padding: 52px var(--pad) 60px;
  border-top: 1px solid var(--rule-soft);
}

/* auto-fit rather than a hard three columns: the facts sit in a row on a wide
   screen and stack themselves once a column would be too narrow to hold its
   value, without a breakpoint deciding that for them. */
.foot-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 26px 40px;
  margin-bottom: 56px;
}

.foot-fact { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.foot-fact-k { font-size: 13px; font-weight: 700; color: var(--ink); }
.foot-fact-v { font-size: 14px; line-height: 1.5; color: var(--text-soft); }

.foot-main {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 40px;
  flex-wrap: wrap;
}

.foot-name {
  margin: 0 0 8px;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.foot-tag {
  margin: 0 0 22px;
  max-width: 34ch;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-soft);
}

/* Right-aligned, so the arrows form a single vertical line rather than
   ragging off the ends of four labels of different lengths. */
.foot-links {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
  text-align: right;
}

.foot-a {
  font-size: 14px;
  color: var(--text-soft);
  text-decoration: none;
  transition: color 0.18s ease;
}

.foot-a:hover { color: var(--accent); }
.foot-a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.foot-legal { margin: 52px 0 0; font-size: 12px; color: var(--text-soft); }

/* Below this the two-column main band has no room for a right-aligned link
   column — stack it and flip the links back to reading left. */
@media (max-width: 700px) {
  .foot { padding-top: 42px; }
  .foot-facts { margin-bottom: 42px; }
  .foot-main { flex-direction: column; }
  .foot-links { align-items: flex-start; text-align: left; }
  .foot-legal { margin-top: 38px; }
}
