/* ═══════════════════════════════════════════════════════════
   Jhonnatan Barbosa — Portfolio Styles
   Updated: fluid container, mobile nav, improved breakpoints,
   all component styles merged from inline <style>
   ═══════════════════════════════════════════════════════════ */

:root {
  --bg: #161619;
  --surface: #1d1d21;
  --surface-hover: #232328;
  --border: #2c2c32;
  --border-soft: #232327;
  --text: #dddbd6;
  --text-mid: #a09e97;
  --text-dim: #918f89;
  /* Two-accent palette. --accent leads; --accent2 is the companion hue that
     used to be a set of one-off blues/ambers scattered through this file. */
  --accent: #ffccec;
  --accent-dim: #b893aa;
  --accent-rgb: 255,204,236;          /* accent as an RGB triple so rgba(var(--accent-rgb), a) can be recolored live */
  --accent2: #b78ed2;
  --accent2-dim: #846697;
  --accent2-rgb: 183,142,210;
  --page-max: 1440px;                 /* max content width — widened from 1140px to fill more of large displays */
  --note-bg: rgba(var(--accent-rgb),0.06);
  --note-border: rgba(var(--accent-rgb),0.25);
  --shadow-soft: 0 4px 24px rgba(0,0,0,0.25), 0 1px 4px rgba(0,0,0,0.15);
  --shadow-glow: 0 4px 32px rgba(var(--accent-rgb),0.08), 0 1px 8px rgba(0,0,0,0.2);
  --transition-lift: transform 0.25s cubic-bezier(0.22,1,0.36,1), box-shadow 0.25s ease, border-color 0.25s ease;
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; overflow-x: hidden; scroll-padding-top: 72px; }

/* ── Safety net for media overflow ──────────────────────────── */
img, video, iframe, table { max-width: 100%; }

/* ── SCROLL PROGRESS BAR ──────────────────────────────────── */
.scroll-progress {
  position: fixed;
  top: 0; left: 0;
  /* Full width, scaled from the left edge by JS. Animating width made the
     browser lay the page out again on every scroll frame; scaleX is a
     compositor transform and never touches layout. */
  width: 100%;
  transform: scaleX(0);
  transform-origin: 0 50%;
  height: 2px;
  background: linear-gradient(90deg, var(--accent-dim), var(--accent), rgba(var(--accent2-rgb),0.7));
  z-index: 100;
  pointer-events: none;
  transition: transform 0.05s linear;
  box-shadow: 0 0 8px rgba(var(--accent-rgb),0.3);
}

/* ── ANIMATED GRADIENT BORDER KEYFRAMES ──────────────────── */
@keyframes border-rotate {
  0%   { --border-angle: 0deg; }
  100% { --border-angle: 360deg; }
}
@property --border-angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

body {
  font-family: 'Geist', -apple-system, sans-serif;
  background: var(--bg);
  color: var(--text);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  /* Governs the elements that inherit rather than declare a rem size:
     .about-text, .doc-desc, .ai-role and bare p/li/td. Those are the
     long-form reading surfaces, and 13.5px was too small for them. */
  font-size: 15px;
  position: relative;
  overflow-x: hidden;
}

/* ── Dot grid pattern fills the blank background ──
   This was a background-image on body with `background-attachment: fixed` on
   pointer devices. Fixed attachment is what the note here used to warn about
   on Android: it defeats the fast scroll path and repaints the whole viewport
   every frame — which is just as true of an old desktop GPU as it is of a
   phone. As its own fixed layer the grid is composited once and never
   repainted, so it can stay put on every device instead of only some.
   body's background-color still propagates to the canvas, so this pseudo at
   z-index -1 sits above the page colour and below everything else. */
html::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    radial-gradient(circle, rgba(var(--accent-rgb),0.045) 1px, transparent 1px);
  background-size: 28px 28px;
}

/* ── Ambient glow blobs — fixed in viewport, sit behind everything ──
   No filter: blur() here on purpose. These are 4.5%-alpha radial gradients
   that already fade to transparent at 70%, so the blur added almost nothing
   visually while forcing a composited layer roughly 3x the blur radius on
   each side — tens of MB of GPU texture that pushed mobile tile memory over
   budget. The gradient stops below are widened to recover the softness. */
body::before,
body::after {
  content: '';
  position: fixed;
  border-radius: 50%;
  pointer-events: none;
  z-index: 0;
}
body::before {
  width: 700px; height: 700px;
  top: -160px; left: -220px;
  background: radial-gradient(circle, rgba(var(--accent-rgb),0.05) 0%, rgba(var(--accent-rgb),0.02) 45%, transparent 75%);
}
body::after {
  width: 600px; height: 600px;
  bottom: 8%; right: -180px;
  background: radial-gradient(circle, rgba(var(--accent2-rgb),0.04) 0%, rgba(var(--accent2-rgb),0.015) 45%, transparent 75%);
}

/* Constrain glow blobs below desktop widths. This threshold has to clear
   tablets too — a Tab S9 reports ~800px wide in portrait and ~1280 in
   landscape, so a 720px cutoff left it running the full-size desktop blobs. */
@media (max-width: 1400px) {
  body::before {
    width: 420px; height: 420px;
    top: -110px; left: -140px;
  }
  body::after {
    width: 360px; height: 360px;
    bottom: 5%; right: -110px;
  }
}
@media (max-width: 720px) {
  body::before {
    width: 320px; height: 320px;
    top: -80px; left: -100px;
  }
  body::after {
    width: 280px; height: 280px;
    bottom: 5%; right: -80px;
  }
}

::selection { background: var(--accent); color: var(--bg); }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ── SCROLL REVEAL ──────────────────────────────────────── */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.6s cubic-bezier(0.22,1,0.36,1),
              transform 0.6s cubic-bezier(0.22,1,0.36,1);
}
.reveal.visible {
  opacity: 1;
  transform: translateY(0);
}
/* stagger children inside a section */
.reveal-stagger > .reveal:nth-child(2) { transition-delay: 0.07s; }
.reveal-stagger > .reveal:nth-child(3) { transition-delay: 0.14s; }
.reveal-stagger > .reveal:nth-child(4) { transition-delay: 0.21s; }
.reveal-stagger > .reveal:nth-child(5) { transition-delay: 0.28s; }

/* Respect reduced-motion preference */
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
}

/* ── FLUID CONTAINER ─────────────────────────────────────
   Scales from ~720px up to 1140px with comfortable padding.
   On 1080p (1920px) this fills ~60% of viewport — still
   readable without the text lines getting too long.
   On 720p (1280px) it fills ~89% — nice and snug.
   ────────────────────────────────────────────────────────── */
.page {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 clamp(1.25rem, 3vw, 3rem);
  position: relative;
  z-index: 1;
}

/* ── Faint vertical guide rails at content edges ── */
.page::before,
.page::after {
  content: '';
  position: fixed;
  top: 0; bottom: 0;
  width: 1px;
  pointer-events: none;
  z-index: 0;
}
.page::before {
  left: calc(50% - (var(--page-max) / 2));
  background: linear-gradient(
    180deg,
    transparent 0%,
    rgba(var(--accent-rgb),0.08) 15%,
    rgba(var(--accent-rgb),0.04) 50%,
    rgba(var(--accent-rgb),0.08) 85%,
    transparent 100%
  );
}
.page::after {
  right: calc(50% - (var(--page-max) / 2));
  background: linear-gradient(
    180deg,
    transparent 0%,
    rgba(var(--accent-rgb),0.08) 15%,
    rgba(var(--accent-rgb),0.04) 50%,
    rgba(var(--accent-rgb),0.08) 85%,
    transparent 100%
  );
}

/* Hide guide rails when viewport is too narrow to show real margin beside the content */
@media (max-width: 1480px) {
  .page::before,
  .page::after { display: none; }
}

/* ── NAV ─────────────────────────────────────────────────── */
nav {
  position: fixed; top: 0; left: 0; right: 0; width: 100%; z-index: 50;
  background: rgba(22,22,25,0.82);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid transparent;
  transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
}
/* Offset page content so the fixed nav doesn't cover it */
body { padding-top: 64px; }
@media (max-width: 560px) {
  body { padding-top: 52px; }
  html { scroll-padding-top: 60px; }
}
nav.scrolled {
  background: rgba(22,22,25,0.95);
  border-bottom-color: var(--border-soft);
  box-shadow: 0 1px 12px rgba(0,0,0,0.3);
}

/* ── Noise / grain overlay for depth ── */
nav::after { display: none; }
/* The grain overlay that used to live here targeted main.page::before — the
   same pseudo-element as the left guide rail above. It won on specificity but
   never set a width, so it inherited the rail's `width: 1px` and rendered as a
   1px strip pinned to the viewport's left edge; below 1480px the rail's
   display:none removed it altogether. So it never drew grain on anything, and
   it cost an feTurbulence rasterisation and took out the left rail while doing
   it. Removed: the rails are symmetric again. If the grain is wanted back it
   needs its own element, not a pseudo the rails already use. */

nav .nav-inner {
  max-width: var(--page-max); margin: 0 auto;
  padding: 0.6rem clamp(1.25rem, 3vw, 3rem);
  display: flex; justify-content: space-between; align-items: center;
  position: relative;
}

nav .name {
  font-family: 'Instrument Serif', serif;
  font-size: 1.05rem; color: var(--text);
}

nav .nav-brand {
  display: flex; flex-direction: column;
  line-height: 1.1;
}
nav .nav-role {
  font-family: 'Geist Mono', monospace;
  font-size: 0.68rem;
  color: var(--text-dim);
  letter-spacing: 0.04em;
  margin-top: 2px;
}
@media (max-width: 560px) {
  nav .nav-role { display: none; }
}

nav .nav-links { display: flex; gap: 1.4rem; list-style: none; }

nav .nav-links a {
  color: var(--text-dim); font-size: 0.8rem; font-weight: 500;
  text-decoration: none; transition: color 0.25s ease;
  position: relative;
}
nav .nav-links a:hover { color: var(--text); }

/* Contact sits in the nav as a pill so it reads as an invitation,
   not just another anchor in the list */
nav .nav-links a.nav-contact {
  color: var(--accent);
  background: rgba(var(--accent-rgb),0.09);
  border: 1px solid rgba(var(--accent-rgb),0.32);
  border-radius: 999px;
  padding: 0.3rem 0.8rem;
  margin: -0.3rem 0;
  transition: background 0.25s ease, border-color 0.25s ease, color 0.25s ease;
}
nav .nav-links a.nav-contact:hover {
  background: rgba(var(--accent-rgb),0.16);
  border-color: var(--accent-dim);
  color: var(--accent);
}
nav .nav-links a.nav-contact.active::after { display: none; }

/* ── Nav active state (set via JS IntersectionObserver) ── */
nav .nav-links a.active {
  color: var(--accent);
}
nav .nav-links a.active::after {
  content: '';
  position: absolute;
  bottom: -6px; left: 0; right: 0;
  height: 1.5px;
  background: var(--accent);
  border-radius: 1px;
  opacity: 0.7;
}

/* ── MOBILE NAV TOGGLE ─────────────────────────────────── */
.nav-toggle {
  display: none;
  background: none; border: none; cursor: pointer;
  width: 28px; height: 20px;
  position: relative;
  flex-shrink: 0;
}

.nav-toggle span {
  display: block;
  position: absolute; left: 0;
  width: 100%; height: 2px;
  background: var(--text-dim);
  border-radius: 1px;
  transition: transform 0.25s, opacity 0.2s;
}
.nav-toggle span:nth-child(1) { top: 0; }
.nav-toggle span:nth-child(2) { top: 9px; }
.nav-toggle span:nth-child(3) { top: 18px; }

.nav-toggle.open span:nth-child(1) { transform: translateY(9px) rotate(45deg); }
.nav-toggle.open span:nth-child(2) { opacity: 0; }
.nav-toggle.open span:nth-child(3) { transform: translateY(-9px) rotate(-45deg); }

/* ── INTRO ───────────────────────────────────────────────── */
.intro {
  padding: 3rem 0 2.25rem;
  border-bottom: none;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2rem;
  align-items: end;
  position: relative;
  z-index: 1;
}

/* ── Staggered intro entrance ── */
.intro h1,
.intro p,
.intro-links {
  opacity: 0;
  transform: translateY(20px);
  animation: intro-in 0.7s cubic-bezier(0.22,1,0.36,1) forwards;
}
.intro h1      { animation-delay: 0.1s; }
.intro p       { animation-delay: 0.3s; }
.intro-links   { animation-delay: 0.5s; }

@keyframes intro-in {
  to { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .intro h1, .intro p, .intro-links {
    opacity: 1; transform: none; animation: none;
  }
}
.intro::after {
  content: '';
  position: absolute;
  bottom: 0; left: 0; right: 0;
  height: 1px;
  background: linear-gradient(90deg, var(--accent-dim) 0%, var(--border-soft) 50%, transparent 100%);
}

.intro h1 {
  font-family: 'Instrument Serif', serif;
  font-size: 1.7rem; font-weight: 400;
  line-height: 1.2; letter-spacing: -0.015em;
  text-shadow: 0 0 40px rgba(var(--accent-rgb),0.08);
}

.intro p {
  color: var(--text-mid); font-size: 0.88rem;
  max-width: 520px; margin-top: 0.5rem;
}

.intro-links { display: flex; gap: 0.6rem; flex-shrink: 0; }

.intro-links a {
  font-size: 0.78rem; font-weight: 500;
  color: var(--text-dim); padding: 0.35rem 0.8rem;
  border: 1px solid var(--border); border-radius: 4px;
  transition: border-color 0.2s ease, color 0.2s ease, background 0.2s ease, transform 0.2s ease;
}
.intro-links a:hover {
  border-color: var(--accent-dim); color: var(--text);
  background: rgba(var(--accent-rgb),0.04);
  text-decoration: none;
  transform: translateY(-1px);
}

/* Address shown in full up top — no hunting for it at the bottom */
.intro-links a.intro-email {
  color: var(--accent);
  border-color: rgba(var(--accent-rgb),0.35);
  background: rgba(var(--accent-rgb),0.06);
}
.intro-links a.intro-email:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: rgba(var(--accent-rgb),0.12);
}

/* ── TIP ─────────────────────────────────────────────────── */
.tip {
  background: var(--note-bg);
  border-left: 2px solid var(--note-border);
  padding: 0.55rem 0.85rem;
  margin: 0.85rem 0;
  font-size: 0.78rem;
  color: var(--accent);
  line-height: 1.55;
}
.tip strong {
  font-weight: 600; font-size: 0.7rem;
  text-transform: uppercase; letter-spacing: 0.06em;
  margin-right: 0.3rem;
}

/* ── SECTIONS ────────────────────────────────────────────── */
section { padding: 2.5rem 0; position: relative; z-index: 1; }
section + section { border-top: 1px solid var(--border-soft); }

/* Ambient section glows — visible on wide screens behind content.
   Same reasoning as the body blobs: 3%-alpha gradients that already fade out,
   so the blur is carried by the gradient stops instead of a filter layer. */
#projects::before {
  content: '';
  position: absolute;
  top: 40px; left: -280px;
  width: 480px; height: 480px;
  background: radial-gradient(circle, rgba(var(--accent-rgb),0.035) 0%, rgba(var(--accent-rgb),0.015) 45%, transparent 75%);
  pointer-events: none;
  z-index: -1;
}
#cyberpunk-analytics::before {
  content: '';
  position: absolute;
  top: 80px; right: -220px;
  width: 420px; height: 420px;
  background: radial-gradient(circle, rgba(var(--accent2-rgb),0.035) 0%, rgba(var(--accent2-rgb),0.015) 45%, transparent 75%);
  pointer-events: none;
  z-index: -1;
}
#about::before {
  content: '';
  position: absolute;
  bottom: 0; left: -200px;
  width: 360px; height: 360px;
  background: radial-gradient(circle, rgba(210,170,120,0.03) 0%, rgba(210,170,120,0.012) 45%, transparent 75%);
  pointer-events: none;
  z-index: -1;
}

.section-header {
  display: flex; justify-content: space-between;
  align-items: baseline; margin-bottom: 1.4rem;
  position: relative;
}

/* ── Section edge crosshair markers ── */
.section-header::before,
.section-header::after {
  content: '+';
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  font-family: 'Geist Mono', monospace;
  font-size: 0.65rem;
  color: var(--accent-dim);
  opacity: 0.35;
  pointer-events: none;
  letter-spacing: 0;
}
.section-header::before { left: -2.2rem; }
.section-header::after  { right: -2.2rem; }

/* Hide crosshairs when they'd overlap content on small screens */
@media (max-width: 1200px) {
  .section-header::before,
  .section-header::after { display: none; }
}

.section-title {
  font-family: 'Instrument Serif', serif;
  font-size: 1.15rem; font-weight: 400;
}

.section-aside { font-size: 0.75rem; color: var(--text-dim); }
/* An aside that names a document should open it. It keeps the aside's dim
   colour rather than turning accent-blue, and carries a faint underline so it
   is findable without reading as a call to action. */
a.section-aside {
  text-decoration: underline;
  text-decoration-color: var(--border);
  text-underline-offset: 3px;
  transition: color 0.2s ease, text-decoration-color 0.2s ease;
}
a.section-aside:hover { color: var(--text-mid); text-decoration-color: var(--accent-dim); }

/* ── PROJECT CARDS ───────────────────────────────────────── */
.project-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
  margin-bottom: 0.85rem;
  display: grid;
  grid-template-columns: 280px 1fr;
  transition: var(--transition-lift);
  position: relative;
}

/* ── Animated gradient border overlay ── */
.project-card::before {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: 7px;
  padding: 1px;
  background: conic-gradient(
    from var(--border-angle, 0deg),
    var(--accent-dim),
    rgba(var(--accent2-rgb),0.55),
    var(--accent),
    var(--accent2-dim),
    var(--accent-dim)
  );
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask-composite: exclude;
  opacity: 0;
  transition: opacity 0.4s ease;
  pointer-events: none;
  z-index: 2;
}

/* The rotation animates a registered custom property, which Chrome cannot
   composite — every frame is a style recalc + conic-gradient repaint + mask
   composite, per card. Running it while the border is invisible was pinning
   the main thread and starving the compositor on mobile GPUs (blank tiles on
   tablets). It only ever shows on hover, so only run it on hover, and only
   where hover exists. */
@media (hover: hover) {
  .project-card:hover::before {
    opacity: 1;
    animation: border-rotate 4s linear infinite;
  }
  /* Scoped alongside the gradient it depends on: border-color goes transparent
     because the rotating ::before takes over as the border. On touch, where a
     tap can leave :hover latched and ::before never fades in, that would just
     erase the card outline. */
  .project-card:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-glow);
    border-color: transparent;
  }
}

.project-thumb {
  background: var(--surface-hover);
  display: flex; align-items: center; justify-content: center;
  border-right: 1px solid var(--border);
  color: var(--text-dim); font-size: 0.75rem; font-weight: 500;
  min-height: 160px;
  position: relative;
}
/* Gradient edge blend on thumbnails — z-index 2 keeps it above the hue overlay */
.project-thumb::after {
  content: '';
  position: absolute;
  top: 0; right: 0;
  width: 40%; height: 100%;
  background: linear-gradient(90deg, transparent 0%, rgba(29,29,33,0.4) 100%);
  pointer-events: none;
  z-index: 2;
}
.project-thumb img,
.project-thumb video {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%; object-fit: cover;
  display: block;
}

/* Thumbnails are a second way into the project modal */
.project-thumb.is-clickable { cursor: pointer; }
.project-thumb.is-clickable:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -3px;
}

/* ── HUE OVERLAY ─────────────────────────────────────────────
   Five games, five clashing key arts. Tinting each thumbnail toward the
   accent hue at rest makes the set read as one palette; hovering a card
   drops the overlay and restores the game's true colours.

   Scoped to the Selected Projects cards only; small media elsewhere on the
   page is left alone. The :not(.wip-layout) guards date from when Dead Saints
   Parade used the stacked variant and sat outside the palette. It now leads
   the section as an ordinary card and takes the overlay like the rest, so the
   guards currently match everything. They are kept because .wip-layout below
   is still a usable stacked layout for any future headline project.

   The knobs below are what the dev palette dock drives live. */
:root {
  --ov-angle: 135deg;
  --ov-tint: 0.69;                    /* overlay opacity at rest */
  --ov-hover-tint: 0;                 /* overlay opacity on hover — 0 reveals true colour */
  --ov-sat: 0.67;                     /* media saturation at rest */
  --ov-blend: color;
  --ov-a: linear-gradient(var(--ov-angle), rgb(var(--accent-rgb)), rgb(var(--accent2-rgb)));
  --ov-b: var(--ov-a);                /* differs from --ov-a only in alternate mode */
}

.project-card:not(.wip-layout) .project-thumb {
  isolation: isolate;                 /* contain the blend to the thumbnail */
}
.project-card:not(.wip-layout) .project-thumb::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--ov-a);
  mix-blend-mode: var(--ov-blend);
  opacity: var(--ov-tint);
  transition: opacity 0.35s ease;
  pointer-events: none;
  z-index: 1;
}
.project-card:not(.wip-layout):nth-of-type(even) .project-thumb::before {
  background: var(--ov-b);
}
.project-card:not(.wip-layout) .project-thumb img,
.project-card:not(.wip-layout) .project-thumb video {
  filter: saturate(var(--ov-sat));
  transition: filter 0.35s ease;
}
.project-card:not(.wip-layout):hover .project-thumb::before {
  opacity: var(--ov-hover-tint);
}
.project-card:not(.wip-layout):hover .project-thumb img,
.project-card:not(.wip-layout):hover .project-thumb video {
  filter: none;
}

.project-card:nth-child(1) .project-thumb {
  background: radial-gradient(ellipse at 30% 50%, rgba(var(--accent-rgb),0.08) 0%, transparent 65%), var(--surface-hover);
}
.project-card:nth-child(2) .project-thumb {
  background: radial-gradient(ellipse at 70% 40%, rgba(var(--accent2-rgb),0.07) 0%, transparent 65%), var(--surface-hover);
}
.project-card:nth-child(3) .project-thumb {
  background: radial-gradient(ellipse at 50% 60%, rgba(var(--accent-rgb),0.07) 0%, transparent 65%), var(--surface-hover);
}
.project-card:nth-child(4) .project-thumb {
  background: radial-gradient(ellipse at 40% 35%, rgba(var(--accent2-rgb),0.07) 0%, transparent 65%), var(--surface-hover);
}
.project-card:nth-child(5) .project-thumb {
  background: radial-gradient(ellipse at 60% 55%, rgba(var(--accent-rgb),0.07) 0%, transparent 65%), var(--surface-hover);
}

.project-body { padding: 1.15rem 1.4rem; display: flex; flex-direction: column; justify-content: center; }

.project-top-row {
  display: flex; justify-content: space-between;
  align-items: baseline; margin-bottom: 0.2rem;
  flex-wrap: wrap; gap: 0.4rem;
}

.project-name {
  font-family: 'Instrument Serif', serif;
  font-size: 1.02rem; font-weight: 400;
}

.project-badge {
  font-family: 'Geist Mono', monospace;
  font-size: 0.62rem; color: var(--text-dim);
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--border);
  padding: 0.1rem 0.45rem; border-radius: 3px;
}

.project-bridge {
  font-family: 'Geist Mono', monospace;
  font-size: 0.72rem;
  color: var(--accent);
  line-height: 1.5;
  margin: 0.15rem 0 0.55rem;
  padding-left: 0.6rem;
  border-left: 2px solid var(--accent);
  opacity: 0.88;
}

.project-desc {
  color: var(--text-mid); font-size: 0.84rem;
  margin-bottom: 0.65rem; line-height: 1.55;
}

.meta-row {
  display: flex; flex-wrap: wrap;
  gap: 0.2rem 1.1rem; font-size: 0.78rem;
  margin-bottom: 0.6rem;
}

.meta-pair { display: flex; gap: 0.3rem; }
.meta-pair .ml { color: var(--text-dim); }
.meta-pair .mv { color: var(--text); font-weight: 500; }

.project-tags { display: flex; flex-wrap: wrap; gap: 0.25rem; }

.ptag {
  font-size: 0.66rem; font-weight: 500;
  color: var(--text-dim);
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--border);
  padding: 0.12rem 0.5rem; border-radius: 3px;
}

.project-actions {
  margin-top: 0.6rem; padding-top: 0.55rem;
  border-top: 1px solid var(--border-soft);
  display: flex; gap: 1.2rem; align-items: center;
  font-size: 0.78rem; font-weight: 500;
}
.project-actions a {
  transition: color 0.15s ease, letter-spacing 0.2s ease;
}
.project-actions a:hover {
  letter-spacing: 0.01em;
}
/* Steam wishlist button (Dead Saints Parade WIP card) */
.steam-btn {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.5rem 1rem; border-radius: 6px;
  background: rgba(var(--accent-rgb),0.08);
  color: var(--accent) !important; font-weight: 600; letter-spacing: 0.01em;
  border: 1px solid var(--accent-dim);
  transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.2s ease, border-color 0.2s ease;
}
.steam-btn svg { flex-shrink: 0; }
.steam-btn:hover {
  transform: translateY(-1px);
  background: var(--accent); color: var(--bg) !important;
  border-color: var(--accent);
  box-shadow: 0 6px 18px rgba(var(--accent-rgb),0.22);
  letter-spacing: 0.01em;
}

/* ── CURRENTLY WORKING ON (WIP) ─────────────────────────── */
/* Stacked card variant: 16:9 media on top, body underneath */
.project-card.wip-layout {
  grid-template-columns: 1fr;
}
.project-card.wip-layout .project-thumb {
  aspect-ratio: 16 / 9;
  /* 16:9 across a 1440px card is 800px of video before a single word of copy.
     Cap it and let object-fit crop — the banner still reads as a banner. */
  max-height: min(48vh, 400px);
  min-height: 0;
  width: 100%;
  border-right: none;
  border-bottom: 1px solid var(--border);
}
/* Gradient edge blend is horizontal — disable for stacked layout */
.project-card.wip-layout .project-thumb::after {
  display: none;
}

/* ── Pulsing status dots ──────────────────────────────────────
   These used to animate box-shadow spread, which the compositor cannot
   touch: every frame of a loop that never ends was a main-thread repaint,
   so the page never went idle. A box-shadow with 0 blur and N spread is
   just a filled disc drawn behind the dot, so scaling a same-coloured
   pseudo-element produces the identical halo for free.
   --pulse-scale is (dot + 2 x spread) / dot. */
.live-dot {
  position: relative;
  --pulse-scale: 3.67;             /* 6px dot + 8px ring = 22px */
  width: 6px; height: 6px;
  background: var(--accent);
  border-radius: 50%;
  display: inline-block;
}
.live-dot::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: 50%;
  background: rgba(var(--accent-rgb),0.55);
  animation: dot-pulse 2.4s infinite;
}
@keyframes dot-pulse {
  0%        { transform: scale(1); opacity: 1; }
  70%, 100% { transform: scale(var(--pulse-scale)); opacity: 0; }
}

.project-badge.active {
  color: var(--accent);
  border-color: var(--accent-dim);
  background: rgba(var(--accent-rgb),0.06);
}

.wip-progress {
  margin-top: 0.4rem;
  padding-top: 0.65rem;
  border-top: 1px solid var(--border-soft);
}
.wip-progress-label {
  display: flex;
  justify-content: space-between;
  font-family: 'Geist Mono', monospace;
  font-size: 0.66rem;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 0.4rem;
}
.wip-progress-label .pct { color: var(--accent); }
.wip-progress-bar {
  height: 3px;
  background: rgba(255,255,255,0.04);
  border-radius: 2px;
  overflow: hidden;
}
.wip-progress-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--accent-dim), var(--accent));
  border-radius: 2px;
  box-shadow: 0 0 8px rgba(var(--accent-rgb),0.3);
}

.wip-column-label {
  font-family: 'Geist Mono', monospace;
  font-size: 0.62rem;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 0.9rem 0 0.5rem;
  padding-bottom: 0.35rem;
  border-bottom: 1px solid var(--border-soft);
}

/* The board runs in two columns on desktop so eight items cost four rows */
.wip-milestones.wip-board {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.3rem 1.6rem;
}
.wip-milestones {
  display: flex;
  flex-direction: column;
  gap: 0.32rem;
}
.wip-milestone {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.74rem;
  color: var(--text-dim);
  font-family: 'Geist Mono', monospace;
  line-height: 1.35;
}
.wip-milestone .check {
  width: 12px; height: 12px;
  border-radius: 50%;
  border: 1px solid var(--border);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font-size: 0.55rem;
  color: var(--text-dim);
}
.wip-milestone.done .check {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg);
}
.wip-milestone.done { color: var(--text-mid); }
.wip-milestone.active .check {
  border-color: var(--accent);
  background: rgba(var(--accent-rgb),0.1);
  color: var(--accent);
}
.wip-milestone.active { color: var(--text); }

/* ── Shipped systems: collapsed receipts ──
   Twenty-two done items are proof of work, not reading material. They sit
   behind a summary row and open as chips, so the default card height is
   governed by what's still in flight. */
.wip-shipped {
  margin-top: 0.9rem;
  border-top: 1px solid var(--border-soft);
}
.wip-shipped > summary {
  display: flex; align-items: center; gap: 0.5rem;
  list-style: none;
  cursor: pointer;
  padding: 0.55rem 0 0;
  font-family: 'Geist Mono', monospace;
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  transition: color 0.2s ease;
}
.wip-shipped > summary::-webkit-details-marker { display: none; }
.wip-shipped > summary:hover { color: var(--text-mid); }
.wip-shipped > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
.wip-shipped .ws-count {
  font-size: 0.72rem; font-weight: 500;
  color: var(--accent);
  background: rgba(var(--accent-rgb),0.08);
  border: 1px solid rgba(var(--accent-rgb),0.22);
  border-radius: 4px;
  padding: 0.05rem 0.4rem;
}
.wip-shipped .ws-chev {
  display: inline-flex; margin-left: auto;
  transition: transform 0.25s ease;
}
.wip-shipped[open] .ws-chev { transform: rotate(180deg); }

.wip-chips {
  display: flex; flex-wrap: wrap; gap: 0.3rem;
  margin-top: 0.7rem;
}
.wip-chip {
  display: inline-flex; align-items: center; gap: 0.35rem;
  font-family: 'Geist Mono', monospace;
  font-size: 0.66rem;
  color: var(--text-mid);
  background: rgba(255,255,255,0.02);
  border: 1px solid var(--border-soft);
  border-radius: 4px;
  padding: 0.16rem 0.5rem;
}
.wip-chip::before {
  content: '✓';
  font-size: 0.6rem;
  color: var(--accent);
}

.wip-updated {
  font-family: 'Geist Mono', monospace;
  font-size: 0.66rem;
  color: var(--text-dim);
  margin-top: 0.85rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--border-soft);
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.wip-updated::before {
  content: '';
  width: 4px; height: 4px;
  background: var(--accent-dim);
  border-radius: 50%;
}

@media (max-width: 760px) {
  .wip-milestones.wip-board { grid-template-columns: 1fr; gap: 0.3rem; }
}

/* ── CASE STUDY ──────────────────────────────────────────── */
.cs-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.7rem;
  margin-top: 1rem;
}

.cs-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 1rem 1.1rem;
  transition: var(--transition-lift);
}
.cs-card:hover {
  border-color: var(--accent-dim);
  transform: translateY(-2px);
  box-shadow: var(--shadow-soft);
}

.cs-card .cs-label {
  font-family: 'Geist Mono', monospace;
  font-size: 0.62rem; color: var(--accent);
  text-transform: uppercase; letter-spacing: 0.08em;
  margin-bottom: 0.35rem;
}

.cs-card h3 {
  font-family: 'Instrument Serif', serif;
  font-size: 0.9rem; font-weight: 400;
  margin-bottom: 0.3rem; line-height: 1.25;
}

.cs-card p {
  font-size: 0.78rem; color: var(--text-mid); line-height: 1.5;
}

/* The outcome card promises three principles that live further down the page,
   past the scope table and the dash-strike spotlight they depend on. */
.cs-card-link {
  display: inline-block;
  margin-top: 0.5rem;
  font-family: 'Geist Mono', monospace;
  font-size: 0.66rem;
  letter-spacing: 0.04em;
}

.media-placeholder {
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: 6px;
  padding: 1.25rem 1rem;
  text-align: center;
  color: var(--text-dim);
  font-size: 0.78rem;
  margin-top: 0.6rem;
}

/* ── DASHBOARD EMBED ─────────────────────────────────────── */
.dashboard-section {
  margin-top: 1.25rem;
}

.dashboard-embed {
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  background: var(--bg);
}

.dashboard-embed iframe {
  width: 100%;
  height: 740px;
  border: none;
  display: block;
}

.dashboard-label {
  padding: 0.55rem 1rem;
  font-size: 0.75rem;
  color: var(--text-dim);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.dashboard-label span {
  font-family: 'Geist Mono', monospace;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--accent);
}

.dashboard-label a {
  font-size: 0.72rem;
  color: var(--text-dim);
  transition: color 0.15s;
}
.dashboard-label a:hover { color: var(--accent); }

/* ── CASE STUDY RETROSPECTIVE ───────────────────────────── */
.cs-retro {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent-dim);
  border-radius: 6px;
  padding: 1rem 1.2rem;
  margin-top: 1rem;
}
.cs-retro-label {
  font-family: 'Geist Mono', monospace;
  font-size: 0.62rem; color: var(--accent);
  text-transform: uppercase; letter-spacing: 0.08em;
  margin-bottom: 0.3rem;
}
.cs-retro-title {
  font-family: 'Instrument Serif', serif;
  font-size: 0.95rem; font-weight: 400;
  margin-bottom: 0.45rem; line-height: 1.25;
}
.cs-retro-body p {
  font-size: 0.8rem; color: var(--text-mid);
  line-height: 1.6; margin-bottom: 0.5rem;
}
.cs-retro-body p:last-child { margin-bottom: 0; }

/* ── ABOUT ───────────────────────────────────────────────── */
.about-layout {
  display: grid;
  grid-template-columns: 150px 1fr;
  gap: 1.5rem; align-items: start;
}

.about-photo {
  width: 150px; height: 184px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.72rem; color: var(--text-dim);
  overflow: hidden;
}
.about-photo img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}

.about-body p {
  color: var(--text-mid); font-size: 0.88rem;
  margin-bottom: 0.6rem; max-width: 68ch; line-height: 1.6;
}

.about-body .design-philosophy {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.85rem 1rem;
  margin: 0.85rem 0;
  font-size: 0.82rem;
  color: var(--text-mid);
  line-height: 1.6;
}
.about-body .design-philosophy strong {
  color: var(--text);
  font-weight: 600;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  display: block;
  margin-bottom: 0.3rem;
}

.tools-row {
  margin-top: 0.75rem;
  display: flex; flex-wrap: wrap;
  gap: 0.3rem 1.8rem;
  font-size: 0.8rem;
}

.tools-row .tr-item { display: flex; gap: 0.3rem; }
.tools-row dt { color: var(--text-dim); font-weight: 500; }
.tools-row dd { color: var(--text); margin: 0; }

/* ── LOOKING FOR ─────────────────────────────────────────── */
.looking-for {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.6rem 1rem;
  margin-top: 0.85rem;
  font-size: 0.82rem;
  color: var(--text-mid);
  line-height: 1.55;
}
.looking-for strong {
  color: var(--text);
  font-weight: 600;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-right: 0.3rem;
}

/* ── ABOUT SUB-SECTIONS ─────────────────────────────────── */
.about-block {
  margin-bottom: 0.5rem;
}

.about-label {
  font-family: 'Geist Mono', monospace;
  font-size: 0.62rem;
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 0.3rem;
}

/* ── ABOUT: three-column prose band ──────────────────────
   Background reads as the lede and the two highlight boxes span the
   full width, so the middle three blocks are what was leaving the
   right half of the section empty. As a 3-up grid they fill it while
   each column stays inside the 68ch measure set on .about-body p. */
/* The lede sits above the full-width philosophy box, so the 68ch measure left
   the right half of the section empty. Widened rather than uncapped: with no
   cap at all it runs ~165 characters per line on a 1440px display, and the
   measure is the one typographic rule this section keeps everywhere else. */
.about-lede p { max-width: 90ch; }

.about-cols {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.6rem 2.2rem;
  margin: 0.9rem 0;
  align-items: start;
}
/* Three blocks, so auto-fit's 2-column step put one alone on a second row with
   an empty cell beside it — the exact hole this band exists to close. Go 3-up
   only where three 280px columns genuinely fit: .about-body is viewport - 6vw
   padding - 150px photo - 1.5rem gap, which clears 3*280 + 2*2.2rem at about
   1154px. Below that they stack full-width, which leaves no hole either. */
@media (min-width: 1160px) {
  .about-cols { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ═══════════════════════════════════════════════════════════
   EXPERIENCE & EDUCATION
   A rail of icon nodes, where the date column is a span bar on a
   shared 2017-2028 axis. Every track is the same width and scale,
   so the overlaps read straight down the column: the studio starts
   while teaching is still running, and Statistics sits alone at the
   far left, which is the credential the rest of the page leans on.

   Bar geometry: pct(year) = (year - 2017) / 11 * 100, set inline as
   left/width so changing a date is a two-number edit in the markup.
   Ongoing roles set left only: --xp-now is where "now" sits on that
   scale, and .is-open ends there, so moving the marker moves the axis
   label, the guide line and every open bar together.

   .xp-sprite is hidden by the shared off-screen sprite rule — see
   .flag-sprite under LANGUAGE SWITCHER, which explains it for both.
   ═══════════════════════════════════════════════════════════ */
.xp-icon {
  width: 15px; height: 15px; display: block;
  fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}

.xp-rail {
  position: relative; padding-left: 2.15rem;
  /* pct(Aug 2026) on the 2017-2028 scale. The one number to edit when
     "now" moves; nothing else in the section hardcodes a date. */
  --xp-now: 87.7%;
}
.xp-rail::before {
  content: ''; position: absolute; left: 11px; top: 30px; bottom: 42px; width: 1px;
  background: linear-gradient(180deg,
    transparent 0%,
    rgba(var(--accent-rgb),0.35) 8%,
    rgba(var(--accent2-rgb),0.28) 92%,
    transparent 100%);
}

/* Axis header shares the row grid so it lines up with every track. */
.xp-axis-row {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(280px, 1fr);
  gap: 0 1.5rem; align-items: end;
}
.xp-axis-lead {
  font-family: 'Geist Mono', monospace; font-size: 0.58rem;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-dim);
}
.xp-axis {
  position: relative; height: 15px;
  font-family: 'Geist Mono', monospace; font-size: 0.55rem; color: var(--text-dim);
  border-bottom: 1px solid var(--border-soft);
}
.xp-axis b { position: absolute; bottom: 2px; font-weight: 400; transform: translateX(-50%); }
.xp-axis b:first-child { transform: none; }
.xp-axis .xp-axis-now { color: var(--accent); left: var(--xp-now); }

.xp-group {
  font-family: 'Geist Mono', monospace; font-size: 0.6rem;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--text-dim); margin: 1.15rem 0 0.45rem; position: relative;
}
.xp-group::before {
  content: ''; position: absolute; left: -2.15rem; top: 0.55em;
  width: 22px; height: 1px; background: var(--border);
}

.xp-row {
  position: relative; display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(280px, 1fr);
  gap: 0 1.5rem; align-items: start; padding: 0.42rem 0 0.6rem;
}
.xp-node {
  position: absolute; left: -2.15rem; top: 0.45rem;
  width: 23px; height: 23px; border-radius: 6px;
  background: var(--surface); border: 1px solid var(--border);
  display: grid; place-items: center; color: var(--text-dim);
  transition: color 0.2s ease, border-color 0.2s ease;
}
.xp-row:hover .xp-node { color: var(--accent); border-color: rgba(var(--accent-rgb),0.45); }
.xp-head { display: flex; align-items: baseline; gap: 0.45rem; flex-wrap: wrap; }
.xp-role { font-size: 0.86rem; color: var(--text); font-weight: 500; }
.xp-org  { font-size: 0.81rem; color: var(--text-mid); }
.xp-org::before { content: '\00b7\00a0'; color: var(--text-dim); }
.xp-desc {
  font-size: 0.8rem; color: var(--text-mid);
  line-height: 1.55; margin-top: 0.12rem; max-width: 54ch;
}

/* ── the inline chart ── */
.xp-track {
  position: relative; height: 27px;
  /* One gridline per year. They line up down the rail across all rows,
     which is what makes the bars readable as a shared scale. */
  background: repeating-linear-gradient(90deg,
    var(--border-soft) 0 1px, transparent 1px calc(100% / 11));
}
/* Baseline groove, so a short bar still reads as sitting on a scale. */
.xp-track::before {
  content: ''; position: absolute; left: 0; right: 0; top: 50%;
  height: 1px; background: var(--border-soft);
}
/* "Now" drawn per track rather than as one absolute line over the rail:
   the segments align into a continuous guide and it survives the mobile
   reflow, where the track column moves under the text. */
.xp-track::after {
  content: ''; position: absolute; top: -2px; bottom: -2px; left: var(--xp-now);
  width: 1px; background: rgba(var(--accent-rgb),0.22);
}
.xp-bar {
  position: absolute; top: 50%; transform: translateY(-50%);
  height: 12px; border-radius: 2px; border: 1px solid;
}
.xp-bar.is-design {
  background: linear-gradient(90deg, rgba(var(--accent-rgb),0.32), rgba(var(--accent-rgb),0.14));
  border-color: rgba(var(--accent-rgb),0.42);
}
.xp-bar.is-edu {
  background: linear-gradient(90deg, rgba(var(--accent2-rgb),0.3), rgba(var(--accent2-rgb),0.11));
  border-color: rgba(var(--accent2-rgb),0.4);
}
.xp-bar.is-side {
  background: rgba(255,255,255,0.05); border-color: var(--border);
}
/* An open span runs from its start to "now", not to the end of the axis.
   Setting right instead of a width keeps that true without restating the
   date: the bar is as long as the role has actually been. */
.xp-bar.is-open {
  right: calc(100% - var(--xp-now));
  border-right-style: dashed; border-right-color: rgba(var(--accent-rgb),0.55);
}
.xp-dot {
  position: absolute; top: 50%; width: 10px; height: 10px; margin-left: -5px;
  transform: translateY(-50%) rotate(45deg);
  background: rgba(var(--accent2-rgb),0.32);
  border: 1px solid rgba(var(--accent2-rgb),0.55);
}
.xp-when {
  font-family: 'Geist Mono', monospace; font-size: 0.62rem;
  color: var(--text-dim); margin-top: 0.1rem; white-space: nowrap;
}
.xp-now {
  position: relative;
  display: inline-flex; align-items: center; gap: 0.32rem;
  font-size: 0.57rem; color: var(--accent);
  text-transform: uppercase; letter-spacing: 0.08em;
}
.xp-now::before {
  content: ''; width: 5px; height: 5px; border-radius: 50%;
  background: var(--accent);
}
/* Same halo, and the same reason, as .live-dot: an animated box-shadow spread
   is a main-thread repaint on every frame of a loop that never ends, so the
   page never goes idle. ::before is the dot itself and cannot carry a pseudo
   of its own, so the ring is a second one scaled over it. It reuses dot-pulse
   verbatim; only --pulse-scale, (dot + 2 x spread) / dot, differs. */
.xp-now::after {
  content: '';
  --pulse-scale: 3.4;              /* 5px dot + 6px ring = 17px */
  position: absolute; left: 0; top: 50%;
  width: 5px; height: 5px; margin-top: -2.5px;
  border-radius: 50%;
  background: rgba(var(--accent-rgb),0.45);
  animation: dot-pulse 2.6s ease-out infinite;
}
@media (prefers-reduced-motion: reduce) { .xp-now::after { display: none; } }

.xp-legend {
  display: flex; gap: 1rem; flex-wrap: wrap;
  margin-top: 1rem; padding-top: 0.6rem;
  border-top: 1px solid var(--border-soft);
  font-size: 0.66rem; color: var(--text-dim);
}
.xp-legend span { display: inline-flex; align-items: center; gap: 0.35rem; }
.xp-legend i { width: 10px; height: 9px; border-radius: 2px; border: 1px solid; font-style: normal; }
.xp-legend .lg-design { background: rgba(var(--accent-rgb),0.28); border-color: rgba(var(--accent-rgb),0.42); }
.xp-legend .lg-edu    { background: rgba(var(--accent2-rgb),0.28); border-color: rgba(var(--accent2-rgb),0.4); }
.xp-legend .lg-side   { background: rgba(255,255,255,0.05); border-color: var(--border); }

/* Below 860px the track column can't hold a readable scale beside text,
   so it drops under the row and spans full width. Bars are percentage
   based, so they rescale with no other change. */
@media (max-width: 860px) {
  .xp-axis-row, .xp-row { grid-template-columns: 1fr; }
  .xp-axis-lead { display: none; }
  .xp-track { margin-top: 0.45rem; }
  .xp-when { margin-top: 0.2rem; }
}

@media print {
  .xp-row { break-inside: avoid; }
  .xp-node { background: #fff !important; border: 1px solid #ccc !important; }
  .xp-track::after, .xp-now::before, .xp-now::after { display: none; }
}

/* ── SITE FOOTER ────────────────────────────────────────── */
.site-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1.2rem 2rem;
  margin-top: 1.5rem;
  border-top: 1px solid var(--border-soft);
  font-family: 'Geist Mono', monospace;
  font-size: 0.66rem;
  color: var(--text-dim);
  max-width: 960px;
  margin-left: auto;
  margin-right: auto;
}

@media (max-width: 480px) {
  .site-footer {
    flex-direction: column;
    gap: 0.25rem;
    text-align: center;
    padding: 1rem 1.25rem;
  }
}

/* ── CONTACT ─────────────────────────────────────────────── */
/* The whole section reads as one lit panel — it's the last thing on
   the page, so it has to say "I'm here, email me" without scrolling
   past it. Accent-tinted card + oversized CTA carry that weight. */
.contact-panel {
  position: relative;
  margin-top: 1.1rem;
  padding: clamp(1.4rem, 3vw, 2.1rem) clamp(1.2rem, 3vw, 2.2rem);
  border: 1px solid rgba(var(--accent-rgb),0.28);
  border-radius: 14px;
  background:
    radial-gradient(ellipse at 8% 0%, rgba(var(--accent-rgb),0.10) 0%, transparent 62%),
    linear-gradient(180deg, var(--surface-hover) 0%, var(--surface) 100%);
  box-shadow: var(--shadow-glow);
  overflow: hidden;
}
/* Accent hairline across the top edge */
.contact-panel::before {
  content: '';
  position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, var(--accent) 0%, var(--accent-dim) 40%, transparent 100%);
  opacity: 0.85;
}

.contact-status {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: 'Geist Mono', monospace;
  font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.11em;
  color: var(--accent);
  background: rgba(var(--accent-rgb),0.08);
  border: 1px solid rgba(var(--accent-rgb),0.24);
  border-radius: 999px;
  padding: 0.3rem 0.75rem 0.3rem 0.6rem;
}

.contact-headline {
  font-family: 'Instrument Serif', serif;
  font-size: clamp(1.45rem, 3vw, 1.95rem);
  font-weight: 400; line-height: 1.15; letter-spacing: -0.015em;
  color: var(--text);
  margin: 0.9rem 0 0.55rem;
  text-shadow: 0 0 40px rgba(var(--accent-rgb),0.10);
}

.contact-intro {
  color: var(--text-mid);
  font-size: 1rem;
  line-height: 1.65;
  max-width: 58ch;
}

/* A recruiter reading from another continent needs this before they invest in
   scheduling a call, not after. */
.contact-location {
  display: inline-flex; align-items: center; gap: 0.4rem;
  margin-top: 0.7rem;
  font-family: 'Geist Mono', monospace;
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  color: var(--text-dim);
}
.contact-location svg { flex-shrink: 0; opacity: 0.8; }

/* Live "available" dot inside the status pill */
#contact .section-aside { display: inline-flex; align-items: center; gap: 0.45rem; }
.contact-avail-dot {
  position: relative;
  --pulse-scale: 3;                /* 7px dot + 7px ring = 21px */
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent); flex-shrink: 0;
}
.contact-avail-dot::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: 50%;
  background: rgba(var(--accent-rgb),0.55);
  animation: dot-pulse 2.4s ease-out infinite;
}

.contact-top { display: grid; gap: 1.4rem; }

.contact-actions {
  display: flex; flex-wrap: wrap; align-items: stretch;
  gap: 0.7rem;
}

/* Wide screens: pitch on the left, the ask on the right at eye level */
@media (min-width: 900px) {
  .contact-top {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 2.5rem;
  }
  .contact-actions { flex-direction: column; align-items: stretch; }
}

/* Primary email CTA */
.contact-cta {
  display: inline-flex; align-items: center; gap: 0.9rem;
  background: var(--accent); color: var(--bg);
  border: 1px solid var(--accent); border-radius: 10px;
  padding: 0.85rem 1.3rem 0.85rem 1.1rem;
  text-decoration: none;
  position: relative;
  box-shadow: 0 8px 22px rgba(var(--accent-rgb),0.22);
  transition: transform 0.25s cubic-bezier(0.22,1,0.36,1), box-shadow 0.25s ease;
}
/* Slow glow so the eye lands here first; stops the moment it's hovered.
   The glow lives on a pseudo-element at its brightest and only its opacity
   animates — animating the box-shadow itself repainted the button on every
   frame, forever. z-index puts it behind the button's own background. */
.contact-cta::before {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  z-index: -1;
  box-shadow: 0 8px 30px rgba(var(--accent-rgb),0.38);
  animation: cta-breathe 3.4s ease-in-out infinite;
}
@keyframes cta-breathe {
  0%, 100% { opacity: 0.55; }
  50%      { opacity: 1; }
}
.contact-cta:hover {
  text-decoration: none;
  transform: translateY(-2px);
  box-shadow: 0 12px 30px rgba(var(--accent-rgb),0.34);
}
.contact-cta:hover::before { animation: none; opacity: 0; }
.contact-cta:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.contact-cta .cta-icon { display: inline-flex; flex-shrink: 0; }
.contact-cta .cta-text { display: flex; flex-direction: column; line-height: 1.25; }
.contact-cta .cta-label {
  font-family: 'Geist Mono', monospace;
  font-size: 0.64rem; text-transform: uppercase; letter-spacing: 0.1em;
  opacity: 0.75;
}
.contact-cta .cta-value { font-size: 1.02rem; font-weight: 600; letter-spacing: -0.005em; }
.contact-cta .cta-arrow { display: inline-flex; margin-left: 0.35rem; transition: transform 0.2s ease; }
.contact-cta:hover .cta-arrow { transform: translateX(3px); }

/* Copy-to-clipboard, for people who write mail somewhere else */
.contact-copy {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  font-family: inherit; font-size: 0.85rem; font-weight: 500;
  color: var(--text-mid); cursor: pointer;
  background: transparent;
  border: 1px solid var(--border); border-radius: 10px;
  padding: 0.85rem 1.1rem;
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease, transform 0.2s ease;
}
.contact-copy:hover {
  color: var(--text); border-color: var(--accent-dim);
  background: rgba(var(--accent-rgb),0.05);
  transform: translateY(-2px);
}
.contact-copy:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.contact-copy .copy-icon { display: inline-flex; flex-shrink: 0; }
.contact-copy.copied {
  color: var(--accent);
  border-color: rgba(var(--accent-rgb),0.45);
  background: rgba(var(--accent-rgb),0.08);
}

@media (prefers-reduced-motion: reduce) {
  .contact-cta::before { animation: none; }
  .contact-avail-dot::after { animation: none; }
  .live-dot::after { animation: none; }
}

/* Secondary contact cards */
.contact-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.6rem;
  margin-top: 1.6rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
}
.contact-item {
  display: flex; align-items: center; gap: 0.7rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.65rem 0.8rem;
  color: var(--text); text-decoration: none;
  transition: var(--transition-lift);
}
.contact-item:hover {
  border-color: var(--accent-dim);
  text-decoration: none;
  transform: translateY(-2px);
  box-shadow: var(--shadow-soft);
}
.contact-item .ci-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; flex-shrink: 0;
  border-radius: 7px;
  background: rgba(var(--accent-rgb),0.08);
  border: 1px solid rgba(var(--accent-rgb),0.18);
  color: var(--accent);
  transition: background 0.2s ease, border-color 0.2s ease;
}
.contact-item:hover .ci-icon {
  background: rgba(var(--accent-rgb),0.14);
  border-color: rgba(var(--accent-rgb),0.35);
}
.contact-item .ci-text { display: flex; flex-direction: column; line-height: 1.3; min-width: 0; }
.contact-item .ci-label {
  color: var(--text-dim);
  font-family: 'Geist Mono', monospace;
  font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.08em;
}
.contact-item .ci-value {
  font-size: 0.85rem; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ── FLOATING CONTACT DOCK ───────────────────────────────
   The contact panel lives at the bottom of a long page. This is the same
   offer, pinned: it slides up once the intro has scrolled away, and slides
   back down over #contact so the two never say the same thing at once.
   Visibility is driven by JS (.show) — see the dock block in index.html. */
.contact-dock {
  position: fixed;
  left: 50%;
  bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
  z-index: 60;
  display: flex; align-items: center; gap: 0.48rem;
  max-width: calc(100vw - 1.5rem);
  padding: 0.48rem 0.6rem 0.48rem 0.52rem;
  border-radius: 999px;
  background: rgba(29,29,33,0.88);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid rgba(var(--accent-rgb),0.22);
  box-shadow: 0 12px 36px rgba(0,0,0,0.45), 0 2px 8px rgba(0,0,0,0.3);
  /* Parked below the fold until .show — transform keeps it off the compositor's
     critical path, so the slide never fights the scroll handler */
  transform: translate(-50%, calc(100% + 1.6rem));
  opacity: 0;
  pointer-events: none;
  transition: transform 0.45s cubic-bezier(0.22,1,0.36,1), opacity 0.3s ease;
}
.contact-dock.show {
  transform: translate(-50%, 0);
  opacity: 1;
  pointer-events: auto;
}
/* A modal owns the screen while it's open */
body.modal-open .contact-dock {
  transform: translate(-50%, calc(100% + 1.6rem));
  opacity: 0;
  pointer-events: none;
}

.contact-dock .cd-primary {
  display: inline-flex; align-items: center; gap: 0.65rem;
  background: var(--accent); color: var(--bg);
  border-radius: 999px;
  padding: 0.5rem 1.1rem 0.5rem 0.85rem;
  text-decoration: none;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.contact-dock .cd-primary:hover {
  text-decoration: none;
  transform: translateY(-1px);
  box-shadow: 0 6px 18px rgba(var(--accent-rgb),0.35);
}
.contact-dock .cd-primary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* Icons carry viewBoxes, so sizing them here overrides the width/height
   attributes in the markup and keeps every glyph on one scale */
.contact-dock svg { width: 19px; height: 19px; }
.contact-dock .cd-lead svg { width: 22px; height: 22px; }

.contact-dock .cd-lead { display: inline-flex; flex-shrink: 0; }
.contact-dock .cd-text { display: flex; flex-direction: column; line-height: 1.25; }
.contact-dock .cd-label {
  display: inline-flex; align-items: center; gap: 0.32rem;
  font-family: 'Geist Mono', monospace;
  font-size: 0.64rem; text-transform: uppercase; letter-spacing: 0.085em;
  opacity: 0.72;
  /* Always one line. Without this the pill shrinks to the email's width and
     the status folds onto a second row, which grows the whole dock by a line */
  white-space: nowrap;
}
.contact-dock .cd-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; flex-shrink: 0;
}
.contact-dock .cd-value { font-size: 1rem; font-weight: 600; letter-spacing: -0.005em; white-space: nowrap; }

.contact-dock .cd-div {
  width: 1px; height: 27px; flex-shrink: 0;
  background: var(--border);
  margin: 0 0.18rem;
}

.contact-dock .cd-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 41px; height: 41px; flex-shrink: 0;
  border-radius: 50%;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-mid);
  cursor: pointer; text-decoration: none;
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease, transform 0.2s ease;
}
.contact-dock .cd-btn:hover {
  color: var(--accent);
  border-color: var(--accent-dim);
  background: rgba(var(--accent-rgb),0.08);
  transform: translateY(-1px);
  text-decoration: none;
}
.contact-dock .cd-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Copy button swaps to a check for two seconds — copyEmail() adds .copied */
.contact-dock .cd-ico-copy,
.contact-dock .cd-ico-check { display: inline-flex; }
.contact-dock .cd-ico-check { display: none; }
.contact-dock .cd-copy.copied {
  color: var(--accent);
  border-color: rgba(var(--accent-rgb),0.45);
  background: rgba(var(--accent-rgb),0.1);
}
.contact-dock .cd-copy.copied .cd-ico-copy { display: none; }
.contact-dock .cd-copy.copied .cd-ico-check { display: inline-flex; }

/* Small tablets / large phones: the status line is still worth showing, but
   it's the first thing to run short of room. Trim the type and the chrome so
   the label keeps its single line instead of pushing the dock to two rows.
   Longer translations ("Disponível para novas vagas") set the width here. */
@media (max-width: 760px) {
  .contact-dock {
    gap: 0.4rem;
    padding: 0.44rem 0.52rem 0.44rem 0.48rem;
  }
  .contact-dock .cd-primary { padding: 0.5rem 0.95rem 0.5rem 0.75rem; gap: 0.55rem; }
  .contact-dock .cd-label { font-size: 0.58rem; letter-spacing: 0.045em; }
  .contact-dock .cd-value { font-size: 0.95rem; }
  .contact-dock .cd-btn { width: 38px; height: 38px; }
  .contact-dock svg { width: 18px; height: 18px; }
  .contact-dock .cd-lead svg { width: 21px; height: 21px; }
  .contact-dock .cd-div { height: 25px; margin: 0 0.1rem; }
}

/* The dock is wide at full size — step it down before it hits the viewport
   edges rather than letting it wrap */
@media (max-width: 580px) {
  .contact-dock {
    gap: 0.35rem;
    padding: 0.4rem 0.45rem;
    bottom: calc(0.7rem + env(safe-area-inset-bottom, 0px));
  }
  /* Only the address survives — the status line is the first thing to go */
  .contact-dock .cd-label { display: none; }
  .contact-dock .cd-primary { padding: 0.5rem 0.88rem 0.5rem 0.7rem; gap: 0.5rem; }
  .contact-dock .cd-value { font-size: 0.9rem; }
  .contact-dock .cd-btn { width: 36px; height: 36px; }
  .contact-dock svg { width: 18px; height: 18px; }
  .contact-dock .cd-lead svg { width: 20px; height: 20px; }
  .contact-dock .cd-div { margin: 0; height: 24px; }
}
@media (max-width: 400px) {
  /* Email + copy + LinkedIn is the useful minimum on a small phone */
  .contact-dock .cd-btn:nth-last-child(-n+2) { display: none; }
  .contact-dock .cd-btn { width: 34px; height: 34px; }
  .contact-dock .cd-value { font-size: 0.85rem; }
}

@media (prefers-reduced-motion: reduce) {
  .contact-dock { transition: opacity 0.2s ease; }
  .contact-dock .cd-primary:hover,
  .contact-dock .cd-btn:hover { transform: none; }
}

/* ── ADDITIONAL WORK GRID ────────────────────────────────── */
.additional-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.5rem;
  margin-top: 0.85rem;
}

.additional-item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
  display: grid;
  grid-template-columns: 68px 1fr;
  transition: var(--transition-lift);
}
.additional-item:hover {
  border-color: var(--accent-dim);
  transform: translateY(-1px);
  box-shadow: var(--shadow-soft);
}

.additional-item .ai-thumb {
  background: var(--surface-hover);
  display: flex; align-items: center; justify-content: center;
  border-right: 1px solid var(--border);
  color: var(--text-dim);
  font-size: 0.58rem;
  font-weight: 500;
  min-height: 90px;
  position: relative;
}
.additional-item .ai-thumb img,
.additional-item .ai-thumb video {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%; object-fit: cover;
  display: block;
}

.additional-item .ai-body {
  padding: 0.5rem 0.7rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.additional-item .ai-name {
  font-family: 'Instrument Serif', serif;
  font-size: 0.85rem;
  font-weight: 400;
  line-height: 1.25;
}

.additional-item .ai-genre {
  font-size: 0.68rem;
  color: var(--text-dim);
  margin-top: 0.1rem;
}

.additional-item .ai-role {
  font-size: 0.72rem;
  color: var(--text-mid);
  margin-top: 0.25rem;
  line-height: 1.4;
}

.additional-item .ai-link {
  margin-top: 0.3rem;
  font-size: 0.68rem;
  font-weight: 500;
}

/* ── DOCUMENT GRID ───────────────────────────────────────── */
.doc-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
  margin-top: 0.85rem;
}

.doc-item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.7rem 0.9rem;
  display: flex;
  gap: 0.7rem;
  align-items: flex-start;
  transition: var(--transition-lift);
}
.doc-item:hover {
  border-color: var(--accent-dim);
  transform: translateY(-1px);
  box-shadow: var(--shadow-soft);
}

.doc-item .doc-type {
  font-family: 'Geist Mono', monospace;
  font-size: 0.58rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--bg);
  background: var(--accent-dim);
  padding: 0.15rem 0.4rem;
  border-radius: 3px;
  white-space: nowrap;
  flex-shrink: 0;
  margin-top: 0.1rem;
}

.doc-item .doc-type.doc-qdd { background: #8a7abf; }
.doc-item .doc-type.doc-ldd { background: #7aabb8; }
.doc-item .doc-type.doc-test { background: #b8967a; }

.doc-item .doc-body { flex: 1; min-width: 0; }

.doc-item .doc-name {
  font-family: 'Instrument Serif', serif;
  font-size: 0.88rem;
  font-weight: 400;
  line-height: 1.25;
}

.doc-item .doc-desc {
  font-size: 0.72rem;
  color: var(--text-mid);
  margin-top: 0.15rem;
  line-height: 1.45;
}

.doc-item a {
  margin-top: 0.25rem;
  font-size: 0.68rem;
  font-weight: 500;
  display: inline-block;
}

/* ── SCOPE TABLE (Guardian's Falls) ──────────────────────── */
.scope-table {
  width: 100%; border-collapse: collapse;
  margin: 0.85rem 0; font-size: 0.8rem;
}
.scope-table th {
  text-align: left; font-family: 'Geist Mono', monospace;
  font-size: 0.6rem; color: var(--accent);
  text-transform: uppercase; letter-spacing: 0.08em;
  padding: 0.55rem 0.75rem;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.scope-table td {
  padding: 0.55rem 0.75rem;
  border-bottom: 1px solid var(--border-soft);
  vertical-align: top; line-height: 1.5;
}
.scope-table .feature { color: var(--text); font-weight: 500; }
.scope-table .vision { color: var(--text-dim); font-size: 0.75rem; }
.scope-table .final { color: var(--text-mid); font-size: 0.75rem; }

/* ── SCOPE TABLE WRAPPER (for mobile horizontal scroll) ────── */
.scope-table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin: 0 -1rem;
  padding: 0 1rem;
  /* Four columns of prose never fit a phone, so this one keeps scrolling
     sideways rather than stacking. The shadow says so: it rides on
     background-attachment: scroll, so it appears only while there is still
     table to the right and fades out at the end. Without it the Status
     column — the point of the table — is simply invisible on a phone. */
  background:
    linear-gradient(to left, var(--bg) 45%, rgba(0,0,0,0)) right center / 52px 100% no-repeat,
    linear-gradient(to left, rgba(0,0,0,0.55), rgba(0,0,0,0)) right center / 28px 100% no-repeat;
  background-attachment: local, scroll;
}

@media (max-width: 640px) {
  .scope-table {
    min-width: 560px;
  }
}

/* ── BADGES ──────────────────────────────────────────────── */
.badge {
  display: inline-block; font-family: 'Geist Mono', monospace;
  font-size: 0.56rem; padding: 0.1rem 0.4rem; border-radius: 3px;
  text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap;
}
.badge-shipped { background: rgba(122,171,139,0.06); border: 1px solid rgba(122,171,139,0.3); color: #7aab8b; }
.badge-evolved { background: rgba(140,160,210,0.06); border: 1px solid rgba(140,160,210,0.3); color: #8ca0d2; }
.badge-simplified { background: rgba(201,179,106,0.06); border: 1px solid rgba(201,179,106,0.3); color: #c9b36a; }
.badge-cut { background: rgba(196,115,110,0.06); border: 1px solid rgba(196,115,110,0.25); color: #c4736e; }

/* ── MECHANIC SPOTLIGHT ──────────────────────────────────── */
.mechanic-spotlight {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px; padding: 0.9rem 1.1rem;
  margin: 0.85rem 0;
  display: grid; grid-template-columns: 180px 1fr;
  gap: 0; overflow: hidden;
}
.mechanic-spotlight .ms-visual {
  background: var(--surface-hover);
  border-right: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  color: var(--text-dim); font-size: 0.72rem;
  margin: -0.9rem 0 -0.9rem -1.1rem;
  padding: 0; text-align: center; line-height: 1.45;
  overflow: hidden;
  position: relative;
}
.mechanic-spotlight .ms-visual img,
.mechanic-spotlight .ms-visual video {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.mechanic-spotlight .ms-body { padding-left: 1rem; }
.mechanic-spotlight .ms-tag {
  font-family: 'Geist Mono', monospace;
  font-size: 0.58rem; text-transform: uppercase;
  letter-spacing: 0.06em; margin-bottom: 0.25rem;
  display: flex; align-items: center; gap: 0.5rem;
  color: var(--accent);
}
.mechanic-spotlight h3 {
  font-family: 'Instrument Serif', serif;
  font-size: 0.92rem; font-weight: 400;
  margin-bottom: 0.3rem; line-height: 1.25;
}
.mechanic-spotlight p {
  font-size: 0.78rem; color: var(--text-mid); line-height: 1.55;
}
.mechanic-spotlight .ms-iter {
  font-size: 0.75rem; color: var(--text-dim);
  padding-top: 0.35rem; border-top: 1px solid var(--border-soft);
  margin-top: 0.35rem; line-height: 1.5;
}
.mechanic-spotlight .ms-iter strong { color: var(--text-mid); font-weight: 500; }

/* ── TAKEAWAY GRID ───────────────────────────────────────── */
.takeaway-grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 0.6rem; margin: 0.85rem 0;
}
.takeaway-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px; padding: 0.85rem 1rem;
  transition: var(--transition-lift);
}
.takeaway-card:hover {
  border-color: var(--accent-dim);
  transform: translateY(-2px);
  box-shadow: var(--shadow-soft);
}
.takeaway-card h3 {
  font-family: 'Instrument Serif', serif;
  font-size: 0.85rem; font-weight: 400;
  margin-bottom: 0.2rem; line-height: 1.25;
}
.takeaway-card p {
  font-size: 0.75rem; color: var(--text-mid); line-height: 1.5;
}

/* ── GF SECTION LABELS ───────────────────────────────────── */
.gf-label {
  font-family: 'Geist Mono', monospace;
  font-size: 0.6rem; color: var(--accent);
  text-transform: uppercase; letter-spacing: 0.08em;
  margin-bottom: 0.2rem; margin-top: 1.25rem;
}
.gf-label:first-child { margin-top: 0; }
.gf-subtitle {
  font-family: 'Instrument Serif', serif;
  font-size: 1rem; font-weight: 400;
  margin-bottom: 0.3rem;
}
.gf-desc {
  color: var(--text-mid); font-size: 0.82rem;
  max-width: 620px; line-height: 1.6;
  margin-bottom: 0.5rem;
}

/* ── FOOTER ──────────────────────────────────────────────── */
footer {
  padding: 1.25rem 0; border-top: 1px solid var(--border-soft);
  font-size: 0.72rem; color: var(--text-dim);
  display: flex; justify-content: space-between;
}


/* ═══════════════════════════════════════════════════════════
   RESPONSIVE BREAKPOINTS
   ═══════════════════════════════════════════════════════════ */

/* ── TABLET / SMALL LAPTOP (≤900px) ──────────────────────── */
@media (max-width: 900px) {
  .project-card { grid-template-columns: 220px 1fr; }
  .cs-grid { grid-template-columns: repeat(2, 1fr); }
  .additional-grid { grid-template-columns: 1fr 1fr; }
  .contact-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Show hamburger, collapse nav links into dropdown. Measured, not guessed:
     the seven links plus the brand and the language picker need ~830px of
     content width in pt, the longest of the three locales, which is a ~883px
     viewport once the 3vw side padding is out. Below that they go to the
     dropdown instead of wrapping to a second row or colliding with the
     brand. Every other mobile collapse still waits for 720px. */
  .nav-toggle {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
  }

  nav .nav-links {
    display: none;
    position: absolute;
    top: 100%; left: 0; right: 0;
    flex-direction: column;
    gap: 0;
    background: rgba(22,22,25,0.96);
    backdrop-filter: blur(14px);
    border-bottom: 1px solid var(--border);
    padding: 0.5rem 0;
  }

  nav .nav-links.show { display: flex; }

  nav .nav-links li { list-style: none; }

  nav .nav-links a {
    display: block;
    padding: 0.65rem clamp(1.25rem, 3vw, 3rem);
    font-size: 0.88rem;
    border-bottom: 1px solid var(--border-soft);
  }
  nav .nav-links a.active::after { display: none; }
  nav .nav-links a.active { border-left: 2px solid var(--accent); }
  nav .nav-links li:last-child a { border-bottom: none; }

  /* Group the language control against the hamburger on the right instead of
     leaving the burger stranded mid-bar, and keep the picker the first control
     on that side so it stays easy to spot. */
  /* nav-scoped so it outranks .lang-switch's own margin-left, which is
     declared further down the file and would otherwise win on source order. */
  nav .lang-switch { order: 2; margin-left: auto; margin-right: 0.9rem; }
  .nav-toggle { order: 3; }

  /* Pill styling doesn't fit the dropdown — keep only the accent color */
  nav .nav-links a.nav-contact {
    background: none;
    border: none;
    border-bottom: 1px solid var(--border-soft);
    border-radius: 0;
    margin: 0;
    padding: 0.65rem clamp(1.25rem, 3vw, 3rem);
  }
}

/* ── MOBILE (≤720px) ─────────────────────────────────────── */
@media (max-width: 720px) {

  /* Layout collapses */
  .intro { grid-template-columns: 1fr; }
  .intro-links { margin-top: 0.5rem; }

  .project-card { grid-template-columns: 1fr; }
  .project-card:hover { transform: none; box-shadow: none; }
  .project-card::before { display: none; }
  .project-card:hover { border-color: var(--accent-dim); }
  .project-thumb {
    min-height: 140px;
    border-right: none;
    border-bottom: 1px solid var(--border);
  }
  .project-thumb::after {
    top: auto; bottom: 0; left: 0; right: 0;
    width: 100%; height: 35%;
    background: linear-gradient(180deg, transparent 0%, rgba(29,29,33,0.5) 100%);
  }

  .cs-grid { grid-template-columns: 1fr 1fr; }
  .about-layout { grid-template-columns: 1fr; }
  .about-photo { width: 90px; height: 110px; }

  .dashboard-embed iframe { height: 620px; }

  .section-header { flex-direction: column; gap: 0.25rem; }

  .additional-grid { grid-template-columns: 1fr 1fr; }
  .doc-grid { grid-template-columns: 1fr; }
  .takeaway-grid { grid-template-columns: 1fr; }

  .mechanic-spotlight { grid-template-columns: 1fr; }
  .mechanic-spotlight .ms-visual {
    margin: -0.9rem -1.1rem 0; border-right: none;
    border-bottom: 1px solid var(--border); min-height: 80px;
  }
  .mechanic-spotlight .ms-body { padding-left: 0; padding-top: 0.75rem; }

  .scope-table { font-size: 0.72rem; }

  footer { flex-direction: column; gap: 0.2rem; }
}

/* ── SMALL PHONE (≤480px) ────────────────────────────────── */
@media (max-width: 480px) {
  body { font-size: 14px; }

  .intro { padding: 2rem 0 2.25rem; }
  .intro h1 { font-size: 1.4rem; }

  .intro-links { flex-wrap: wrap; }
  .intro-links a {
    padding: 0.4rem 0.7rem;
    font-size: 0.75rem;
  }

  .cs-grid { grid-template-columns: 1fr; }
  .additional-grid { grid-template-columns: 1fr; }
  .contact-grid { grid-template-columns: 1fr; }
  .contact-actions { flex-direction: column; align-items: stretch; }
  .contact-cta { width: 100%; }
  .contact-copy { width: 100%; }
  .contact-intro { font-size: 0.94rem; }

  /* The dashboard keeps its own grouped nav and stacks to one column below
     700px, but it still needs vertical room — a chart plus the text that
     explains it does not fit in 440px. */
  .dashboard-embed iframe { height: 560px; }

  .tools-row {
    flex-direction: column;
    gap: 0.4rem;
  }

  .meta-row { font-size: 0.72rem; }
  .project-actions { flex-wrap: wrap; gap: 0.8rem; }
}

/* ═══════════════════════════════════════════════════════════
   LANGUAGE SWITCHER
   ═══════════════════════════════════════════════════════════ */

/* Flags are inline SVG, not emoji: Windows has no glyphs for regional-indicator
   pairs, so emoji flags degrade to bare letters there while looking correct on
   mobile. The sprite itself never renders — only its <symbol>s, via <use>. */
.flag-sprite, .xp-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.lang-flag {
  width: 18px;
  height: 13.5px;   /* 4:3, matching the symbols' 20×15 viewBox */
  border-radius: 2px;
  flex-shrink: 0;
  /* Hairline edge so the white fields of the US and Japan flags don't bleed
     into the surface behind them */
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.28), 0 0 0 1px rgba(255,255,255,0.06);
}

/* ── Trigger in nav ─────────────────────────────────────── */
.lang-switch {
  position: relative;
  margin-left: 8px;
  flex-shrink: 0;
}

.lang-trigger {
  display: flex;
  align-items: center;
  gap: 5px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px 8px;
  color: var(--text-mid);
  font-family: inherit;
  font-size: 0.75rem;
  font-weight: 500;
  cursor: pointer;
  transition: border-color 0.2s, color 0.2s, background 0.2s;
}
.lang-trigger:hover,
.lang-trigger[aria-expanded="true"] {
  border-color: var(--accent-dim);
  background: var(--surface-hover);
  color: var(--text);
}
.lang-trigger:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.lang-caret, .lang-check {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex-shrink: 0;
}
.lang-caret {
  width: 11px; height: 11px;
  stroke-width: 2.2;
  opacity: 0.65;
  transition: transform 0.2s ease;
}
.lang-trigger[aria-expanded="true"] .lang-caret { transform: rotate(180deg); }
.lang-code {
  font-family: 'Geist Mono', monospace;
  font-size: 0.7rem;
  letter-spacing: 0.04em;
}

/* ── Dropdown ───────────────────────────────────────────── */
.lang-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 158px;
  padding: 5px;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow-soft), 0 0 40px rgba(var(--accent-rgb),0.05);
  z-index: 60;
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.lang-menu.open { opacity: 1; transform: translateY(0); }
/* Beats the flex display above once the menu is closed for good */
.lang-menu[hidden] { display: none; }

/* Each option is written in its own language, so the way out of a language you
   can't read is legible to the person who needs it. */
.lang-option {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 8px 10px;
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--text-mid);
  font-family: inherit;
  font-size: 0.82rem;
  text-align: left;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
/* Takes the slack so the flag stays left and the checkmark stays right */
.lang-native { flex: 1; }

.lang-option:hover { background: var(--surface-hover); color: var(--text); }
.lang-option:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  color: var(--text);
}
.lang-option[aria-checked="true"] { color: var(--accent); }

.lang-check {
  width: 13px; height: 13px;
  stroke-width: 2.4;
  opacity: 0;
}
.lang-option[aria-checked="true"] .lang-check { opacity: 1; }

@media (max-width: 480px) {
  .lang-menu { min-width: 168px; }
  .lang-option { padding: 10px 10px; font-size: 0.86rem; }
}

@media (prefers-reduced-motion: reduce) {
  .lang-menu, .lang-caret { transition: none; }
}

/* ══════════════════════════════════════════════════════════
   Project Detail Modals
   ══════════════════════════════════════════════════════════ */

.pd-overlay {
  position: fixed; inset: 0;
  background: rgba(10, 10, 12, 0.82);
  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
  z-index: 9000;
  display: flex; align-items: center; justify-content: center;
  padding: clamp(0px, 3vw, 24px);
  opacity: 0; pointer-events: none;
  transition: opacity 0.3s ease;
}
.pd-overlay.open { opacity: 1; pointer-events: auto; }

/* A closed overlay is transparent but still a full-viewport fixed box, so the
   backdrop blur above stayed live in the compositor on all five modals at
   once — seven blurred layers with the nav and the contact dock. Dropping the
   filter while opacity is 0 costs nothing visually and gives the compositor
   five fewer full-screen surfaces to maintain on every scroll frame. */
.pd-overlay:not(.open) {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.pd-modal {
  background: var(--bg); border: 1px solid var(--border); border-radius: 16px;
  width: 100%; max-width: 860px; max-height: min(88dvh, 88vh); overflow-y: auto;
  box-shadow: 0 24px 80px rgba(0,0,0,0.5), 0 0 120px rgba(var(--accent-rgb),0.04);
  transform: translateY(16px) scale(0.98);
  transition: transform 0.35s cubic-bezier(0.22,1,0.36,1);
  scrollbar-width: thin; scrollbar-color: var(--border) transparent;
}
.pd-overlay.open .pd-modal { transform: translateY(0) scale(1); }
.pd-modal::-webkit-scrollbar { width: 6px; }
.pd-modal::-webkit-scrollbar-track { background: transparent; }
.pd-modal::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }

.pd-close {
  position: sticky; top: 0; z-index: 10;
  display: flex; justify-content: flex-end; padding: 16px 20px 0;
}
.pd-close button {
  width: 36px; height: 36px; border-radius: 50%;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--text-mid); font-size: 18px; cursor: pointer;
  transition: border-color 0.2s, color 0.2s, background 0.2s;
  display: flex; align-items: center; justify-content: center;
  line-height: 1; backdrop-filter: blur(8px);
}
.pd-close button:hover {
  border-color: var(--accent-dim); color: var(--text); background: var(--surface-hover);
}

.pd-hero { padding: 8px clamp(16px, 4vw, 32px) 0; }
.pd-hero-top { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 6px; }
.pd-hero h2 { font-family: 'Instrument Serif', serif; font-size: 1.6rem; font-weight: 400; color: var(--text); }
.pd-badge-pill {
  font-size: 0.7rem; font-weight: 600; color: var(--accent);
  background: rgba(var(--accent-rgb),0.1); border: 1px solid rgba(var(--accent-rgb),0.2);
  padding: 3px 10px; border-radius: 5px; white-space: nowrap;
}
.pd-badge-cyan { background: rgba(126,200,200,0.1); border-color: rgba(126,200,200,0.2); color: #7ec8c8; }
.pd-badge-amber { background: rgba(212,169,90,0.1); border-color: rgba(212,169,90,0.2); color: #d4a95a; }
.pd-badge-purple { background: rgba(167,139,250,0.1); border-color: rgba(167,139,250,0.2); color: #a78bfa; }
.pd-tagline { color: var(--text-mid); font-size: 0.88rem; line-height: 1.6; max-width: 640px; }

.pd-gallery {
  display: flex; gap: 8px; padding: 20px clamp(16px, 4vw, 32px);
  overflow-x: auto; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.pd-gallery::-webkit-scrollbar { display: none; }
.pd-gallery-item {
  flex: 0 0 auto; width: clamp(220px, 72vw, 280px); height: 170px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--surface); overflow: hidden;
  scroll-snap-align: start; position: relative;
}
.pd-gallery-item video, .pd-gallery-item img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.pd-gallery-caption {
  position: absolute; bottom: 0; left: 0; right: 0;
  padding: 20px 12px 8px; background: linear-gradient(transparent, rgba(22,22,25,0.85));
  font-size: 0.72rem; color: var(--text-mid);
}

/* Swipe affordance for mobile modal galleries */
.pd-gallery-wrap { position: relative; }
.pd-gallery-hint {
  display: none; /* hidden on desktop */
  position: absolute;
  top: 20px; /* matches .pd-gallery vertical padding */
  right: 0;
  width: 72px;
  height: 170px; /* matches .pd-gallery-item height */
  pointer-events: none;
  align-items: center;
  justify-content: flex-end;
  padding-right: clamp(16px, 4vw, 32px);
  background: linear-gradient(
    to right,
    rgba(22,22,25,0) 0%,
    rgba(22,22,25,0.75) 70%,
    rgba(22,22,25,0.9) 100%
  );
  color: var(--accent);
  opacity: 1;
  transition: opacity 0.35s ease;
}
.pd-gallery-hint svg {
  animation: pd-hint-pulse 1.6s ease-in-out infinite;
  filter: drop-shadow(0 0 4px rgba(0,0,0,0.5));
}
@keyframes pd-hint-pulse {
  0%, 100% { transform: translateX(0); opacity: 0.75; }
  50%      { transform: translateX(4px); opacity: 1; }
}
.pd-gallery-wrap.scrolled .pd-gallery-hint { opacity: 0; }
@media (max-width: 640px) {
  .pd-gallery-hint { display: flex; height: 150px; }
}
@media (prefers-reduced-motion: reduce) {
  .pd-gallery-hint svg { animation: none; }
}

.pd-body { padding: 0 clamp(16px, 4vw, 32px) 28px; }

.pd-metrics { display: flex; gap: 8px; margin-bottom: 24px; flex-wrap: wrap; }
.pd-metric {
  flex: 1; min-width: 130px; padding: 14px 16px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
}
.pd-metric .val { font-size: 1.3rem; font-weight: 700; color: var(--text); }
.pd-metric .lbl { font-size: 0.7rem; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.05em; margin-top: 2px; }

.pd-section { margin-bottom: 22px; }
.pd-section-label {
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--accent-dim); font-weight: 600; margin-bottom: 8px;
}
.pd-section p { color: var(--text-mid); font-size: 0.84rem; line-height: 1.65; }

.pd-details-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 8px; }
.pd-detail-card {
  padding: 14px 16px; background: var(--surface);
  border: 1px solid var(--border); border-radius: 10px;
}
.pd-detail-card h3 { font-size: 0.82rem; font-weight: 600; color: var(--text); margin-bottom: 4px; }
.pd-detail-card p { font-size: 0.78rem; color: var(--text-dim); line-height: 1.55; }

.pd-meta-row { display: flex; gap: 24px; flex-wrap: wrap; margin-bottom: 20px; font-size: 0.78rem; }
.pd-meta-item .pd-ml { color: var(--text-dim); margin-right: 4px; }
.pd-meta-item .pd-mv { color: var(--text-mid); font-weight: 500; }

.pd-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 20px; }
.pd-tag {
  font-size: 0.72rem; padding: 4px 12px; border-radius: 5px;
  background: rgba(var(--accent-rgb),0.08); border: 1px solid rgba(var(--accent-rgb),0.15); color: var(--accent);
}

.pd-actions { display: flex; gap: 10px; flex-wrap: wrap; padding-top: 4px; }
.pd-actions a {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 10px 20px; border-radius: 8px; font-size: 0.82rem;
  font-weight: 500; text-decoration: none; transition: transform 0.15s, border-color 0.2s;
}
.pd-actions a:hover { transform: translateY(-1px); }
.pd-actions .pd-primary { background: var(--accent); color: var(--bg); }
.pd-actions .pd-secondary {
  background: transparent; border: 1px solid var(--border); color: var(--accent);
}
.pd-actions .pd-secondary:hover { border-color: var(--accent-dim); }

.pd-divider { height: 1px; background: var(--border); margin: 20px 0; }

/* View Details button in project cards */
/* View Details button — styled in the section below */

@media (max-width: 640px) {
  .pd-modal { border-radius: 12px; }
  .pd-gallery-item { height: 150px; }
  .pd-details-grid { grid-template-columns: 1fr; }
  .pd-hero h2 { font-size: 1.3rem; }
  .pd-metrics { gap: 6px; }
  .pd-metric { min-width: 100px; padding: 10px 12px; }
  .pd-close { padding: 12px 16px 0; }
}

@media (max-width: 480px) {
  .pd-overlay { padding: 0; }
  .pd-modal { max-height: 100dvh; border-radius: 0; border-left: none; border-right: none; }
}

/* ── Doc thumbnails (CSS-only mini page previews) ──────── */
.doc-preview {
  width: 52px;
  height: 64px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
/* Corner fold */
.doc-preview::after {
  content: '';
  position: absolute;
  top: 0; right: 0;
  width: 12px; height: 12px;
  background: linear-gradient(225deg, var(--surface) 50%, var(--border) 50%);
}
/* Faint text lines */
.doc-preview-lines {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 0 8px;
  width: 100%;
}
.doc-preview-lines span {
  height: 2px;
  background: var(--border);
  border-radius: 1px;
}
.doc-preview-lines span:nth-child(1) { width: 70%; }
.doc-preview-lines span:nth-child(2) { width: 90%; }
.doc-preview-lines span:nth-child(3) { width: 55%; }
.doc-preview-lines span:nth-child(4) { width: 80%; }
/* Type label overlay on preview */
.doc-preview .doc-preview-type {
  position: absolute;
  bottom: 4px; left: 0; right: 0;
  text-align: center;
  font-family: 'Geist Mono', monospace;
  font-size: 0.5rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  opacity: 0.6;
}
/* Preview color variants */
.doc-preview { border-top: 2px solid var(--accent-dim); }
.doc-prev-qdd { border-top-color: #8a7abf; }
.doc-prev-ldd { border-top-color: #7aabb8; }
.doc-prev-test { border-top-color: #b8967a; }
/* Hide the old type badge — preview replaces it */
.doc-item > .doc-type { display: none; }

/* ── Prominent "View details" button in project cards ──── */
.pd-trigger {
  display: inline-flex;
  align-items: center;
  padding: 6px 14px;
  background: rgba(var(--accent-rgb),0.1);
  border: 1px solid rgba(var(--accent-rgb),0.25);
  border-radius: 6px;
  cursor: pointer;
  color: var(--accent);
  font-weight: 500;
  text-decoration: none;
  transition: background 0.2s, border-color 0.2s, transform 0.15s;
}
.pd-trigger:hover {
  background: rgba(var(--accent-rgb),0.18);
  border-color: var(--accent);
  color: var(--text);
  transform: translateY(-1px);
}

/* ══════════════════════════════════════════════════════════
   Print Stylesheet
   ══════════════════════════════════════════════════════════ */

@media print {
  /* Reset backgrounds for ink saving */
  :root {
    --bg: #fff;
    --surface: #fff;
    --text: #111;
    --text-mid: #333;
    --text-dim: #666;
    --accent: #5f3d78;
    --border: #ccc;
  }

  body {
    background: #fff !important;
    background-image: none !important;
    color: #111;
    font-size: 11pt;
  }
  body::before, body::after { display: none !important; }

  /* Hide interactive/nav elements */
  nav, .scroll-progress, .lang-switch,
  .pd-overlay, .nav-toggle, .doc-preview, .contact-dock,
  .dashboard-embed, iframe { display: none !important; }

  /* The shipped-systems list is forced open for print (see beforeprint in
     index.html) — the toggle row itself has nothing to say on paper */
  .wip-shipped > summary { display: none !important; }
  .wip-shipped { border-top: none !important; }

  /* Reset card backgrounds */
  .project-card, .cs-card, .takeaway-card, .doc-item,
  .mechanic-spotlight, .additional-item, .about-layout,
  .contact-item, .contact-panel, .scope-table, .pd-detail-card {
    background: #fff !important;
    border: 1px solid #ccc !important;
    box-shadow: none !important;
    break-inside: avoid;
  }

  .contact-copy { display: none !important; }
  .contact-cta {
    animation: none !important;
    background: #fff !important;
    color: #111 !important;
    border: 1px solid #5f3d78 !important;
  }

  /* No hover effects */
  .project-card:hover, .doc-item:hover, .additional-item:hover {
    transform: none !important;
    box-shadow: none !important;
  }

  /* Video placeholders */
  video { display: none; }
  .project-thumb { display: none; }
  /* The wrapper too, or the clip prints as an empty framed box under a
     caption describing footage that is not on the page. */
  .dsp-clip { display: none; }

  /* Restore doc-type badge in print (preview hidden) */
  .doc-item > .doc-type { display: inline-block !important; }

  /* Ensure links are visible */
  a { color: #5f3d78 !important; text-decoration: underline; }
  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 8pt;
    color: #666;
    word-break: break-all;
  }
  /* Don't expand internal anchors */
  a[href^="#"]::after { content: none; }

  /* Page breaks */
  section { break-before: auto; }
  .section-header { break-after: avoid; }
  h2, h3, h4 { break-after: avoid; }

  /* Full width */
  .page { max-width: 100%; padding: 0; }
  .project-card { flex-direction: column; }

  /* Footer */
  .site-footer {
    border-top: 1px solid #ccc;
    padding-top: 12px;
    text-align: center;
  }

  /* Comments/tips hidden */
  .tip { display: none !important; }

  /* Reveal all elements (no scroll animations) */
  .reveal { opacity: 1 !important; transform: none !important; }
}

/* ═══════════════════════════════════════════════════════════
   DEAD SAINTS PARADE — SYSTEM DEEP-DIVES
   Ported from the long-form case study. Both files use the same
   token names, so these rules carry over unchanged.
   ═══════════════════════════════════════════════════════════ */

/* DSP leads Selected Projects, so it needs to look chosen rather than
   merely first. */
.project-card.is-flagship {
  border-color: var(--accent-dim);
  box-shadow: var(--shadow-glow);
}
.flagship-flag {
  display: inline-block;
  font-family: 'Geist Mono', monospace;
  font-size: 0.58rem;
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 0.5rem;
}

/* The systems deep-dive is a different kind of content from the card list
   above it, and at the default 1.15rem section title the transition was easy
   to scroll straight past. Give this one a real opening edge. */
.section-header.is-major {
  margin-top: 4.5rem;
  padding-top: 1.7rem;
  border-top: 1px solid var(--border);
}
/* Sits the aside on the title's bottom edge rather than its baseline — but only
   while the header is still a row. At 720px .section-header stacks, and flex-end
   on a column flips to the cross axis and right-aligns the whole heading against
   an otherwise left-aligned page. */
@media (min-width: 721px) {
  .section-header.is-major { align-items: flex-end; }
}
.section-heading-group { display: flex; flex-direction: column; gap: 0.35rem; }
.section-eyebrow {
  font-family: 'Geist Mono', monospace;
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}
.section-header.is-major .section-title {
  font-size: 1.9rem;
  line-height: 1.1;
}

.dsp-systems { margin-top: 1.1rem; }

/* Direct-child paragraphs carry the rule narration. The lead and the closing
   note are qualified on the same selector so they keep their own treatment
   rather than losing the cascade to a more specific sibling rule. */
.dsp-systems > p {
  font-size: 0.85rem;
  color: var(--text-mid);
  line-height: 1.7;
  max-width: 74ch;
  margin-top: 0.8rem;
}
.dsp-systems > p.dsp-intro {
  font-size: 0.9rem;
  color: var(--text);
  margin-top: 0;
}
.dsp-systems > p.dsp-rule-note {
  border-left: 2px solid var(--accent-dim);
  padding-left: 0.9rem;
  color: var(--text-dim);
  margin-top: 1.3rem;
}
.dsp-rule-h {
  font-family: 'Instrument Serif', serif;
  font-size: 1.3rem;
  font-weight: 400;
  line-height: 1.2;
  margin-top: 1.9rem;
}
/* The two worked examples: features that lost an argument to a higher pillar. */
.dsp-cuts {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.6rem;
  margin: 1.2rem 0;
}
.dsp-cut {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.85rem 1rem;
}
.dsp-cut .k {
  font-family: 'Geist Mono', monospace;
  font-size: 0.56rem;
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 0.3rem;
}
.dsp-cut h4 {
  font-family: 'Instrument Serif', serif;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.25;
  margin-bottom: 0.3rem;
}
.dsp-cut p {
  font-size: 0.78rem;
  color: var(--text-mid);
  line-height: 1.6;
}

/* Priority rail: the rule both systems below are applications of. */
.prio { display: flex; gap: 0.4rem; margin: 1rem 0 0.4rem; flex-wrap: wrap; }
.prio-item {
  flex: 1; min-width: 140px; background: var(--surface);
  border: 1px solid var(--border); border-radius: 6px; padding: 0.7rem 0.85rem;
}
.prio-item .rank {
  font-family: 'Geist Mono', monospace; font-size: 0.55rem; color: var(--accent);
  text-transform: uppercase; letter-spacing: 0.07em; margin-bottom: 0.25rem;
}
.prio-item .pname { font-size: 0.8rem; color: var(--text); font-weight: 500; line-height: 1.3; }
.prio-item:first-child { border-color: var(--accent-dim); background: var(--note-bg); }

.dsp-system {
  border-top: 1px solid var(--border);
  padding-top: 1.5rem;
  margin-top: 1.6rem;
}
.dsp-system-label {
  font-family: 'Geist Mono', monospace; font-size: 0.6rem; color: var(--accent);
  text-transform: uppercase; letter-spacing: 0.08em; display: block; margin-bottom: 0.3rem;
}
.dsp-system h3 {
  font-family: 'Instrument Serif', serif; font-size: 1.28rem; font-weight: 400;
  line-height: 1.2; margin-bottom: 0.5rem;
}
.dsp-system p {
  font-size: 0.82rem; color: var(--text-mid); line-height: 1.7;
  max-width: 74ch; margin-bottom: 0.8rem;
}
.dsp-system code, .dsp-system figcaption code {
  font-family: 'Geist Mono', monospace; font-size: 0.92em; color: var(--text);
}

.tbl-wrap { overflow-x: auto; margin: 0.9rem 0 1.1rem; }
table.tbl { width: 100%; border-collapse: collapse; font-size: 0.78rem; min-width: 460px; }
table.tbl th {
  text-align: left; font-family: 'Geist Mono', monospace; font-size: 0.57rem;
  color: var(--accent); text-transform: uppercase; letter-spacing: 0.08em;
  padding: 0.5rem 0.7rem; border-bottom: 1px solid var(--border);
  background: var(--surface); font-weight: 500; white-space: nowrap;
}
table.tbl td {
  padding: 0.55rem 0.7rem; border-bottom: 1px solid var(--border-soft);
  vertical-align: top; line-height: 1.55; color: var(--text-mid);
}
table.tbl td.key { color: var(--text); font-weight: 500; }
table.tbl td.num { font-family: 'Geist Mono', monospace; color: var(--text); white-space: nowrap; }

/* The "I built it and said no" callout. */
/* Spans the column like the table and the figures above it. A 760px cap used
   to leave it stranded under a full-width table, reading as a block that had
   failed to lay out rather than a deliberate callout. The line length is held
   on the text instead, at the same 74ch the section's body copy uses. */
.refusal {
  background: linear-gradient(180deg, rgba(var(--accent-rgb),0.05), rgba(var(--accent-rgb),0.015));
  border: 1px solid var(--accent-dim); border-radius: 6px;
  padding: 1rem 1.15rem; margin: 1.1rem 0;
}
.refusal .k {
  font-family: 'Geist Mono', monospace; font-size: 0.6rem; color: var(--accent);
  text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 0.4rem;
}
.refusal h4 {
  font-family: 'Instrument Serif', serif; font-size: 1.05rem; font-weight: 400;
  margin-bottom: 0.35rem; line-height: 1.3;
}
.refusal p {
  font-size: 0.81rem; color: var(--text-mid); line-height: 1.7;
  margin-bottom: 0.4rem; max-width: 74ch;
}
.refusal p:last-child { margin-bottom: 0; }
.refusal .because {
  font-size: 0.76rem; color: var(--accent); padding-top: 0.5rem;
  border-top: 1px solid rgba(var(--accent-rgb),0.18); margin-top: 0.6rem;
}

/* On a wide column the argument reads down the left and the conclusion sits
   beside it, so the callout fills the width it spans instead of leaving half
   the box empty. The rule between them turns from a top border into a left
   one. Same breakpoint as the figure pairs above, for one consistent switch. */
@media (min-width: 1100px) {
  .refusal {
    display: grid;
    grid-template-columns: minmax(0, 74ch) 1fr;
    column-gap: 1.5rem;
    align-items: center;
  }
  .refusal .because {
    margin-top: 0; padding-top: 0;
    padding-left: 1.5rem;
    border-top: none;
    border-left: 1px solid rgba(var(--accent-rgb),0.18);
  }
}

/* ── Build figures ──────────────────────────────────────────────
   The drawing and the footage are one exhibit, so both figures are built the
   same way: a media band running edge to edge, then a caption plate under a
   hairline. Framing the clip the way .project-thumb frames a thumbnail —
   flush to the card — drops the second border and the dead gutters that came
   from floating a fixed-width video inside a padded box. */
.dsp-system figure {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;                 /* clips the media to the corner radius */
  margin: 1.1rem 0;
  /* Stacked, the card hugs its media instead of stretching to the full column
     and leaving a 560px drawing marooned in the middle of a 1000px box. The cap
     is the prose measure above it, so the figure's left edge and its width both
     line up with the paragraph it illustrates. */
  max-width: 700px;
}

/* Ground for the drawing. It is a measured top-down plan, so it sits on graph
   paper — a finer, squared echo of the page's own dot grid. */
.dsp-plate {
  display: flex; align-items: center; justify-content: center;
  padding: 1.15rem 1rem;
  background-image:
    linear-gradient(rgba(var(--accent-rgb),0.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(var(--accent-rgb),0.035) 1px, transparent 1px);
  background-size: 26px 26px;
  background-position: center center;
}
/* Labels are absolute px inside the viewBox, so the drawing needs a ceiling or
   9px mono scales past 17px. The figure's own max-width is that ceiling now:
   the widest column this pair ever gets is ~654px, which lands the labels at
   about 13px. */
.dsp-system figure svg { display: block; width: 100%; height: auto; }

/* Build footage, flush to the card on three sides. 7/4 is the clips' own ratio
   (864x494 and 810x460), so the frame reserves the right height before the file
   decodes — the section below never jumps — and cover crops nothing worth
   seeing. The mat is darker than the surface so a source that is off ratio
   letterboxes into black instead of into the card colour. */
.dsp-system figure.dsp-clip video {
  display: block;
  width: 100%;
  aspect-ratio: 7 / 4;
  object-fit: cover;
  background: #0e0e11;
}

/* Diagram beside its footage. The pair only splits once each half can still
   give the drawing ~480px; below that they stack, which is the layout they had. */
@media (min-width: 1100px) {
  .dsp-figpair {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
    align-items: stretch;
  }
  .dsp-figpair > figure { max-width: none; margin: 1.1rem 0; }
  /* Both media bands take the clip's ratio so the two captions start on the
     same line. The drawing keeps its own viewBox ratio and simply centres
     inside the taller band. */
  .dsp-figpair > figure > .dsp-plate { aspect-ratio: 7 / 4; }
}

/* The caption is the plate under the media, not a note appended to it, so it
   fills whatever height is left once the media bands have matched. */
.dsp-system figcaption {
  flex: 1;
  font-size: 0.74rem; color: var(--text-dim); line-height: 1.6;
  padding: 0.75rem 1rem 0.9rem;
  border-top: 1px solid var(--border);
}
.dsp-system figcaption strong { color: var(--text-mid); font-weight: 500; }
.svg-label { font-family: 'Geist Mono', monospace; font-size: 9px; fill: var(--text-dim); letter-spacing: 0.04em; }
.svg-label.hi { fill: var(--accent); }
.svg-label.b { fill: var(--text-mid); }

@media (max-width: 640px) {
  .prio-item { min-width: 100%; }
  .dsp-system h3 { font-size: 1.12rem; }
  .dsp-cuts { grid-template-columns: 1fr; }
  .section-header.is-major { margin-top: 3rem; padding-top: 1.2rem; }
  .section-header.is-major .section-title { font-size: 1.45rem; }

  /* The tuning tables are 460px wide at minimum, so on a phone the last
     column used to sit outside the scroll container with nothing to say it
     was there. Stack each row instead: parameter, then value, then what the
     value buys. The header row carries no information once the rows read
     top-to-bottom, so it goes. */
  .tbl-wrap { overflow-x: visible; }
  table.tbl { min-width: 0; }
  table.tbl thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  table.tbl, table.tbl tbody, table.tbl tr, table.tbl td { display: block; width: 100%; }
  table.tbl tr {
    padding: 0.6rem 0 0.65rem;
    border-bottom: 1px solid var(--border-soft);
  }
  table.tbl tr:last-child { border-bottom: none; }
  table.tbl td {
    padding: 0; border-bottom: none; font-size: 0.82rem;
  }
  table.tbl td.key { font-size: 0.88rem; margin-bottom: 0.1rem; }
  table.tbl td.num { color: var(--accent); margin-bottom: 0.22rem; }
}
