/* ============================= Base ============================= */
* { box-sizing: border-box; }
html { scroll-behavior: smooth; scroll-padding-top: 80px; }
body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--ink);
  background: var(--bg);
  line-height: 1.65;
  transition: background 0.25s ease, color 0.25s ease;
}
h1, h2, h3, h4 { font-family: var(--font-heading); color: var(--ink); margin: 0 0 var(--space-sm); line-height: 1.25; }
h1 { font-size: var(--text-3xl); }
h2 { font-size: var(--text-2xl); letter-spacing: -0.01em; }
h3 { font-size: var(--text-lg); }
p { margin: 0 0 var(--space-sm); color: var(--ink-soft); }
a { color: var(--brand); text-decoration: none; }
a:hover { text-decoration: underline; }
ul { padding-left: 1.15rem; }
img { max-width: 100%; }

/* Page-wide cartographic wash. Fixed, so the map holds still while the content
   scrolls over it and the whole page sits on one continuous sheet rather than a
   repeating tile. Every card is opaque, so this only shows in the gutters and
   the gaps between sections and never sits behind body text. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  background: url("../img/map-texture.jpg") center / cover no-repeat;
  opacity: var(--page-map-opacity);
  mix-blend-mode: var(--page-map-blend);
  pointer-events: none;
  z-index: 0;
}
/* Content sits above the wash. */
.site-header, main, .site-footer { position: relative; z-index: 1; }

.container { max-width: var(--max-width); margin: 0 auto; padding: 0 var(--space-md); }

/* ============================= Header / nav ============================= */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}
.nav {
  max-width: var(--max-width);
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: 0.85rem var(--space-md);
  flex-wrap: wrap;
}
.nav-brand {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--ink);
  margin-right: auto;
}
.nav-brand:hover { text-decoration: none; color: var(--brand); }
.nav-links {
  list-style: none;
  display: flex;
  gap: 1.4rem;
  margin: 0;
  padding: 0;
  flex-wrap: wrap;
}
.nav-links a { color: var(--ink-soft); font-size: 0.92rem; font-weight: 500; }
.nav-links a:hover { color: var(--brand); text-decoration: none; }

.theme-toggle {
  width: 36px; height: 36px;
  display: grid; place-items: center;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--surface);
  color: var(--ink-soft);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  transition: background 0.2s, color 0.2s;
}
.theme-toggle:hover { background: var(--brand-pale); color: var(--brand); }

/* ============================= Cinematic hero =============================
   Full-viewport establishing shot: the Layyah study-area map as the plate, a
   scrim for legibility, and the title set over it. The scrim is what makes this
   safe — text sits on a controlled dark wash, never directly on the imagery. */
.hero {
  position: relative;
  isolation: isolate;
  min-height: 100svh;
  display: grid;
  /* Spacer absorbs leftover space only, so the hero fills exactly one screen
     and never more. Two fr tracks (1fr 1.618fr) split space derived from the
     content instead, which inflated the hero to ~1.6x its content height and
     overflowed the viewport; `auto` on the content row stops that.
     The golden ratio still governs the type scale, the spacing steps and the
     about split — forcing it onto these rows only produced overflow. */
  grid-template-rows: 1fr auto;
  align-content: end;
  padding: var(--space-xl) clamp(1.25rem, 6vw, 5rem) var(--space-2xl);
  overflow: hidden;
}
.hero-bg {
  position: absolute;
  inset: -8% 0 0;              /* headroom so the parallax never exposes an edge */
  /* The Lahore land-change plate: six epochs across a 21:9 frame with the
     measured present on the golden-section point. Rendered by
     lahore-lulc-carbon/src/web_export.py from the same rasters as the thesis
     figures, so the hero is the actual study rather than stock cartography.
     `center right 38.2%` lands that focal point near the golden section of the
     viewport rather than dead centre, and holds it there as the frame narrows. */
  /* Versioned like the stylesheets: the plate is re-rendered from the
     rasters, so a returning visitor must not keep an older banner. */
  /* 1920px by default and the 3840px plate only where it can be resolved. The
     full plate is 532 KB against 158 KB, and it is the largest paint on the
     page, so most visitors were downloading 374 KB they could not see. */
  background: url("../img/hero-lahore-1920.jpg?v=45") center right 38.2% / cover no-repeat;
  will-change: transform;
  z-index: 0;
}
/* Width only, deliberately. Keying this to device pixel ratio as well would
   have handed a 390px phone at 3x the full 3840px plate, which is 532 KB to
   paint behind a scrim. Width has an exact complement, so the preload in
   index.html can mirror it and no screen ever fetches both. */
@media (min-width: 1600px) {
  .hero-bg { background-image: url("../img/hero-lahore.jpg?v=45"); }
}
.hero-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  /* Two layers: a colour bed that keeps the brand green, and a vertical ramp
     that deepens toward the type. */
  /* Ramped, not flat: light at the top so the map reads, deepening toward the
     lower third where the type sits. A uniform scrim heavy enough to protect
     the text would erase the map entirely.
     Every layer here must carry alpha — an opaque gradient in this stack hides
     the plate underneath no matter what blend mode is set. */
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--forest-deep) 22%, transparent) 0%,
      color-mix(in srgb, var(--forest-deep) 42%, transparent) 40%,
      color-mix(in srgb, var(--forest-deep) 84%, transparent) 72%,
      color-mix(in srgb, var(--forest-deep) 94%, transparent) 100%),
    linear-gradient(120deg,
      color-mix(in srgb, var(--forest) 55%, transparent) 0%,
      color-mix(in srgb, var(--forest-deep) 35%, transparent) 60%,
      color-mix(in srgb, var(--sage) 22%, transparent) 100%);
}
/* Centred: the title block sits on the vertical axis of the frame, which reads
   as a title card rather than a caption in the corner. */
.hero-inner {
  position: relative;
  z-index: 2;
  grid-row: 2;
  /* Wide enough for the name to sit on one line at every width. The reading
     measures that still need one are set per element below, so widening the
     frame does not stretch the prose. */
  width: 100%;
  max-width: min(1120px, 92vw);
  margin-inline: auto;
  text-align: center;
}

.hero-photo {
  /* Scales with the viewport. Capped at 168px: the source is 800x800, so even
     at 2x device pixel ratio that stays well inside the native resolution. */
  width: clamp(120px, 11vw, 168px);
  aspect-ratio: 1;
  height: auto;
  border-radius: 50%;
  object-fit: cover;
  border: 3px solid color-mix(in srgb, var(--meadow) 60%, transparent);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.35);
  margin: 0 auto var(--space-md);
  display: block;
}
.hero-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  margin: 0 0 var(--space-sm);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--meadow);
}
/* One unbroken line, title-card style. The size is driven by the viewport
   rather than the type scale because the constraint here is fit, not rhythm:
   the name measures 12.9x its own font size, so 5.6vw holds it at about 72% of the
   viewport and always clear of the hero's side padding. Tracking is opened from
   -0.02em to 0.01em — at a smaller size the tight setting reads cramped, and a
   little air across a single long line is what makes it feel cinematic. */
.hero-name {
  font-size: clamp(1.25rem, 5.6vw, 4rem);
  white-space: nowrap;
  line-height: 1.06;
  letter-spacing: 0.01em;
  color: #fff;
  margin: 0 0 var(--space-xs);
}
.hero-role {
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--sage-bright);
  margin: 0 0 var(--space-sm);
}
/* Cinematic subtitle: one wide tracked line under the title.
   The line is 120 characters, so it only fits on one line above roughly 1030px
   — below that it wraps, with the tracking eased off, because heavy letter
   spacing across multiple lines hurts readability rather than helping it. */
.hero-tagline {
  font-size: var(--text-base);
  line-height: 1.6;
  letter-spacing: 0.04em;
  color: color-mix(in srgb, #fff 82%, transparent);
  margin: 0 auto var(--space-lg);
  max-width: 52ch;
}
@media (min-width: 1080px) {
  /* The subtitle is measured against the NAME, not the frame: a line running
     wider than the title above it reads as a mistake rather than a subtitle.
     The name is 12.9x its font size, which is min(5.6vw, 4rem), so its width is
     min(72vw, 51.6rem), which is the ceiling used here. At 87 characters the
     tracked line runs 56x its own font size, so the clamp below leaves it
     comfortably inside that ceiling. The copy was shortened to reach this; at
     the original 120 characters no readable size fitted inside the title. */
  .hero-tagline {
    max-width: min(72vw, 51.6rem);
    white-space: nowrap;
    font-size: clamp(0.78rem, 1.19vw, 0.85rem);
    letter-spacing: 0.1em;
    line-height: 1.5;
  }
}
.hero .social-row { justify-content: center; margin-top: var(--space-md); }
.hero .social-btn {
  background: color-mix(in srgb, #fff 10%, transparent);
  border-color: color-mix(in srgb, #fff 24%, transparent);
  color: #fff;
}
.hero .social-btn:hover { background: #fff; color: var(--forest); }

/* Ghost button reads on imagery without a filled block */
.btn-ghost {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.72rem 1.4rem;
  border-radius: 10px;
  border: 1px solid color-mix(in srgb, #fff 52%, transparent);
  color: #fff;
  font-weight: 600;
  font-size: var(--text-sm);
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.btn-ghost:hover { background: color-mix(in srgb, #fff 14%, transparent); text-decoration: none; }

.scroll-cue {
  position: absolute;
  left: 50%;
  bottom: var(--space-lg);
  transform: translateX(-50%);
  z-index: 2;
  width: 24px; height: 42px;
  display: grid;
  place-items: start center;
  padding-top: 8px;
  border: 1px solid color-mix(in srgb, #fff 34%, transparent);
  border-radius: 12px;
}
.scroll-line {
  width: 2px; height: 8px;
  border-radius: 2px;
  background: color-mix(in srgb, #fff 75%, transparent);
  animation: cue 1.9s var(--ease) infinite;
}
@keyframes cue {
  0%   { transform: translateY(0);    opacity: 0; }
  30%  { opacity: 1; }
  100% { transform: translateY(16px); opacity: 0; }
}

/* Hero entrance: each element rises as the shot settles. */
.hero .cine {
  opacity: 0;
  transform: translateY(22px);
}
.hero.is-ready .cine {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--dur-cine) var(--ease-cine) calc(120ms + var(--i, 0) * 90ms),
    transform var(--dur-cine) var(--ease-cine) calc(120ms + var(--i, 0) * 90ms);
}

/* Split layout on the golden section */
.split {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-lg);
}
@media (min-width: 900px) {
  /* φ : 1 — literal fr values, for the same calc() reason as the hero rows. */
  .split { grid-template-columns: 1.618fr 1fr; gap: var(--space-xl); }
}
.lede { font-size: var(--text-lg); line-height: 1.62; color: var(--ink-soft); }
.meta-line { font-size: var(--text-sm); color: var(--ink-faint); margin-top: var(--space-md); }

.social-row { display: flex; justify-content: center; gap: 0.6rem; flex-wrap: wrap; }
.social-btn {
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--surface-alt);
  color: var(--ink-soft);
  border: 1px solid var(--border);
  transition: background 0.2s, color 0.2s, transform 0.2s;
}
.social-btn:hover {
  background: var(--brand);
  color: var(--white);
  text-decoration: none;
  transform: translateY(-2px);
}

/* Availability / PhD-seeking signal */
.availability {
  margin-top: var(--space-md);
  padding: 0.85rem 1rem;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-alt);
  text-align: left;
}
.availability-status {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.83rem;
  font-weight: 700;
  color: var(--brand-strong);
  line-height: 1.35;
}
.availability p { margin: 0.4rem 0 0; font-size: 0.8rem; line-height: 1.5; color: var(--ink-faint); }
.pulse {
  width: 8px; height: 8px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--live);
  box-shadow: 0 0 0 0 var(--live-glow);
  animation: pulse 2.2s infinite;
}
@keyframes pulse {
  70% { box-shadow: 0 0 0 8px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}

.hero-cta { display: flex; gap: 0.6rem; flex-wrap: wrap; justify-content: center; margin-top: var(--space-sm); }
.hero-cta .btn-outline { display: inline-flex; align-items: center; gap: 0.45rem; padding: 0.62rem 1.15rem; }

.section-lede {
  margin: -0.35rem 0 var(--space-md);
  color: var(--ink-faint);
  font-size: 0.94rem;
}

/* ---- Methods audit figure ----
   Emphasis pattern: the corrected value carries the accent, the as-applied value
   sits in a muted serious tone. Both are always text-labelled, so identity never
   depends on colour alone. */
.audit-legend {
  display: flex;
  gap: 1.2rem;
  flex-wrap: wrap;
  margin: -0.35rem 0 var(--space-md);
  font-size: 0.8rem;
  color: var(--ink-faint);
}
.audit-legend span { display: inline-flex; align-items: center; gap: 0.4rem; }
.swatch { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.swatch.is-claimed { background: var(--audit-claimed); }
.swatch.is-actual { background: var(--audit-actual); }

/* Two columns rather than auto-fit: four cards land as a balanced 2×2 instead
   of 3 + an orphan, and the extra width stops values wrapping mid-phrase. */
.audit-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-md);
}
@media (min-width: 760px) {
  .audit-grid { grid-template-columns: repeat(2, 1fr); }
}
.audit-card {
  margin: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-md);
  box-shadow: var(--shadow-sm);
}
.audit-method {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 0.7rem;
}
.audit-pair { display: flex; align-items: center; gap: 0.7rem; }
.audit-side { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.audit-value {
  font-family: var(--font-heading);
  font-size: 1.32rem;
  font-weight: 700;
  line-height: 1.15;
}
/* Only forbid wrapping where the two-column layout guarantees room — on narrow
   screens nowrap clips the longer values, which is worse than letting them wrap. */
@media (min-width: 760px) {
  .audit-value { white-space: nowrap; }
}
.audit-label { font-size: 0.72rem; color: var(--ink-faint); line-height: 1.35; margin-top: 0.15rem; }
.audit-side.is-claimed .audit-value { color: var(--audit-claimed); }
.audit-side.is-actual .audit-value { color: var(--audit-actual); }
.audit-arrow { color: var(--ink-faint); opacity: 0.5; flex-shrink: 0; }
.audit-note {
  margin: 0.85rem 0 0;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border);
  font-size: 0.83rem;
  line-height: 1.5;
}

/* ---- Stat tiles on project slides ---- */
.stat-row {
  display: flex;
  gap: var(--space-md);
  flex-wrap: wrap;
  padding: 0.75rem 0 0.9rem;
  margin-bottom: 0.5rem;
  border-bottom: 1px solid var(--border);
}
.stat { display: flex; flex-direction: column; min-width: 0; }
.stat-value {
  font-family: var(--font-heading);
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--brand-strong);
  line-height: 1.1;
}
.stat-label { font-size: 0.7rem; color: var(--ink-faint); line-height: 1.3; margin-top: 0.15rem; }

/* ---- Collapsible method notes ---- */
.method-notes { margin: var(--space-xs) 0; }
.method-notes summary {
  cursor: pointer;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--brand);
  padding: 0.3rem 0;
  list-style: none;
}
.method-notes summary::-webkit-details-marker { display: none; }
.method-notes summary::before { content: "▸ "; display: inline-block; transition: transform 0.2s; }
.method-notes[open] summary::before { content: "▾ "; }
.method-notes summary:hover { text-decoration: underline; }
.method-notes .highlights { margin-top: 0.5rem; }

/* Research interests */
.interest-card h3 { font-size: 1.02rem; color: var(--brand-strong); margin-bottom: 0.4rem; }
.interest-card p { margin: 0; font-size: 0.9rem; }

/* How I work */
.approach-card { display: flex; gap: 0.9rem; align-items: flex-start; }
.approach-num {
  font-family: var(--font-heading);
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--brand);
  opacity: 0.45;
  line-height: 1.2;
  flex-shrink: 0;
}
.approach-card h3 { font-size: 1rem; margin-bottom: 0.3rem; }
.approach-card p { margin: 0; font-size: 0.89rem; }

/* ---- Section header, AstroWind pattern ----
   Uppercase tagline, large centred heading, optional muted subtitle. Their
   proportions: 16px/700 tagline at 0.4px tracking, 36px/700 heading on a tight
   40px line, 16px subtitle. */
.sec-head {
  text-align: center;
  max-width: 48rem;
  margin: 0 auto var(--space-lg);
}
.sec-head.is-start { text-align: left; margin-left: 0; margin-right: 0; }
.sec-tagline {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0 0 var(--space-xs);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--brand);
}
.sec-tagline svg { opacity: 0.85; }
.sec-head h2 {
  font-size: clamp(1.75rem, 3.2vw, 2.25rem);
  line-height: 1.14;
  font-weight: 700;
  margin: 0;
}
.sec-sub {
  margin: var(--space-sm) 0 0;
  font-size: var(--text-base);
  line-height: 1.6;
  color: var(--ink-faint);
}
.sec-head.is-start .sec-sub { margin-left: 0; }

/* ---- Tinted full-bleed band ----
   AstroWind alternates plain and tinted sections to separate them without
   rules. Full-bleed, so it breaks out of the centred container. */
.band-tint {
  background: var(--surface-alt);
  border-block: 1px solid var(--border);
  padding: var(--space-lg) var(--space-md);
  margin-block: var(--space-xl) 0;
}
.stat-band {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-md);
  text-align: center;
}
@media (min-width: 760px) {
  .stat-band { grid-template-columns: repeat(4, 1fr); }
}
.stat-big { display: flex; flex-direction: column; gap: 0.25rem; }
.stat-big-value {
  font-family: var(--font-heading);
  font-size: clamp(1.75rem, 3.4vw, 2.4rem);
  font-weight: 700;
  line-height: 1;
  color: var(--brand-strong);
}
.stat-big-label {
  font-size: var(--text-sm);
  color: var(--ink-faint);
  line-height: 1.35;
}

/* Section heading with icon badge */
.badge-heading {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-bottom: var(--space-sm);
}
.badge-heading h2, .badge-heading h3 { margin: 0; }
.icon-badge {
  flex-shrink: 0;
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border-radius: 10px;
  background: var(--brand-pale);
  color: var(--brand);
}

.btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--brand);
  color: var(--white);
  font-weight: 600;
  font-size: 0.92rem;
  padding: 0.65rem 1.3rem;
  border-radius: 10px;
  box-shadow: var(--shadow-sm);
  transition: background 0.2s, transform 0.2s;
}
.btn-primary:hover { background: var(--brand-strong); text-decoration: none; transform: translateY(-1px); }

/* ============================= Sections ============================= */
.section { padding: var(--space-lg) 0; }
.section > .badge-heading { margin-bottom: var(--space-md); }

/* Generic card */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-md);
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.2s, transform 0.2s;
}
.card:hover { box-shadow: var(--shadow); }

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-md);
}

/* Education / experience cards */
.edu-card .edu-degree { font-weight: 700; color: var(--ink); margin-bottom: 0.2rem; }
.edu-card .edu-org { color: var(--brand); font-size: 0.92rem; margin-bottom: 0.15rem; }
.edu-card .edu-period { color: var(--ink-faint); font-size: 0.82rem; margin-bottom: var(--space-xs); }
.edu-card ul { margin: 0; }
.edu-card li { color: var(--ink-soft); font-size: 0.9rem; margin-bottom: 0.3rem; }

.exp-card .exp-role { font-weight: 700; color: var(--ink); }
.exp-card .exp-meta { color: var(--brand); font-size: 0.92rem; }
.exp-card .exp-period { color: var(--ink-faint); font-size: 0.82rem; margin-bottom: var(--space-xs); }
.exp-card li { color: var(--ink-soft); margin-bottom: 0.35rem; }

/* Publications */
.pub-card {
  border-left: 4px solid var(--brand);
  margin-bottom: var(--space-md);
}
.pub-card:last-child { margin-bottom: 0; }
.pub-card h3 { font-size: 1.05rem; margin-bottom: 0.4rem; }
.pub-meta { color: var(--ink-faint); font-size: 0.88rem; margin-bottom: 0.6rem; }
.status-badge {
  display: inline-block;
  padding: 0.22rem 0.75rem;
  border-radius: 999px;
  font-size: 0.76rem;
  font-weight: 700;
  background: var(--brand-pale);
  color: var(--brand);
  border: 1px solid var(--border);
}
.status-badge.is-prep { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--accent); }

/* ============================= Projects carousel ============================= */
.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  margin-bottom: var(--space-md);
}
.section-head .badge-heading { margin-bottom: 0; }

.carousel-nav { display: flex; align-items: center; gap: 0.6rem; }
.cbtn {
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--surface);
  color: var(--ink-soft);
  cursor: pointer;
  transition: background 0.2s, color 0.2s, border-color 0.2s, transform 0.2s;
}
.cbtn:hover { background: var(--brand); color: var(--white); border-color: var(--brand); }
.cbtn:active { transform: scale(0.93); }
.counter { font-size: 0.85rem; color: var(--ink-faint); font-variant-numeric: tabular-nums; min-width: 3.2rem; text-align: center; }

.carousel {
  overflow: hidden;
  border-radius: var(--radius-lg);
  outline: none;
}
.carousel:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 45%, transparent); }
.track {
  display: flex;
  will-change: transform;
  transition: transform var(--dur-slow) var(--ease-entrance);
}
.slide { flex: 0 0 100%; min-width: 0; }

/* Each slide is styled like the hero card */
.showcase {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  overflow: hidden;
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  min-height: 400px;
}
.showcase-media {
  background: var(--surface-alt);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  padding: var(--space-md);
  border-right: 1px solid var(--border);
}

/* Optional per-project image gallery */
.thumbs { display: flex; gap: 0.5rem; flex-wrap: wrap; justify-content: center; }
.thumb {
  width: 58px; height: 42px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 7px;
  overflow: hidden;
  background: var(--surface);
  cursor: pointer;
  opacity: 0.62;
  transition: opacity 0.2s, border-color 0.2s, transform 0.2s;
}
.thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.thumb:hover { opacity: 1; transform: translateY(-2px); }
.thumb.active { opacity: 1; border-color: var(--brand); }
.media-caption {
  margin: 0;
  font-size: 0.76rem;
  color: var(--ink-faint);
  text-align: center;
  line-height: 1.4;
}
.showcase-media .media-main {
  width: 100%;
  max-height: 320px;
  object-fit: contain;
  border-radius: 10px;
  opacity: 0;
  transform: scale(0.98);
  transition: opacity var(--dur) var(--ease) 0.05s, transform var(--dur) var(--ease) 0.05s;
}
.slide.is-active .showcase-media .media-main { opacity: 1; transform: scale(1); }

.showcase-body {
  padding: clamp(1.2rem, 2.4vw, 2rem);
  display: flex;
  flex-direction: column;
  min-width: 0;
}
/* Keeps every slide the same height no matter how long the copy runs. */
.showcase-scroll {
  flex: 1;
  overflow-y: auto;
  max-height: 330px;
  padding-right: 0.4rem;
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
.showcase-scroll::-webkit-scrollbar { width: 6px; }
.showcase-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 999px; }
.showcase-body h3 { font-size: 1.22rem; margin-bottom: 0.55rem; }
.showcase-body p { font-size: 0.94rem; }
.highlights { margin: 0 0 var(--space-xs); padding-left: 1.1rem; }
.highlights li { color: var(--ink-soft); font-size: 0.9rem; margin-bottom: 0.35rem; }
.showcase-links {
  display: flex; gap: 0.6rem; flex-wrap: wrap;
  padding-top: var(--space-sm);
  margin-top: var(--space-xs);
  border-top: 1px solid var(--border);
}

/* Slide content eases in when it becomes active */
.slide .showcase-body > * { opacity: 0; transform: translateY(10px); transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease); }
.slide.is-active .showcase-body > * { opacity: 1; transform: none; }
.slide.is-active .showcase-body > *:nth-child(1) { transition-delay: 0.05s; }
.slide.is-active .showcase-body > *:nth-child(2) { transition-delay: 0.1s; }
.slide.is-active .showcase-body > *:nth-child(3) { transition-delay: 0.15s; }

/* Carousel controls sit under the track now that the header is centred. */
.carousel-foot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-md);
  flex-wrap: wrap;
  margin-top: var(--space-md);
}
.dots { display: flex; justify-content: center; gap: 0.5rem; }
.dot {
  width: 8px; height: 8px;
  padding: 0;
  border: none;
  border-radius: 999px;
  background: var(--border);
  cursor: pointer;
  transition: width 0.3s ease, background 0.3s ease;
}
.dot:hover { background: var(--brand-light, var(--brand)); }
.dot.active { width: 26px; background: var(--brand); }

.tag-row { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: var(--space-xs) 0; }
.tag {
  background: var(--brand-pale);
  color: var(--brand);
  font-size: 0.72rem;
  font-weight: 700;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
}
.project-links { margin-top: auto; padding-top: var(--space-sm); display: flex; gap: 0.6rem; flex-wrap: wrap; }
.btn-outline {
  font-weight: 600;
  font-size: 0.86rem;
  border: 1.5px solid var(--brand);
  border-radius: 9px;
  padding: 0.4rem 0.9rem;
  color: var(--brand);
  transition: background 0.2s, color 0.2s;
}
.btn-outline:hover { background: var(--brand); color: var(--white); text-decoration: none; }

/* Skills */
.skills-group h3 { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--brand); }
.skills-group ul { list-style: none; padding: 0; margin: 0; }
.skills-group li {
  padding: 0.35rem 0;
  border-bottom: 1px dashed var(--border);
  color: var(--ink-soft);
  font-size: 0.9rem;
}
.skills-group li:last-child { border-bottom: none; }

.sub-head {
  text-align: center;
  margin: var(--space-lg) 0 0;
  font-size: var(--text-lg);
}
.chip-list { display: flex; flex-wrap: wrap; gap: 0.5rem; list-style: none; padding: 0; margin: var(--space-sm) 0 0; justify-content: center; }
.chip {
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--ink-soft);
  padding: 0.35rem 0.85rem;
  border-radius: 999px;
  font-size: 0.84rem;
}

/* Talks / certifications */
.talk-card .talk-role { font-weight: 700; color: var(--ink); }
.talk-card .talk-period { color: var(--ink-faint); font-size: 0.82rem; margin-bottom: 0.3rem; }
.talk-card p { margin: 0; font-size: 0.9rem; }
.cert-card { display: flex; align-items: center; gap: 0.7rem; font-size: 0.92rem; color: var(--ink-soft); }

/* ============================= Footer ============================= */
.site-footer {
  background: var(--hero-gradient);
  color: var(--white);
  padding: var(--space-lg) var(--space-md);
  text-align: center;
  margin-top: var(--space-lg);
}
.site-footer h2 { color: var(--white); }
.site-footer p { color: rgba(255, 255, 255, 0.82); }
.site-footer .social-row { margin: var(--space-sm) 0; }
.site-footer .social-btn {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.3);
  color: var(--white);
}
.site-footer .social-btn:hover { background: var(--white); color: var(--forest); }
.site-footer .copyright { font-size: 0.82rem; color: rgba(255, 255, 255, 0.65); margin-top: var(--space-sm); }
.footer-lede {
  max-width: 620px;
  margin: 0 auto var(--space-sm);
  color: rgba(255, 255, 255, 0.88);
}
.btn-contact {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--white);
  color: var(--forest) !important;
  font-weight: 700;
  padding: 0.7rem 1.5rem;
  border-radius: 10px;
  transition: transform 0.2s, box-shadow 0.2s;
}
.btn-contact:hover { text-decoration: none; transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25); }

/* ============================= Motion ============================= */
/* Scroll reveal — short travel, quick settle. */
.reveal { opacity: 0; transform: translateY(var(--reveal-shift)); }
.reveal.revealed {
  opacity: 1;
  transform: none;
  transition: opacity var(--dur-slow) var(--ease-entrance), transform var(--dur-slow) var(--ease-entrance);
}

/* Children stagger in behind their section, ~50ms apart. The delay is set from
   JS as --i so any number of children works without hardcoded nth-child rules. */
.stagger > * {
  opacity: 0;
  transform: translateY(8px);
}
.revealed .stagger > * {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--dur) var(--ease-entrance) calc(var(--i, 0) * 50ms),
    transform var(--dur) var(--ease-entrance) calc(var(--i, 0) * 50ms);
}

/* Subtle lift on interactive cards */
.card { transition: box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease), border-color var(--dur) var(--ease); }
.card:hover { transform: translateY(var(--lift)); border-color: color-mix(in srgb, var(--brand) 35%, var(--border)); }

/* Reading-progress bar: how far through the story you are. */
.progress-bar {
  position: fixed;
  top: 0; left: 0;
  height: 2px;
  width: 100%;
  transform: scaleX(0);
  transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--forest) 0%, var(--sage) 55%, var(--gold) 100%);
  z-index: 60;
  will-change: transform;
}

/* Scroll-spy: the nav marks where you are in the narrative. */
.nav-links a {
  position: relative;
  transition: color var(--dur-fast) var(--ease);
}
.nav-links a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -4px;
  height: 1.5px;
  background: var(--brand);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--dur) var(--ease);
}
.nav-links a:hover::after { transform: scaleX(1); }
.nav-links a.is-current { color: var(--brand); font-weight: 600; }
.nav-links a.is-current::after { transform: scaleX(1); }

.btn-primary:active, .btn-outline:active { transform: scale(0.97); }

/* Focus visibility for keyboard users */
a:focus-visible, button:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: 6px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .reveal { opacity: 1; transform: none; }
  .slide .showcase-body > *, .showcase-media .media-main { opacity: 1; transform: none; }
  /* Never let the stagger leave content invisible when motion is suppressed. */
  .stagger > *, .revealed .stagger > * { opacity: 1; transform: none; }
  .progress-bar { display: none; }
  .pulse { animation: none; }
  /* The hero must never depend on its entrance to become visible. */
  .hero .cine, .hero.is-ready .cine { opacity: 1; transform: none; }
  .hero-bg { transform: none !important; }
  .scroll-line { animation: none; }
}

/* ============================= Responsive ============================= */
@media (max-width: 900px) {
  .showcase { grid-template-columns: 1fr; min-height: 0; }
  .showcase-media { border-right: none; border-bottom: 1px solid var(--border); }
  .showcase-media .media-main { max-height: 240px; }
  .showcase-scroll { max-height: none; overflow: visible; }
}
@media (max-width: 640px) {
  /* The φ row split leaves too little headroom on a phone; let the hero size to
     its content instead of forcing the golden section. */
  .hero {
    grid-template-rows: auto;
    min-height: 92svh;
    padding-bottom: var(--space-xl);
  }
  .hero-inner { grid-row: auto; }
  .hero-photo { width: 112px; }
  .hero-cta { width: 100%; }
  .hero-cta > a { flex: 1 1 auto; justify-content: center; }
  .nav { gap: var(--space-sm); }
  .nav-brand { margin-right: 0; }
  .nav-links { order: 3; width: 100%; justify-content: center; gap: 1rem; }
  .theme-toggle { margin-left: auto; }
  .section-head { flex-direction: column; align-items: flex-start; gap: var(--space-sm); }
  .carousel-nav { align-self: flex-end; }
}


/* ================== Lahore land-change scrubber ==================
   Layout is the site's φ split: the map stage takes 1.618 to the readout's 1,
   the same ratio as the hero rows and the about section, so this reads as part
   of the page rather than a widget dropped into it.

   The map plate has a fixed aspect ratio, which matters: the tiles are stacked
   absolutely, so without one the container would collapse to zero height and
   the section would appear empty. */
.lc {
  display: grid;
  grid-template-columns: 1.618fr 1fr;   /* literal fr — calc() cannot scale fr */
  grid-template-areas:
    "stage panel"
    "controls controls";
  gap: var(--space-md);
  align-items: start;
}
.lc-stage {
  grid-area: stage;
  position: relative;
  margin: 0;
  border-radius: var(--radius-lg, 14px);
  overflow: hidden;
  /* The district used to float on a dark radial gradient, which read as a
     product shot rather than as a map. It now sits on a real basemap, so the
     stage only has to supply the surface colour underneath while tiles load. */
  background: var(--surface);
  border: 1px solid var(--border);
  transition: border-color var(--dur) var(--ease);
}
.lc-plate {
  position: relative;
  /* Close to the district's own latitude-corrected shape (1.18:1), with a
     little extra height for the year badge. Matching it matters: because the
     tile is fitted with object-fit: contain, whichever axis is proportionally
     shorter decides the painted size, so a stage much wider than the district
     just adds side gutters and shrinks the map. */
  aspect-ratio: 1.16 / 1;
}
/* Plain <img> stack: what shows before Leaflet arrives, and what stays if it
   never does. Scoped away from the Leaflet case, where Leaflet owns position,
   size and transform on this same element and a stray transform here would
   throw the overlay off its own bounds. */
.lc-plate:not(.leaflet-container) .lc-tile {
  position: absolute;
  /* An absolutely positioned <img> with `inset` but `width/height: auto` falls
     back to its INTRINSIC size, so object-fit had no box to fit into and the
     district overflowed the stage by 85px. A definite 100% box plus padding
     gives object-fit something to work against, and guarantees the whole
     district is visible whatever the stage aspect turns out to be.
     The deeper bottom padding keeps the map clear of the year badge. */
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 5% 6% 13%;
  box-sizing: border-box;
  object-fit: contain;
  opacity: 0;
  transform: scale(0.995);
  transition: opacity var(--dur-cine, 0.6s) ease,
              transform var(--dur-cine, 0.6s) ease;
  image-rendering: -webkit-optimize-contrast;
}
.lc-plate:not(.leaflet-container) .lc-tile.is-on { opacity: 1; transform: scale(1); }

/* Leaflet image overlay. Opacity is driven by setOpacity(), so all this adds is
   the cross-fade between years. Nothing here may touch transform or position. */
.leaflet-image-layer.lc-tile {
  transition: opacity var(--dur-cine, 0.6s) ease;
  image-rendering: -webkit-optimize-contrast;
}
.lc-plate.leaflet-container { background: var(--surface); }

/* Over the map now rather than over a dark gradient, so the year needs its own
   ground to sit on. z-index clears Leaflet's panes, which top out at 700. */
.lc-badge {
  position: absolute;
  left: var(--space-sm);
  bottom: var(--space-sm);
  z-index: 900;
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.25rem 0.7rem 0.35rem;
  border-radius: 12px;
  background: color-mix(in srgb, var(--surface) 86%, transparent);
  border: 1px solid var(--border);
  backdrop-filter: blur(6px);
  pointer-events: none;
}
.lc-year {
  font-size: clamp(2rem, 4.6vw, 3.4rem);
  font-weight: 700;
  line-height: 1;
  color: var(--ink);
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}
.lc-flag {
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--forest-deep);
  background: var(--gold);
  padding: 0.2rem 0.5rem;
  border-radius: 999px;
  font-weight: 700;
}

/* Projected epochs ring the stage in gold, so the difference between measured
   and modelled is visible before the label is read. It used to be a warm cast
   over the whole plate, which is not available now that the plate is a map. */
.lc.is-projected .lc-stage {
  border-color: var(--gold);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--gold) 35%, transparent);
}

.lc-panel { grid-area: panel; display: grid; gap: var(--space-sm); }
.lc-readout { display: grid; gap: 0.55rem; }
.lc-row {
  display: grid;
  grid-template-columns: 0.7rem 1fr auto auto;
  grid-template-areas: "chip name val pct" "bar bar bar bar";
  align-items: center;
  gap: 0.35rem 0.55rem;
}
.lc-chip {
  grid-area: chip;
  width: 0.7rem; height: 0.7rem;
  border-radius: 3px;
  background: var(--c);
  border: 1px solid color-mix(in srgb, var(--ink) 25%, transparent);
}
.lc-name { grid-area: name; font-size: 0.92rem; color: var(--ink); }
.lc-val  { grid-area: val;  font-size: 0.92rem; color: var(--ink-soft);
           font-variant-numeric: tabular-nums; }
.lc-val b { color: var(--ink); font-weight: 650; }
.lc-pct  { grid-area: pct;  font-size: 0.82rem; color: var(--ink-faint);
           font-variant-numeric: tabular-nums; min-width: 3.2em; text-align: right; }
.lc-bar  { grid-area: bar; height: 5px; border-radius: 3px;
           background: color-mix(in srgb, var(--ink) 8%, transparent);
           overflow: hidden; }
.lc-bar i { display: block; height: 100%; width: 0;
            background: var(--c); border-radius: 3px;
            transition: width var(--dur-cine, 0.6s) cubic-bezier(.22,.61,.36,1); }

.lc-carbon {
  border-top: 1px solid var(--border);
  padding-top: var(--space-sm);
}
.lc-carbon-label {
  margin: 0; font-size: 0.74rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-faint);
}
.lc-carbon-value {
  margin: 0.15rem 0 0; font-size: clamp(1.5rem, 2.6vw, 2.1rem);
  font-weight: 700; color: var(--ink); line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.lc-carbon-value span { font-size: 0.9rem; font-weight: 500; color: var(--ink-faint); }
.lc-carbon-band, .lc-carbon-note {
  margin: 0.15rem 0 0; font-size: 0.8rem; color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.lc-carbon-note.is-loss { color: var(--accent); font-weight: 600; }
.lc-foot { margin: 0; font-size: 0.78rem; color: var(--ink-faint); line-height: 1.55; }

.lc-controls {
  grid-area: controls;
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas: "play range" "play ticks";
  align-items: center;
  gap: 0.4rem var(--space-sm);
  margin-top: 0.25rem;
}
.lc-play {
  grid-area: play;
  min-width: 5.2rem;
  padding: 0.5rem 1rem;
  border-radius: 999px;
  border: 1px solid var(--brand);
  background: var(--brand);
  color: var(--paper);
  font: inherit; font-size: 0.86rem; font-weight: 600;
  cursor: pointer;
  transition: transform var(--dur, .25s) ease, background var(--dur, .25s) ease;
}
.lc-play:hover { transform: translateY(-2px); background: var(--brand-strong); }
.lc-play:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.lc-range {
  grid-area: range;
  width: 100%;
  accent-color: var(--brand);
  cursor: pointer;
}
.lc-ticks {
  grid-area: ticks;
  display: flex; justify-content: space-between; gap: 0.25rem;
}
.lc-tick {
  border: 0; background: none; cursor: pointer;
  font: inherit; font-size: 0.78rem; font-variant-numeric: tabular-nums;
  color: var(--ink-faint); padding: 0.15rem 0.3rem; border-radius: 6px;
  transition: color var(--dur, .25s) ease, background var(--dur, .25s) ease;
}
.lc-tick:hover { color: var(--ink); background: var(--surface-alt); }
.lc-tick.is-proj { font-style: italic; }
.lc-tick.is-on { color: var(--ink); font-weight: 700;
                 background: var(--surface-alt); }
.lc-tick:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

@media (max-width: 860px) {
  .lc {
    grid-template-columns: 1fr;
    grid-template-areas: "stage" "panel" "controls";
  }
  .lc-controls { grid-template-columns: 1fr;
                 grid-template-areas: "range" "ticks" "play"; }
  .lc-play { width: 100%; margin-top: 0.4rem; }
  .lc-ticks { flex-wrap: wrap; justify-content: center; }
}

@media (prefers-reduced-motion: reduce) {
  .lc-tile { transition: none; }
  .lc-bar i { transition: none; }
  .lc-play:hover { transform: none; }
}

/* ==================== Pakistan district map ====================
   Two columns on the golden split: the map carries the shape, the side panel
   carries the numbers. The ranked list in the side panel is not decoration, it
   is the keyboard and screen-reader route into the same data, so it stays
   visible at every width rather than collapsing into a toggle. */
.pm {
  display: grid;
  grid-template-columns: 1.618fr 1fr;
  gap: var(--space-md);
  align-items: start;
}
.pm-tabs {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.pm-tab {
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink-soft);
  border-radius: 999px;
  padding: 0.42rem 0.95rem;
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.pm-tab:hover { background: var(--brand-pale); color: var(--brand); }
.pm-tab.is-on {
  background: var(--brand-strong);
  border-color: var(--brand-strong);
  color: var(--paper);
}
[data-theme="dark"] .pm-tab.is-on { color: var(--forest-deep); }
.pm-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.pm-stage { position: relative; }
.pm-map {
  /* Pakistan is close to square in this projection, so a square frame wastes
     the least space. Capped so the map never outgrows one screen. */
  aspect-ratio: 1 / 1;
  max-height: 72svh;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--surface);
  overflow: hidden;
}
.pm-hint {
  position: absolute;
  left: 0.75rem;
  bottom: 0.75rem;
  z-index: 500;
  font-size: var(--text-xs);
  color: var(--ink-faint);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.2rem 0.6rem;
  pointer-events: none;
}
.pm-fallback {
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: var(--space-md);
  color: var(--ink-soft);
}

.pm-side { display: grid; gap: var(--space-sm); }
.pm-readout {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: var(--space-sm);
}
.pm-place { margin: 0; font-family: var(--font-heading); font-size: var(--text-lg); color: var(--ink); }
.pm-prov { margin: 0.1rem 0 0.7rem; font-size: var(--text-xs); color: var(--ink-faint); }
.pm-figs { margin: 0; display: grid; gap: 0.35rem; }
.pm-figs div { display: flex; justify-content: space-between; gap: 1rem; align-items: baseline; }
.pm-figs dt { font-size: var(--text-sm); color: var(--ink-soft); }
.pm-figs dd { margin: 0; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }

.pm-legend-title { margin: 0 0 0.4rem; font-size: var(--text-xs); color: var(--ink-soft); }
.pm-strip { display: flex; gap: 2px; }
.pm-strip i { flex: 1; height: 12px; background: var(--c); border-radius: 2px; }
.pm-ends {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: 0.3rem;
  font-size: var(--text-xs);
  color: var(--ink-faint);
}
.pm-ends span:nth-child(2) { text-align: center; }
.pm-ends span:last-child { text-align: right; }
.pm-nodata {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0.5rem 0 0;
  font-size: var(--text-xs);
  color: var(--ink-faint);
}
.pm-nodata i { width: 12px; height: 12px; border-radius: 2px; background: var(--c); }

.pm-rank { display: grid; gap: var(--space-sm); }
.pm-rank-title {
  margin: 0 0 0.3rem;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
}
.pm-rank ol { margin: 0; padding: 0; list-style: none; }
.pm-rank li + li { border-top: 1px solid var(--border); }
.pm-rank button {
  width: 100%;
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
  align-items: baseline;
  padding: 0.34rem 0.2rem;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--text-sm);
  color: var(--ink-soft);
  text-align: left;
  cursor: pointer;
}
.pm-rank button:hover { color: var(--brand); background: var(--brand-pale); }
.pm-rank button b { color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.pm-rank button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.pm-foot {
  grid-column: 1 / -1;
  margin: 0;
  font-size: var(--text-xs);
  color: var(--ink-faint);
}

/* Leaflet ships its own light chrome; these pull it onto the site's surfaces.
   The container rule needs both classes on one selector, not a descendant one:
   .pm-map IS the Leaflet container, and Leaflet's stylesheet is injected after
   this one, so at equal specificity its flat #ddd would win and the map would
   sit on a grey slab in dark mode. */
.pm-map.leaflet-container { background: var(--surface); font: inherit; }
.pm-map .leaflet-bar a {
  background: var(--surface);
  color: var(--ink);
  border-bottom-color: var(--border);
}
.pm-map .leaflet-bar a:hover { background: var(--brand-pale); }
.leaflet-tooltip.pm-tip {
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  font-size: var(--text-xs);
  padding: 0.3rem 0.5rem;
}
.leaflet-tooltip.pm-tip::before { display: none; }

@media (max-width: 860px) {
  .pm { grid-template-columns: 1fr; }
  .pm-map { max-height: none; }
}

/* ============================ Tabs ============================
   Used for the two interactive maps and for the CV detail. A closed panel is
   `hidden`, so it contributes no height at all; that is the whole point, and it
   is why the page went from twelve screens to seven. */
.tablist {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-bottom: var(--space-md);
  padding-bottom: 0.45rem;
  border-bottom: 1px solid var(--border);
}
.tab {
  border: 1px solid transparent;
  background: none;
  color: var(--ink-faint);
  border-radius: 999px;
  padding: 0.45rem 1rem;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.tab:hover { color: var(--brand); background: var(--brand-pale); }
.tab.is-on {
  background: var(--brand-strong);
  border-color: var(--brand-strong);
  color: var(--paper);
}
[data-theme="dark"] .tab.is-on { color: var(--forest-deep); }
.tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The reveal animation starts sections at opacity 0. A panel opened by a click
   is already past that, so it must not inherit a half-finished entrance. */
.tabpanel { animation: panel-in var(--dur-slow) var(--ease-entrance) both; }
@keyframes panel-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .tabpanel { animation: none; }
}

/* Two related card grids side by side, which is what Research Direction needs:
   the questions and the method are a pair and were two stacked full-width
   sections before. */
.dual {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-lg);
  align-items: start;
}
.dual .sub-head { margin-top: 0; }
.dual .card-grid { grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr)); }

@media (max-width: 860px) {
  .dual { grid-template-columns: 1fr; gap: var(--space-md); }
  .tablist { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .tablist::-webkit-scrollbar { display: none; }
}

/* The availability line at the top of the stats band. It answers "is this
   person aimed at my field" before a supervisor has to decide whether to
   scroll, which is the only question that matters at that moment. */
.stat-open {
  max-width: 46rem;
  margin: 0 auto var(--space-md);
  text-align: center;
  font-size: var(--text-lg);
  line-height: 1.55;
  color: var(--ink);
}
@media (max-width: 700px) {
  .stat-open { font-size: var(--text-base); }
}

/* What is left of a section header once its title has moved to the tab label. */
.panel-note {
  max-width: 62ch;
  margin: 0 0 var(--space-md);
  font-size: var(--text-sm);
  color: var(--ink-faint);
}

/* Manuscript number beside the status badge. Kept small: it is there so an
   editor or supervisor can check the submission is real, not to be read. */
.pub-ms {
  margin-left: 0.6rem;
  font-size: var(--text-xs);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Seven nav links sit before the content, so keyboard and screen-reader users
   get a way past them. Off-screen until focused, which is the only state a
   sighted mouse user ever sees. */
.skip-link {
  position: fixed;
  top: 0;
  left: 50%;
  transform: translate(-50%, -110%);
  z-index: 200;
  padding: 0.7rem 1.3rem;
  border-radius: 0 0 10px 10px;
  background: var(--brand-strong);
  color: var(--paper);
  font-weight: 600;
  transition: transform var(--dur) var(--ease);
}
.skip-link:focus {
  transform: translate(-50%, 0);
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  text-decoration: none;
}
[data-theme="dark"] .skip-link { color: var(--forest-deep); }
main:focus { outline: none; }

/* ==================== Project theme filter ====================
   Twelve studies is more than a supervisor will page through hoping to meet
   the one that matches their group, so the themes let them cut straight to it.
   The count on each chip is the useful part: it says what is behind the label
   before anything is clicked. */
.pfilters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: var(--space-md);
}
.pfilter {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink-soft);
  border-radius: 999px;
  padding: 0.4rem 0.9rem;
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.pfilter span {
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  background: var(--surface-alt);
  color: var(--ink-faint);
}
.pfilter:hover { border-color: var(--brand); color: var(--brand); }
.pfilter.is-on {
  background: var(--brand-strong);
  border-color: var(--brand-strong);
  color: var(--paper);
}
.pfilter.is-on span {
  background: color-mix(in srgb, #fff 22%, transparent);
  color: var(--paper);
}
[data-theme="dark"] .pfilter.is-on,
[data-theme="dark"] .pfilter.is-on span { color: var(--forest-deep); }
.pfilter:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* A dot for a filtered-out slide is hidden rather than removed, so the row does
   not reflow under the pointer mid-click. */
#projects .dot[hidden] { display: none; }

/* Third action on a study, quieter than the two that leave the site. */
.btn-quiet {
  border: 1px dashed var(--border);
  background: none;
  color: var(--ink-faint);
  border-radius: 10px;
  padding: 0.72rem 1.1rem;
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.btn-quiet:hover { color: var(--brand); border-color: var(--brand); }
.btn-quiet:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (max-width: 700px) {
  .pfilters { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; padding-bottom: 2px; }
  .pfilters::-webkit-scrollbar { display: none; }
  .pfilter { white-space: nowrap; }
}

/* ============================ Print ============================
   Supervisors print candidates, and this page printed as a mess: a full-bleed
   dark hero on page one, eleven of twelve studies missing because the carousel
   only shows one, and every tab panel except the open one absent entirely.

   Printing now produces a dossier. Interactive chrome goes, hidden content is
   forced back into the flow, the carousel unrolls into a list, and link targets
   are written out because a printed link with no URL is a dead end. */
@media print {
  @page { margin: 16mm 14mm; }

  :root, [data-theme="dark"] {
    --bg: #fff; --surface: #fff; --surface-alt: #fff;
    --ink: #111; --ink-soft: #222; --ink-faint: #444;
    --border: #bbb; --brand: #1c3a2e; --brand-strong: #1c3a2e; --brand-pale: #fff;
  }
  body { background: #fff; color: #111; font-size: 10.5pt; line-height: 1.45; }
  body::before { display: none; }           /* the map wash is toner for nothing */

  .site-header, .skip-link, .theme-toggle, .hero-cta, .social-row,
  .carousel-foot, .pfilters, .btn-quiet, .lc-controls, .pm-tabs, .pm-hint,
  .progress-bar, .scroll-cue, .thumbs { display: none !important; }

  /* Every tab panel prints, since a printout has no tabs to click. */
  .tabpanel[hidden] { display: block !important; }
  .tablist { display: none !important; }
  .tabpanel { animation: none; break-inside: avoid; }

  /* The carousel unrolls: all twelve studies, stacked, instead of one. */
  .carousel { overflow: visible !important; }
  .track { display: block !important; transform: none !important; }
  .slide {
    display: block !important;
    width: 100% !important;
    break-inside: avoid;
    break-before: page;
    padding-bottom: 6mm;
  }
  .slide[aria-hidden="true"] { visibility: visible !important; }
  .showcase { display: block !important; }
  .showcase-scroll { max-height: none !important; overflow: visible !important; }
  .method-notes[open] summary, .method-notes summary { font-weight: 700; }
  .method-notes > ul { display: block !important; }

  /* The hero becomes a title block rather than a full-bleed photograph. */
  .hero { min-height: auto !important; padding: 0 0 8mm !important; display: block; }
  .hero-bg, .hero-scrim { display: none !important; }
  .hero-inner { max-width: none; text-align: left; }
  .hero-name, .hero-role, .hero-tagline, .hero-eyebrow { color: #111 !important; }
  .hero-name { font-size: 24pt !important; white-space: normal !important; }
  .hero-tagline { white-space: normal !important; letter-spacing: 0 !important;
                  max-width: none !important; font-size: 11pt !important; }
  .hero-photo { width: 26mm !important; margin: 0 0 4mm !important; }

  /* Maps and images are screen things; keep the figures, drop the interactives. */
  .lc-plate, .pm-map, .pm-stage { display: none !important; }
  img { max-height: 70mm; object-fit: contain; }

  .section { padding: 4mm 0 !important; break-inside: auto; }
  h2, h3 { break-after: avoid; color: #111 !important; }
  .card, .pub-card, .exp-card { border: 1px solid #ccc !important; box-shadow: none !important;
                                break-inside: avoid; }

  /* A printed link that does not say where it goes is a dead end. */
  .showcase-links a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 8.5pt; color: #555; }

  * { animation: none !important; transition: none !important; }
  .reveal, .cine { opacity: 1 !important; transform: none !important; }
}
