/* =============================================================================
   site.css — normal mode: hero, section nav, about, timeline, projects, contact.
   Depends on tokens.css + base.css.

   TWO PIECES ARE SHARED WITH THE GAME and are documented as a DOM contract so
   the shootout can render them without importing anything from site.js:

   .proj-card  — see "PROJECT CARD — DOM CONTRACT" below.
   .tl-*       — the timeline components.
   ========================================================================== */

/* --- section scaffolding -------------------------------------------------- */

.section {
  padding-block: var(--section-gap);
  scroll-margin-top: calc(var(--header-h) + var(--sp-6));
}
.section + .section { padding-top: 0; }

.section__head {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  margin-bottom: var(--sp-8);
}
.section__title {
  font-size: var(--step-3);
  letter-spacing: var(--tracking-tight);
}
.section__note {
  color: var(--ink-muted);
  max-width: var(--measure);
  font-size: var(--step-0);
}

/* A short accent rule under a section heading. Kept — read mode is quiet,
   not colourless, and this is where the brand still shows. */
.section__rule {
  height: 2px;
  width: min(100%, 3.5rem);
  border-radius: var(--radius-pill);
  background: var(--accent);
}

/* Scroll-reveal. Purely additive: the un-revealed state is only applied when
   JS has marked the element, so with JS broken nothing is ever hidden. And
   travel is multiplied by --motion-ok, so reduced motion gets a plain fade. */
[data-reveal] {
  opacity: 0;
  transform: translateY(calc(18px * var(--motion-ok)));
  transition: opacity var(--dur-3) var(--ease-out),
              transform var(--dur-3) var(--ease-out);
}
[data-reveal="in"] { opacity: 1; transform: none; }

/* =============================================================================
   HERO — photo left, text right, social links under the photo.

   This is a person introducing himself, so it opens with his face and his own
   sentence, set in the page's text face at a readable size. There is no
   floodlight, no chalk arc and no all-caps surname: read mode is a document,
   and this is the top of it.

   THE PHOTO MAY NOT EXIST. assets/gauresh.jpg is loaded optimistically and the
   frame is built so that its absence costs nothing:

     - .hero__portrait sets aspect-ratio and its own size, so the frame
       occupies exactly the same box before, during and after the load. Nothing
       below it can move, whatever happens.
     - The monogram sits in the same grid cell UNDERNEATH the image and is the
       resting state. The image fades in over it on load; on error site.js
       removes the <img> and the monogram is simply what remains. A broken
       image icon is never reachable, because a failed <img> is not kept.

   Structure (built by site.js):
     section.hero
       div.hero__inner
         div.hero__aside
           div.hero__portrait[data-state=pending|ok|fallback]
             img.hero__photo          removed on error
             span.hero__monogram      initials, aria-hidden
           ul.hero__links             social links, under the photo
         div.hero__text
           p.hero__location / h1.hero__name / p.hero__tagline / div.hero__actions
   ========================================================================== */

.hero {
  position: relative;
  padding-block: clamp(2.5rem, 8vw, 4.5rem) clamp(2rem, 6vw, 3.5rem);
}

.hero__inner {
  display: grid;
  gap: var(--sp-7);
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Photo above text on mobile, beside it from 46rem. The photo column is a
   fixed width so the text measure is predictable at every size. */
@media (min-width: 46rem) {
  .hero__inner {
    grid-template-columns: 13.5rem minmax(0, 1fr);
    gap: clamp(var(--sp-8), 5vw, var(--sp-11));
    align-items: start;
  }
}

.hero__aside {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  align-items: flex-start;
  min-width: 0;
}

/* The frame. Fixed box, set by aspect-ratio, occupied in every state. */
.hero__portrait {
  display: grid;
  width: min(9.5rem, 42vw);
  aspect-ratio: 1 / 1;
  flex: none;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--brand-tint);
  box-shadow: inset 0 0 0 1px var(--brand-edge);
}
@media (min-width: 46rem) {
  .hero__portrait { width: 100%; }
}

/* Both children share one grid cell, so they overlay without positioning. */
.hero__photo,
.hero__monogram { grid-area: 1 / 1; }

.hero__photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-out);
}
.hero__portrait[data-state="ok"] .hero__photo { opacity: 1; }

/* The resting state and the fallback are the same thing: initials, set in the
   page's own face, on the brand tint. It reads as a monogram rather than as a
   missing asset, which is the whole point. */
.hero__monogram {
  display: grid;
  place-items: center;
  font-size: clamp(2rem, 7vw, 2.75rem);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  color: var(--brand-ink);
  user-select: none;
}
.hero__portrait[data-state="ok"] .hero__monogram { visibility: hidden; }

.hero__text {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  min-width: 0;
}

.hero__location {
  font-size: var(--step--1);
  color: var(--ink-faint);
  margin: 0;
}

.hero__name {
  font-size: var(--step-5);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  font-weight: var(--weight-bold);
  color: var(--ink);
  margin: 0;
}

.hero__tagline {
  font-size: var(--step-1);
  line-height: var(--leading-normal);
  font-weight: var(--weight-regular);
  color: var(--ink-muted);
  max-width: var(--measure-narrow);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  margin-top: var(--sp-2);
}

/* Social links under the photo: plain underlined text links with a small
   CSS-drawn glyph. Pills here would make three short words look like a
   toolbar; underlined text looks like someone wrote them. */
.hero__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-5);
  padding: 0;
  margin: 0;
  list-style: none;
}
.link-plain {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  color: var(--ink-muted);
  font-size: var(--step--1);
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: color-mix(in oklab, currentColor 40%, transparent);
  transition: color var(--dur-1) var(--ease-out),
              text-decoration-color var(--dur-1) var(--ease-out);
}
.link-plain:hover {
  color: var(--brand-ink);
  text-decoration-color: currentColor;
}
/* The pill variant, used where links are a deliberate block of actions rather
   than a line of text — the contact panel. */
.link-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-5);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
  font-size: var(--step--1);
  font-weight: var(--weight-medium);
  transition: border-color var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out),
              transform var(--dur-1) var(--ease-out);
}
.link-pill:hover {
  border-color: var(--brand);
  color: var(--brand-ink);
  transform: translateY(calc(-1px * var(--motion-ok)));
}

/* Kind glyphs, drawn in CSS and shared by both link variants. Distinct
   silhouettes, no icon font, no request. */
.link-glyph {
  width: 0.8rem;
  height: 0.8rem;
  flex: none;
  background: currentColor;
  opacity: 0.8;
}
.link-glyph--github   { clip-path: polygon(50% 0,100% 28%,100% 72%,50% 100%,0 72%,0 28%); }
.link-glyph--linkedin { border-radius: 2px; clip-path: polygon(0 0,100% 0,100% 100%,0 100%,0 38%,38% 38%,38% 100%,0 100%); }
.link-glyph--email    { clip-path: polygon(0 12%,100% 12%,100% 88%,0 88%,0 12%,50% 58%,100% 12%); }

/* =============================================================================
   SECTION NAV — a scrollable pill rail. Wide content scrolls in its own box.
   ========================================================================== */

.jumpnav {
  position: relative;
  border-block: 1px solid var(--line);
  background: color-mix(in oklab, var(--surface) 55%, transparent);
}
.jumpnav__scroll {
  display: flex;
  gap: var(--sp-3);
  max-width: var(--wrap);
  margin-inline: auto;
  padding: var(--sp-4) var(--gutter);
  list-style: none;
}
.jumpnav a {
  display: inline-block;
  padding: var(--sp-2) var(--sp-5);
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  color: var(--ink-muted);
  text-decoration: none;
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-normal);
  white-space: nowrap;
  transition: background-color var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out);
}
.jumpnav a:hover,
.jumpnav a[aria-current="true"] {
  background: var(--brand-tint);
  border-color: var(--brand-edge);
  color: var(--brand-ink);
}

/* =============================================================================
   ABOUT
   ========================================================================== */

.about__grid {
  display: grid;
  gap: var(--sp-8);
}
@media (min-width: 56rem) {
  .about__grid { grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr); gap: var(--sp-10); }
}

.about__lede {
  font-size: var(--step-1);
  line-height: var(--leading-normal);
  letter-spacing: var(--tracking-normal);
  color: var(--ink);
  max-width: 34ch;
  /* A rule down the left, picking up the timeline rail motif. */
  padding-left: var(--sp-6);
  border-left: 2px solid var(--accent);
}

/* =============================================================================
   TIMELINE — the touchline.

   Structure per entry (rendered by site.js):
     li.tl-item[data-kind]
       span.tl-item__mark       the node on the rail. Shape varies by kind.
       span.tl-item__period     mono minute-marker; moves to the gutter >=56rem
       details.tl-item__body
         summary.tl-item__summary   title, org, kind badge, one-line summary
         div.tl-item__detail        paragraphs + tech chips

   Why a rail rather than the usual alternating two-column timeline: alternating
   forces the eye to zig-zag, doubles the reading path on desktop and collapses
   to a single column on mobile anyway. A single left rail with the period in a
   right-aligned gutter is one reading path at every width, and the gutter reads
   as a broadcast clock.
   ========================================================================== */

.timeline {
  position: relative;
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}

.tl-item {
  position: relative;
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  column-gap: var(--sp-5);
  align-items: start;
}

/* The rail itself: one continuous chalk line behind every node, faded at both
   ends so it does not look like a cropped border. Drawn per item and joined by
   negative offsets, so an item can be added or removed without maths. */
.tl-item::before {
  content: "";
  position: absolute;
  left: calc(var(--rail-w) / 2 - 1px);
  top: 0;
  bottom: calc(var(--sp-6) * -1);
  width: 2px;
  background: var(--line-strong);
}
.tl-item:first-child::before { top: 0.9rem; }
.tl-item:last-child::before {
  bottom: auto;
  height: 0.9rem;
  background: linear-gradient(var(--line-strong), transparent);
}

/* The node. Shape encodes kind, so the four categories are distinguishable
   without relying on colour alone. */
.tl-item__mark {
  position: relative;
  z-index: 1;
  grid-column: 1;
  width: var(--rail-w);
  height: var(--rail-w);
  margin-top: 0.35rem;
  background: var(--kind-color, var(--brand));
  /* A ring of page ground so the rail appears to pass behind the node. */
  box-shadow: 0 0 0 4px var(--bg);
  border-radius: 50%;
}
.tl-item[data-kind="research"] .tl-item__mark {
  border-radius: 2px;
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
  box-shadow: none;
  outline: 4px solid var(--bg);
}
.tl-item[data-kind="venture"] .tl-item__mark {
  border-radius: 0;
  clip-path: polygon(50% 0%, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%);
  box-shadow: none;
  outline: 4px solid var(--bg);
}
.tl-item[data-kind="project"] .tl-item__mark {
  border-radius: 2px;
  clip-path: polygon(0 0, 100% 0, 100% 68%, 68% 100%, 0 100%);
  box-shadow: none;
  outline: 4px solid var(--bg);
}

.tl-item[data-kind="work"]     { --kind-color: var(--kind-work-ink);     --kind-tint: var(--kind-work-tint);     --kind-edge: var(--kind-work-edge); }
.tl-item[data-kind="research"] { --kind-color: var(--kind-research-ink); --kind-tint: var(--kind-research-tint); --kind-edge: var(--kind-research-edge); }
.tl-item[data-kind="venture"]  { --kind-color: var(--kind-venture-ink);  --kind-tint: var(--kind-venture-tint);  --kind-edge: var(--kind-venture-edge); }
.tl-item[data-kind="project"]  { --kind-color: var(--kind-project-ink);  --kind-tint: var(--kind-project-tint);  --kind-edge: var(--kind-project-edge); }

.tl-item__period {
  grid-column: 2;
  display: block;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: var(--tracking-normal);
  color: var(--ink-faint);
  margin-bottom: var(--sp-2);
}

.tl-item__body {
  grid-column: 2;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  /* The kind colour enters as a top edge rather than a full-height left bar:
     a left bar fights the rail two rem away. */
  border-top: 3px solid var(--kind-color);
  box-shadow: var(--shadow-1);
  transition: box-shadow var(--dur-2) var(--ease-out),
              border-color var(--dur-2) var(--ease-out);
}
.tl-item__body[open] { box-shadow: var(--shadow-2); }
.tl-item__body:hover { border-color: var(--line-strong); border-top-color: var(--kind-color); }

/* <summary> accepts phrasing content plus headings and nothing else, so the
   children are flat and the grid does the placing. Rows: heading + kind badge,
   then blurb + chevron. */
.tl-item__summary {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  column-gap: var(--sp-4);
  row-gap: var(--sp-3);
  padding: var(--sp-5) var(--sp-5) var(--sp-5) var(--sp-6);
  cursor: pointer;
  list-style: none;
}
.tl-item__heading { grid-column: 1; grid-row: 1; }
.tl-item__kind    { grid-column: 2; grid-row: 1; justify-self: end; }
.tl-item__blurb   { grid-column: 1; grid-row: 2; }
.tl-item__chev    { grid-column: 2; grid-row: 2; justify-self: end; align-self: end; }
.tl-item__summary::-webkit-details-marker { display: none; }
.tl-item__summary:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: -3px;
  border-radius: var(--radius-md);
}

.tl-item__heading {
  font-family: var(--font-heading);
  font-size: var(--step-1);
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-normal);
  color: var(--ink);
  margin: 0;
}
.tl-item__org {
  display: block;
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-normal);
  color: var(--kind-color);
  margin-top: 0.15em;
}
.tl-item__blurb {
  display: block;
  color: var(--ink-muted);
  font-size: var(--step--1);
  max-width: var(--measure);
}

/* Kind badge: a small rotated tag, like a card shown by an official. */
.tl-item__kind {
  align-self: start;
  padding: 0.18em 0.6em;
  border: 1px solid var(--kind-edge);
  border-radius: var(--radius-xs);
  background: var(--kind-tint);
  color: var(--kind-color);
  font-family: var(--font-body);
  font-size: var(--step--2);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-normal);
  white-space: nowrap;
}

/* Disclosure chevron, drawn from two borders. Rotates on open. */
.tl-item__chev {
  width: 0.62rem;
  height: 0.62rem;
  margin-bottom: 0.3rem;
  border-right: 2px solid var(--ink-faint);
  border-bottom: 2px solid var(--ink-faint);
  transform: rotate(45deg);
  transform-origin: center;
  transition: transform var(--dur-2) var(--ease-out),
              border-color var(--dur-1) var(--ease-out);
}
.tl-item__body[open] .tl-item__chev { transform: rotate(-135deg); }
.tl-item__summary:hover .tl-item__chev { border-color: var(--kind-color); }

.tl-item__detail {
  padding: 0 var(--sp-5) var(--sp-6) var(--sp-6);
  border-top: 1px dashed var(--line);
  margin-top: 0;
  padding-top: var(--sp-5);
}
.tl-item__detail p {
  color: var(--ink-muted);
  max-width: var(--measure);
  font-size: var(--step--1);
}
.tl-item__detail p + p { margin-top: var(--sp-4); }
.tl-item__detail .chip-row { margin-top: var(--sp-5); }

/* Content fade-in on open. Collapsed to 1ms by the reduced-motion block. */
.tl-item__body[open] .tl-item__detail {
  animation: tl-open var(--dur-3) var(--ease-out) both;
}
@keyframes tl-open {
  from { opacity: 0; transform: translateY(calc(-6px * var(--motion-ok))); }
  to   { opacity: 1; transform: none; }
}

/* Desktop: the period moves into its own right-aligned gutter. */
@media (min-width: 56rem) {
  .tl-item {
    grid-template-columns: var(--gutter-period) var(--rail-w) minmax(0, 1fr);
    column-gap: var(--sp-6);
  }
  .tl-item::before { left: calc(var(--gutter-period) + var(--sp-6) + var(--rail-w) / 2 - 1px); }
  .tl-item__mark   { grid-column: 2; }
  .tl-item__period {
    grid-column: 1;
    grid-row: 1;
    text-align: right;
    margin-top: 0.3rem;
    margin-bottom: 0;
    font-size: var(--step--1);
    color: var(--ink-muted);
  }
  .tl-item__body { grid-column: 3; grid-row: 1; }
}

/* Legend + expand-all live in the section head. */
.tl-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
  margin-bottom: var(--sp-7);
}
.tl-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-5);
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: var(--step--2);
  color: var(--ink-muted);
}
.tl-legend li {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  letter-spacing: var(--tracking-normal);
  font-family: var(--font-body);
  font-weight: var(--weight-medium);
}
.tl-legend__mark {
  width: 0.75rem;
  height: 0.75rem;
  flex: none;
  background: var(--kind-color, var(--brand));
  border-radius: 50%;
}
.tl-legend li[data-kind="work"]     { --kind-color: var(--kind-work-ink); }
.tl-legend li[data-kind="research"] { --kind-color: var(--kind-research-ink); }
.tl-legend li[data-kind="venture"]  { --kind-color: var(--kind-venture-ink); }
.tl-legend li[data-kind="project"]  { --kind-color: var(--kind-project-ink); }
.tl-legend li[data-kind="research"] .tl-legend__mark { border-radius: 2px; clip-path: polygon(50% 0,100% 50%,50% 100%,0 50%); }
.tl-legend li[data-kind="venture"]  .tl-legend__mark { border-radius: 0; clip-path: polygon(50% 0%,95% 25%,95% 75%,50% 100%,5% 75%,5% 25%); }
.tl-legend li[data-kind="project"]  .tl-legend__mark { border-radius: 2px; clip-path: polygon(0 0,100% 0,100% 68%,68% 100%,0 100%); }

/* =============================================================================
   PROJECT CARD — DOM CONTRACT (shared with the shootout)

   The card is designed to stand alone: it carries its own number, name, blurb,
   tech, highlight and provenance, and needs no surrounding section to make
   sense. The game can build the same tree and get the same card.

     article.proj-card
       div.proj-card__top
         span.proj-card__num        "01".."05"
         h3.proj-card__name
       p.proj-card__blurb
       ul.chip-row                  tech
       div.proj-card__highlight     the one detail an interviewer digs into
         span.proj-card__hl-label
         p.proj-card__hl-text
       div.proj-card__body          1-2 paragraphs
       p.proj-card__prov            provenance note, optional
       a.proj-card__repo            repo link, optional

   Use .proj-card--revealed to play the flip-in when the game uncovers one.
   ========================================================================== */

.projects-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-6);
  list-style: none;
  padding: 0;
  margin: 0;
}
@media (min-width: 45rem) {
  .projects-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Five cards in two columns leaves a hole; the fifth takes the full row. */
  .projects-grid > li:nth-child(5) { grid-column: 1 / -1; }
}
@media (min-width: 73.75rem) {
  /* Six tracks: three cards of two, then two cards of three. No hole, and the
     change of card width across rows is the variety the layout needs. */
  .projects-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .projects-grid > li:nth-child(-n+3) { grid-column: span 2; }
  .projects-grid > li:nth-child(n+4)  { grid-column: span 3; }
}

.proj-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  height: 100%;
  padding: var(--sp-7) var(--sp-6) var(--sp-6);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  overflow: clip;
  transition: transform var(--dur-2) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out),
              border-color var(--dur-2) var(--ease-out);
}
/* The ribbon along the top edge — the card's team colour. */
.proj-card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 4px;
  background: linear-gradient(90deg, var(--brand) 0%, var(--accent) 100%);
}
.proj-card:hover {
  transform: translateY(calc(-3px * var(--motion-ok)));
  box-shadow: var(--shadow-lift);
  border-color: var(--line-strong);
}
.proj-card:focus-within { border-color: var(--brand); }

.proj-card__top {
  display: flex;
  align-items: baseline;
  gap: var(--sp-4);
}
/* Shirt number. Mono, oversized, low contrast — a structural digit, not a
   decoration, and it is also the penalty order in the shootout. */
.proj-card__num {
  font-family: var(--font-mono);
  font-size: var(--step-3);
  font-weight: var(--weight-bold);
  line-height: 1;
  color: var(--line-strong);
  letter-spacing: var(--tracking-tighter);
  flex: none;
}
.proj-card__name {
  font-size: var(--step-2);
  letter-spacing: var(--tracking-tight);
  color: var(--ink);
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

.proj-card__blurb {
  font-size: var(--step-0);
  line-height: var(--leading-normal);
  color: var(--ink);
  max-width: var(--measure-narrow);
}

/* The highlight. This is the card's reason to exist, so it gets a distinct
   ground, a label and a left bar — it must not read as another paragraph. */
.proj-card__highlight {
  position: relative;
  padding: var(--sp-5) var(--sp-5) var(--sp-5) var(--sp-6);
  border: 1px solid var(--signal-edge);
  border-left: 3px solid var(--signal-strong);
  border-radius: var(--radius-sm);
  background: var(--signal-tint);
}
.proj-card__hl-label {
  display: inline-block;
  margin-bottom: var(--sp-3);
  font-family: var(--font-body);
  font-size: var(--step--2);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-normal);
  color: var(--signal-ink);
}
.proj-card__hl-text {
  font-size: var(--step--1);
  line-height: var(--leading-normal);
  color: var(--ink);
  max-width: var(--measure);
}

.proj-card__body p {
  font-size: var(--step--1);
  color: var(--ink-muted);
  max-width: var(--measure);
}
.proj-card__body p + p { margin-top: var(--sp-4); }

/* Provenance. An honest footnote, marked as one: dotted rule, small, quiet,
   with a CSS-drawn asterisk-ish marker so it is visually distinct from body. */
.proj-card__prov {
  display: flex;
  gap: var(--sp-3);
  padding-top: var(--sp-4);
  border-top: 1px dotted var(--line-strong);
  font-size: var(--step--2);
  line-height: var(--leading-normal);
  color: var(--ink-faint);
  font-style: italic;
}
.proj-card__prov::before {
  content: "";
  flex: none;
  width: 0.55rem;
  height: 0.55rem;
  margin-top: 0.45em;
  background: var(--ink-faint);
  clip-path: polygon(50% 0,61% 35%,98% 35%,68% 57%,79% 91%,50% 70%,21% 91%,32% 57%,2% 35%,39% 35%);
}

/* Push the repo link to the bottom so cards of different height still line up
   along their action row. */
.proj-card__foot { margin-top: auto; padding-top: var(--sp-2); }
.proj-card__repo {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  color: var(--brand-ink);
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-normal);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  transition: border-color var(--dur-1) var(--ease-out);
}
.proj-card__repo::after {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  border-top: 2px solid currentColor;
  border-right: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform var(--dur-2) var(--ease-out);
}
.proj-card__repo:hover { border-bottom-color: var(--brand); }
.proj-card__repo:hover::after { transform: rotate(45deg) translate(2px, -2px); }

/* Entrance used by the shootout when a card is revealed. */
.proj-card--revealed { animation: card-flip-in var(--dur-4) var(--ease-emphatic) both; }
@keyframes card-flip-in {
  from { opacity: 0; transform: perspective(900px) rotateX(calc(-28deg * var(--motion-ok))); }
  to   { opacity: 1; transform: none; }
}

/* =============================================================================
   CONTACT — a calm closing panel.

   This was a floodlit night-match slab: near-black pitch gradient, a chalk
   centre circle, amber broadcast type. That belongs to the game. Here it is a
   card on the page's own surface with one accent edge — the same confidence,
   none of the stadium.
   ========================================================================== */

.contact {
  position: relative;
  padding: clamp(1.75rem, 5vw, 3rem);
  border: 1px solid var(--line);
  border-top: 3px solid var(--accent);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-1);
}

.contact__title {
  font-size: var(--step-3);
  letter-spacing: var(--tracking-tight);
  color: var(--ink);
}
.contact__note {
  margin-top: var(--sp-4);
  max-width: var(--measure-narrow);
  color: var(--ink-muted);
}

/* The email is the point of the section, so it is the largest thing in it and
   it is underlined like the link it is. */
.contact__email {
  display: inline-block;
  margin-top: var(--sp-7);
  font-family: var(--font-body);
  font-size: var(--step-2);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-normal);
  color: var(--brand-ink);
  text-decoration-line: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.2em;
  text-decoration-color: color-mix(in oklab, currentColor 35%, transparent);
  overflow-wrap: anywhere;
  transition: text-decoration-color var(--dur-2) var(--ease-out);
}
.contact__email:hover { text-decoration-color: currentColor; }

.contact__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  margin-top: var(--sp-8);
  padding: 0;
  list-style: none;
}

.contact__meta {
  margin-top: var(--sp-8);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: var(--tracking-normal);
  color: var(--ink-faint);
}

/* =============================================================================
   PLAY CTA — the way back to the game, at the end of the read.

   The shootout is the default landing experience, so someone reading this has
   already chosen to leave it. The header switch is always there, but a reader
   who has scrolled to the bottom is furthest from it and has just finished the
   thing they came for. This is the second, unmissable door back.
   ========================================================================== */

.playcta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
  margin-top: var(--section-gap);
  padding: var(--sp-7);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-lg);
  background: var(--brand-tint);
}
.playcta__text { min-width: 0; }
.playcta__title {
  font-size: var(--step-1);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-normal);
  color: var(--ink);
  margin: 0;
}
.playcta__note {
  margin-top: var(--sp-2);
  font-size: var(--step--1);
  color: var(--ink-muted);
  max-width: 46ch;
}

/* =============================================================================
   SHOOTOUT RESTORES — broadcast treatment for the components the game shares.

   .proj-card and .tl-* are rendered by BOTH modes (see the DOM contract above),
   so quietening them for the document would have quietened them inside the
   game too. These rules hand the broadcast voice back, and only there.
   --font-heading already resolves to the condensed display face under this
   selector, so what is left is mostly case and tracking.
   ========================================================================== */

html[data-mode="shootout"] .proj-card__name,
html[data-mode="shootout"] .tl-item__kind {
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
}
html[data-mode="shootout"] .proj-card__name {
  font-size: var(--step-3);
  letter-spacing: var(--tracking-tight);
}
html[data-mode="shootout"] .tl-item__kind {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
}
html[data-mode="shootout"] .proj-card__hl-label {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
}
html[data-mode="shootout"] .proj-card__repo {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
html[data-mode="shootout"] .tl-item__period {
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

/* =========================================================================
   READ MODE — paper and branches
   ========================================================================= */

html[data-mode="site"],
html[data-mode="site"] body {
  background: var(--paper);
  color: var(--ink);
}

/* The branches.
   One fixed, pointer-events:none layer behind everything, carrying an inline
   SVG as a data: URI. Drawn once by the browser and composited — no DOM nodes,
   no scroll listener, no per-frame work, which is what keeps a decorative
   background from costing anything. `data:` in img-src is already allowed by
   the CSP, and this is the reason that allowance exists. */
html[data-mode="site"] body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-repeat: repeat;
  background-size: 900px 900px;
  opacity: 0.85;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='900' height='900' viewBox='0 0 900 900'%3E%3Cg fill='none' stroke='%23b8892b' stroke-opacity='0.17' stroke-linecap='round'%3E%3Cpath d='M-20 760 C 120 700 180 560 300 520 S 520 470 610 380 S 760 240 900 190' stroke-width='2.4'/%3E%3Cpath d='M300 520 C 330 440 300 380 250 320' stroke-width='1.6'/%3E%3Cpath d='M250 320 C 210 280 150 270 100 280' stroke-width='1.1'/%3E%3Cpath d='M250 320 C 280 270 270 210 240 170' stroke-width='1.1'/%3E%3Cpath d='M610 380 C 650 300 640 230 600 170' stroke-width='1.6'/%3E%3Cpath d='M600 170 C 560 130 500 120 460 130' stroke-width='1.1'/%3E%3Cpath d='M600 170 C 650 140 700 150 730 180' stroke-width='1.1'/%3E%3Cpath d='M-20 200 C 90 240 160 330 210 430' stroke-width='1.8'/%3E%3Cpath d='M210 430 C 250 470 320 490 380 480' stroke-width='1.1'/%3E%3Cpath d='M900 640 C 800 660 720 740 660 820' stroke-width='1.8'/%3E%3Cpath d='M660 820 C 610 850 540 855 490 840' stroke-width='1.1'/%3E%3Cpath d='M480 640 C 560 620 620 660 660 720' stroke-width='1.3'/%3E%3C/g%3E%3Cg fill='%23b8892b' fill-opacity='0.13'%3E%3Ccircle cx='100' cy='280' r='5'/%3E%3Ccircle cx='240' cy='170' r='4'/%3E%3Ccircle cx='460' cy='130' r='5'/%3E%3Ccircle cx='730' cy='180' r='4'/%3E%3Ccircle cx='380' cy='480' r='4.5'/%3E%3Ccircle cx='490' cy='840' r='5'/%3E%3Ccircle cx='660' cy='720' r='4'/%3E%3C/g%3E%3C/svg%3E");
}

/* Dark mode repaints the same artwork in the warmer gold. */
@media (prefers-color-scheme: dark) {
  html[data-mode="site"]:not([data-theme="light"]) body::before {
    filter: brightness(1.45) saturate(1.1);
    opacity: 0.7;
  }
}
html[data-mode="site"][data-theme="dark"] body::before {
  filter: brightness(1.45) saturate(1.1);
  opacity: 0.7;
}

/* Everything real sits above the branch layer. */
html[data-mode="site"] .site-header,
html[data-mode="site"] #app-root,
html[data-mode="site"] .site-footer { position: relative; z-index: 1; }

/* --- theme toggle -------------------------------------------------------- */

.theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--paper-raised);
}
.theme-toggle__opt {
  appearance: none;
  border: 0;
  background: none;
  color: var(--ink-faint);
  font: inherit;
  font-size: 0.78rem;
  letter-spacing: 0.01em;
  padding: 5px 12px;
  border-radius: 999px;
  cursor: pointer;
}
.theme-toggle__opt[aria-pressed="true"] {
  background: var(--gold-wash);
  color: var(--ink);
  font-weight: 650;
}
.theme-toggle__opt:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}
html[data-mode="shootout"] .theme-toggle { display: none; }

/* --- player number, no widget ------------------------------------------- */

html[data-mode="site"] .player-number {
  background: none;
  border: 0;
  padding: 0;
  font-size: 0.82rem;
  color: var(--ink-faint);
  letter-spacing: 0.02em;
  white-space: nowrap;
}
html[data-mode="site"] .player-number__plate {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--ink-soft);
  font-weight: 650;
}
html[data-mode="site"] .player-number__caption {
  /* The label now leads ("Player 4271"), so the trailing caption is redundant. */
  display: none;
}

/* =========================================================================
   HERO — portrait left, text right, links on one line below
   ========================================================================= */

html[data-mode="site"] .hero--split { padding: clamp(2rem, 6vw, 4.5rem) 0 1.5rem; }
html[data-mode="site"] .hero--split .hero__inner {
  display: grid;
  grid-template-columns: minmax(150px, 220px) 1fr;
  gap: clamp(1.25rem, 4vw, 2.75rem);
  align-items: center;
  max-width: 62rem;
  margin: 0 auto;
  padding: 0 clamp(1rem, 4vw, 2rem);
}
html[data-mode="site"] .hero__portrait {
  width: 100%;
  aspect-ratio: 3 / 4;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid var(--rule);
  box-shadow: 0 10px 30px -18px rgba(0, 0, 0, 0.45);
  background: var(--gold-wash);
  display: grid;
  place-items: center;
}
html[data-mode="site"] .hero__photo {
  grid-area: 1 / 1;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 50% 22%;
}
html[data-mode="site"] .hero__monogram {
  grid-area: 1 / 1;
  font-size: 2.5rem; font-weight: 700; color: var(--gold);
}
html[data-mode="site"] .hero__name {
  font-size: clamp(1.9rem, 5.2vw, 3rem);
  line-height: 1.05; margin: 0 0 0.4rem; color: var(--ink);
}
html[data-mode="site"] .hero__location { margin: 0 0 0.15rem; color: var(--ink-faint); font-size: 0.95rem; }
html[data-mode="site"] .hero__role { margin: 0 0 0.15rem; color: var(--ink); font-weight: 600; }
html[data-mode="site"] .hero__tagline { margin: 0 0 1rem; color: var(--ink-soft); }
html[data-mode="site"] .hero__links--inline {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 0 1.35rem; list-style: none; margin: 0; padding: 0;
}
html[data-mode="site"] .hero__links--inline a {
  color: var(--gold); text-decoration: none;
  border-bottom: 1px solid var(--gold-wash); padding-bottom: 1px;
}
html[data-mode="site"] .hero__links--inline a:hover { border-bottom-color: var(--gold); }

@media (max-width: 640px) {
  html[data-mode="site"] .hero--split .hero__inner { grid-template-columns: 1fr; justify-items: start; }
  html[data-mode="site"] .hero__portrait { max-width: 190px; }
}

/* =========================================================================
   TABS — About / My journey / Projects, sized to be obviously clickable
   ========================================================================= */

html[data-mode="site"] .jumpnav {
  position: sticky; top: 0; z-index: 5;
  background: color-mix(in srgb, var(--paper) 90%, transparent);
  backdrop-filter: blur(8px);
  border-block: 1px solid var(--rule);
  margin-bottom: clamp(1.5rem, 4vw, 2.5rem);
}
html[data-mode="site"] .jumpnav__scroll {
  display: flex; gap: clamp(0.25rem, 2vw, 1rem);
  list-style: none; margin: 0 auto; padding: 0 clamp(1rem, 4vw, 2rem);
  max-width: 62rem;
}
html[data-mode="site"] .jumpnav a {
  display: block;
  padding: clamp(0.85rem, 2.4vw, 1.15rem) clamp(0.9rem, 3vw, 1.6rem);
  font-size: clamp(0.95rem, 2.4vw, 1.1rem);
  font-weight: 650;
  color: var(--ink-soft);
  text-decoration: none;
  border-bottom: 3px solid transparent;
  white-space: nowrap;
}
html[data-mode="site"] .jumpnav a:hover { color: var(--ink); background: var(--gold-wash); }
html[data-mode="site"] .jumpnav a[aria-current="true"] {
  color: var(--ink); border-bottom-color: var(--gold);
}
html[data-mode="site"] .jumpnav a:focus-visible { outline: 2px solid var(--gold); outline-offset: -4px; }

/* =========================================================================
   ABOUT — narrative left, football aside right
   ========================================================================= */

html[data-mode="site"] .about__split {
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 5vw, 3.5rem);
  align-items: start;
}
@media (max-width: 860px) {
  html[data-mode="site"] .about__split { grid-template-columns: 1fr; }
}
html[data-mode="site"] .about__greeting {
  font-size: clamp(1.6rem, 4.5vw, 2.2rem); margin: 0 0 0.6rem; color: var(--ink);
}
html[data-mode="site"] .about__lede {
  font-size: clamp(1.05rem, 2.6vw, 1.2rem); color: var(--ink-soft); margin: 0 0 1.25rem;
}
html[data-mode="site"] .prose p { margin: 0 0 1rem; line-height: 1.72; color: var(--ink); }
html[data-mode="site"] .prose--tight p { font-size: 0.95rem; line-height: 1.65; }

html[data-mode="site"] .about__aside {
  position: relative;
  padding: clamp(1.1rem, 3vw, 1.6rem);
  border: 1px solid var(--rule);
  border-radius: 14px;
  background: color-mix(in srgb, var(--paper-raised) 88%, transparent);
}
html[data-mode="site"] .about__aside-title {
  margin: 0 0 0.75rem; font-size: 1.15rem; color: var(--gold);
  letter-spacing: 0.01em;
}
html[data-mode="site"] .aside-invite {
  margin-top: 1.1rem; padding-top: 1.1rem; border-top: 1px dashed var(--rule);
}
html[data-mode="site"] .aside-invite__lead {
  margin: 0 0 0.85rem; font-size: 0.95rem; line-height: 1.65; color: var(--ink-soft);
}
html[data-mode="site"] .aside-invite__cta {
  display: inline-flex; align-items: center; gap: 0.55rem;
  padding: 0.6rem 1.1rem; border-radius: 999px;
  background: var(--gold); color: #1a1408;
  font-weight: 700; text-decoration: none;
}
html[data-mode="site"] .aside-invite__cta:hover { filter: brightness(1.07); }
html[data-mode="site"] .aside-invite__ball {
  width: 1em; height: 1em; border-radius: 50%;
  background: #fff;
  box-shadow: inset 0 0 0 2px #1a1408, inset -3px -3px 0 -1px rgba(0,0,0,0.25);
}
@media (prefers-reduced-motion: no-preference) {
  html[data-mode="site"] .aside-invite__cta:hover .aside-invite__ball {
    transform: translateX(3px) rotate(140deg);
    transition: transform 320ms cubic-bezier(.34,1.56,.64,1);
  }
}

/* =========================================================================
   JOURNEY TREE — a trunk, branches, and a node per stop
   ========================================================================= */

html[data-mode="site"] .tree {
  list-style: none; margin: 0; padding: 0;
  position: relative;
}
/* The trunk. */
html[data-mode="site"] .tree::before {
  content: ""; position: absolute; top: 0.6rem; bottom: 0.6rem;
  left: 11px; width: 2px;
  background: linear-gradient(to bottom, transparent, var(--branch-thick) 8%, var(--branch-thick) 92%, transparent);
}
html[data-mode="site"] .stop {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: clamp(0.75rem, 3vw, 1.5rem);
  padding: 0 0 clamp(1.6rem, 4vw, 2.4rem) clamp(2.1rem, 5vw, 2.75rem);
}
/* The branch out to each stop. */
html[data-mode="site"] .stop::before {
  content: ""; position: absolute; left: 12px; top: 0.95rem;
  width: clamp(1rem, 3vw, 1.5rem); height: 2px; background: var(--branch-thick);
}
/* The fruit. */
html[data-mode="site"] .stop__node {
  position: absolute; left: 4px; top: 0.6rem;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--paper);
  border: 2px solid var(--gold);
}
html[data-mode="site"] .stop[data-kind="work"] .stop__node,
html[data-mode="site"] .stop[data-kind="venture"] .stop__node { background: var(--gold); }

html[data-mode="site"] .stop__date {
  margin: 0 0 0.2rem; font-size: 0.8rem; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--gold);
  font-weight: 700;
}
html[data-mode="site"] .stop__title { margin: 0 0 0.1rem; font-size: clamp(1.1rem, 2.8vw, 1.3rem); }
html[data-mode="site"] .stop__link { color: var(--ink); text-decoration: none; border-bottom: 2px solid var(--gold-wash); }
html[data-mode="site"] .stop__link:hover { border-bottom-color: var(--gold); }
html[data-mode="site"] .stop__arrow::after { content: " \2192"; color: var(--gold); }
html[data-mode="site"] .stop__org { margin: 0 0 0.5rem; font-size: 0.9rem; color: var(--ink-faint); }
html[data-mode="site"] .stop__summary { margin: 0; line-height: 1.68; color: var(--ink-soft); max-width: 46ch; }

html[data-mode="site"] .stop__photo { flex-shrink: 0; }
html[data-mode="site"] .stop__photo img {
  width: clamp(88px, 18vw, 132px); height: clamp(88px, 18vw, 132px);
  object-fit: cover; border-radius: 12px;
  border: 1px solid var(--rule);
  box-shadow: 0 8px 22px -16px rgba(0,0,0,0.5);
}
@media (max-width: 560px) {
  html[data-mode="site"] .stop { grid-template-columns: 1fr; }
  html[data-mode="site"] .stop__photo img { width: 96px; height: 96px; }
}

/* Portrait stacking: photo above monogram, monogram retired once it decodes. */
html[data-mode="site"] .hero__portrait { position: relative; }
html[data-mode="site"] .hero__photo { z-index: 1; opacity: 0; transition: opacity 260ms ease; }
html[data-mode="site"] .hero__portrait[data-state="ready"] .hero__photo { opacity: 1; }
html[data-mode="site"] .hero__portrait[data-state="ready"] .hero__monogram { opacity: 0; }
html[data-mode="site"] .hero__monogram { z-index: 0; transition: opacity 200ms ease; }
@media (prefers-reduced-motion: reduce) {
  html[data-mode="site"] .hero__photo,
  html[data-mode="site"] .hero__monogram { transition: none; }
}

/* =========================================================================
   FULL-WIDTH LAYOUT
   The page was living in a 62rem column down the middle. These widen every
   read-mode container so the content uses the screen it is given, while
   keeping a measure cap on running prose — a 1600px-wide paragraph is wide,
   not readable, and the two goals are different.
   ========================================================================= */

html[data-mode="site"] .wrap,
html[data-mode="site"] .hero--split .hero__inner,
html[data-mode="site"] .jumpnav__scroll,
html[data-mode="site"] .site-header__inner,
html[data-mode="site"] .site-footer__inner {
  max-width: min(1600px, 94vw);
  margin-inline: auto;
  padding-inline: clamp(1rem, 3vw, 3rem);
}

html[data-mode="site"] .section { padding-block: clamp(2rem, 5vw, 4rem); }

/* About: narrative gets the measure cap, the aside gets real width. */
html[data-mode="site"] .about__split {
  grid-template-columns: minmax(0, 1.35fr) minmax(320px, 0.85fr);
  gap: clamp(2rem, 5vw, 4.5rem);
}
html[data-mode="site"] .about__main .prose p,
html[data-mode="site"] .about__lede { max-width: 68ch; }

/* Projects use the width too, instead of a single narrow column. */
html[data-mode="site"] .projects-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr));
  gap: clamp(1rem, 2.5vw, 2rem);
  list-style: none; margin: 0; padding: 0;
}

/* =========================================================================
   OFF THE CLOCK — wider, larger type, faded pitch green
   ========================================================================= */

html[data-mode="site"] .about__aside {
  --pitch: 132 38% 45%;
  background:
    linear-gradient(180deg,
      hsl(var(--pitch) / 0.10),
      hsl(var(--pitch) / 0.05));
  border: 1px solid hsl(var(--pitch) / 0.28);
  border-radius: 18px;
  padding: clamp(1.4rem, 3vw, 2.25rem);
}
html[data-mode="site"] .about__aside-title {
  font-size: clamp(1.2rem, 2.6vw, 1.5rem);
  color: hsl(var(--pitch) / 0.95);
  margin-bottom: 1rem;
}
html[data-mode="site"] .about__aside .prose--tight p {
  font-size: clamp(1rem, 1.15vw, 1.08rem);
  line-height: 1.78;
  color: var(--ink);
  margin-bottom: 1.1rem;
}
html[data-mode="site"] .aside-invite { border-top-color: hsl(var(--pitch) / 0.3); }
html[data-mode="site"] .aside-invite__lead { font-size: 1rem; line-height: 1.72; }
@media (prefers-color-scheme: dark) {
  html[data-mode="site"]:not([data-theme="light"]) .about__aside { --pitch: 132 30% 62%; }
}
html[data-mode="site"][data-theme="dark"] .about__aside { --pitch: 132 30% 62%; }

/* =========================================================================
   THE TREE — central gold trunk, stops alternating left and right
   ========================================================================= */

html[data-mode="site"] .tree {
  --trunk: clamp(48px, 6vw, 92px);
  list-style: none; margin: 0; padding: 0;
  position: relative;
}
/* Trunk, centred. Tapers at both ends so it reads as growth, not a rule. */
html[data-mode="site"] .tree::before {
  content: "";
  position: absolute; top: 0; bottom: 0; left: 50%;
  width: 3px; margin-left: -1.5px;
  background: linear-gradient(to bottom,
    transparent, var(--branch-thick) 5%, var(--gold) 50%, var(--branch-thick) 95%, transparent);
  border-radius: 3px;
}

html[data-mode="site"] .stop {
  position: relative;
  display: grid;
  grid-template-columns: 1fr var(--trunk) 1fr;
  align-items: center;
  padding: 0 0 clamp(1.5rem, 3.5vw, 3rem);
}
html[data-mode="site"] .stop::before { content: none; }

/* The fruit on the trunk. */
html[data-mode="site"] .stop__node {
  grid-column: 2; justify-self: center;
  position: relative; z-index: 2;
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--paper);
  border: 3px solid var(--gold);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--paper) 85%, transparent);
}
html[data-mode="site"] .stop[data-kind="work"] .stop__node,
html[data-mode="site"] .stop[data-kind="venture"] .stop__node { background: var(--gold); }

/* Card: alternating sides, with a branch reaching back to the trunk. */
html[data-mode="site"] .stop__card {
  position: relative;
  display: flex; gap: clamp(1rem, 2vw, 1.75rem); align-items: center;
}
html[data-mode="site"] .stop:nth-child(odd) .stop__card {
  grid-column: 1; flex-direction: row-reverse; text-align: right;
}
html[data-mode="site"] .stop:nth-child(even) .stop__card { grid-column: 3; }

/* The branch. Curves out of the trunk to meet each card. */
html[data-mode="site"] .stop__card::after {
  content: "";
  position: absolute; top: 50%;
  width: calc(var(--trunk) / 2 + 12px); height: 2px;
  background: var(--branch-thick);
}
html[data-mode="site"] .stop:nth-child(odd) .stop__card::after { right: calc(var(--trunk) / -2 - 12px); }
html[data-mode="site"] .stop:nth-child(even) .stop__card::after { left: calc(var(--trunk) / -2 - 12px); }

html[data-mode="site"] .stop__body { flex: 1 1 auto; min-width: 0; }
html[data-mode="site"] .stop__summary { max-width: none; }
html[data-mode="site"] .stop__date { font-size: 0.85rem; }
html[data-mode="site"] .stop__title { font-size: clamp(1.2rem, 2vw, 1.5rem); }

/* Bigger picture slots. The image keeps its own aspect ratio inside a fixed
   box height, so a portrait and a landscape shot both sit correctly instead of
   being centre-cropped into a square. */
html[data-mode="site"] .stop__photo {
  flex: 0 0 auto;
  width: clamp(150px, 19vw, 280px);
}
html[data-mode="site"] .stop__photo img {
  width: 100%; height: auto;
  max-height: clamp(190px, 24vw, 330px);
  object-fit: contain;
  border-radius: 14px;
  border: 1px solid var(--rule);
  background: color-mix(in srgb, var(--gold-wash) 60%, transparent);
  box-shadow: 0 14px 34px -22px rgba(0, 0, 0, 0.6);
}

/* Below 900px the trunk moves left and every stop stacks on one side: two
   narrow columns either side of a centre line is unreadable on a phone. */
@media (max-width: 900px) {
  html[data-mode="site"] .tree { --trunk: 34px; }
  html[data-mode="site"] .tree::before { left: 17px; margin-left: 0; }
  html[data-mode="site"] .stop { grid-template-columns: var(--trunk) 1fr; align-items: start; }
  html[data-mode="site"] .stop__node { grid-column: 1; margin-top: 0.4rem; }
  html[data-mode="site"] .stop:nth-child(odd) .stop__card,
  html[data-mode="site"] .stop:nth-child(even) .stop__card {
    grid-column: 2; flex-direction: row; text-align: left;
  }
  html[data-mode="site"] .stop:nth-child(odd) .stop__card::after,
  html[data-mode="site"] .stop:nth-child(even) .stop__card::after {
    left: calc(var(--trunk) / -2 - 6px); right: auto; top: 1.1rem;
    width: calc(var(--trunk) / 2 + 6px);
  }
}
@media (max-width: 620px) {
  html[data-mode="site"] .stop__card { flex-direction: column !important; align-items: flex-start; }
  html[data-mode="site"] .stop__photo { width: min(220px, 60vw); }
}

/* =========================================================================
   SIZE AND DENSITY PASS
   Bigger type, more horizontal room, less vertical sprawl.
   ========================================================================= */

/* Player number: a squad number should look like one. */
html[data-mode="site"] .player-number { font-size: clamp(0.95rem, 1.3vw, 1.15rem); }
html[data-mode="site"] .player-number__plate {
  font-size: clamp(1.05rem, 1.5vw, 1.3rem);
  font-weight: 800; color: var(--gold); letter-spacing: 0.01em;
}

/* About: two genuinely equal halves, and larger body copy. */
html[data-mode="site"] .about__split {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1.75rem, 4vw, 3.5rem);
}
html[data-mode="site"] .about__greeting { font-size: clamp(2rem, 3.4vw, 2.9rem); }
html[data-mode="site"] .about__lede {
  font-size: clamp(1.2rem, 1.7vw, 1.5rem);
  line-height: 1.6;
  max-width: none;
}
html[data-mode="site"] .about__main .prose p {
  font-size: clamp(1.05rem, 1.25vw, 1.18rem);
  line-height: 1.78;
  max-width: none;
}
html[data-mode="site"] .about__aside .prose--tight p {
  font-size: clamp(1.05rem, 1.25vw, 1.18rem);
  line-height: 1.8;
}
html[data-mode="site"] .about__aside-title { font-size: clamp(1.35rem, 2.2vw, 1.75rem); }
html[data-mode="site"] .aside-invite__lead { font-size: clamp(1rem, 1.2vw, 1.12rem); }
html[data-mode="site"] .aside-invite__cta { font-size: 1.05rem; padding: 0.7rem 1.3rem; }

/* Journey: tighter vertically, bigger picture slots. */
html[data-mode="site"] .stop { padding-bottom: clamp(0.5rem, 1.4vw, 1.1rem); }
html[data-mode="site"] .stop__summary {
  font-size: clamp(1rem, 1.15vw, 1.1rem); line-height: 1.6;
}
html[data-mode="site"] .stop__title { font-size: clamp(1.25rem, 1.9vw, 1.6rem); }
html[data-mode="site"] .stop__org { margin-bottom: 0.35rem; }
html[data-mode="site"] .stop__photo { width: clamp(190px, 24vw, 380px); }
html[data-mode="site"] .stop__photo img { max-height: clamp(230px, 30vw, 420px); }

/* Projects: wider cards, bigger text, and the bullet list. */
html[data-mode="site"] .projects-grid {
  grid-template-columns: repeat(auto-fit, minmax(min(560px, 100%), 1fr));
  gap: clamp(1.25rem, 2.5vw, 2.25rem);
}
html[data-mode="site"] .proj-card { padding: clamp(1.4rem, 2.4vw, 2.25rem); }
html[data-mode="site"] .proj-card__name { font-size: clamp(1.4rem, 2.1vw, 1.85rem); }
html[data-mode="site"] .proj-card__blurb { font-size: clamp(1.05rem, 1.3vw, 1.2rem); line-height: 1.6; }
html[data-mode="site"] .proj-card__body p {
  font-size: clamp(1.02rem, 1.2vw, 1.14rem); line-height: 1.78; margin-bottom: 1rem;
}
html[data-mode="site"] .proj-card__hl-text { font-size: clamp(1rem, 1.15vw, 1.1rem); line-height: 1.7; }
html[data-mode="site"] .proj-card__hl-label { font-size: 0.95rem; }
html[data-mode="site"] .proj-card__points {
  margin: 0.25rem 0 1.25rem; padding-left: 1.15rem;
  display: grid; gap: 0.5rem;
}
html[data-mode="site"] .proj-card__points li {
  font-size: clamp(1rem, 1.15vw, 1.1rem);
  line-height: 1.6; color: var(--ink);
}
html[data-mode="site"] .proj-card__points li::marker { color: var(--gold); }
html[data-mode="site"] .proj-card__prov { font-size: 0.95rem; }
html[data-mode="site"] .chip { font-size: 0.88rem; padding: 0.3rem 0.7rem; }

/* Section headings scale with the wider page. */
html[data-mode="site"] .section__title { font-size: clamp(1.9rem, 3.2vw, 2.8rem); }
html[data-mode="site"] .section__note { font-size: clamp(1rem, 1.15vw, 1.1rem); }

/* =========================================================================
   PROJECT CARDS — no tinted callout, full-width text, 2–3 per row
   ========================================================================= */

/* The highlight was an amber panel inset inside the card, which made the one
   paragraph worth reading the narrowest thing on the page. It keeps its label
   so it still reads as the standout detail, but it is now part of the card's
   own column rather than a box sitting inside it. */
html[data-mode="site"] .proj-card__highlight {
  background: none;
  border: 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  border-radius: 0;
  padding: 1rem 0;
  margin: 0.5rem 0 1.25rem;
}
html[data-mode="site"] .proj-card__hl-label {
  color: var(--gold);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  font-size: 0.8rem;
  font-weight: 800;
  margin: 0 0 0.4rem;
}
html[data-mode="site"] .proj-card__hl-text { margin: 0; }

/* Every block runs the full width of the card. */
html[data-mode="site"] .proj-card > *,
html[data-mode="site"] .proj-card__body,
html[data-mode="site"] .proj-card__body p,
html[data-mode="site"] .proj-card__blurb,
html[data-mode="site"] .proj-card__hl-text,
html[data-mode="site"] .proj-card__points,
html[data-mode="site"] .proj-card__points li {
  max-width: none;
}

/* Two or three across, so a screenful shows several projects rather than one.
   auto-fit with a 380px floor gives 3 on a wide desktop, 2 on a laptop and 1
   on a phone, without a media query per breakpoint. */
html[data-mode="site"] .projects-grid {
  grid-template-columns: repeat(auto-fit, minmax(min(380px, 100%), 1fr));
  gap: clamp(1rem, 1.8vw, 1.75rem);
  align-items: start;
}
html[data-mode="site"] .proj-card {
  padding: clamp(1.15rem, 1.6vw, 1.6rem);
  height: 100%;
}

/* Type steps back a little now that a card is a column rather than the page. */
html[data-mode="site"] .proj-card__name { font-size: clamp(1.25rem, 1.5vw, 1.5rem); }
html[data-mode="site"] .proj-card__blurb { font-size: clamp(1rem, 1.1vw, 1.08rem); }
html[data-mode="site"] .proj-card__body p,
html[data-mode="site"] .proj-card__points li,
html[data-mode="site"] .proj-card__hl-text {
  font-size: clamp(0.98rem, 1.02vw, 1.05rem);
  line-height: 1.7;
}
html[data-mode="site"] .proj-card .chip { font-size: 0.8rem; padding: 0.24rem 0.6rem; }

/* The original grid hand-placed spans for a five-project layout
   (li:nth-child(-n+3) span 2, li:nth-child(n+4) span 3). With eight projects
   and an auto-fit track list those rules silently made every card two tracks
   wide, which is why one card filled the row. Every item is one track now and
   auto-fit decides how many tracks there are. */
html[data-mode="site"] .projects-grid > li {
  grid-column: auto !important;
  min-width: 0;
}

/* Project artwork: a generated line drawing, tinted gold, sitting above the
   title. It takes its colour from the card so light and dark both work off
   one asset. */
html[data-mode="site"] .proj-card__art {
  color: var(--gold);
  margin-bottom: 0.6rem;
  opacity: 0.9;
}
html[data-mode="site"] .proj-card__art svg {
  width: clamp(84px, 30%, 120px);
  height: auto;
  display: block;
}

/* Journey: bigger and darker, per the brief. */
html[data-mode="site"] .stop__summary {
  font-size: clamp(1.05rem, 1.25vw, 1.2rem);
  line-height: 1.62;
  color: var(--ink);
  font-weight: 500;
}
html[data-mode="site"] .stop__title { font-weight: 800; color: var(--ink); }
html[data-mode="site"] .stop__org { color: var(--ink-soft); font-weight: 600; }
html[data-mode="site"] .stop__date { font-weight: 800; }

/* =========================================================================
   HEADER — larger controls, now that the player chip has left it
   ========================================================================= */

html[data-mode="site"] .site-header__inner {
  gap: clamp(0.75rem, 2vw, 1.75rem);
  padding-block: clamp(0.6rem, 1.2vw, 1rem);
}
html[data-mode="site"] .wordmark__text { font-size: clamp(1.05rem, 1.4vw, 1.3rem); font-weight: 700; }
html[data-mode="site"] .wordmark__mark { width: 2.2rem; height: 2.2rem; font-size: 0.95rem; }

html[data-mode="site"] .theme-toggle { padding: 4px; }
html[data-mode="site"] .theme-toggle__opt {
  font-size: clamp(0.95rem, 1.1vw, 1.08rem);
  padding: 0.5rem 1.1rem;
}
html[data-mode="site"] .mode-cue { font-size: clamp(0.95rem, 1.1vw, 1.05rem); }
html[data-mode="site"] .mode-toggle { font-size: clamp(0.95rem, 1.1vw, 1.08rem); }
html[data-mode="site"] .mode-toggle__opt { padding: 0.55rem 1.05rem; }

/* =========================================================================
   HERO PLAYER WIDGET — far right, vertical, beside the portrait
   ========================================================================= */

html[data-mode="site"] .hero--split .hero__inner {
  grid-template-columns: minmax(150px, 220px) minmax(0, 1fr) clamp(170px, 18%, 240px);
}
html[data-mode="site"] .hero__player {
  display: grid;
  justify-items: center;
  align-content: center;
  gap: 0.5rem;
  padding: clamp(0.9rem, 1.6vw, 1.4rem);
  border: 1px solid var(--rule);
  border-radius: 16px;
  background: color-mix(in srgb, var(--gold-wash) 55%, transparent);
  color: var(--ink);
  --player-kit: var(--gold);
}
html[data-mode="site"] .hero__player-greet {
  margin: 0; text-align: center; line-height: 1.25;
}
html[data-mode="site"] .hero__player-hello {
  display: block; font-size: 0.9rem; color: var(--ink-faint);
}
html[data-mode="site"] .hero__player-name {
  display: block;
  font-size: clamp(1.05rem, 1.35vw, 1.28rem);
  font-weight: 800; color: var(--gold);
}
html[data-mode="site"] .hero__player-art svg { width: clamp(90px, 9vw, 130px); height: auto; }

@media (max-width: 980px) {
  html[data-mode="site"] .hero--split .hero__inner { grid-template-columns: minmax(140px, 200px) 1fr; }
  html[data-mode="site"] .hero__player {
    grid-column: 1 / -1;
    grid-auto-flow: column;
    justify-items: start;
    align-items: center;
    justify-content: start;
    gap: 1rem;
  }
  html[data-mode="site"] .hero__player-greet { text-align: left; }
}
@media (max-width: 640px) {
  html[data-mode="site"] .hero--split .hero__inner { grid-template-columns: 1fr; }
}

/* The kick animation lives with the figure, so the hero copy gets it too. */
@media (prefers-reduced-motion: no-preference) {
  html[data-mode="site"] .gm-player__leg-kick {
    transform-origin: 60px 96px;
    animation: gm-kick 1.8s ease-in-out infinite;
  }
  html[data-mode="site"] .gm-player__ball { animation: gm-ball 1.8s ease-in-out infinite; }
}
@keyframes gm-kick {
  0%, 62%, 100% { transform: rotate(0deg); }
  74% { transform: rotate(-26deg); }
}
@keyframes gm-ball {
  0%, 62%, 100% { transform: translate(0, 0) rotate(0deg); }
  74% { transform: translate(7px, -12px) rotate(90deg); }
  88% { transform: translate(2px, -3px) rotate(180deg); }
}

/* =========================================================================
   SMALL SCREENS
   -------------------------------------------------------------------------
   The header carried four things — wordmark, theme switch, a prose cue and
   the mode switch — which fits a laptop and overflows a phone. Rather than
   shrinking all four until none of them are tappable, the two that are pure
   affordance drop out: the wordmark's text (the mark still identifies the
   page) and the "Fancy a penalty?" cue (the toggle already says Play).
   ========================================================================= */

@media (max-width: 720px) {
  html[data-mode="site"] .site-header__inner {
    flex-wrap: wrap;
    gap: 0.5rem 0.65rem;
    padding-inline: 0.85rem;
  }
  html[data-mode="site"] .wordmark__text { display: none; }
  html[data-mode="site"] .mode-cue { display: none; }

  html[data-mode="site"] .theme-toggle__opt {
    font-size: 0.9rem;
    padding: 0.45rem 0.8rem;
  }
  html[data-mode="site"] .mode-toggle { font-size: 0.9rem; }
  html[data-mode="site"] .mode-toggle__opt { padding: 0.45rem 0.75rem; }

  /* Links wrap onto a second line rather than running off the edge; the row
     gap matters because without it wrapped rows collide. */
  html[data-mode="site"] .hero__links--inline {
    gap: 0.5rem 1.1rem;
    row-gap: 0.5rem;
  }

  /* Type steps down: the desktop clamps bottom out too large for 375px. */
  html[data-mode="site"] .hero__name { font-size: clamp(1.7rem, 8vw, 2.1rem); }
  html[data-mode="site"] .about__greeting { font-size: clamp(1.5rem, 7vw, 1.9rem); }
  html[data-mode="site"] .about__lede { font-size: 1.08rem; }
  html[data-mode="site"] .about__main .prose p,
  html[data-mode="site"] .about__aside .prose--tight p { font-size: 1rem; line-height: 1.7; }
  html[data-mode="site"] .section__title { font-size: clamp(1.6rem, 7.5vw, 2.1rem); }
  html[data-mode="site"] .stop__summary { font-size: 1rem; }
  html[data-mode="site"] .stop__title { font-size: 1.15rem; }
  html[data-mode="site"] .proj-card__name { font-size: 1.25rem; }

  /* Sections breathe less; a phone screen is mostly padding otherwise. */
  html[data-mode="site"] .section { padding-block: 1.75rem; }
  html[data-mode="site"] .wrap { padding-inline: 1rem; }
  html[data-mode="site"] .proj-card { padding: 1.1rem; }

  /* The portrait should not eat the first screen. */
  html[data-mode="site"] .hero__portrait { max-width: 150px; }
  html[data-mode="site"] .hero--split { padding-top: 1.25rem; }
}

/* Nothing anywhere may push the page sideways. A phone that scrolls
   horizontally feels broken even when every element is individually fine. */
html[data-mode="site"] body { overflow-x: hidden; }
html[data-mode="site"] .wrap,
html[data-mode="site"] .proj-card,
html[data-mode="site"] .stop,
html[data-mode="site"] .about__split { min-width: 0; }

/* The section tabs do not fit four labels across a phone, and shrinking them
   until they do makes them hard to hit. They scroll instead — the pattern the
   `scroll-x` class was named for — with snap points so a swipe lands on a tab
   rather than halfway between two.

   `overscroll-behavior-inline: contain` stops a horizontal swipe here from
   turning into a browser back-gesture, which is the failure that makes this
   pattern feel broken on iOS. */
@media (max-width: 720px) {
  html[data-mode="site"] .jumpnav__scroll {
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-inline: contain;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    padding-inline: 0.85rem;
    /* Room for the last tab to clear the edge when snapped. */
    scroll-padding-inline: 0.85rem;
  }
  html[data-mode="site"] .jumpnav__scroll::-webkit-scrollbar { display: none; }
  html[data-mode="site"] .jumpnav__scroll > li { scroll-snap-align: start; flex: 0 0 auto; }
  html[data-mode="site"] .jumpnav a {
    padding: 0.8rem 0.85rem;
    font-size: 0.95rem;
  }
}

/* The full-width pass sets a two-column About unconditionally, and because it
   comes later in this file it was overriding the 860px stacking rule — leaving
   two 146px columns on a phone. Restated here, after it, where it wins. */
@media (max-width: 860px) {
  html[data-mode="site"] .about__split { grid-template-columns: minmax(0, 1fr); }
}

/* Touch targets: 44px is the smallest reliably tappable size, and the tab bar
   was sitting at 39. */
@media (max-width: 720px) {
  html[data-mode="site"] .jumpnav a { min-height: 44px; display: flex; align-items: center; }
  html[data-mode="site"] .theme-toggle__opt { min-height: 44px; }
  html[data-mode="site"] .hero__links--inline a { min-height: 32px; display: inline-flex; align-items: center; }
}
