/* ==========================================================================
   Radhian Sobarna — portfolio
   Source of truth: personal-portfolio-v3.md
   Order: 1 tokens · 2 fonts & base · 3 typography · 4 layout · 5 components
          · 6 sections · 7 utilities. Mobile-first; media queries sit with
          the component they change.
   ========================================================================== */

/* 1. Tokens ---------------------------------------------------------------- */
:root {
  color-scheme: dark;

  --bg: #061019;
  --bg-2: #09151f;
  --surface: #132634;
  --border: rgba(129, 190, 222, .14);
  --border-strong: #56748a;       /* ≥3:1 on --bg and --surface */

  --text: #f4f8fb;
  --text-2: #c4d1d9;
  --text-muted: #91a5b4;

  --accent: #40e0d0;              /* interaction only */
  --accent-hover: #63e8dd;
  --accent-rgb: 64, 224, 208;
  --accent-2: #60a5fa;            /* secondary accent: the blue of the About collage and the ambient light */
  --accent-2-rgb: 96, 165, 250;
  --on-accent: #061019;
  --status-ok: #31e981;           /* status dot only */

  --sans: "Inter", "Inter Fallback", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  --fs-h1: clamp(2.75rem, 1.9rem + 3.8vw, 5rem);
  --fs-h2: clamp(1.75rem, 1.45rem + 1.35vw, 2.5rem);
  --fs-h3: clamp(1.125rem, 1.05rem + .35vw, 1.375rem);
  --fs-role: clamp(1.125rem, 1rem + .55vw, 1.5rem);
  --fs-lead: clamp(1.0625rem, 1rem + .3vw, 1.25rem);
  --fs-body: 1rem;
  --fs-small: .875rem;
  --fs-label: .8125rem;

  --container: 1200px;
  --gutter: 16px;
  --header-h: 64px;
  --section-y: 64px;

  --r-sm: 6px;
  --r-md: 12px;
  --r-lg: 20px;

  --shadow: 0 16px 40px rgba(0, 0, 0, .35);
  --dur-fast: 150ms;
  --dur: 250ms;
  --ease: cubic-bezier(.2, .7, .2, 1);
}

@media (min-width: 600px) { :root { --gutter: 24px; } }
@media (min-width: 1024px) { :root { --gutter: 32px; --section-y: 112px; } }

/* 2. Fonts & base ------------------------------------------------------------ */
@font-face {
  font-family: "Inter";
  src: url("../fonts/inter-latin-var.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                 U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Caveat";
  src: url("../fonts/caveat-latin.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+2074, U+20AC, U+2122, U+FEFF, U+FFFD;
}
/* Metric-matched fallback: keeps the large H1 from jumping when Inter loads. */
@font-face {
  font-family: "Inter Fallback";
  src: local("Arial"), local("Liberation Sans"), local("Helvetica");
  size-adjust: 107.4%;
  ascent-override: 90.2%;
  descent-override: 22.48%;
  line-gap-override: 0%;
}

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-padding-top: calc(var(--header-h) + 16px);
  scroll-behavior: smooth;
}

body {
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: var(--fs-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-wrap: break-word;
}

img, picture, svg { display: block; max-width: 100%; }
img { height: auto; }
button { font: inherit; color: inherit; }
ul, ol { padding: 0; }
main:focus { outline: none; }

::selection { background: rgba(64, 224, 208, .3); color: var(--text); }

/* 3. Typography -------------------------------------------------------------- */
h1, h2, h3, h4 { font-weight: 700; line-height: 1.15; text-wrap: balance; }
h3, h4 { font-weight: 600; line-height: 1.3; }
p { text-wrap: pretty; }

.section-title {
  font-size: var(--fs-h2);
  letter-spacing: -.025em;
  margin-bottom: 32px;
}
.page-title {
  font-size: clamp(2.25rem, 1.7rem + 2.4vw, 3.75rem);
  letter-spacing: -.035em;
  line-height: 1.05;
}
.page-lead {
  margin-top: 16px;
  max-width: 60ch;
  color: var(--text-2);
  font-size: var(--fs-lead);
}
.eyebrow {
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 16px;
}
.micro-label {
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.meta, .stack, .meta-inline {
  font-family: var(--mono);
  font-size: var(--fs-label);
  color: var(--text-muted);
  line-height: 1.5;
}
.stack { margin-top: 16px; }

/* 4. Layout ------------------------------------------------------------------ */
.container {
  width: min(100% - 2 * var(--gutter), var(--container));
  margin-inline: auto;
}
.section { padding-block: var(--section-y); }
/* Sections are separated by a soft hairline, see "Section backgrounds" at the end. */
.section-tight { padding-top: 0; }

.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 24px;
  margin-bottom: 32px;
}
.section-head .section-title { margin-bottom: 0; }

/* 5. Components -------------------------------------------------------------- */

/* Skip link */
.skip-link {
  position: absolute;
  left: 16px;
  top: -100px;
  z-index: 100;
  padding: 12px 16px;
  border-radius: var(--r-md);
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus { top: 12px; }

/* Focus */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}
.btn:focus-visible { border-radius: var(--r-md); }

/* Links */
a { color: inherit; }
.text-link, .case-content a, .about-story a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: text-decoration-thickness var(--dur-fast) var(--ease);
}
.text-link:hover, .case-content a:hover { text-decoration-thickness: 2px; }
.text-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  font-weight: 600;
}
.text-link .icon { flex: none; }

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 48px;
  padding: 12px 20px;
  border-radius: var(--r-md);
  border: 1px solid transparent;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--accent); color: var(--on-accent); }
.btn-primary:hover { background: var(--accent-hover); }
.btn-secondary { background: transparent; border-color: var(--border-strong); color: var(--text); }
.btn-secondary:hover { border-color: var(--text-muted); background: rgba(255, 255, 255, .04); }
.btn .icon { flex: none; }
.cta-note { color: var(--text-muted); font-weight: 500; }
.btn-copy { min-width: 11.5rem; }

.cta-row {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 24px;
}
@media (min-width: 600px) {
  .cta-row { flex-direction: row; flex-wrap: wrap; }
}

/* Card (projects & status only) */
.card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}
.card.is-linked { transition: transform var(--dur) var(--ease), border-color var(--dur) var(--ease); }
.card.is-linked:hover { transform: translateY(-2px); border-color: var(--border-strong); }
@media (hover: hover) {
  .card.is-linked:hover .project-visual img { transform: scale(1.02); }
}
.card.is-linked:focus-within { border-color: var(--border-strong); }
/* Whole card clickable via the single title/case-study link. */
.card-link::after { content: ""; position: absolute; inset: 0; border-radius: var(--r-lg); }
.card-link { position: static; }

/* Placeholder marker (development builds only) */
mark.tbd {
  background: rgba(246, 200, 95, .12);
  color: #f6c85f;
  border: 1px dashed #f6c85f;
  border-radius: 4px;
  padding: 0 4px;
  font-family: var(--mono);
  font-size: max(.8em, 13px);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
.dev-banner {
  padding: 8px var(--gutter);
  background: #2a2410;
  border-bottom: 1px solid #6b5a1e;
  color: #f6c85f;
  font-family: var(--mono);
  font-size: var(--fs-label);
}
.dev-banner a { color: inherit; text-underline-offset: 3px; }

/* Header & navigation -------------------------------------------------------- */
.site-header {
  /* Bar height and top gap always add up to --header-h, so the page below never
     shifts when the bar changes state (see "Floating command bar" below). */
  --bar-h: var(--header-h);
  --bar-gap: 0px;
  position: sticky;
  top: 0;
  z-index: 50;
  padding-top: var(--bar-gap);
  background: transparent;
}
.header-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  column-gap: 24px;
  min-height: var(--bar-h);
  padding-inline: max(0px, env(safe-area-inset-left));
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  text-decoration: none;
  font-weight: 600;
}
.brand-mark {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border: 1.5px solid var(--text-muted);
  border-radius: var(--r-md);
  font-size: .8125rem;
  font-weight: 700;
  letter-spacing: -.02em;
}
.brand-name { font-size: .9375rem; }

.nav-toggle { display: none; }
.nav-list { list-style: none; }
.nav-link {
  display: flex;
  align-items: center;
  min-height: 48px;
  color: var(--text-2);
  text-decoration: none;
  font-size: .9375rem;
  font-weight: 500;
}
.nav-link:hover { color: var(--text); }
.nav-link[aria-current] { color: var(--text); }
.header-cta { display: none; }

/* No-JS mobile: links as a scrollable row under the brand. */
.primary-nav { position: relative; flex-basis: 100%; min-width: 0; max-width: 100%; overflow-x: auto; }
.primary-nav .nav-list { display: flex; gap: 20px; white-space: nowrap; }

/* JS mobile: disclosure panel. */
.js .nav-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 8px 14px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: transparent;
  cursor: pointer;
  font-weight: 500;
}
.js .primary-nav {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  max-height: calc(100dvh - var(--header-h));
  overflow-y: auto;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow);
}
.js .primary-nav.is-open { display: block; }
.js .primary-nav .nav-list {
  display: block;
  width: min(100% - 2 * var(--gutter), var(--container));
  margin-inline: auto;
  padding-block: 8px 16px;
}
.js .primary-nav .nav-list li + li { border-top: 1px solid var(--border); }
.js .primary-nav .nav-link { min-height: 52px; font-size: 1.0625rem; }
.js .primary-nav .nav-link[aria-current] { color: var(--accent); }

@media (min-width: 1024px) {
  .header-inner { flex-wrap: nowrap; }
  .js .nav-toggle { display: none; }
  .primary-nav, .js .primary-nav {
    display: block;
    position: static;
    flex-basis: auto;
    overflow: visible;
    max-height: none;
    background: none;
    border: 0;
    box-shadow: none;
    margin-left: auto;
  }
  .primary-nav .nav-list, .js .primary-nav .nav-list {
    display: flex;
    gap: 28px;
    width: auto;
    padding: 0;
  }
  .js .primary-nav .nav-list li + li { border: 0; }
  .js .primary-nav .nav-link { min-height: var(--bar-h); font-size: .9375rem; }
  .nav-link { position: relative; min-height: var(--bar-h); }
  .nav-link[aria-current]::after,
  .js .primary-nav .nav-link[aria-current]::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 2px;
    background: var(--accent);
  }
  .js .primary-nav .nav-link[aria-current] { color: var(--text); }
  .nav-cv { display: none; }
  .header-cta { display: inline-flex; min-height: 44px; padding: 8px 16px; font-size: .9375rem; }
}

/* Floating command bar ------------------------------------------------------
   Top of the page: the bar is transparent and full container width, part of the
   hero. After 20px of scroll main.js adds .scrolled: the bar shrinks into a
   centred dark-glass panel. The 1px outline is an inset shadow (not a border) so
   the box never changes size, and every shadow list has the same shape so it
   animates. */
.header-inner {
  border-radius: 18px;
  backdrop-filter: blur(0) saturate(100%);
  box-shadow:
    inset 0 0 0 1px rgba(80, 210, 210, 0),
    0 14px 40px rgba(0, 0, 0, 0),
    0 0 28px rgba(64, 224, 208, 0);
  transition:
    width 300ms cubic-bezier(.22, 1, .36, 1),
    min-height 300ms cubic-bezier(.22, 1, .36, 1),
    padding 300ms cubic-bezier(.22, 1, .36, 1),
    background-color 300ms cubic-bezier(.22, 1, .36, 1),
    border-radius 300ms cubic-bezier(.22, 1, .36, 1),
    box-shadow 300ms cubic-bezier(.22, 1, .36, 1),
    backdrop-filter 300ms cubic-bezier(.22, 1, .36, 1);
}
.site-header { transition: padding-top 300ms cubic-bezier(.22, 1, .36, 1); }
.nav-link { transition: color var(--dur-fast) var(--ease), min-height 300ms cubic-bezier(.22, 1, .36, 1); }
.brand-mark, .header-cta {
  transition: border-color 300ms cubic-bezier(.22, 1, .36, 1), background-color 300ms cubic-bezier(.22, 1, .36, 1),
              box-shadow 300ms cubic-bezier(.22, 1, .36, 1);
}

.site-header.scrolled { --bar-h: 56px; --bar-gap: 8px; }
.site-header.scrolled .header-inner {
  width: min(100% - 24px, 1160px);
  padding-inline: 12px;
  background-color: rgba(6, 18, 28, .94);
  backdrop-filter: blur(14px) saturate(130%);
  box-shadow:
    inset 0 0 0 1px rgba(80, 210, 210, .18),
    0 14px 40px rgba(0, 0, 0, .25),
    0 0 28px rgba(64, 224, 208, .07);
}
@supports (backdrop-filter: blur(1px)) {
  .site-header.scrolled .header-inner { background-color: rgba(6, 18, 28, .82); }
}
@media (min-width: 768px) {
  .site-header.scrolled .header-inner { width: min(100% - 48px, 1160px); padding-inline: 14px; }
}
.site-header.scrolled .brand-mark { border-color: rgba(80, 210, 210, .55); box-shadow: 0 0 12px rgba(64, 224, 208, .2); }
.site-header.scrolled .header-cta {
  border-color: rgba(64, 224, 208, .45);
  background-color: rgba(64, 224, 208, .07);
  box-shadow: 0 0 16px rgba(64, 224, 208, .15);
}
.site-header.scrolled .header-cta:hover { border-color: rgba(64, 224, 208, .7); background-color: rgba(64, 224, 208, .12); }

@media (min-width: 1024px) {
  /* The bar is shorter now: lift the active underline off the panel edge. */
  .site-header.scrolled .nav-link[aria-current]::after,
  .js .site-header.scrolled .primary-nav .nav-link[aria-current]::after { bottom: 6px; }
}
@media (max-width: 1023.98px) {
  /* Phone menu opens as a panel under the floating bar, same width and glass. */
  .js .site-header.scrolled .header-inner { position: relative; }
  .js .site-header.scrolled .primary-nav {
    top: calc(100% + 8px);
    max-height: calc(100dvh - 88px);
    border: 1px solid rgba(80, 210, 210, .18);
    border-radius: 16px;
    background: #08151f;
    box-shadow: 0 14px 40px rgba(0, 0, 0, .35);
  }
  .js .site-header.scrolled .primary-nav .nav-list { width: auto; padding-inline: 16px; }
}

/* 6. Sections ---------------------------------------------------------------- */

/* Hero */
.hero {
  position: relative;
  padding-block: 40px 64px;
  overflow: hidden;
}
.hero::before {
  /* Subtle grid (V3 §7.1). CSS only, fades out to the right and bottom. */
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(129, 190, 222, .05) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(129, 190, 222, .05) 1px, transparent 1px);
  background-size: 32px 32px;
  -webkit-mask-image: radial-gradient(ellipse 70% 60% at 20% 30%, #000 30%, transparent 75%);
  mask-image: radial-gradient(ellipse 70% 60% at 20% 30%, #000 30%, transparent 75%);
}
.hero-inner { position: relative; z-index: 1; display: grid; gap: 40px; }   /* above the map backdrop */
.hero-name {
  font-size: var(--fs-h1);
  line-height: 1.02;
  letter-spacing: -.04em;
}
.hero-role {
  margin-top: 12px;
  font-size: var(--fs-role);
  font-weight: 500;
  line-height: 1.35;
  color: var(--text-2);
}
.hero-clarifier, .hero-at { color: var(--text-muted); }
.hero-lead {
  margin-top: 12px;
  max-width: 36em;
  font-size: clamp(1rem, .95rem + .25vw, 1.125rem);
  color: var(--text-muted);
}
.hero-lead p + p { margin-top: 4px; }

.hero-visual { display: grid; gap: 16px; }
.portrait {
  width: 100%;
  aspect-ratio: 1 / 1;
  max-height: 60svh;
  overflow: hidden;
  border-radius: var(--r-lg);
  border: 1px solid var(--border);
  background: var(--surface);
}
.portrait img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 20%; }
.portrait-pending { display: grid; place-items: center; padding: 24px; text-align: center; border-style: dashed; }

/* Status panel — the signature element. */
.status {
  padding: 20px;
  /* Almost the page background, so the card barely lifts off the map. */
  background: rgba(6, 16, 25, .86);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}
@supports (backdrop-filter: blur(1px)) {
  .status { background: rgba(6, 16, 25, .82); backdrop-filter: blur(8px); }
}
.status-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 12px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--border);
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--text-2);
}
.status-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--status-ok); flex: none; }
.status-list { display: grid; }
.status-row {
  display: grid;
  grid-template-columns: 6.5rem 1fr;
  gap: 12px;
  padding-block: 8px;
}
.status-row dt { font-family: var(--mono); font-size: var(--fs-label); color: var(--text-muted); padding-top: 2px; }
.status-row dd { font-size: .9375rem; color: var(--text); }
.status-checked dd { font-family: var(--mono); font-size: var(--fs-label); color: var(--text-muted); padding-top: 2px; }
/* Mobile: compact panel (first field + location + checked). */
@media (max-width: 599px) {
  .status-optional { display: none; }
}

@media (min-width: 600px) {
  .hero-visual { grid-template-columns: 5fr 7fr; align-items: end; }
  .hero-visual.no-photo { grid-template-columns: 1fr; max-width: 480px; }
}

@media (min-width: 1024px) {
  .hero { padding-block: 36px 40px; }
  .hero-inner {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: 32px;
    align-items: center;
    min-height: min(calc(100svh - var(--header-h) - 96px), 720px);
  }
  .hero-text { grid-column: 1 / span 7; }
  .hero-visual {
    grid-column: 8 / span 5;
    display: block;
    position: relative;
    padding-bottom: 24px;
  }
  .hero-visual .portrait { margin-left: auto; }
  .portrait {
    /* Width limited by both the column and the viewport height (1366×768). */
    width: min(100%, calc(min(520px, 100svh - var(--header-h) - 120px) * .8));
    height: auto;
    max-height: none;
    aspect-ratio: 4 / 5;
  }
  .hero-visual.no-photo { justify-self: stretch; padding-bottom: 0; max-width: none; }
}

/* About: copy on the left, a wall of monitoring screens on hexagon tiles on the right.
   Ported from the shared design. The 975 x 750 collage is laid out in fixed pixels and
   scaled to fit its column (--collage-scale is set by main.js). */
.about { overflow: hidden; }
/* Clip only sideways (the collage is wider than its column), so the section's
   ambient light can still fade out below it. Older browsers keep "hidden". */
@supports (overflow: clip) { .about { overflow: visible; overflow-x: clip; } }
.about-showcase { display: grid; gap: 48px; align-items: center; }
@media (min-width: 1024px) {
  .about-showcase { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: 48px; }
}
.about-copy { max-width: 612px; }
.about-kicker {
  display: flex;
  align-items: center;
  gap: 15px;
  height: 24px;
  margin: 0;
  font-size: clamp(1rem, .75rem + .6vw, 1.25rem);
  font-weight: 500;
  letter-spacing: .01em;
  color: var(--accent);
  text-shadow: 0 0 10px rgba(var(--accent-rgb), .4);
}
.about-kicker-bar { width: 3px; height: 24px; border-radius: 1px; background: var(--accent); box-shadow: 0 0 8px rgba(var(--accent-rgb), .55); }
.about-title {
  margin: 14px 0 0;
  font-size: clamp(2.5rem, 1.6rem + 3.2vw, 3.75rem);
  line-height: 1.1;
  font-weight: 800;
  letter-spacing: 0;
  color: #fff;
  text-shadow: 0 0 16px rgba(160, 200, 255, .18);
}
.about-text {
  margin-top: 14px;
  display: flex;
  flex-direction: column;
  gap: 19px;
  font-size: clamp(1rem, .5rem + .5vw, 1.125rem);
  line-height: 1.611;
  color: #b4c9df;
  text-shadow: 0 0 10px rgba(120, 170, 230, .12);
  text-wrap: pretty;
}
.about-text p { margin: 0; }
.about-collage {
  --g: 1;
  position: relative;
  aspect-ratio: 975 / 750;
  min-width: 0;
}
.collage-stage {
  position: absolute;
  left: 0;
  top: 0;
  width: 975px;
  height: 750px;
  transform-origin: 0 0;
  transform: scale(var(--collage-scale, .5));
}
/* Needs the script to size it; without JS it would overflow, so it is left out. */
html:not(.js) .about-collage { display: none; }
@keyframes about-blink { 50% { opacity: .1; } }
@keyframes noc-map-pulse { 0%, 100% { opacity: .25; } 50% { opacity: .75; } }
.about-cursor { animation: about-blink 1.1s steps(1) infinite; }
.noc-map .nm-halo { animation: noc-map-pulse 2.8s ease-in-out infinite; }

/* Selected work */
/* Homepage grid: one card per published project (values from the redesign source). */
.project-track {
  --gap: 20px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--gap);
}
.project-card {
  background: linear-gradient(160deg, rgba(19, 38, 52, .55), var(--bg-2) 55%);
  border-color: rgba(86, 116, 138, .3);
  border-radius: 16px;
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow: hidden;
  isolation: isolate;
  /* Landscape card; taller only if the content (wrapped tags) needs more room. */
  min-height: 16rem;
}
.project-card .card-link::after { border-radius: 16px; z-index: 2; }   /* above the text layers: the whole card is the link */
.project-card > * { margin: 0; flex-shrink: 0; }
/* Text on the left stays clear of the app preview in the top-right corner. */
.project-card-head,
.project-card .project-title,
.project-card .project-summary { max-width: 64%; position: relative; z-index: 1; }
/* Title and summary are clamped so cards stay the same landscape shape; tags wrap in full and the row grows if they need the room. */
.project-card .project-title,
.project-card .project-summary { display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }
.project-card .project-title { -webkit-line-clamp: 2; line-clamp: 2; }
.project-card .project-summary { -webkit-line-clamp: 2; line-clamp: 2; }
.project-card-head { display: flex; align-items: center; gap: 10px; min-height: 36px; }
.project-icon {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  flex: none;
  color: var(--accent);
}
.project-icon svg { filter: drop-shadow(0 0 10px rgba(64, 224, 208, .35)); }
.project-flag {
  font-size: .75rem;
  font-weight: 700;
  color: var(--on-accent);
  background: var(--accent);
  padding: 4px 10px;
  border-radius: 5px;
  line-height: 1.2;
}
.project-card .project-title { font-size: 1.1875rem; line-height: 1.25; letter-spacing: -.01em; margin: 0; }
.project-card .project-summary { font-size: .8125rem; line-height: 1.55; color: var(--text-2); margin: 0; }
.chips { list-style: none; display: flex; flex-wrap: wrap; gap: 8px; }
.chips li {
  font-size: .8125rem;
  color: var(--text-2);
  background: rgba(19, 38, 52, .6);
  border: 1px solid rgba(86, 116, 138, .35);
  padding: 7px 12px;
  border-radius: var(--r-sm);
}
.project-card .chips {
  margin-top: auto;
  padding-top: 4px;
  position: relative;
  z-index: 1;
  gap: 6px;
}
.project-card .chips li { flex: none; font-size: .75rem; padding: 5px 10px; }
.project-card .text-link { font-size: .875rem; font-weight: 700; align-self: flex-start; min-height: 32px; position: static; text-decoration: none; gap: 10px; }
.project-card .text-link:hover { text-decoration: underline; }
.project-card .project-summary + .card-link { margin-top: auto; }

/* Hover: the card lifts and glows, the preview slides in, the icon brightens
   and the arrow nudges forward. Keyboard focus gets the same treatment.
   Reduced motion turns the movement off (see below). */
.project-card {
  transition: transform var(--dur) var(--ease), border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.project-card::before {
  /* Soft teal light that follows in from the top-right, behind the content. */
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(120% 90% at 100% 0%, rgba(64, 224, 208, .16), transparent 60%);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.project-preview {
  transition: top .4s var(--ease), right .4s var(--ease), width .4s var(--ease), height .4s var(--ease),
              border-radius .4s var(--ease), --mv .4s var(--ease), --mh .4s var(--ease), opacity var(--dur) var(--ease);
}
@property --mv { syntax: "<percentage>"; inherits: false; initial-value: 45%; }
@property --mh { syntax: "<percentage>"; inherits: false; initial-value: 25%; }
.project-preview img { transition: filter var(--dur) var(--ease), transform .5s var(--ease); }
.project-icon svg { transition: filter var(--dur) var(--ease), transform var(--dur) var(--ease); }
.project-card .text-link .icon { transition: transform var(--dur) var(--ease); }
.project-card .project-title { transition: color var(--dur) var(--ease); }
.project-card.card:hover, .project-card.card:focus-within { transform: translateY(-6px); }
.project-card.card:hover,
.project-card.card:focus-within {
  border-color: rgba(64, 224, 208, .55);
  box-shadow: 0 14px 32px rgba(0, 0, 0, .45), 0 0 0 1px rgba(64, 224, 208, .12), 0 0 28px rgba(64, 224, 208, .1);
}
.project-card:hover::before,
.project-card:focus-within::before { opacity: 1; }
.project-card:hover .project-preview,
.project-card:focus-within .project-preview { transform: translateX(-8px); opacity: 1; }
.project-card:hover .project-preview img,
.project-card:focus-within .project-preview img { filter: brightness(1) saturate(1.05); transform: scale(1.06); }
.project-card:hover .project-icon svg,
.project-card:focus-within .project-icon svg { transform: scale(1.1); filter: drop-shadow(0 0 14px rgba(64, 224, 208, .7)); }
.project-card:hover .project-title,
.project-card:focus-within .project-title { color: var(--accent-hover); }
.project-card:hover .text-link .icon,
.project-card:focus-within .text-link .icon { transform: translateX(6px); }
.project-card:hover .text-link,
.project-card:focus-within .text-link { text-decoration: underline; }
@media (prefers-reduced-motion: reduce) {
  .project-card, .project-card::before, .project-preview, .project-preview img,
  .project-icon svg, .project-card .text-link .icon { transition: none; }
  .project-card:hover, .project-card:hover .project-preview,
  .project-card:hover .project-preview img, .project-card:hover .project-icon svg,
  .project-card:hover .text-link .icon { transform: none; }
}

/* App preview peeking in from the top-right edge, fading into the card. */
.project-preview {
  position: absolute;
  top: 18px;
  right: -1px;
  width: 36%;
  height: 64%;
  z-index: 0;
  border: 1px solid rgba(129, 190, 222, .18);
  border-right: 0;
  border-radius: 8px 0 0 8px;
  background: #0b1822;
  overflow: hidden;
  --mv: 45%;
  --mh: 25%;
  mask-image: linear-gradient(to bottom, #000 var(--mv), transparent calc(var(--mv) + 50%)), linear-gradient(to right, transparent, #000 var(--mh));
  mask-composite: intersect;
  -webkit-mask-composite: source-in;
  opacity: .85;
}
.project-preview img { filter: brightness(.8) saturate(.9); }
.project-preview picture, .project-preview img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: left top; }
.preview-mock { display: flex; flex-direction: column; height: 100%; }
.mock-bar { display: flex; gap: 4px; padding: 7px 8px; border-bottom: 1px solid rgba(129, 190, 222, .1); }
.mock-bar i { width: 5px; height: 5px; border-radius: 50%; background: #ff5f57; }
.mock-bar i:nth-child(2) { background: #febc2e; }
.mock-bar i:nth-child(3) { background: #28c840; }
.mock-body { display: flex; flex: 1; }
.mock-side { width: 26%; padding: 8px 6px; display: flex; flex-direction: column; gap: 8px; border-right: 1px solid rgba(129, 190, 222, .08); }
.mock-side i { height: 4px; border-radius: 2px; background: rgba(145, 165, 180, .25); }
.mock-main { flex: 1; padding: 8px; display: flex; flex-direction: column; gap: 7px; }
.mock-row { display: flex; gap: 5px; align-items: center; padding: 6px; border-radius: 4px; background: rgba(19, 38, 52, .7); }
.mock-row i { height: 4px; border-radius: 2px; background: rgba(145, 165, 180, .3); flex: 1; }
.mock-row i:first-child { flex: none; width: 8px; height: 8px; border-radius: 50%; background: rgba(64, 224, 208, .45); }
.mock-row.mock-accent { height: 14px; background: linear-gradient(90deg, rgba(255, 95, 87, .45), rgba(64, 224, 208, .35)); }

.work-stack { display: grid; gap: 20px; }
.project-title { font-size: var(--fs-h3); letter-spacing: -.01em; margin-top: 8px; }
.project-body { padding: 20px; display: flex; flex-direction: column; }
.project-body .text-link { margin-top: 8px; align-self: flex-start; }
.project-summary { margin-top: 8px; color: var(--text-2); max-width: 65ch; }

.project-featured { overflow: hidden; display: grid; }
.project-featured .project-title { font-size: clamp(1.5rem, 1.25rem + 1vw, 2rem); letter-spacing: -.02em; }
.project-visual {
  background: var(--bg-2);
  border-bottom: 1px solid var(--border);
}
.project-visual { display: grid; place-items: center; padding: 16px; overflow: hidden; }
.project-visual picture { display: block; width: 100%; }
.project-visual img {
  transition: transform var(--dur) var(--ease);
  /* UI screenshots are wide: show them whole instead of cropping them. */
  width: 100%;
  height: auto;
  max-height: 100%;
  object-fit: contain;
  border-radius: 8px;
}
.project-visual:not(.project-visual-flow) { aspect-ratio: 16 / 10; }
.project-visual-flow { display: grid; align-items: center; justify-content: center; padding: 24px 20px; }

.problem-result { display: grid; gap: 16px; margin-top: 16px; }
.problem-result dd { margin-top: 4px; color: var(--text-2); max-width: 60ch; }

.project-row { display: grid; overflow: hidden; }
.project-thumb { display: none; }

@media (min-width: 768px) {
  .project-body { padding: 28px 32px; }
}
@media (min-width: 1024px) {
  .project-featured { grid-template-columns: 7fr 5fr; }
  .project-visual { border-bottom: 0; border-right: 1px solid var(--border); }
  .project-visual:not(.project-visual-flow) { aspect-ratio: auto; min-height: 360px; }
  .project-visual-flow { padding: 40px; }
  .project-featured .project-body { padding: 40px; justify-content: center; }
  .project-row:has(.project-thumb) { grid-template-columns: 8fr 4fr; }
  .project-thumb { display: block; border-left: 1px solid var(--border); background: var(--bg-2); }
  .project-thumb picture { display: block; width: 100%; height: 100%; }
.project-thumb { display: grid; place-items: center; padding: 12px; }
.project-thumb img { width: 100%; height: auto; max-height: 100%; object-fit: contain; border-radius: 6px; }
}

/* Flow (investigation / architecture) */
.flow { list-style: none; display: grid; gap: 0; counter-reset: none; }
.flow-step {
  position: relative;
  display: grid;
  grid-template-columns: 2.5rem 1fr;
  column-gap: 12px;
  padding-bottom: 20px;
}
.flow-step:last-child { padding-bottom: 0; }
.flow-step::before {
  /* vertical connector */
  content: "";
  position: absolute;
  left: calc(1.25rem - .5px);
  top: 2rem;
  bottom: 4px;
  width: 1px;
  background: var(--border-strong);
}
.flow-step:last-child::before { display: none; }
.flow-index {
  grid-row: span 2;
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  font-family: var(--mono);
  font-size: var(--fs-label);
  color: var(--text-muted);
  background: var(--bg);
}
.flow-label { font-weight: 600; padding-top: 4px; }
.flow-desc { color: var(--text-muted); font-size: var(--fs-small); }
.flow-compact .flow-step { padding-bottom: 14px; }
@media (min-width: 768px) {
  .case-content .flow {
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 24px 0;
    margin-top: 24px;
  }
  .case-content .flow-step {
    grid-template-columns: 1fr;
    row-gap: 8px;
    padding: 0 20px;
    border-left: 1px solid var(--border);
  }
  .case-content .flow-step:first-child { padding-left: 0; border-left: 0; }
  .case-content .flow-step::before { display: none; }
  .case-content .flow-index { grid-row: auto; }
  .case-content .flow-label { padding-top: 0; }
}

/* Tools list */
.tools { margin-top: 48px; }
.tools-heading { font-size: var(--fs-h3); margin-bottom: 8px; }
.tools-item { padding-block: 16px; border-top: 1px solid var(--border); }
.tools-item dt { font-weight: 600; }
.tool-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-family: var(--mono);
  font-size: var(--fs-label);
  font-weight: 400;
  color: var(--text-muted);
  vertical-align: 1px;
}
.tools-item dd { color: var(--text-muted); }
@media (min-width: 600px) {
  .tools-item { display: grid; grid-template-columns: 3fr 9fr; gap: 24px; }
}

/* Experience timeline */
.timeline { list-style: none; }
.tl-group + .tl-group { margin-top: 8px; }
.tl-roles { list-style: none; }
.tl-entry {
  position: relative;
  padding: 0 0 32px 24px;
  border-left: 1px solid var(--border);
}
.tl-entry::before {
  content: "";
  position: absolute;
  left: -4.5px;
  top: 6px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-muted);
}
.tl-date { font-family: var(--mono); font-size: var(--fs-label); color: var(--text-muted); }
.tl-title { font-size: var(--fs-h3); margin-top: 4px; }
.tl-at { color: var(--text-muted); font-weight: 500; }
.tl-body > .meta { margin-top: 2px; }
.tl-scope { margin-top: 8px; font-size: .9375rem; color: var(--text-muted); max-width: 65ch; }
.achievements { list-style: none; margin-top: 12px; display: grid; gap: 8px; max-width: 65ch; }
.achievements li { position: relative; padding-left: 20px; }
.achievements li::before { content: "–"; position: absolute; left: 0; color: var(--text-muted); }
.tl-company { padding: 0 0 16px 24px; border-left: 1px solid var(--border); }
.tl-company-name { font-size: var(--fs-h3); }
.tl-roles .tl-title { font-size: 1.0625rem; }

.earlier { margin-top: 32px; }
.earlier-list { list-style: none; margin-top: 12px; display: grid; gap: 8px; color: var(--text-2); }

@media (min-width: 1024px) {
  .tl-entry { display: grid; grid-template-columns: 200px 1fr; column-gap: 32px; padding: 0; border: 0; }
  .tl-entry::before { display: none; }
  .tl-date { text-align: right; padding-top: 4px; }
  .tl-body { position: relative; padding: 0 0 40px 32px; border-left: 1px solid var(--border); }
  .tl-body::before {
    content: "";
    position: absolute;
    left: -4.5px;
    top: 10px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--text-muted);
  }
  .tl-company { margin-left: 232px; padding: 0 0 16px 32px; }
  .earlier, .credentials { margin-left: 232px; }
}

/* Credentials */
.credentials { margin-top: 48px; display: grid; gap: 32px; }
.credential-heading { font-size: var(--fs-h3); margin-bottom: 8px; }
.credential-list { list-style: none; display: grid; gap: 8px; }
.credential-list .meta-inline { display: block; }
@media (min-width: 768px) { .credentials { grid-template-columns: 1fr 1fr; } }

/* Skills */
/* Skills: one card per category, two columns from tablet width up. Cards, chips
   and hover follow the Selected Work cards so the two sections read as one system. */
.skill-grid { display: grid; gap: 16px; }
/* One accent per card, as an r, g, b triplet so it can carry any alpha. */
.skill-card { --c: 64, 224, 208; }
.skill-card:nth-child(5n + 2) { --c: var(--accent-2-rgb); }
.skill-card:nth-child(5n + 3) { --c: 167, 139, 250; }
.skill-card:nth-child(5n + 4) { --c: 251, 191, 36; }
.skill-card:nth-child(5n + 5) { --c: 244, 114, 182; }
/* A colour chosen in the Admin Panel wins over the order-based one. */
.skill-card[data-accent="teal"] { --c: 64, 224, 208; }
.skill-card[data-accent="blue"] { --c: var(--accent-2-rgb); }
.skill-card[data-accent="purple"] { --c: 167, 139, 250; }
.skill-card[data-accent="amber"] { --c: 251, 191, 36; }
.skill-card[data-accent="pink"] { --c: 244, 114, 182; }
.skill-card[data-accent="green"] { --c: 74, 222, 128; }
@media (min-width: 640px) { .skill-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .skill-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.skill-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px 16px 14px;
  /* Colour wash from the left edge, then the usual dark card. */
  background:
    linear-gradient(90deg, rgba(var(--c), .11), rgba(var(--c), .033) 40%, transparent 70%),
    linear-gradient(160deg, rgba(19, 38, 52, .55), var(--bg-2) 55%);
  border: 1px solid rgba(86, 116, 138, .3);
  border-radius: 14px;
  transition: transform var(--dur) var(--ease), border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.skill-card:hover, .skill-card:focus-within {
  transform: translateY(-4px);
  border-color: rgba(var(--c), .5);
  box-shadow: 0 14px 32px rgba(0, 0, 0, .4), 0 0 26px rgba(var(--c), .08);
}
.skill-category { display: flex; align-items: center; gap: 10px; font-size: 1rem; letter-spacing: -.01em; }
.cat-icon {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: none;
  border: 1px solid rgba(var(--c), .22);
  border-radius: 9px;
  background: rgba(var(--c), .08);
  color: rgb(var(--c));
  transition: box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.skill-card:hover .cat-icon, .skill-card:focus-within .cat-icon { box-shadow: 0 0 18px rgba(var(--c), .35); transform: scale(1.06); }
/* Accent bar directly left of the title, in the card's own colour. */
.skill-title { display: flex; align-items: center; gap: 10px; }
.skill-title::before {
  content: "";
  flex: none;
  width: 4px;
  height: 30px;
  border-radius: 999px;
  background: linear-gradient(to bottom, rgb(var(--c)), rgba(var(--c), .55));
  box-shadow: 0 0 10px rgba(var(--c), .55);
}
.skill-list { list-style: none; display: flex; flex-wrap: wrap; gap: 6px; }
.skill-list li {
  font-size: .75rem;
  padding: 4px 9px;
  border-radius: var(--r-sm);
  border: 1px solid rgba(86, 116, 138, .35);
  background: rgba(19, 38, 52, .6);
  color: var(--text-muted);
}
/* Primary skills: teal outline and a faint teal fill. */
.skill-list .skill-primary {
  color: var(--text);
  font-weight: 500;
  border-color: rgba(var(--c), .5);
  background: rgba(var(--c), .1);
}

/* Contact */
.contact-inner { display: grid; gap: 32px; }
.contact-headline { font-size: var(--fs-lead); color: var(--text-2); max-width: 44ch; }
.contact-supporting { margin-top: 8px; color: var(--text-muted); max-width: 60ch; }
.section-contact .cta-row { margin-top: 24px; }
.copy-fallback { margin-top: 12px; font-family: var(--mono); user-select: all; }

.channels { list-style: none; display: grid; gap: 12px; align-content: start; }
.channel {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  height: 100%;
  padding: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.channel:hover { border-color: var(--border-strong); background: var(--bg-2); }
.channel-icon {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: var(--r-sm);
  background: var(--bg-2);
  color: var(--text-muted);
}
.channel-text { display: grid; gap: 2px; min-width: 0; }
.channel-label { font-size: var(--fs-small); font-weight: 600; color: var(--text); }
.channel-value {
  font-family: var(--mono);
  font-size: var(--fs-label);
  color: var(--text-muted);
  /* One line per card, so the cards stay the same height. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.channel-go { color: var(--text-muted); display: grid; place-items: center; }
.channel:hover .channel-go { color: var(--accent); }

@media (min-width: 1024px) {
  .contact-inner { grid-template-columns: minmax(0, 1fr) minmax(360px, 420px); gap: 48px; align-items: start; }
  .channels { gap: 14px; }
}

/* Footer */
.site-footer { border-top: 1px solid var(--border); padding-block: 32px 40px; font-size: var(--fs-small); color: var(--text-muted); }
.footer-inner { display: grid; gap: 16px; }
.footer-id { display: grid; }
.footer-name { color: var(--text); font-weight: 600; }
.footer-links { list-style: none; display: flex; flex-wrap: wrap; gap: 0 20px; }
.footer-links a { display: inline-flex; align-items: center; min-height: 44px; text-underline-offset: 3px; }
.footer-links a:hover { color: var(--text); }
@media (min-width: 1024px) {
  .footer-inner { grid-template-columns: 1fr auto auto; align-items: center; gap: 40px; }
}

/* Project pages */
.page-head { padding-bottom: 32px; }
.back-link { margin-bottom: 24px; }
.case-header { padding-block: 40px 48px; }
.case-meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 16px 32px;
  margin-top: 32px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
  max-width: 900px;
}
.case-meta dt { font-family: var(--mono); font-size: var(--fs-label); color: var(--text-muted); text-transform: uppercase; letter-spacing: .08em; }
.case-meta dd { margin-top: 4px; color: var(--text-2); }
.case-meta-wide { grid-column: 1 / -1; }
.notice {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  margin-top: 24px;
  padding: 16px;
  max-width: 900px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  color: var(--text-2);
  font-size: var(--fs-small);
}
.notice .icon { flex: none; margin-top: 2px; color: var(--text-muted); }

.case-cover { margin-bottom: 48px; }
.case-cover img { width: 100%; border-radius: var(--r-lg); border: 1px solid var(--border); }
figcaption { margin-top: 8px; font-size: var(--fs-small); color: var(--text-muted); }

.case-body { padding-bottom: 32px; }
.case-section { padding-block: 32px; border-top: 1px solid var(--border); }
.case-section h2 { font-size: var(--fs-h3); letter-spacing: -.01em; margin-bottom: 16px; }
.case-content { max-width: 65ch; color: var(--text-2); }
.case-content-wide { max-width: none; }
.case-content > * + * { margin-top: 16px; }
.case-content p { max-width: 65ch; }
.case-role { color: var(--text); font-weight: 600; }
.bullets { padding-left: 1.25em; display: grid; gap: 8px; }
.bullets li::marker { color: var(--text-muted); }
.inline-facts { display: flex; flex-wrap: wrap; gap: 8px 32px; }
.inline-facts dt { font-family: var(--mono); font-size: var(--fs-label); color: var(--text-muted); }
.decisions { display: grid; gap: 16px; }
.decision { padding-left: 16px; border-left: 1px solid var(--border-strong); }
.decision dt { color: var(--text); font-weight: 600; }
.decision dd { margin-top: 4px; }
.before-after { display: grid; gap: 12px; }
.before-after > div { padding: 16px; border: 1px solid var(--border); border-radius: var(--r-md); }
.before-after dt { font-family: var(--mono); font-size: var(--fs-label); color: var(--text-muted); text-transform: uppercase; letter-spacing: .08em; }
.before-after dd { margin-top: 4px; color: var(--text); }
.feature + .feature { margin-top: 32px; }
.feature-title { font-size: 1.0625rem; color: var(--text); margin-bottom: 4px; }
.case-figure img { width: 100%; border-radius: var(--r-md); border: 1px solid var(--border); }
.figure-link { display: block; border-radius: var(--r-md); }
.figure-link:hover img { border-color: var(--border-strong); }
.figure-hint { font-size: var(--fs-label); color: var(--text-muted); }
.gallery { display: grid; gap: 32px; }
.link-list { list-style: none; display: flex; flex-wrap: wrap; gap: 0 24px; }
.case-nav {
  display: grid;
  gap: 16px;
  padding-block: 32px 16px;
  border-top: 1px solid var(--border);
}
.case-nav-link {
  display: grid;
  gap: 4px;
  padding: 16px 0;
  min-height: 44px;
  text-decoration: none;
}
.case-nav-title { font-weight: 600; color: var(--text); text-decoration: underline; text-decoration-color: var(--border-strong); text-underline-offset: 3px; }
.case-nav-link:hover .case-nav-title { text-decoration-color: var(--accent); }
@media (min-width: 600px) {
  .before-after { grid-template-columns: 1fr 1fr; }
  .case-nav { grid-template-columns: 1fr 1fr; }
  .case-nav-next { text-align: right; }
}
@media (min-width: 1024px) {
  .case-header { padding-block: 56px 64px; }
  .case-section { display: grid; grid-template-columns: 3fr 9fr; column-gap: 32px; padding-block: 40px; }
  .case-section h2 { margin-bottom: 0; }
}

.not-found { min-height: 50vh; }
.report-list { list-style: none; display: grid; gap: 6px; margin-bottom: 40px; }
.report-list code { font-family: var(--mono); font-size: var(--fs-small); color: var(--text-2); word-break: break-word; }
.report .section-title { font-size: var(--fs-h3); margin: 32px 0 12px; }

/* 7. Utilities --------------------------------------------------------------- */
.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    transition-duration: 0s !important;
    animation-duration: 0s !important;
    animation-iteration-count: 1 !important;
    /* Delays too: an entrance waiting out its delay would hold the element invisible. */
    animation-delay: 0s !important;
    transition-delay: 0s !important;
  }
  .card.is-linked:hover, .btn:active, .project-card.card:hover, .project-card.card:focus-within { transform: none; }
  .card.is-linked:hover .project-visual img { transform: none; }
}

@media (forced-colors: active) {
  .status-dot { forced-color-adjust: none; }
  .card, .status, .btn-secondary { border: 1px solid CanvasText; }
}

/* Development-only: placeholder for a missing CV file. */
.btn.is-pending { cursor: not-allowed; border-style: dashed; }

/* ===== Ported from the redesign =========================================== */

/* Hero: the portrait panel carries the regional network map. */
.portrait { position: relative; isolation: isolate; background-color: var(--bg-2); }
.portrait::before {
  content: "";
  position: absolute;
  inset: 0;
  background: url("../images/hero-network.webp") center / cover no-repeat;
  opacity: .6;
}
.portrait::after {                       /* keeps the signature readable */
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 45%;
  background: linear-gradient(transparent, rgba(6, 16, 25, .75));
  z-index: 1;
}
.portrait picture, .portrait img { position: relative; z-index: 1; }
.portrait img {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 34%);
  mask-image: linear-gradient(to right, transparent, #000 34%);
}

/* Section heading: title left, supporting line and action right. */
.section-aside {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 20px;
}
.section-note { color: var(--text-muted); font-size: var(--fs-small); }
.section-aside .text-link { min-height: 32px; }

/* ===== Adopted hero layout ================================================ */

.eyebrow-bar {
  display: inline-block;
  width: 2px;
  height: 1em;
  margin-right: 10px;
  background: var(--accent);
  vertical-align: -2px;
}
.hero .eyebrow { font-family: var(--mono); color: var(--accent); letter-spacing: .16em; }

.hero-heading {
  display: grid;
  gap: 2px;
  font-size: clamp(2rem, 1.45rem + 2.1vw, 2.75rem);
  line-height: 1.05;
  letter-spacing: -.035em;
}
.hero-greeting { color: var(--text); }
.hero-name { color: var(--accent); }

.hero-roles {
  margin-top: 18px;
  font-family: var(--mono);
  font-size: var(--fs-label);
  color: var(--accent);
  display: flex;
  flex-wrap: wrap;
  gap: 4px 8px;
}
.hero-roles .dot { color: var(--border-strong); }

/* Handwritten name over the portrait. */
.signature {
  position: absolute;
  left: 20px;
  bottom: 18px;
  z-index: 2;
  display: grid;
  gap: 4px;
  text-shadow: 0 2px 12px rgba(6, 16, 25, .75);
}
.signature-name { font-family: "Caveat", cursive; font-size: 30px; line-height: 1; color: var(--text); }
.signature-note {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--mono);
  font-size: var(--fs-label);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-2);
}
.signature-dash { width: 22px; height: 2px; background: var(--accent); flex: none; }

/* Status panel is its own column now (it no longer overlaps the photo). */
.status-head { text-transform: uppercase; }
.status-row { grid-template-columns: 1fr; gap: 2px; }
.status-row dt { display: flex; align-items: center; gap: 8px; font-family: var(--mono); }
.status-icon { display: grid; place-items: center; width: 28px; height: 28px; flex: none;
  border-radius: var(--r-sm); border: 1px solid var(--border); background: rgba(19, 38, 52, .6);
  color: var(--text-muted); }
.status-row dd { padding-left: 36px; }
.status-checked {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  font-family: var(--mono);
  font-size: var(--fs-label);
  color: var(--text-muted);
}

@media (min-width: 1024px) {
  /* Two columns: text, and one panel holding the map, the portrait and the status card. */
  .hero-inner {
    --hero-panel: clamp(300px, 32vw, 470px);
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    grid-template-areas: "text visual";
    align-items: center;
    column-gap: 36px;
  }
  .hero-text { grid-area: text; grid-column: auto; align-self: center; }
  .hero-visual {
    grid-area: visual;
    grid-column: auto;
    position: relative;
    display: block;
    padding-bottom: 0;
  }
  .hero-visual:not(.no-photo) .portrait {
    width: 100%;
    height: var(--hero-panel);
    max-height: none;
    min-height: 0;
    aspect-ratio: auto;
    /* No frame: the map and the portrait fade straight into the page. */
    border: 0;
    border-radius: 0;
    background: none;
    overflow: visible;
  }
  .hero-visual:not(.no-photo) .portrait::before {
    -webkit-mask-image: linear-gradient(to right, transparent, #000 18%, #000 82%, transparent),
                        linear-gradient(to bottom, transparent, #000 14%, #000 78%, transparent);
    mask-image: linear-gradient(to right, transparent, #000 18%, #000 82%, transparent),
                linear-gradient(to bottom, transparent, #000 14%, #000 78%, transparent);
    -webkit-mask-composite: source-in;
    mask-composite: intersect;
  }
  .hero-visual:not(.no-photo) .portrait::after { display: none; }
  .hero-visual:not(.no-photo) .portrait img {
    -webkit-mask-image: linear-gradient(to left, transparent, #000 30%, #000 90%, transparent),
                        linear-gradient(to bottom, #000 72%, transparent);
    mask-image: linear-gradient(to left, transparent, #000 30%, #000 90%, transparent),
                linear-gradient(to bottom, #000 72%, transparent);
    -webkit-mask-composite: source-in;
    mask-composite: intersect;
  }
  /* The portrait stands on the right of the panel; the map stays readable on the left. */
  .hero-visual:not(.no-photo) .portrait picture {
    position: absolute;
    inset: 0 auto 0 0;
    width: 56%;
  }
  .hero-visual:not(.no-photo) .portrait img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 50% 10%;
  }
  /* 1024–1279: the card sits under the portrait, still inside the same cell. */
  .hero-visual { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .hero-visual:not(.no-photo) .status { position: static; width: auto; height: auto; }
}

@media (min-width: 1280px) {
  /* Wide enough for the card to float over the map half of the panel. */
  .hero-visual { display: block; }
  .hero-visual:not(.no-photo) .status {
    position: absolute;
    left: auto;
    right: 0;
    bottom: 20px;
    z-index: 2;
    width: min(330px, 50%);
    box-shadow: var(--shadow);
  }
  .signature { left: 20px; right: auto; bottom: 18px; }
}

/* ===== Stats band ========================================================= */
.stats-band { padding-block: 0; }
/* The card already closes the hero block, so the next section starts closer. */
.stats-band + .section { padding-top: clamp(64px, 7vw, 96px); }
.stats-inner {
  display: grid;
  gap: 24px;
  padding: 24px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}
.stats { display: grid; gap: 20px 28px; }
.stat {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: 12px;
  align-items: start;
  align-content: start;   /* a taller neighbour must not stretch the rows and push the value down */
  /* Never split a word: these columns get narrow. */
  overflow-wrap: normal;
  word-break: normal;
  hyphens: none;
}
.stat-icon {
  grid-row: 1;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: var(--r-sm);
  background: var(--bg-2);
  color: var(--text-muted);
}
.stat dt {
  grid-column: 2;
  align-self: center;
  min-height: 34px;
  display: flex;
  align-items: center;
  font-size: 1.125rem;
  font-weight: 700;
  line-height: 1.2;
}
.stat dd {
  grid-column: 2;
  margin-top: 4px;
  font-size: var(--fs-small);
  line-height: 1.45;
  color: var(--text-muted);
}
.stats-quote {
  display: flex;
  gap: 10px;
  font-size: var(--fs-small);
  font-style: italic;
  color: var(--text-2);
}
.quote-mark { font-size: 1.5rem; line-height: 1; color: var(--accent); font-style: normal; }

@media (min-width: 600px) {
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1280px) {
  .stats-inner { grid-template-columns: minmax(0, 1fr) minmax(220px, 300px); align-items: center; gap: 32px; }
  .stats { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; }
  .stat { padding-right: 16px; }
  .stat + .stat { padding-left: 24px; border-left: 1px solid var(--border); }
  /* Keep every value on one line so the labels below line up. */
  .stat dt { font-size: 1.0625rem; }
  .stats-quote { padding-left: 32px; border-left: 1px solid var(--border); }
}

/* ===== Hero entrance ======================================================
   Plays once on load, CSS only: the text column builds top to bottom (100 ms
   apart), the portrait fades in with a slight zoom and the status card slides in
   from the right. `translate` and `scale` are used instead of `transform`, and the
   fill is `backwards`, so nothing is left overriding later styles. The status dot
   also gives off a slow, quiet pulse. Reduced motion turns all of it off (global
   rule near the top of the file). */
@keyframes hero-rise {
  from { opacity: 0; translate: 0 24px; }
}
@keyframes hero-slide {
  from { opacity: 0; translate: 32px 0; }
}
@keyframes hero-zoom {
  from { opacity: 0; scale: .96; translate: 0 16px; }
}
@keyframes hero-grow {
  from { transform: scaleX(0); }
}
@keyframes status-pulse {
  0% { box-shadow: 0 0 0 0 rgba(49, 233, 129, .5); }
  70%, 100% { box-shadow: 0 0 0 8px rgba(49, 233, 129, 0); }
}
.hero-text > *, .hero-greeting, .hero-name {
  animation: hero-rise 800ms cubic-bezier(.22, 1, .36, 1) backwards;
}
.hero-text > .eyebrow { animation-delay: 80ms; }
.hero-greeting { display: inline-block; animation-delay: 200ms; }
.hero-name { animation-delay: 320ms; }
.hero-text > .hero-heading { animation: none; }
.hero-text > .hero-role { animation-delay: 460ms; }
.hero-text > .hero-lead { animation-delay: 560ms; }
.hero-text > .hero-roles { animation-delay: 660ms; }
.hero-text > .cta-row { animation-delay: 760ms; }
.eyebrow-bar { transform-origin: left center; animation: hero-grow 600ms cubic-bezier(.22, 1, .36, 1) 200ms backwards; }
.hero-visual .portrait { animation: hero-zoom 1000ms cubic-bezier(.22, 1, .36, 1) 250ms backwards; }
.hero-visual .status { animation: hero-slide 900ms cubic-bezier(.22, 1, .36, 1) 550ms backwards; }
.status-dot { animation: status-pulse 2.4s ease-out 1.6s infinite; }

/* ===== Scroll reveal ======================================================
   Section titles and cards fade in and travel 22px each time they enter the
   viewport: they rise when the page scrolls down and drop in when it scrolls up
   (main.js sets --reveal-y to +22px or -22px). main.js adds .reveal to them and
   toggles .is-in as they enter and leave, and hands out the stagger delay.
   Without JS or with reduced motion nothing is hidden. Animates `translate`, not
   `transform`, so the card hover lift keeps working. */
@keyframes reveal-in {
  from { opacity: 0; translate: 0 var(--reveal-y, 22px); }
}
.reveal:not(.is-in) { opacity: 0; }
.reveal.is-in { animation: reveal-in 650ms cubic-bezier(.22, 1, .36, 1) var(--reveal-delay, 0ms) backwards; }
.reveal.reveal-skip { animation: none; }

/* ===== Selected Work carousel ==============================================
   Three cards stay in view (two on tablets) and the rest sit to the right of
   a horizontal scroller. Nothing moves on its own: with the pointer over the
   row, the mouse wheel slides it left or right (main.js); touch, trackpad,
   the scrollbar and keyboard focus all scroll it natively. Phones keep the
   plain stacked grid. */
@media (min-width: 768px) {
  .project-carousel.is-scrollable {
    --per: 2;
    --gap: 20px;
    --fade: 48px;
  }
  .project-carousel.is-scrollable .carousel-viewport {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    container-type: inline-size;
    /* Room for the hover lift and glow, which a scroller would otherwise clip. */
    padding-block: 16px 28px;
    margin-block: -16px -28px;
    /* Fade only the edge that still has cards behind it (set by main.js). */
    --fade-l: 0px;
    --fade-r: var(--fade);
    mask-image: linear-gradient(to right, transparent, #000 var(--fade-l), #000 calc(100% - var(--fade-r)), transparent);
    transition: --fade-l var(--dur) var(--ease), --fade-r var(--dur) var(--ease);
  }
  .project-carousel.is-scrollable .carousel-viewport::-webkit-scrollbar { display: none; }
  .project-carousel.is-scrollable .carousel-viewport.is-scrolled { --fade-l: var(--fade); }
  .project-carousel.is-scrollable .carousel-viewport.is-end { --fade-r: 0px; }
  .project-carousel.is-scrollable .project-track {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: calc((100cqi - (var(--per) - 1) * var(--gap)) / var(--per));
    width: max-content;
  }
}
/* Three cards only where each still gets ~360px; 1024-1199 shows two. */
@media (min-width: 1200px) {
  .project-carousel.is-scrollable { --per: 3; }
}
@property --fade-l { syntax: "<length>"; inherits: true; initial-value: 0px; }
@property --fade-r { syntax: "<length>"; inherits: true; initial-value: 0px; }

/* ===== Motion & polish =====================================================
   Micro-interactions and page-level motion, added on top of the sections above.
   All of it is decoration: without JS, with reduced motion or in browsers that
   lack a feature, the page reads and works the same. The navbar is left as is. */
:root { --ease-out: cubic-bezier(.22, 1, .36, 1); }
html { scrollbar-color: #26404f var(--bg); }

/* -- Buttons: lift, soft glow, one sheen across the primary button, icon nudge */
.btn:not(.header-cta) {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease),
              color var(--dur) var(--ease), box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease-out);
}
.btn-primary::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(105deg, transparent 30%, rgba(255, 255, 255, .5) 50%, transparent 70%);
  transform: translateX(-120%);
  pointer-events: none;
}
.btn .icon { transition: transform var(--dur) var(--ease-out); }
@keyframes icon-drop {
  40% { transform: translateY(3px); }
  70% { transform: translateY(-1px); }
}
@media (hover: hover) {
  .btn-primary:hover { transform: translateY(-2px); box-shadow: 0 10px 26px rgba(64, 224, 208, .28); }
  .btn-primary:hover::after { transform: translateX(120%); transition: transform 750ms var(--ease-out); }
  .btn-secondary:not(.header-cta):hover {
    transform: translateY(-2px);
    border-color: rgba(64, 224, 208, .55);
    background: rgba(64, 224, 208, .06);
    box-shadow: 0 8px 22px rgba(0, 0, 0, .28);
  }
  .btn[download]:not(.header-cta):hover .icon { animation: icon-drop 650ms var(--ease-out); }
  .btn[href^="mailto:"]:hover .icon { transform: rotate(-8deg) scale(1.08); }
  .btn[href="#work"]:hover .icon { transform: translateY(-1px) rotate(-6deg); }
}
.btn:not(.header-cta):active { transform: translateY(0) scale(.98); transition-duration: 80ms; }

/* -- Text links: the arrow leans the way it points */
.text-link .icon { transition: transform var(--dur) var(--ease-out); }
@media (hover: hover) {
  .text-link:hover .icon { transform: translateX(3px); }
  .back-link:hover .icon { transform: translateX(-3px); }
  .link-list .text-link:hover .icon { transform: translate(2px, -2px); }
}

/* -- Whole-card links: the focus ring goes round the card, not just the link text */
@supports selector(:has(*)) {
  .card:has(.card-link:focus-visible) { outline: 2px solid var(--accent); outline-offset: 3px; }
  .project-card:has(.card-link:focus-visible) { outline-offset: -2px; }   /* the Work strip clips outside */
  .card-link:focus-visible { outline: none; }
}

/* -- Spotlight: a soft light that follows the mouse across a card (main.js adds
      .spotlight and sets --mx/--my). Its colour is --spot, an r, g, b triplet. */
.skill-card, .channel, .stats-inner { position: relative; isolation: isolate; }
.card.spotlight { isolation: isolate; }
.spotlight { --spot: 64, 224, 208; --mx: 50%; --my: 0%; }
.skill-card.spotlight { --spot: var(--c); }
@media (hover: hover) and (pointer: fine) {
  .spotlight::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    pointer-events: none;
    background: radial-gradient(300px circle at var(--mx) var(--my), rgba(var(--spot), .11), transparent 70%);
    opacity: 0;
    transition: opacity 350ms var(--ease);
  }
  .spotlight:hover::after { opacity: 1; }
}

/* -- Hero: the map drifts slowly, the name catches one sheen of light, the
      signature writes itself and the status rows boot one by one. */
@keyframes map-drift {
  from { transform: scale(1) translate3d(0, 0, 0); }
  to { transform: scale(1.06) translate3d(-1.2%, -1%, 0); }
}
.portrait::before { animation: map-drift 26s ease-in-out infinite alternate; }

@keyframes name-sheen {
  from { background-position: 100% 0; }
  to { background-position: 0 0; }
}
.hero-name {
  background: linear-gradient(100deg, var(--accent) 44%, #e6fffc 50%, var(--accent) 56%) 100% 0 / 250% 100% no-repeat;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: hero-rise 800ms var(--ease-out) 320ms backwards, name-sheen 1.8s cubic-bezier(.4, 0, .2, 1) 1.5s both;
}

@keyframes write-on {
  from { clip-path: inset(-20% 100% -30% 0); }
  to { clip-path: inset(-20% 0 -30% 0); }
}
.signature-name { animation: write-on 1.4s cubic-bezier(.65, 0, .35, 1) 1s backwards; }
.signature-note { animation: hero-rise 700ms var(--ease-out) 1.9s backwards; }
.signature-dash { transform-origin: left center; animation: hero-grow 700ms var(--ease-out) 2s backwards; }

.hero-visual .status-list > *, .hero-visual .status-checked { animation: hero-rise 600ms var(--ease-out) backwards; }
.hero-visual .status-list > :nth-child(1) { animation-delay: 820ms; }
.hero-visual .status-list > :nth-child(2) { animation-delay: 900ms; }
.hero-visual .status-list > :nth-child(3) { animation-delay: 980ms; }
.hero-visual .status-list > :nth-child(4) { animation-delay: 1060ms; }
.hero-visual .status-checked { animation-delay: 1140ms; }

/* -- Stats band: numbers count up once (main.js), icons light up on hover */
.count-num { display: inline-block; font-variant-numeric: tabular-nums; text-align: right; }
.stat-icon { transition: color var(--dur) var(--ease), background-color var(--dur) var(--ease); }
@media (hover: hover) {
  .stat:hover .stat-icon { color: var(--accent); background: rgba(64, 224, 208, .1); }
}

/* -- Skills: tags follow their card in, one after another; they lift on hover */
@keyframes chip-in {
  from { opacity: 0; translate: 0 8px; }
}
.skill-card.reveal.is-in:not(.reveal-skip) .skill-list li {
  animation: chip-in 420ms var(--ease-out) backwards;
  animation-delay: calc(var(--reveal-delay, 0ms) + 180ms + var(--i, 0) * 40ms);
}
.skill-list li {
  transition: transform var(--dur) var(--ease-out), border-color var(--dur) var(--ease),
              color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
@media (hover: hover) {
  .skill-list li:hover { transform: translateY(-2px); border-color: rgba(var(--c), .6); color: var(--text); }
}

/* -- Work strip: previous/next buttons, a position bar, drag with the mouse */
.carousel-controls { display: flex; align-items: center; gap: 10px; margin-top: 18px; }
.carousel-controls[hidden] { display: none; }
.carousel-progress {
  position: relative;
  flex: 1;
  height: 2px;
  margin-right: 8px;
  border-radius: 2px;
  background: var(--border);
  overflow: hidden;
}
.carousel-thumb {
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--thumb-w, 40%);
  border-radius: inherit;
  background: linear-gradient(90deg, var(--accent), rgba(64, 224, 208, .55));
  box-shadow: 0 0 8px rgba(64, 224, 208, .5);
  transform: translateX(var(--thumb-x, 0%));
}
.carousel-btn {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  background: rgba(19, 38, 52, .6);
  color: var(--text-2);
  cursor: pointer;
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease),
              background-color var(--dur) var(--ease), transform var(--dur-fast) var(--ease), opacity var(--dur) var(--ease);
}
.carousel-btn:focus-visible { border-radius: 50%; }
.carousel-btn[aria-disabled="true"] { opacity: .35; cursor: default; }
@media (hover: hover) {
  .carousel-btn:not([aria-disabled="true"]):hover { border-color: rgba(64, 224, 208, .6); color: var(--accent); background: rgba(64, 224, 208, .08); }
}
.carousel-btn:not([aria-disabled="true"]):active { transform: scale(.93); }
.carousel-viewport.is-draggable { cursor: grab; }
.carousel-viewport.is-dragging { cursor: grabbing; user-select: none; }
.carousel-viewport.is-dragging .project-card { pointer-events: none; }

/* -- Experience: a line fills down the timeline as you read (main.js sets
      --tl-p), dots light up as it passes, the current role keeps pulsing. */
.timeline { position: relative; --tl-x: 0px; }
@media (min-width: 1024px) { .timeline { --tl-x: 232px; } }   /* 200px date column + 32px gap */
.js .timeline::after {
  content: "";
  position: absolute;
  top: 0;
  left: calc(var(--tl-x) - .5px);
  width: 2px;
  height: 100%;
  border-radius: 2px;
  background: linear-gradient(to bottom, var(--accent), rgba(64, 224, 208, .3));
  box-shadow: 0 0 10px rgba(64, 224, 208, .45);
  transform-origin: top;
  transform: scaleY(var(--tl-p, 0));
  pointer-events: none;
}
.tl-entry::before, .tl-body::before {
  z-index: 1;
  transition: background-color 400ms var(--ease), box-shadow 400ms var(--ease), transform 400ms var(--ease-out);
}
.tl-date { transition: color 400ms var(--ease); }
.tl-entry.is-lit::before, .tl-entry.is-lit .tl-body::before, .tl-entry.is-current::before, .tl-entry.is-current .tl-body::before {
  background: var(--accent);
  box-shadow: 0 0 0 4px rgba(64, 224, 208, .14), 0 0 12px rgba(64, 224, 208, .55);
  transform: scale(1.15);
}
.tl-entry.is-lit .tl-date { color: var(--accent); }
@keyframes tl-pulse {
  0% { box-shadow: 0 0 0 0 rgba(64, 224, 208, .55), 0 0 12px rgba(64, 224, 208, .55); }
  70%, 100% { box-shadow: 0 0 0 10px rgba(64, 224, 208, 0), 0 0 12px rgba(64, 224, 208, .55); }
}
.tl-entry.is-current::before, .tl-entry.is-current .tl-body::before { animation: tl-pulse 2.4s ease-out infinite; }

/* -- About collage: tilts a little towards the mouse; its animations pause
      while it is off screen (main.js toggles .is-live). */
/* Registered and not inherited: a tilt update must not restyle the hundreds of
   elements inside the collage. */
@property --rx { syntax: "<angle>"; inherits: false; initial-value: 0deg; }
@property --ry { syntax: "<angle>"; inherits: false; initial-value: 0deg; }
@media (hover: hover) and (pointer: fine) {
  .about-collage {
    transform: perspective(1400px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
    transition: transform 700ms var(--ease-out);
  }
  .about-collage.is-tilting { transition-duration: 180ms; }
}
.js .about-collage:not(.is-live) .about-cursor,
.js .about-collage:not(.is-live) .nm-halo { animation-play-state: paused; }

/* -- Contact: channels lift and light up, the copy button confirms */
.channel {
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease),
              transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease);
}
.channel-icon, .channel-go { transition: color var(--dur) var(--ease), background-color var(--dur) var(--ease), transform var(--dur) var(--ease-out); }
@media (hover: hover) {
  .channel:hover { transform: translateY(-2px); border-color: rgba(64, 224, 208, .45); box-shadow: 0 10px 24px rgba(0, 0, 0, .3); }
  .channel:hover .channel-icon { color: var(--accent); background: rgba(64, 224, 208, .1); }
  .channel:hover .channel-go { transform: translateX(4px); }
}
@keyframes pop {
  0% { transform: scale(.6); }
  60% { transform: scale(1.15); }
}
.btn-copy.is-copied { border-color: rgba(49, 233, 129, .6); color: var(--status-ok); background: rgba(49, 233, 129, .08); }
.btn-copy.is-copied .icon { animation: pop 420ms var(--ease-out); }

/* -- Back to top: appears after the first screen; the ring shows how far down you are */
.to-top {
  position: fixed;
  right: max(16px, env(safe-area-inset-right));
  bottom: max(16px, env(safe-area-inset-bottom));
  z-index: 40;
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(9, 21, 31, .9);
  color: var(--text-2);
  box-shadow: 0 10px 28px rgba(0, 0, 0, .35);
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transform: translateY(12px) scale(.9);
  transition: opacity 300ms var(--ease), transform 300ms var(--ease-out), visibility 0s linear 300ms, color var(--dur) var(--ease);
}
@supports (backdrop-filter: blur(1px)) {
  .to-top { background: rgba(9, 21, 31, .72); backdrop-filter: blur(10px); }
}
.to-top[hidden] { display: none; }
@media (max-width: 599px) {
  .to-top { width: 42px; height: 42px; right: max(12px, env(safe-area-inset-right)); bottom: max(12px, env(safe-area-inset-bottom)); }
  .to-top-ring { width: 42px; height: 42px; }
}
/* Room under the footer so the button never covers its last line. */
.js .site-footer { padding-bottom: 88px; }
.to-top.is-visible { opacity: 1; visibility: visible; transform: none; transition-delay: 0s; }
.to-top > * { grid-area: 1 / 1; }
.to-top-ring { width: 48px; height: 48px; transform: rotate(-90deg); }
.to-top-track { fill: none; stroke: var(--border); stroke-width: 2; }
.to-top-bar {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  filter: drop-shadow(0 0 3px rgba(64, 224, 208, .6));
}
.to-top > .icon { transition: transform var(--dur) var(--ease-out); }
.to-top:focus-visible { border-radius: 50%; }
@media (hover: hover) {
  .to-top:hover { color: var(--accent); }
  .to-top:hover > .icon { transform: translateY(-2px); }
}

/* -- Images: lazy images fade in once loaded instead of popping in */
.js img.lazy-fade { opacity: 0; transition: opacity 600ms var(--ease); }
.js img.lazy-fade.is-loaded { opacity: 1; }

/* -- Project pages: the header builds in, section titles stay in view while
      their section is read, the current one is marked. */
.case-header .container > *, .page-head .container > * { animation: hero-rise 700ms var(--ease-out) backwards; }
.case-header .container > :nth-child(2), .page-head .container > :nth-child(2) { animation-delay: 70ms; }
.case-header .container > :nth-child(3), .page-head .container > :nth-child(3) { animation-delay: 140ms; }
.case-header .container > :nth-child(4), .page-head .container > :nth-child(4) { animation-delay: 210ms; }
.case-header .container > :nth-child(5) { animation-delay: 280ms; }
.case-header .container > :nth-child(6) { animation-delay: 350ms; }
.case-cover { animation: hero-zoom 900ms var(--ease-out) 300ms backwards; }
@media (min-width: 1024px) {
  .case-section h2 { position: sticky; top: calc(var(--header-h) + 40px); align-self: start; transition: color var(--dur) var(--ease); }
  .case-section h2::before {
    content: "";
    position: absolute;
    left: 0;
    top: -12px;
    width: 24px;
    height: 2px;
    border-radius: 2px;
    background: var(--accent);
    box-shadow: 0 0 8px rgba(64, 224, 208, .5);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 450ms var(--ease-out);
  }
  .case-section.is-current h2::before { transform: scaleX(1); }
}
.figure-link { overflow: hidden; cursor: zoom-in; }
.figure-link img { transition: transform 600ms var(--ease-out), border-color var(--dur) var(--ease); }
@media (hover: hover) {
  .figure-link:hover img { transform: scale(1.015); }
  .case-nav-link .case-nav-title { transition: text-decoration-color var(--dur) var(--ease); }
}
.case-nav-link { transition: transform var(--dur) var(--ease-out); }
@media (hover: hover) {
  .case-nav-link:hover { transform: translateX(-4px); }
  .case-nav-next:hover { transform: translateX(4px); }
}

/* -- Image preview (gallery): a dialog instead of leaving the page */
.lightbox {
  margin: auto;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text);
  max-width: min(96vw, 1400px);
  max-height: 94vh;
  overflow: visible;
}
.lightbox::backdrop { background: rgba(3, 8, 13, .84); }
@supports (backdrop-filter: blur(1px)) { .lightbox::backdrop { backdrop-filter: blur(6px); } }
.lightbox figure { display: grid; justify-items: center; }
.lightbox img {
  max-width: min(96vw, 1400px);
  max-height: 84vh;
  width: auto;
  height: auto;
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 30px 80px rgba(0, 0, 0, .6);
}
.lightbox figcaption { margin-top: 12px; text-align: center; }
.lightbox-close {
  position: absolute;
  top: -52px;
  right: 0;
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  background: rgba(19, 38, 52, .8);
  color: var(--text);
  cursor: pointer;
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.lightbox-close:hover { border-color: rgba(64, 224, 208, .6); color: var(--accent); }
.lightbox-close:focus-visible { border-radius: 50%; }
@keyframes lb-in { from { opacity: 0; transform: translateY(10px) scale(.97); } }
@keyframes lb-fade { from { opacity: 0; } }
.lightbox[open] { animation: lb-in 320ms var(--ease-out); }
.lightbox[open]::backdrop { animation: lb-fade 320ms var(--ease); }

/* -- 404: a ping that never gets an answer, one line at a time */
.not-found { display: grid; align-items: center; min-height: calc(100svh - 220px); }
.nf-inner { display: grid; gap: 40px; align-items: center; }
@media (min-width: 1024px) { .nf-inner { grid-template-columns: minmax(0, 1fr) minmax(0, 540px); gap: 64px; } }
.nf-copy > * { animation: hero-rise 700ms var(--ease-out) backwards; }
.nf-copy > :nth-child(2) { animation-delay: 80ms; }
.nf-copy > :nth-child(3) { animation-delay: 160ms; }
.nf-copy > :nth-child(4) { animation-delay: 240ms; }
.ping {
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: #050d15;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .45), 0 0 0 1px rgba(64, 224, 208, .04);
  font-family: var(--mono);
  font-size: .8125rem;
  animation: hero-zoom 800ms var(--ease-out) 200ms backwards;
}
.ping-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  background: #08131d;
  color: var(--text-muted);
  font-size: .75rem;
}
.ping-bar i { width: 9px; height: 9px; border-radius: 50%; background: #ff5f57; }
.ping-bar i:nth-child(2) { background: #febc2e; }
.ping-bar i:nth-child(3) { background: #28c840; }
.ping-bar span { margin-left: 8px; }
.ping-body { display: grid; gap: 4px; align-content: start; min-height: 236px; padding: 16px 18px 20px; color: var(--text-2); }
.ping-line { white-space: pre-wrap; overflow-wrap: anywhere; }
.ping-prompt { color: var(--accent); }
.ping-miss { color: #f2a08f; }
.ping-loss { color: #ff6b6b; }
@keyframes ping-show { from { opacity: 0; } }
.ping-line { animation: ping-show 1ms linear backwards; }
.ping-line:nth-child(1) { animation-delay: 500ms; }
.ping-line:nth-child(2) { animation-delay: 1000ms; }
.ping-line:nth-child(3) { animation-delay: 2000ms; }
.ping-line:nth-child(4) { animation-delay: 3000ms; }
.ping-line:nth-child(5) { animation-delay: 4000ms; }
.ping-line:nth-child(6) { animation-delay: 4600ms; }
.ping-line:nth-child(7) { animation-delay: 4700ms; }
.ping-line:nth-child(8) { animation-delay: 5100ms; }
@keyframes caret-blink { 50% { opacity: 0; } }
.ping-caret { display: inline-block; width: 8px; height: 1.05em; vertical-align: -3px; background: var(--accent); animation: caret-blink 1s steps(1) infinite; }

/* -- Page-to-page transitions (Chrome, Edge, Safari 18.2+). The project title
      morphs from its card into the case-study heading; the rest cross-fades. */
@view-transition { navigation: auto; }
#case-title { view-transition-name: project-title; }
@keyframes vt-out { to { opacity: 0; transform: translateY(-8px); } }
@keyframes vt-in { from { opacity: 0; transform: translateY(16px); } }
::view-transition-old(root) { animation: 200ms cubic-bezier(.4, 0, 1, 1) both vt-out; }
::view-transition-new(root) { animation: 420ms cubic-bezier(.22, 1, .36, 1) 80ms both vt-in; }
::view-transition-group(project-title) { animation-duration: 480ms; animation-timing-function: cubic-bezier(.22, 1, .36, 1); }
/* Arriving through a transition: the page is already animating in, so the
   one-off entrance animations are skipped (the head script adds .vt-in). */
.vt-in .hero-text > *, .vt-in .hero-greeting, .vt-in .hero-name, .vt-in .eyebrow-bar,
.vt-in .hero-visual .portrait, .vt-in .hero-visual .status, .vt-in .hero-visual .status-list > *,
.vt-in .hero-visual .status-checked, .vt-in .signature-name, .vt-in .signature-note, .vt-in .signature-dash,
.vt-in .case-header .container > *, .vt-in .case-cover, .vt-in .page-head .container > *,
.vt-in .nf-copy > *, .vt-in .ping { animation: none; }

/* -- Accessibility fallbacks */
@media (forced-colors: active) {
  .hero-name { background: none; -webkit-text-fill-color: currentColor; }
  .spotlight::after, .js .timeline::after { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
  .btn:not(.header-cta):hover, .btn:not(.header-cta):active, .channel:hover, .skill-list li:hover,
  .case-nav-link:hover, .figure-link:hover img, .about-collage { transform: none !important; }
  .portrait::before { animation: none; }
}

/* ===== Section backgrounds =================================================
   Everything after the hero shares one dark background. Three quiet layers give
   it depth without competing with the text:
   - ambient light: one large, very faint glow per section, alternating sides
     (teal, then blue), so the rhythm holds whatever order the sections are in;
   - soft dividers: a hairline as wide as the content that fades out at both
     ends, instead of a rule across the whole screen;
   - the closing: the hero's network map returns, barely visible, behind Contact.
   Only CSS gradients plus one image the hero already loads. */
main > .section { position: relative; isolation: isolate; }
main > .section::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0 0 -120px;   /* the light may spill into the top of the next section, never sideways */
  pointer-events: none;
  /* Fixed sizes, placed so each glow has faded out before the box edges: no
     visible cut, however tall the section is. */
  background:
    radial-gradient(560px 380px at 8% 300px, rgba(var(--accent-rgb), .055), transparent 70%),
    radial-gradient(520px 340px at 94% calc(100% - 240px), rgba(var(--accent-2-rgb), .04), transparent 70%);
}
main > .section:nth-child(even of .section)::before {
  background:
    radial-gradient(560px 380px at 92% 300px, rgba(var(--accent-2-rgb), .06), transparent 70%),
    radial-gradient(520px 340px at 6% calc(100% - 240px), rgba(var(--accent-rgb), .04), transparent 70%);
}
main > .section:last-child::before { bottom: 0; }   /* the footer follows: nothing spills over it */

main > .section + .section:not(.section-tight)::after {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: min(100% - 2 * var(--gutter), var(--container));
  height: 1px;
  transform: translateX(-50%);
  pointer-events: none;
  background: linear-gradient(90deg, transparent, rgba(129, 190, 222, .16) 18%, rgba(64, 224, 208, .26) 50%,
                              rgba(129, 190, 222, .16) 82%, transparent);
}

/* The hero's grid returns, barely there, in the empty space beside the
   Experience timeline (wide screens only; on phones the timeline fills the width). */
@media (min-width: 1024px) {
  .timeline::before {
    content: "";
    position: absolute;
    z-index: -1;
    top: -40px;
    bottom: -40px;
    right: 0;
    width: 34%;
    pointer-events: none;
    background-image:
      linear-gradient(to right, rgba(129, 190, 222, .05) 1px, transparent 1px),
      linear-gradient(to bottom, rgba(129, 190, 222, .05) 1px, transparent 1px);
    background-size: 32px 32px;
    -webkit-mask-image: radial-gradient(ellipse 58% 46% at 62% 42%, #000 10%, transparent 75%);
    mask-image: radial-gradient(ellipse 58% 46% at 62% 42%, #000 10%, transparent 75%);
  }
}

/* About: the collage's own blue light widens into the whole section, so the
   collage no longer sits in the page as a lit island. */
main > #about::before {
  background:
    radial-gradient(640px 460px at 50% 72%, rgba(8, 70, 140, .2), transparent 70%),
    radial-gradient(520px 360px at 8% 260px, rgba(var(--accent-rgb), .045), transparent 70%);
}
@media (min-width: 1024px) {
  main > #about::before {
    background:
      radial-gradient(820px 540px at 70% 46%, rgba(8, 70, 140, .2), transparent 70%),
      radial-gradient(520px 360px at 8% 300px, rgba(var(--accent-rgb), .045), transparent 70%);
  }
}

/* Contact closes the page with the map the hero opened it with. */
main > #contact::before {
  inset: 0;
  background: url("../images/hero-network.webp") 50% 70% / 170% auto no-repeat;
  opacity: .2;
  -webkit-mask-image: radial-gradient(ellipse 60% 50% at 50% 65%, #000 20%, transparent 75%);
  mask-image: radial-gradient(ellipse 60% 50% at 50% 65%, #000 20%, transparent 75%);
}
@media (min-width: 1024px) {
  main > #contact::before {
    background-position: 92% 50%;
    background-size: min(1000px, 72%) auto;
    opacity: .24;
    -webkit-mask-image: radial-gradient(ellipse 42% 62% at 70% 50%, #000 25%, transparent 78%);
    mask-image: radial-gradient(ellipse 42% 62% at 70% 50%, #000 25%, transparent 78%);
  }
}

@media (forced-colors: active) {
  main > .section + .section:not(.section-tight) { border-top: 1px solid CanvasText; }
  main > .section::before, main > .section + .section::after { display: none; }
}

/* ===== Responsive refinements ==============================================
   Phones and tablets (checked at 360, 390, 430, 600, 768, 820, 1024 and 1180 px). */

/* Hero, 600-1023: portrait and status card side by side at the same height,
   the photo filling its card instead of floating small at the bottom. */
@media (min-width: 600px) and (max-width: 1023.98px) {
  .hero-visual:not(.no-photo) { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: stretch; }
  .hero-visual:not(.no-photo) .portrait { aspect-ratio: auto; height: auto; min-height: 340px; max-height: none; }
  /* The status card sets the height; the photo covers whatever that is. */
  .hero-visual:not(.no-photo) .portrait picture { position: absolute; inset: 0; }
  .hero-visual:not(.no-photo) .portrait img { object-position: 50% 0%; }
}
/* The handwritten signature scales with the card it sits on, never over the face. */
.portrait { container-type: inline-size; }
@container (max-width: 360px) {
  .signature { left: 16px; right: 12px; bottom: 14px; }
  .signature-name { font-size: 26px; }
  .signature-note { gap: 6px; font-size: .6875rem; letter-spacing: .05em; white-space: nowrap; }
  .signature-dash { width: 14px; }
}
@container (max-width: 290px) {
  .signature-note { display: none; }
}

/* Selected Work on phones: the preview becomes a banner across the top of the
   card and the text gets the full width, instead of both sharing one row. */
@media (max-width: 599.98px) {
  .project-card { min-height: 0; }
  .project-card .project-preview {
    position: relative;
    top: auto;
    right: auto;
    width: calc(100% + 40px);
    height: 132px;
    margin: -18px -20px 4px;
    border: 0;
    border-bottom: 1px solid rgba(129, 190, 222, .12);
    border-radius: 0;
    --mv: 42%;
    --mh: 0%;
    opacity: 1;
  }
  .project-card:hover .project-preview, .project-card:focus-within .project-preview { transform: none; }
  .project-card-head, .project-card .project-title, .project-card .project-summary { max-width: none; }
}
/* Stacked cards (600-767) only need the room their content takes. */
@media (min-width: 600px) and (max-width: 767.98px) {
  .project-card { min-height: 13rem; }
}

/* Experience: below the two-column layout, Education and earlier roles line up
   with the text of the timeline above them. */
@media (max-width: 1023.98px) {
  .earlier, .credentials { margin-left: 25px; }
}

/* Case study flow on tablets: every step starts at the top and has the same
   divider, including the first step of the second row. */
@media (min-width: 768px) {
  .case-content .flow-step { align-content: start; padding: 0 16px; border-left: 1px solid var(--border); }
  .case-content .flow-step:first-child { padding-left: 16px; border-left: 1px solid var(--border); }
}
