/* ==========================================================================
   One-page portfolio — Jimuelle R. Patron
   Composition follows skills/lexicon-for-creators-DESIGN.md:
   deliberate stable spacing, distinct surface/border/text roles, restrained
   easing, masked reveals, hover lift, ambient effects kept behind content.

   1  Reset and base
   2  Atmosphere
   3  Type primitives
   4  Top navigation
   5  Sections
   6  Buttons, labels, chips, cards
   7  Hero
   8  The 3D ID
   9  Education
   10 Organizations timeline
   11 Projects
   12 Skills and certifications
   13 Contact and footer
   14 Reveals
   15 Responsive
   ========================================================================== */

/* ============================ 1  RESET AND BASE ========================== */

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

html {
  scroll-behavior: smooth;
  /* Anchors land below the fixed nav instead of under it. */
  scroll-padding-top: calc(var(--nav-h) + var(--s-2));
  -webkit-text-size-adjust: 100%;
  overflow-y: scroll;
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  scrollbar-color: var(--c-orange-400) var(--c-bg-tint);
}

html::-webkit-scrollbar {
  width: 12px;
}

html::-webkit-scrollbar-track {
  background: var(--c-bg-tint);
}

html::-webkit-scrollbar-thumb {
  min-height: 48px;
  border: 3px solid var(--c-bg-tint);
  border-radius: var(--r-pill);
  background: var(--c-orange-400);
}

html::-webkit-scrollbar-thumb:hover {
  background: var(--c-orange-600);
}

body {
  margin: 0;
  background: var(--c-bg);
  color: var(--c-ink);
  font-family: var(--font-ui);
  font-size: var(--fs-body-md);
  line-height: var(--lh-body);
  font-weight: var(--fw-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img,
svg,
canvas {
  display: block;
  max-width: 100%;
}

svg {
  width: 1em;
  height: 1em;
  flex: none;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  border: 0;
  background: none;
  cursor: pointer;
}

ul,
ol,
dl,
dd {
  margin: 0;
  padding: 0;
  list-style: none;
}

h1,
h2,
h3,
h4,
p,
figure {
  margin: 0;
}

:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 3px;
  border-radius: 4px;
}

::selection {
  background: var(--c-orange-200);
  color: var(--c-orange-900);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: fixed;
  top: var(--s-1);
  left: var(--s-1);
  z-index: var(--z-skip);
  padding: 0.625rem var(--s-2);
  border-radius: var(--r-control);
  background: var(--c-primary);
  color: var(--c-on-primary);
  font-family: var(--font-mono);
  font-size: var(--fs-label-md);
  font-weight: var(--fw-label);
  transform: translateY(-160%);
  transition: transform var(--t-fast) var(--ease-out);
}

.skip-link:focus-visible {
  transform: translateY(0);
}

/* ============================ 2  ATMOSPHERE ============================== */

/* Ambient layers only. Both sit behind every section and never take pointer
   events, per the doc: effects support the interface, they do not lead it. */

.atmos {
  position: fixed;
  inset: 0;
  z-index: var(--z-atmos);
  pointer-events: none;
  overflow: hidden;
}

.atmos__canvas {
  position: absolute;
  inset: 0;
  opacity: 0.85;
}

/* Two crossing warm washes, so the pastel white is never flat. */
.atmos__glow {
  position: absolute;
  border-radius: 50%;
  filter: blur(70px);
}

.atmos__glow--a {
  top: -18vh;
  right: -8vw;
  width: 52vw;
  height: 52vw;
  background: radial-gradient(
    circle,
    rgba(249, 115, 22, 0.2) 0%,
    rgba(249, 115, 22, 0.06) 45%,
    transparent 70%
  );
}

.atmos__glow--b {
  bottom: -22vh;
  left: -12vw;
  width: 46vw;
  height: 46vw;
  background: radial-gradient(
    circle,
    rgba(234, 88, 12, 0.14) 0%,
    rgba(253, 186, 116, 0.06) 45%,
    transparent 70%
  );
}

.atmos__grain {
  position: absolute;
  inset: 0;
  opacity: 0.5;
  background-image: radial-gradient(rgba(154, 52, 18, 0.055) 1px, transparent 1px);
  background-size: 4px 4px;
}

/* ========================= 3  TYPE PRIMITIVES ============================ */

.display-xl,
.display-lg,
.display-md {
  font-family: var(--font-display);
  font-weight: var(--fw-display);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
  color: var(--c-ink);
  text-wrap: balance;
}

.display-xl {
  font-size: var(--fs-display-xl);
}

.display-lg {
  font-size: var(--fs-display-lg);
}

.display-md {
  font-size: var(--fs-display-md);
  line-height: var(--lh-heading);
}

/* The orange half of a headline. Display sizes only, and that is a contrast
   constraint rather than a stylistic one: --c-accent reads 3.4:1 on the pastel
   ground, which clears AA at 3:1 for large text and fails it everywhere else.
   Anything smaller must use --c-orange-ink.

   .hero__title-accent is the surname in the hero headline. It shares this rule
   rather than restating it, so the accent voice cannot drift between the
   headline and the section titles. It previously kept the Inter display face so
   both lines of the name matched; the italic Playfair is the deliberate choice
   now, and it works here because the name sets on two separate grid rows, so
   there is no inline baseline to reconcile between the faces. Playfair's italic
   400 is a real cut, loaded as 1,400 in the font request, not a synthesised
   slant. */
.clause,
.hero__title-accent {
  color: var(--c-accent);
  font-style: italic;
  font-family: var(--font-body);
  font-weight: 400;
  letter-spacing: -0.01em;
}

.title {
  font-family: var(--font-display);
  font-size: var(--fs-title);
  font-weight: 600;
  line-height: var(--lh-heading);
  letter-spacing: -0.012em;
  color: var(--c-ink);
}

/* Editorial voice: the doc's Playfair Display body-md. */
.lede {
  font-family: var(--font-body);
  font-size: clamp(1rem, 1.25vw, 1.1875rem);
  line-height: var(--lh-body);
  color: var(--c-ink-soft);
  max-width: 46ch;
}

.prose {
  font-family: var(--font-body);
  font-size: var(--fs-body-md);
  line-height: var(--lh-body);
  color: var(--c-ink-soft);
}

.mono {
  font-family: var(--font-mono);
  font-size: var(--fs-label-md);
  font-weight: 400;
  line-height: var(--lh-label);
  letter-spacing: 0.02em;
  color: var(--c-ink-muted);
}

.num {
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 2.6vw, 2.375rem);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--c-ink);
  font-variant-numeric: tabular-nums;
}

.text-orange {
  color: var(--c-orange-ink);
}

/* Label: the doc's label-md, mono and tracked out. */
.label {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: var(--fs-label-md);
  font-weight: var(--fw-label);
  line-height: var(--lh-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--c-ink-muted);
}

.label--orange {
  color: var(--c-orange-ink);
}

.label__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--c-primary);
  box-shadow: 0 0 0 3px rgba(249, 115, 22, 0.18);
}

.link {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  align-self: flex-start;
  font-family: var(--font-mono);
  font-size: var(--fs-label-md);
  font-weight: var(--fw-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--c-orange-ink);
  padding-bottom: 2px;
  border-bottom: 1px solid var(--c-border-orange);
  transition: color var(--t-fast) var(--ease-out),
    border-color var(--t-fast) var(--ease-out), gap var(--t-fast) var(--ease-out);
}

.link:hover {
  color: var(--c-orange-800);
  border-color: var(--c-accent);
  gap: 0.65rem;
}

/* ========================= 4  TOP NAVIGATION ============================= */

.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-nav);
  height: var(--nav-h);
  background: rgba(255, 249, 244, 0.72);
  backdrop-filter: blur(16px) saturate(150%);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
  border-bottom: 1px solid transparent;
  transition: background var(--t-med) var(--ease-out),
    border-color var(--t-med) var(--ease-out), box-shadow var(--t-med) var(--ease-out);
}

.nav.is-stuck {
  background: rgba(255, 249, 244, 0.9);
  border-bottom-color: var(--c-border);
  box-shadow: var(--shadow-sm);
}

.nav__inner {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  height: 100%;
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.nav__brand {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  flex: none;
}

.nav__brand-text {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
}

.nav__name {
  font-family: var(--font-display);
  font-size: var(--fs-body-xs);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--c-ink);
}

.nav__role {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-ink-muted);
}

.nav__links {
  display: flex;
  align-items: center;
  gap: 0.125rem;
  margin: 0 auto;
  position: relative;
  padding: 4px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid var(--c-border);
}

/* The pill that slides to the active section. */
.nav__marker {
  position: absolute;
  top: 4px;
  left: 0;
  height: calc(100% - 8px);
  width: 0;
  border-radius: var(--r-pill);
  background: var(--c-orange-100);
  box-shadow: inset 0 0 0 1px var(--c-border-orange);
  opacity: 0;
  transition: transform var(--t-med) var(--ease-out),
    width var(--t-med) var(--ease-out), opacity var(--t-fast) linear;
  pointer-events: none;
}

.nav__marker.is-on {
  opacity: 1;
}

.nav__link {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.875rem;
  border-radius: var(--r-pill);
  font-size: var(--fs-body-xs);
  font-weight: 500;
  color: var(--c-ink-soft);
  white-space: nowrap;
  /* Scrollspy flips this on its own as you scroll, not just on hover, so the
     fade runs at the same duration as the pill marker's slide. At --t-fast the
     colour landed well before the pill arrived and the two read as separate
     events. */
  transition: color var(--t-med) var(--ease-out);
}

.nav__link:hover {
  color: var(--c-orange-ink);
}

.nav__link[aria-current="true"] {
  color: var(--c-orange-800);
  font-weight: 600;
}

/* The index has its own active colour, so it needs its own transition. Without
   it the number snapped to orange while the label beside it was still fading. */
.nav__link-idx {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  font-weight: 600;
  color: var(--c-ink-muted);
  transition: color var(--t-med) var(--ease-out);
}

.nav__link[aria-current="true"] .nav__link-idx {
  color: var(--c-orange-ink);
}

.nav__cta {
  flex: none;
}

.nav__toggle {
  display: none;
  place-items: center;
  width: 40px;
  height: 40px;
  margin-left: auto;
  border-radius: var(--r-control);
  border: 1px solid var(--c-border-strong);
  background: var(--c-surface);
  color: var(--c-ink);
}

.nav__toggle svg {
  width: 20px;
  height: 20px;
}

.nav__toggle-close {
  display: none;
}

.nav.is-open .nav__toggle-open {
  display: none;
}

.nav.is-open .nav__toggle-close {
  display: block;
}

/* Mobile drawer. Collapsed by default at every width; only the mobile
   breakpoint reveals the toggle that opens it. */
.nav__drawer {
  position: fixed;
  top: var(--nav-h);
  left: 0;
  right: 0;
  z-index: var(--z-drawer);
  display: none;
  padding: var(--s-2) var(--gutter) var(--s-3);
  background: var(--c-surface);
  border-bottom: 1px solid var(--c-border);
  box-shadow: var(--shadow-lg);
}

.nav__drawer-list {
  display: grid;
  gap: 2px;
}

.nav__drawer-link {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.8125rem var(--s-2);
  border-radius: var(--r-control);
  font-size: var(--fs-body-sm);
  font-weight: 500;
  color: var(--c-ink-soft);
  /* The drawer shares the scrollspy, so it gets the same fade. Its active state
     also paints a background, which would otherwise appear instantly. */
  transition: color var(--t-med) var(--ease-out),
    background-color var(--t-med) var(--ease-out);
}

.nav__drawer-link[aria-current="true"] {
  background: var(--c-orange-50);
  color: var(--c-orange-800);
}

.nav__drawer-link .nav__link-idx {
  min-width: 1.5em;
}

.nav__drawer .btn {
  margin-top: var(--s-2);
  width: 100%;
  justify-content: center;
}

/* ============================= 5  SECTIONS =============================== */

.main {
  position: relative;
  z-index: var(--z-content);
}

.section {
  position: relative;
  padding: var(--section-pad) 0;
}

/* The nav is fixed, so its height is padding here rather than layout. The
   second term is the breathing room below the bar; keep it well clear of zero
   so the badge never crowds the nav's bottom edge. */
.section--hero {
  padding-top: calc(var(--nav-h) + var(--s-3));
  padding-bottom: var(--s-8);
}

/* Hairline rule between sections, inset to the content measure. */
.section + .section::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--gutter);
  right: var(--gutter);
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent,
    var(--c-border-strong) 18%,
    var(--c-border-strong) 82%,
    transparent
  );
}

.wrap {
  width: 100%;
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.section__head {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  margin-bottom: var(--s-6);
  max-width: 62ch;
}

.section__head .lede {
  max-width: 58ch;
}

.grid {
  display: grid;
  column-gap: var(--s-4);
  row-gap: var(--s-3);
  align-items: stretch;
}

.grid--2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.grid--3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.grid--4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: var(--s-3);
  row-gap: var(--s-2);
}

/* ================ 6  BUTTONS, LABELS, CHIPS, CARDS ======================= */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.6875rem 1.125rem;
  border-radius: var(--r-control);
  font-family: var(--font-ui);
  font-size: var(--fs-body-xs);
  font-weight: 600;
  letter-spacing: 0.005em;
  white-space: nowrap;
  border: 1px solid transparent;
  transition: transform var(--t-fast) var(--ease-out),
    box-shadow var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out),
    border-color var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}

.btn svg {
  width: 15px;
  height: 15px;
  transition: transform var(--t-fast) var(--ease-out);
}

.btn--primary {
  background-color: var(--c-accent);
  background-image: linear-gradient(140deg, var(--c-primary), var(--c-accent));
  color: var(--c-on-primary);
  box-shadow: var(--shadow-md), inset 0 1px 0 rgba(255, 255, 255, 0.28);
}

.btn--primary:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg), var(--glow-primary);
}

.btn--ghost {
  background: var(--c-surface);
  border-color: var(--c-border-strong);
  color: var(--c-ink);
  box-shadow: var(--shadow-xs);
}

.btn--ghost:hover {
  transform: translateY(-2px);
  border-color: var(--c-primary);
  color: var(--c-orange-ink);
  box-shadow: var(--shadow-md);
}

.btn--sm {
  padding: 0.5rem 0.875rem;
  font-size: var(--fs-label-md);
}

.btn:hover svg {
  transform: translateX(2px);
}

.btn--ghost:hover svg {
  transform: translateY(2px);
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.375rem 0.75rem 0.375rem 0.5rem;
  border-radius: var(--r-pill);
  background: var(--c-surface);
  border: 1px solid var(--c-border-strong);
  box-shadow: var(--shadow-xs);
  font-family: var(--font-mono);
  font-size: var(--fs-label-md);
  font-weight: var(--fw-label);
  letter-spacing: 0.06em;
  color: var(--c-ink-soft);
  align-self: flex-start;
}

.badge__icon {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background-color: var(--c-accent);
  background-image: linear-gradient(140deg, var(--c-primary), var(--c-accent));
  color: var(--c-on-primary);
}

.badge__icon svg {
  width: 11px;
  height: 11px;
}

.chip {
  display: inline-flex;
  align-items: center;
  padding: 0.3125rem 0.625rem;
  border-radius: var(--r-pill);
  background: var(--c-orange-50);
  border: 1px solid var(--c-border);
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  font-weight: var(--fw-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--c-ink-soft);
  white-space: nowrap;
}

.chip-set {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  align-items: flex-start;
}

.award {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3125rem 0.6875rem;
  border-radius: var(--r-pill);
  background-color: var(--c-accent);
  background-image: linear-gradient(140deg, var(--c-primary), var(--c-accent));
  color: var(--c-on-primary);
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  box-shadow: var(--shadow-sm);
  white-space: nowrap;
}

.award svg {
  width: 12px;
  height: 12px;
}

/* Card: doc surface role with subtle borders and matched shadow depth. */
.card {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  padding: var(--card-pad);
  border-radius: var(--r-card);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  box-shadow: var(--shadow-card);
  position: relative;
  transition: transform var(--t-med) var(--ease-out),
    box-shadow var(--t-med) var(--ease-out), border-color var(--t-med) var(--ease-out);
}

.card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  flex-wrap: wrap;
}

/* Hover lift, per the doc's motion notes. */
.card--lift:hover {
  transform: translateY(-4px);
  border-color: var(--c-border-orange);
  box-shadow: var(--shadow-lg);
}

/* Registered so the conic gradient's start angle can be interpolated. Without
   the @property declaration the angle is just a string to the animation engine
   and the trace would jump between keyframes instead of travelling. */
@property --card-trace {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

/* A restrained accent trace around interactive cards. The low-opacity floor
   keeps the perimeter legible without turning the component into a neon glow. */
.card--lift::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: calc(var(--r-card) + 1px);
  padding: 2px;
  background: conic-gradient(
      from var(--card-trace, 0deg),
    var(--c-accent) 0deg,
    var(--c-primary) 10deg,
    rgba(249, 115, 22, 0.4) 34deg,
    rgba(249, 115, 22, 0.1) 80deg,
    rgba(249, 115, 22, 0.1) 320deg,
    rgba(249, 115, 22, 0.3) 358deg
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  filter: drop-shadow(0 0 5px rgba(249, 115, 22, 0.35));
  opacity: 0;
  pointer-events: none;
  animation: card-trace var(--t-trace) linear infinite;
  animation-play-state: paused;
  transition: opacity var(--t-med) var(--ease-out);
}

.card--lift:hover::after {
  opacity: 1;
  animation-play-state: running;
}

@keyframes card-trace {
  to {
    --card-trace: 360deg;
  }
}

.nested {
  padding: 0.875rem var(--s-2);
  border-radius: var(--r-nested);
  background: var(--c-surface-sunk);
  border: 1px solid var(--c-border);
}

.stat {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}

.stat__num {
  font-size: clamp(1.25rem, 1.8vw, 1.625rem);
}

.stat__label {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  font-weight: var(--fw-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-ink-muted);
  line-height: 1.35;
}

.spec-list {
  display: grid;
  gap: 0.5rem;
}

.spec-list li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-2);
  padding-bottom: 0.5rem;
  border-bottom: 1px dashed var(--c-border);
  font-size: var(--fs-body-xs);
}

.spec-list li:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

.spec-list b {
  font-weight: 600;
  color: var(--c-ink);
  white-space: nowrap;
}

.spec-list span {
  color: var(--c-ink-muted);
  text-align: right;
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  letter-spacing: 0.01em;
}

.bullets {
  display: grid;
  gap: 0.5rem;
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body);
  color: var(--c-ink-soft);
}

.bullets li {
  position: relative;
  padding-left: 1.125rem;
}

.bullets li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.6em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--c-primary);
  opacity: 0.75;
}

/* ============================== 7  HERO ================================== */

.hero {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(var(--s-4), 6vw, var(--s-10));
  align-items: center;
}

.hero__copy {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  min-width: 0;
}

/* The shadow is sized in em so it tracks the clamped display size instead of
   growing heavy on small screens. It also has to stay within the 0.06em
   padding-bottom on .reveal-mask, since that mask clips its overflow: offset
   plus blur here reaches 0.05em, so nothing gets cut off. */
.hero__title {
  display: grid;
  gap: 0.02em;
  text-shadow: 0 0.02em 0.03em rgba(124, 45, 18, 0.18);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.625rem;
  align-items: center;
}

.hero__meta {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-2);
  padding-top: var(--s-2);
  border-top: 1px solid var(--c-border);
}

.hero__meta-item {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}

.hero__meta-key {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  font-weight: var(--fw-label);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-ink-muted);
}

.hero__meta-val {
  font-size: var(--fs-body-xs);
  font-weight: 500;
  color: var(--c-ink);
  line-height: 1.4;
}

.hero__stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-3);
  min-width: 0;
}

/* Scroll cue under the hero. */
.scroll-cue {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  margin-top: var(--s-6);
  font-family: var(--font-mono);
  font-size: var(--fs-label-md);
  font-weight: var(--fw-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--c-ink-muted);
}

.scroll-cue__line {
  width: 56px;
  height: 1px;
  background: linear-gradient(90deg, var(--c-primary), transparent);
  position: relative;
  overflow: hidden;
}

.scroll-cue__line::after {
  content: "";
  position: absolute;
  inset: 0;
  width: 40%;
  background: var(--c-accent);
  animation: cue 2.4s var(--ease-in-out) infinite;
}

@keyframes cue {
  0% {
    transform: translateX(-100%);
  }
  60%,
  100% {
    transform: translateX(250%);
  }
}

/* Vertical twin of the cue, used once the rail flips upright on small screens
   so the travelling dot points the way the page actually moves. */
@keyframes cue-down {
  0% {
    transform: translateY(-100%);
  }
  60%,
  100% {
    transform: translateY(250%);
  }
}

/* ============================ 8  THE 3D ID =============================== */

/* The hero's focal object. A student ID on a lanyard that tilts toward the
   cursor and catches a specular sweep as it turns.
   JS writes five custom properties on .id and nothing else:
     --rx --ry   tilt in degrees
     --mx --my   cursor position, for the sheen and foil origin
     --lift      z translation
   With scripts off, or under reduced motion, every one of them holds its
   declared default and the card renders as a flat, complete badge. */

.id {
  --rx: 0deg;
  --ry: 0deg;
  --mx: 50%;
  --my: 30%;
  --lift: 0px;
  --gloss: 0;
  --id-offset-x: calc(clamp(12px, 1.5vw, 24px) + 16px);

  /* Lace geometry, in one place because the strap rotation, the crimp width
     and the bail all have to agree.

     --lace-h is set to run the ribbon off the top of the frame rather than end
     somewhere in the air. On the two-column hero the crimp sits ~180px below
     the viewport top, so 300px at 26deg gives 270px of rise: it leaves the
     frame with room to spare and the splay stays inside the card's own
     footprint, which is what keeps the ID inside its column. */
  --lace-w: 16px; /* ribbon width, before --lace-turn foreshortens it */
  --lace-h: 300px; /* ribbon length from the crimp to past the frame edge */
  --lace-open: 26deg; /* half-angle of the V */
  /* How far the ribbon's face turns toward the neck, expressed as the width it
     foreshortens to rather than as a rotateY. 0.809 is cos(36deg).

     It has to be a scale and not a rotation. A rotateY tilts the ribbon's plane
     out of the card's plane, and two non-parallel quads in one preserve-3d
     scene make Chrome cut the card along the infinite line where the planes
     meet, then paint the two halves separately. The seam between them showed as
     a hairline crack, and because each ribbon's plane contains its own axis,
     the two cracks fanned down across the face from the crimp at exactly
     --lace-open: a pale dotted V over the header and the punch hole.
     scaleX foreshortens by the same factor without leaving the card's plane, so
     the splay reads the same and there is nothing left to intersect. The
     ribbon's shading, not its geometry, is what sells the turn at this size. */
  --lace-turn: 0.809;
  --lace-seat: 18px; /* where the ribbon ends sit above the card's top edge */

  position: relative;
  width: min(330px, 76vw);
  perspective: 1200px;
  perspective-origin: 50% 40%;
  margin: 0;
  transform: translateX(var(--id-offset-x));
}

/* --- The lace ---------------------------------------------------------- */

/* Everything that hangs, hangs from here: lace, crimp and card move as one
   piece. No animation of its own: the object is at rest until it is pointed at
   or focused, and the only motion is the tilt and lift the pointer drives.
   The wrapper still earns its place by holding the lace and the card in one 3D
   scene, which is what stops the crimp drifting off the bail. */
.id__hang {
  position: relative;
  transform-style: preserve-3d;
}

.id__lanyard {
  position: relative;
  height: 84px;
  transform-style: preserve-3d;
}

/* Two ribbons into a crimp. Each one pivots about its *bottom* edge, because
   that is the end that must not move: the join into the crimp then holds at
   any angle. Pivoting about the top, as this did before, swung the tips
   outward and left them 10px clear of the metal on both sides. */
.id__strap {
  position: absolute;
  left: 50%;
  bottom: var(--lace-seat);
  z-index: 1;
  width: var(--lace-w);
  height: var(--lace-h);
  margin-left: calc(var(--lace-w) / -2);
  transform-origin: 50% 100%;
  border-radius: 1px;

  /* Woven, in three layers: a twill running across the weft, a barrel shade
     that puts the specular down the middle and rolls the edges off into
     shadow, and the dye underneath darkening toward the neck. */
  background-color: var(--c-accent);
  background-image: repeating-linear-gradient(
      68deg,
      rgba(255, 255, 255, 0.14) 0 1px,
      rgba(0, 0, 0, 0.1) 1px 2px,
      transparent 2px 4px
    ),
    linear-gradient(
      90deg,
      rgba(0, 0, 0, 0.45) 0%,
      rgba(255, 255, 255, 0.22) 30%,
      rgba(255, 255, 255, 0.05) 58%,
      rgba(0, 0, 0, 0.4) 100%
    ),
    linear-gradient(180deg, var(--c-orange-700) 0%, var(--c-accent) 58%, var(--c-orange-700) 100%);

  /* Selvedge: a lit edge one side, a dark one the other, plus the ribbon's own
     drop onto whatever is behind it. */
  box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.24),
    inset -1px 0 0 rgba(0, 0, 0, 0.38), 0 6px 12px -8px rgba(25, 28, 33, 0.7);

  /* No fade here: the ribbon is long enough to leave the frame, and the body is
     a scroll container (overflow-x: hidden makes overflow-y compute to auto),
     so the viewport edge does the cutting. Nothing above the document origin is
     ever scrollable into view. */
  transition: transform 620ms var(--ease-out);
}

/* Splayed on Z and narrowed on X, so each ribbon reads as a face angling away
   toward the neck rather than a bar lying flat on the page. Both terms are
   in-plane, which is the point: see --lace-turn. The --ry term is the lace
   answering the card: a fifth of the card's yaw, which is enough to read as
   slack without looking hinged. */
.id__strap--l {
  transform: translateZ(calc(var(--lift) * 0.55))
    rotate(calc(var(--lace-open) * -1 + var(--ry) * 0.2)) scaleX(var(--lace-turn));
}

.id__strap--r {
  transform: translateZ(calc(var(--lift) * 0.55))
    rotate(calc(var(--lace-open) + var(--ry) * 0.2)) scaleX(var(--lace-turn));
}

.id.is-live .id__strap {
  transition: transform 80ms linear;
}

/* Specular sweep across the weave, on the same --gloss master as the card. */
.id__strap::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(255, 255, 255, 0.5) 34%,
    rgba(255, 255, 255, 0.08) 62%,
    transparent 100%
  );
  opacity: calc(var(--gloss) * 0.5);
  transition: opacity var(--t-med) var(--ease-out);
  pointer-events: none;
}

/* The crimp: the ferrule that gathers both ribbon ends and grips the bail.
   It straddles the strap pivots, so the pivot itself is never visible. */
.id__crimp {
  position: absolute;
  left: 50%;
  bottom: 6px;
  z-index: 3;
  width: 30px;
  height: 18px;
  margin-left: -15px;
  border-radius: 3px / 4px;

  /* Across the width, not down the height: a cylinder reads from the
     highlight travelling around it. */
  background-color: #a8aeb7;
  background-image: linear-gradient(
    90deg,
    #6d737c 0%,
    #edeff2 24%,
    #b8bdc5 50%,
    #7a8089 76%,
    #d2d6db 100%
  );
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.75),
    inset 0 -1px 0 rgba(0, 0, 0, 0.35), 0 5px 10px -6px rgba(25, 28, 33, 0.75);

  /* The full lift, not a fraction of it. The bail sits at the card's own depth
     minus 3px, so anything less than the whole lift lets the bail overtake the
     crimp in Z on hover and paint straight over the metal that is supposed to
     be holding it. Matching the lift keeps the crimp a constant 9px in front at
     every point in the gesture, and incidentally keeps the swivel joint from
     drifting as the card's top edge tips back. */
  transform: translateZ(calc(var(--lift) + 6px));
  transition: transform 620ms var(--ease-out);
}

.id.is-live .id__crimp {
  transition: transform 80ms linear;
}

/* The seam where the two ribbon ends meet inside the ferrule. */
.id__crimp::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 2px;
  bottom: 2px;
  width: 1px;
  background: rgba(25, 28, 33, 0.4);
}

/* The tilting body. */
.id__card {
  position: relative;
  transform-style: preserve-3d;

  /* Pivot at the punch hole, not at the card's middle. A badge on a lanyard
     turns about the point it hangs from, and the geometry follows: with the
     origin at the centre, the top edge sat ~316px out on the lever, so a 12deg
     pitch swung the bail 68px through Z and dropped it 11px down the screen,
     out from under the crimp. Rotating a rigid plane about a different in-plane
     point is the same rotation plus a translation, so the card's shape reads
     exactly as before. The joint is what changes. */
  transform-origin: 50% 0;
  transform: translate3d(0, 0, var(--lift)) rotateX(var(--rx)) rotateY(var(--ry));
  transition: transform 620ms var(--ease-out), box-shadow var(--t-med) var(--ease-out);
  border-radius: 14px;
  /* Lightest stop as the fallback colour: the worst case for the light text
     printed on this band, so a contrast reading against it is conservative. */
  background-color: var(--c-deep-2);
  background-image: linear-gradient(
    165deg,
    var(--c-deep-2) 0%,
    var(--c-deep) 58%,
    #14171b 100%
  );
  box-shadow: 0 18px 40px -22px rgba(25, 28, 33, 0.55),
    0 2px 0 rgba(255, 255, 255, 0.06) inset;
  will-change: transform;
}

/* While the pointer is driving it, follow immediately. The slow easing above
   is what makes the release settle rather than snap. */
.id.is-live .id__card {
  transition: transform 80ms linear, box-shadow var(--t-med) var(--ease-out);
}

.id.is-live .id__card,
.id:focus-within .id__card {
  box-shadow: 0 40px 70px -30px rgba(124, 45, 18, 0.55),
    0 0 0 1px rgba(249, 115, 22, 0.35),
    0 2px 0 rgba(255, 255, 255, 0.1) inset;
}


.id__face {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: var(--s-2) var(--s-2) 0.875rem;
  transform-style: preserve-3d;
}

/* The bail: the ring threaded through the punch hole. This is the one piece of
   hardware parented to the card rather than to the lace, because it has to hold
   its place in the slot through every degree of tilt, and inheriting the card's
   own transform is the only way to guarantee that.

   It sits at negative Z, so the card's own body occludes the arc that has
   passed behind it and only the part above the top edge shows. Border colours
   do the metal: lit on top, shadowed underneath. */
.id__bail {
  position: absolute;
  left: 50%;
  top: -19px;
  z-index: 6;
  box-sizing: border-box;
  width: 26px;
  height: 32px;
  margin-left: -13px;
  border: 4px solid;
  border-color: #eceef1 #b3b8c0 #797f88 #b3b8c0;
  border-radius: 13px;
  background: none;
  transform: translateZ(-3px);
  pointer-events: none;
}

/* Punch hole the bail passes through. */
.id__punch {
  position: absolute;
  top: 9px;
  left: 50%;
  width: 44px;
  height: 8px;
  margin-left: -22px;
  border-radius: var(--r-pill);
  background: #0d0f12;
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.9), 0 1px 0 rgba(255, 255, 255, 0.08);
  z-index: 4;
}

.id__brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.875rem;
  padding-bottom: 0.625rem;
  border-bottom: 1px solid var(--c-deep-border);
  transform: translateZ(16px);
}

.id__seal {
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  object-fit: contain;
  background: #fff;
  box-shadow: 0 0 0 2px rgba(249, 115, 22, 0.22);
}

.id__brand-text {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
  min-width: 0;
}

.id__brand-text b {
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--c-on-deep);
}

.id__brand-text small {
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-on-deep-soft);
}

/* Photo sits highest in the stack, which is what sells the depth. */
.id__photo {
  position: relative;
  border-radius: 6px;
  overflow: hidden;
  background: var(--c-deep-3);
  box-shadow: 0 10px 22px -12px rgba(0, 0, 0, 0.85),
    0 0 0 1px rgba(249, 115, 22, 0.3);
  transform: translateZ(34px);
}

.id__photo img {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  object-position: 50% 22%;
}

/* Orange rim light across the photo, brightening as the card turns. */
.id__photo::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    200deg,
    rgba(249, 115, 22, 0.28) 0%,
    transparent 42%,
    transparent 70%,
    rgba(25, 28, 33, 0.5) 100%
  );
  opacity: calc(0.4 + var(--gloss) * 0.6);
  transition: opacity var(--t-med) var(--ease-out);
  pointer-events: none;
}

.id__plate {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  transform: translateZ(24px);
}

.id__name {
  font-family: var(--font-display);
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.2;
  color: var(--c-on-deep);
}

.id__course {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--c-primary);
}

.id__rows {
  display: grid;
  gap: 0.3125rem;
  margin: 0;
  transform: translateZ(18px);
}

.id__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.06em;
}

.id__row dt {
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--c-on-deep-faint);
}

.id__row dd {
  margin: 0;
  font-weight: 600;
  color: var(--c-on-deep);
  text-align: right;
}

.id__foot {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 0.5rem;
  padding-top: 0.625rem;
  border-top: 1px solid var(--c-deep-border);
  transform: translateZ(14px);
}

.id__bars {
  display: flex;
  align-items: flex-end;
  gap: 1.5px;
  height: 20px;
}

.id__bars i {
  display: block;
  width: 2px;
  height: 100%;
  background: #c9ccd2;
  border-radius: 1px;
}

.id__bars i:nth-child(3n) {
  width: 3.5px;
  background: #eef0f3;
}

.id__bars i:nth-child(4n) {
  height: 72%;
}

.id__bars i:nth-child(5n) {
  width: 1.5px;
  height: 88%;
}

.id__sig {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
}

.id__sig span {
  font-family: var(--font-body);
  font-style: italic;
  font-size: 12px;
  color: var(--c-on-deep);
  line-height: 1.1;
}

.id__sig small {
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--c-on-deep-faint);
}

/* --- The shine ------------------------------------------------------- */

/* Specular highlight that tracks the cursor.
   Deliberately plain alpha rather than mix-blend-mode: screen. A blend mode on
   any child forces its parent to form a blending group, and Chromium flattens
   the parent's 3D rendering context when that happens, which would collapse
   every translateZ above. Over a near-black card face, white at low alpha is
   indistinguishable from a screen blend anyway. */
.id__sheen {
  position: absolute;
  inset: 0;
  z-index: 3;
  border-radius: 14px;
  pointer-events: none;
  background: radial-gradient(
    460px circle at var(--mx) var(--my),
    rgba(255, 255, 255, 0.36) 0%,
    rgba(255, 224, 190, 0.11) 28%,
    transparent 60%
  );
  opacity: var(--gloss);
  transition: opacity 220ms var(--ease-out);
}

/* Holographic foil, keyed to the same cursor origin and rotating with the
   tilt. Same no-blend-mode constraint as the sheen. */
.id__foil {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: 14px;
  pointer-events: none;
  background: conic-gradient(
    from calc(var(--ry) * 6) at var(--mx) var(--my),
    rgba(249, 115, 22, 0.6),
    rgba(253, 186, 116, 0.28),
    rgba(255, 247, 237, 0.34),
    rgba(234, 88, 12, 0.55),
    rgba(255, 237, 213, 0.24),
    rgba(249, 115, 22, 0.6)
  );
  opacity: calc(var(--gloss) * 0.45);
  transition: opacity var(--t-med) var(--ease-out);
}

/* Soft contact shadow on the ground, growing with the lift. */
.id__ground {
  position: absolute;
  left: 12%;
  right: 12%;
  bottom: -22px;
  height: 26px;
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(124, 45, 18, 0.34), transparent 72%);
  filter: blur(9px);
  opacity: calc(0.55 + var(--gloss) * 0.45);
  transform: scale(calc(1 + var(--gloss) * 0.12));
  transition: opacity var(--t-med) var(--ease-out), transform var(--t-med) var(--ease-out);
  pointer-events: none;
}

/* Keyboard users get the same lift the pointer produces. */
.id__hit {
  position: absolute;
  inset: 0;
  z-index: 5;
  border-radius: 14px;
  cursor: grab;
}

.id__hit:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 6px;
}

.id__caption {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin-top: var(--s-3);
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  font-weight: var(--fw-label);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-ink-muted);
}

/* ============================ 9  EDUCATION =============================== */

.edu {
  gap: var(--s-2);
}

.edu__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-2);
  flex-wrap: wrap;
}

.edu__org {
  font-family: var(--font-mono);
  font-size: var(--fs-label-md);
  font-weight: var(--fw-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-orange-ink);
  max-width: 26ch;
  line-height: 1.4;
}

.edu__figure {
  display: flex;
  align-items: baseline;
  gap: 0.625rem;
  flex-wrap: wrap;
}

.edu__num {
  font-size: clamp(2rem, 3.4vw, 2.75rem);
  background: linear-gradient(140deg, var(--c-accent), var(--c-orange-800));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.edu__stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-2);
  margin-top: auto;
}

/* ==================== 10  ORGANIZATIONS: FLOW CANVAS ===================== */

/* Thirteen overlapping roles are rendered as a node canvas instead of a card
   grid, using the portfolio's shared orange and warm-dark palette. */

.flow {
  position: relative;
  border-radius: var(--r-card);
  /* Solid colour under the gradients, not just background-image. Every string
     painted in here needs a real ground to be measured against. */
  background-color: var(--c-deep);
  background-image:
    radial-gradient(110% 80% at 8% -10%, rgba(249, 115, 22, 0.16), transparent 62%),
    linear-gradient(180deg, var(--c-deep-2), var(--c-deep) 46%);
  border: 1px solid var(--c-deep-border);
  box-shadow: var(--shadow-xl);
  overflow: hidden;
}

/* --- Editor chrome ---------------------------------------------------- */

.flow__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  flex-wrap: wrap;
  padding: 0.875rem var(--s-3);
  border-bottom: 1px solid var(--c-deep-border);
  background: rgba(0, 0, 0, 0.22);
}

.flow__file {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  min-width: 0;
}

.flow__file-icon {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: var(--r-nested);
  background: var(--c-deep-warm);
  border: 1px solid var(--c-border-orange);
  color: var(--c-on-deep-orange);
}

.flow__file-icon svg {
  width: 1rem;
  height: 1rem;
}

.flow__file-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.flow__file-text b {
  font-family: var(--font-mono);
  font-size: var(--fs-body-xs);
  font-weight: var(--fw-label);
  color: var(--c-on-deep);
}

.flow__file-text small {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  letter-spacing: 0.04em;
  color: var(--c-on-deep-soft);
}

.flow__jump {
  display: inline-flex;
  gap: 0.375rem;
  padding: 3px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--c-deep-border);
}

.flow__chip {
  padding: 0.3125rem 0.75rem;
  border-radius: var(--r-pill);
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  font-weight: var(--fw-label);
  letter-spacing: 0.08em;
  color: var(--c-on-deep-soft);
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}

.flow__chip:hover {
  color: var(--c-on-deep);
  background: rgba(255, 255, 255, 0.07);
}

.flow__chip[aria-pressed="true"] {
  background: var(--c-deep-warm);
  color: var(--c-on-deep-orange);
  box-shadow: inset 0 0 0 1px var(--c-border-orange);
}

/* --- The scroll port -------------------------------------------------- */

/* Sideways only. Height follows the canvas exactly, so nothing inside is ever
   cut off with no way to reach it, and the page keeps its own vertical scroll
   when a touch starts on the canvas. */

.flow__stage {
  position: relative;
}

/* Edge fades live on the stage, not inside the scroller, so they stay pinned
   to the visible edges while the canvas travels underneath. */
.flow__stage::before,
.flow__stage::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: clamp(24px, 5vw, 56px);
  pointer-events: none;
  z-index: 2;
}

.flow__stage::before {
  left: 0;
  background: linear-gradient(90deg, var(--c-deep), transparent);
}

.flow__stage::after {
  right: 0;
  background: linear-gradient(270deg, var(--c-deep), transparent);
}

.flow__viewport {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  cursor: grab;
  /* The page hides its own scrollbar; a visible one here would also eat into
     the port's height and leave the canvas short of its own box. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.flow__viewport::-webkit-scrollbar {
  width: 0;
  height: 0;
}

.flow__viewport.is-grabbing {
  cursor: grabbing;
  user-select: none;
}

.flow__viewport:focus-visible {
  outline: 2px solid var(--c-primary);
  outline-offset: -2px;
}

.flow__canvas {
  position: relative;
  display: flex;
  align-items: stretch;
  gap: var(--s-2);
  width: max-content;
  padding: var(--s-4) var(--s-4) var(--s-5);
  /* n8n's tell: a dot grid that travels with the graph, not with the frame. */
  background-image: radial-gradient(
    circle at 1px 1px,
    rgba(255, 255, 255, 0.1) 1px,
    transparent 0
  );
  background-size: 22px 22px;
}

/* --- Wires ------------------------------------------------------------ */

/* Drawn in JS from the ports' measured positions rather than hardcoded
   coordinates, so a text reflow or a new node cannot leave a wire pointing at
   nothing. */

.flow__wires {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  z-index: 0;
}

.flow__wire {
  fill: none;
  stroke: rgba(255, 255, 255, 0.24);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  transition: stroke var(--t-fast) var(--ease-out);
}

.flow__wire--trunk {
  stroke: rgba(255, 255, 255, 0.16);
  stroke-width: 1.4;
}

.flow__wire.is-live {
  stroke: var(--c-orange-400);
  stroke-width: 2;
}

/* --- Columns ---------------------------------------------------------- */

.flow__col {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--s-3);
  flex: none;
}

.flow__col--nodes {
  width: 268px;
}

.flow__col--gate {
  width: 116px;
  align-items: center;
}

/* --- Date gate -------------------------------------------------------- */

.fgate {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  padding: 0.5rem 0.75rem;
  border-radius: var(--r-pill);
  background: var(--c-deep-warm);
  border: 1px solid var(--c-border-orange);
  box-shadow: var(--glow-primary);
  text-align: center;
}

.fgate__kind {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  font-weight: var(--fw-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--c-on-deep-orange);
}

.fgate__val {
  font-family: var(--font-mono);
  font-size: var(--fs-body-xs);
  font-weight: var(--fw-label);
  line-height: 1.3;
  color: var(--c-on-deep);
}

/* --- Node ------------------------------------------------------------- */

.fnode {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  padding: 0.875rem;
  border-radius: var(--r-card);
  background: var(--c-deep-2);
  border: 1px solid var(--c-deep-border);
  box-shadow: 0 14px 30px -20px rgba(0, 0, 0, 0.95);
  transition: transform var(--t-fast) var(--ease-out),
    border-color var(--t-fast) var(--ease-out), box-shadow var(--t-fast) var(--ease-out);
}

.fnode:hover {
  transform: translateY(-3px);
  border-color: var(--c-border-orange);
  box-shadow: 0 20px 40px -22px rgba(0, 0, 0, 0.95), var(--glow-primary);
}

/* The entry point of the whole chain, marked the way a trigger is: a solid
   brand edge on the side nothing feeds into. */
.fnode--trigger {
  border-left: 2px solid var(--c-primary);
}

.fnode__port {
  position: absolute;
  top: 50%;
  width: 9px;
  height: 9px;
  margin-top: -5px;
  border-radius: 50%;
  background: var(--c-deep);
  border: 1.5px solid var(--c-orange-400);
  z-index: 1;
}

.fnode__port--in {
  left: -5px;
}

.fnode__port--out {
  right: -5px;
  background: var(--c-primary);
  border-color: var(--c-accent);
}

.fnode__head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: 0.625rem;
}

.fnode__icon {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: var(--r-nested);
  background: var(--c-deep-warm);
  border: 1px solid var(--c-border-orange);
  color: var(--c-on-deep-orange);
}

.fnode__icon svg {
  width: 0.9375rem;
  height: 0.9375rem;
}

.fnode__ident {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.fnode__org {
  font-family: var(--font-ui);
  font-size: var(--fs-body-sm);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.006em;
  color: var(--c-on-deep);
}

.fnode__role {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  font-weight: var(--fw-label);
  letter-spacing: 0.05em;
  line-height: 1.4;
  color: var(--c-on-deep-orange);
}

.fnode__idx {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  letter-spacing: 0.08em;
  color: var(--c-on-deep-soft);
  padding-top: 2px;
}

.fnode__note {
  font-family: var(--font-body);
  font-size: var(--fs-body-xs);
  line-height: 1.55;
  color: var(--c-on-deep-soft);
}

.fnode__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-1);
  margin-top: auto;
  padding-top: 0.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}

.fnode__date {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  letter-spacing: 0.02em;
  color: var(--c-on-deep-soft);
}

.fnode__state {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.1875rem 0.5rem;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.06);
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  font-weight: var(--fw-label);
  letter-spacing: 0.06em;
  color: var(--c-on-deep-soft);
  white-space: nowrap;
}

.fnode__state::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--c-on-deep-faint);
}

.fnode--live .fnode__state {
  color: var(--c-on-deep-orange);
}

.fnode--live .fnode__state::before {
  background: var(--c-primary);
  box-shadow: 0 0 0 3px rgba(249, 115, 22, 0.18);
}

/* --- Footer ----------------------------------------------------------- */

.flow__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  flex-wrap: wrap;
  padding: 0.75rem var(--s-3);
  border-top: 1px solid var(--c-deep-border);
  background: rgba(0, 0, 0, 0.22);
}

.flow__legend {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
}

.flow__legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.4375rem;
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--c-on-deep-soft);
}

.flow__legend-item::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--c-on-deep-faint);
}

.flow__legend-item--live::before {
  background: var(--c-primary);
}

.flow__legend-item--gate::before {
  border-radius: 2px;
  background: var(--c-on-deep-orange);
  transform: rotate(45deg);
}

.flow__hint {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  letter-spacing: 0.06em;
  color: var(--c-on-deep-faint);
}

/* --- Soft workflow palette ------------------------------------------- */

/* A warm-stone editor sits naturally beside the portfolio's pastel paper.
   Orange is reserved for state and interaction instead of tinting every
   surface; removing the dark gradient and glow keeps the graph utilitarian. */
.flow {
  --c-accent: #c2410c;
  --c-orange-100: #ffedd5;
  --c-orange-400: #c2410c;
  --c-deep: #eee7df;
  --c-deep-2: #fffaf5;
  --c-deep-3: #e6ddd4;
  --c-deep-border: #d6cbc0;
  --c-deep-warm: #f4e2d3;
  --c-border-orange: rgba(194, 65, 12, 0.28);
  --c-on-deep: #2d2723;
  --c-on-deep-soft: #625a54;
  --c-on-deep-faint: #6a615b;
  --c-on-deep-orange: #9a3412;
  --glow-primary: none;
  background: #eee7df;
  box-shadow: 0 24px 60px -38px rgba(73, 54, 42, 0.34);
}

.flow__bar,
.flow__foot {
  background: rgba(255, 252, 248, 0.62);
}

.flow__jump {
  background: rgba(255, 255, 255, 0.52);
}

.flow__chip:hover {
  background: rgba(194, 65, 12, 0.07);
}

.flow__canvas {
  background-image: radial-gradient(
    circle at 1px 1px,
    rgba(76, 65, 57, 0.13) 1px,
    transparent 0
  );
}

.flow__wire {
  stroke: rgba(76, 65, 57, 0.3);
}

.flow__wire--trunk {
  stroke: rgba(76, 65, 57, 0.2);
}

.flow__wire.is-live {
  stroke: var(--c-orange-400);
}

.fgate {
  box-shadow: 0 6px 16px -12px rgba(73, 54, 42, 0.3);
}

.fnode {
  box-shadow: 0 8px 20px -18px rgba(73, 54, 42, 0.36);
}

.fnode:hover,
.fnode.is-expanded {
  transform: translateY(-1px);
  border-color: var(--c-border-orange);
  box-shadow: 0 12px 26px -20px rgba(73, 54, 42, 0.4);
}

.fnode__port {
  background: var(--c-deep-2);
}

.fnode__meta {
  border-top-color: rgba(76, 65, 57, 0.12);
}

.fnode__state,
.fnode.is-collapsible .fnode__idx {
  background: rgba(255, 255, 255, 0.62);
}

/* Ended roles recede without reducing text opacity or readability. */
.flow .fnode:not(.fnode--live) {
  background: #f3efeb;
  border-color: #ddd5cd;
  box-shadow: none;
}

.flow .fnode:not(.fnode--live):hover,
.flow .fnode:not(.fnode--live).is-expanded {
  border-color: #cfc5bc;
  box-shadow: 0 8px 18px -18px rgba(73, 54, 42, 0.28);
}

.flow .fnode:not(.fnode--live).fnode--trigger {
  border-left-color: #b9afa6;
}

.flow .fnode:not(.fnode--live) .fnode__org {
  color: #514b46;
}

.flow .fnode:not(.fnode--live) .fnode__role,
.flow .fnode:not(.fnode--live) .fnode__date,
.flow .fnode:not(.fnode--live) .fnode__state {
  color: #685f59;
}

.flow .fnode:not(.fnode--live) .fnode__icon {
  color: #716861;
  background: #ebe5df;
  border-color: #d8d0c8;
}

.flow .fnode:not(.fnode--live) .fnode__port {
  background: #aaa097;
  border-color: #92887f;
}

/* ============================= 11  PROJECTS ============================== */

.section--projects .section__head {
  max-width: 72ch;
}

.project__index {
  flex: none;
  font-family: var(--font-mono);
  font-size: var(--fs-label-md);
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--c-orange-ink);
}

/* One flagship carries the visual weight; the remaining work reads as an
   editorial index rather than eleven identical cards. */
.project-feature {
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(360px, 0.92fr);
  gap: 0;
  padding: 0;
  overflow: hidden;
}

.project-feature__visual {
  position: relative;
  min-height: 430px;
  overflow: hidden;
  background: var(--c-deep);
}

.project-feature__visual::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent 62%, rgba(25, 28, 33, 0.58));
  pointer-events: none;
}

.project-feature__visual img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  transition: transform var(--t-slow) var(--ease-out);
}

.project-feature:hover .project-feature__visual img {
  transform: scale(1.025);
}

.project-feature__flag {
  position: absolute;
  z-index: 1;
  left: var(--s-3);
  bottom: var(--s-3);
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.45rem 0.75rem;
  border-radius: var(--r-pill);
  border: 1px solid rgba(255, 255, 255, 0.24);
  background: rgba(25, 28, 33, 0.8);
  backdrop-filter: blur(10px);
  color: #fff;
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  font-weight: var(--fw-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.project-feature__flag > span {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--c-primary);
  box-shadow: 0 0 0 4px rgba(249, 115, 22, 0.2);
}

.project-feature__content {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--s-3);
  padding: clamp(1.5rem, 4vw, 3.5rem);
}

.project-feature__eyebrow,
.project-feature__awards,
.project-feature__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.project-feature__intro {
  margin-top: 0.75rem;
  max-width: 45ch;
  font-family: var(--font-body);
  font-size: clamp(1rem, 1.4vw, 1.125rem);
  line-height: var(--lh-body);
  color: var(--c-ink-soft);
}

.award--soft {
  background: var(--c-orange-100);
  background-image: none;
  border: 1px solid var(--c-border-orange);
  box-shadow: none;
  color: var(--c-orange-800);
}

.project-feature__stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 1px solid var(--c-border);
  border-bottom: 1px solid var(--c-border);
}

.project-metric {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: var(--s-2) var(--s-2) var(--s-2) 0;
}

.project-metric + .project-metric {
  padding-left: var(--s-2);
  border-left: 1px solid var(--c-border);
}

.project-metric .num {
  font-size: clamp(1.5rem, 2.5vw, 2rem);
}

.project-metric > :last-child {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  line-height: 1.35;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--c-ink-muted);
}

.project-feature__actions {
  gap: var(--s-3);
}

.project-ledger {
  margin-top: var(--s-6);
  border-top: 1px solid var(--c-border-strong);
  border-bottom: 1px solid var(--c-border-strong);
}

.project-ledger__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) 0;
  border-bottom: 1px solid var(--c-border-strong);
}

.project-row {
  display: grid;
  grid-template-columns: 136px minmax(240px, 1.35fr) minmax(190px, 0.65fr) auto;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2);
  margin-inline: calc(var(--s-2) * -1);
  border-bottom: 1px solid var(--c-border);
  transition: background var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out);
}

.project-row:last-child {
  border-bottom: 0;
}

.project-row:hover {
  background: rgba(255, 255, 255, 0.68);
  transform: translateX(4px);
}

.project-row__media {
  width: 136px;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  border-radius: var(--r-nested);
  border: 1px solid var(--c-border);
  background: var(--c-surface-sunk);
}

.project-row__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--t-med) var(--ease-out);
}

.project-row:hover .project-row__media img {
  transform: scale(1.04);
}

.project-row__media--fallback {
  display: grid;
  place-items: center;
  color: var(--c-orange-ink);
  background-image: radial-gradient(circle at 30% 20%, var(--c-orange-100), transparent 58%);
}

.project-row__media--fallback svg {
  width: 2rem;
  height: 2rem;
}

.project-row__body {
  min-width: 0;
}

.project-row__title {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.project-row__body > p {
  margin-top: 0.35rem;
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  line-height: 1.45;
  color: var(--c-ink-soft);
}

.project-row__tag {
  display: inline-flex;
  padding: 0.2rem 0.45rem;
  border-radius: var(--r-pill);
  background: var(--c-surface-sunk);
  color: var(--c-ink-muted);
  font-family: var(--font-mono);
  font-size: 0.625rem;
  font-weight: var(--fw-label);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.project-row__tag--award {
  background: var(--c-orange-100);
  color: var(--c-orange-800);
}

.project-row__stack {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  line-height: 1.55;
  color: var(--c-ink-muted);
}

.project-row__stack span {
  display: block;
  margin-bottom: 0.25rem;
  color: var(--c-orange-ink);
  font-weight: var(--fw-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.project-row__link {
  justify-self: end;
  white-space: nowrap;
}

/* Every project starts on the same row rhythm; GuidHer expands in place. */
.project-ledger {
  margin-top: 0;
}

.project-row,
.project-expand__summary {
  grid-template-columns: 136px minmax(0, 1fr) 220px 72px;
  column-gap: var(--s-3);
  row-gap: var(--s-1);
  min-height: 110px;
}

.project-row__media,
.project-row__body,
.project-row__stack,
.project-row__link {
  align-self: center;
}

.project-row__link {
  justify-self: start;
}

.project-expand {
  margin-inline: calc(var(--s-2) * -1);
  border-bottom: 1px solid var(--c-border);
  transition: background var(--t-med) var(--ease-out),
    box-shadow var(--t-med) var(--ease-out);
}

.project-expand:last-child {
  border-bottom: 0;
}

.project-expand__summary {
  display: grid;
  align-items: center;
  padding: var(--s-2);
  cursor: pointer;
  list-style: none;
  transition: background var(--t-fast) var(--ease-out);
}

.project-expand__summary::-webkit-details-marker {
  display: none;
}

.project-expand__summary::marker {
  content: "";
}

.project-expand__summary:hover {
  background: rgba(255, 255, 255, 0.68);
}

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

.project-expand__toggle {
  display: inline-flex;
  align-items: center;
  justify-self: start;
  gap: 0.25rem;
  font-family: var(--font-mono);
  font-size: var(--fs-label-md);
  font-weight: var(--fw-label);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--c-orange-ink);
  white-space: nowrap;
}

.project-expand__toggle svg {
  width: 1rem;
  height: 1rem;
  transition: transform var(--t-med) var(--ease-out);
}

.project-expand[open] {
  background: var(--c-surface);
  box-shadow: var(--shadow-card);
}

.project-expand[open] .project-expand__summary {
  border-bottom: 1px solid var(--c-border);
}

.project-expand[open] .project-expand__toggle svg {
  transform: rotate(180deg);
}

.project-expand:hover .project-row__media img {
  transform: scale(1.04);
}

.project-expand__details {
  padding: var(--s-3) var(--s-2) var(--s-3) calc(136px + var(--s-5));
  animation: project-details-in var(--t-med) var(--ease-out) both;
}

.project-expand__story {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-2);
  max-width: 820px;
}

.project-expand__story .project-feature__intro {
  margin-top: 0;
  max-width: 66ch;
}

.project-expand__details .project-feature__stats {
  max-width: 640px;
  margin-top: var(--s-3);
}

@keyframes project-details-in {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@media (max-width: 980px) {
  .project-expand__summary {
    grid-template-columns: 120px minmax(0, 1fr) auto;
    gap: var(--s-2);
  }

  .project-expand__summary .project-row__media {
    grid-row: 1 / span 2;
  }

  .project-expand__summary .project-row__stack {
    grid-column: 2 / 3;
  }

  .project-expand__toggle {
    grid-column: 3;
    grid-row: 1 / span 2;
  }

  .project-expand__details {
    padding-left: calc(120px + var(--s-4));
  }
}

@media (max-width: 640px) {
  .project-expand {
    margin-inline: 0;
  }

  .project-expand__summary {
    grid-template-columns: 88px minmax(0, 1fr);
    align-items: start;
    padding-inline: 0;
  }

  .project-expand__summary .project-row__media {
    grid-row: 1;
  }

  .project-expand__summary .project-row__body {
    grid-column: 2;
  }

  .project-expand__summary .project-row__stack {
    grid-column: 1 / -1;
  }

  .project-expand__toggle {
    grid-column: 1 / -1;
    grid-row: auto;
  }

  .project-expand__details {
    padding: var(--s-2) 0 var(--s-3);
  }
}

@media (max-width: 980px) {
  .project-feature {
    grid-template-columns: minmax(0, 1fr);
  }

  .project-feature__visual {
    min-height: 0;
    aspect-ratio: 16 / 9;
  }

  .project-row {
    grid-template-columns: 120px minmax(0, 1fr) auto;
    gap: var(--s-2);
  }

  .project-row__media {
    width: 120px;
    grid-row: 1 / span 2;
  }

  .project-row__stack {
    grid-column: 2 / 3;
  }

  .project-row__link {
    grid-column: 3;
    grid-row: 1 / span 2;
  }
}

@media (max-width: 640px) {
  .project-feature__content {
    padding: var(--s-3);
  }

  .project-feature__visual {
    aspect-ratio: 4 / 3;
  }

  .project-feature__stats {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .project-metric,
  .project-metric + .project-metric {
    padding: 0.875rem 0.5rem;
  }

  .project-ledger__head {
    align-items: flex-start;
    flex-direction: column;
  }

  .project-row {
    grid-template-columns: 88px minmax(0, 1fr);
    align-items: start;
    margin-inline: 0;
    padding-inline: 0;
  }

  .project-row__media {
    width: 88px;
    grid-row: 1;
  }

  .project-row__body {
    grid-column: 2;
  }

  .project-row__stack {
    grid-column: 1 / -1;
  }

  .project-row__link {
    grid-column: 1 / -1;
    grid-row: auto;
    justify-self: start;
  }
}

@media (prefers-reduced-motion: reduce) {
  .project-feature__visual img,
  .project-row,
  .project-row__media img {
    transition: none;
  }

  .project-feature:hover .project-feature__visual img,
  .project-row:hover,
  .project-row:hover .project-row__media img {
    transform: none;
  }
}

/* Compact boxed gallery: left-to-right, three cards per desktop row. */
.project-ledger {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-2);
  border: 0;
}

.project-ledger__head {
  grid-column: 1 / -1;
  padding-top: 0;
}

.project-expand,
.project-expand:last-child {
  position: relative;
  top: 0;
  z-index: 0;
  align-self: start;
  width: 100%;
  margin: 0;
  overflow: hidden;
  border: 1px solid var(--c-border);
  border-radius: var(--r-card);
  background: var(--c-surface);
  box-shadow: var(--shadow-card);
  transition: top var(--t-med) var(--ease-out),
    border-color var(--t-med) var(--ease-out),
    box-shadow var(--t-med) var(--ease-out);
}

.project-expand:hover {
  top: -2px;
  border-color: var(--c-border-orange);
  box-shadow: 0 7px 16px -14px rgba(124, 45, 18, 0.4);
}

.project-expand[open] {
  top: -6px;
  z-index: 2;
  grid-column: auto;
  overflow: visible;
  border-color: rgba(234, 88, 12, 0.58);
  background: var(--c-orange-50);
  box-shadow: 0 18px 36px -24px rgba(124, 45, 18, 0.58),
    0 7px 15px -12px rgba(26, 20, 17, 0.18);
}

.project-expand__summary {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto 1fr auto auto;
  gap: 0;
  min-height: 0;
  padding: 0;
  border-radius: inherit;
  background: transparent;
  transform-origin: center;
  will-change: transform, box-shadow;
  transition: transform 360ms var(--ease-out),
    box-shadow 360ms var(--ease-out),
    background-color var(--t-med) var(--ease-out);
}

.project-expand:not([open]) .project-expand__summary {
  height: auto;
}

.project-expand__summary:hover {
  background: transparent;
}

.project-expand__summary .project-row__media {
  grid-column: 1;
  grid-row: 1;
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 0;
  border-bottom: 1px solid var(--c-border);
  border-radius: 0;
}

.project-expand__summary .project-row__media img {
  object-position: center;
}

.project-expand__summary .project-row__body {
  grid-column: 1;
  grid-row: 2;
  align-self: start;
  min-height: 108px;
  padding: var(--s-2) var(--s-2) 0.625rem;
}

.project-expand__summary .project-row__title {
  gap: 0.5rem;
}

.project-expand__summary .project-row__title .title {
  font-size: clamp(1rem, 1.35vw, 1.125rem);
}

.project-expand__summary .project-row__body > p {
  display: -webkit-box;
  max-width: 44ch;
  margin-top: 0.4rem;
  overflow: hidden;
  font-size: var(--fs-body-xs);
  line-height: 1.45;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.project-expand__summary .project-row__stack {
  grid-column: 1;
  grid-row: 3;
  align-self: start;
  padding: 0 var(--s-2) 0.75rem;
  font-size: 0.625rem;
}

.project-expand__toggle {
  grid-column: 1;
  grid-row: 4;
  justify-self: start;
  margin: 0 var(--s-2) var(--s-2);
  padding: 0.35rem 0.6rem;
  border: 1px solid var(--c-border-orange);
  border-radius: var(--r-pill);
  background: var(--c-orange-50);
  font-size: var(--fs-label-sm);
  transition: color var(--t-fast) var(--ease-out),
    background var(--t-fast) var(--ease-out);
}

.project-expand__summary:hover .project-expand__toggle {
  color: var(--c-orange-800);
  background: var(--c-orange-100);
}

.project-expand[open] .project-expand__summary {
  border-bottom: 0;
  border-radius: var(--r-card);
  background-color: var(--c-orange-50);
  background-image: linear-gradient(180deg, var(--c-surface) 42%, var(--c-orange-50));
  box-shadow: inset 0 0 0 1px rgba(234, 88, 12, 0.58),
    0 16px 32px -23px rgba(124, 45, 18, 0.62),
    0 5px 12px -10px rgba(26, 20, 17, 0.2);
  transform: scale(1.035);
}

.project-expand[open] .project-row__media img {
  object-fit: cover;
}

/* Details attach to the enlarged card and overlay the rows below, so the
   gallery itself never reflows. */
.project-expand__details {
  position: absolute;
  top: calc(100% + 7px);
  left: 50%;
  z-index: 3;
  width: 103.5%;
  max-height: min(46vh, 320px);
  padding: 0.875rem var(--s-2) var(--s-2);
  overflow-y: auto;
  overscroll-behavior: contain;
  border: 1px solid var(--c-border-orange);
  border-top: 0;
  border-radius: 0 0 var(--r-card) var(--r-card);
  background-color: var(--c-orange-50);
  background-image: linear-gradient(180deg, var(--c-orange-50), var(--c-surface-2));
  box-shadow: 0 18px 34px -24px rgba(124, 45, 18, 0.6),
    0 6px 13px -11px rgba(26, 20, 17, 0.18);
  transform: translateX(-50%);
  will-change: opacity, transform;
  animation: project-overlay-in 280ms var(--ease-out) 260ms both;
}

.project-expand__close {
  position: sticky;
  top: 0;
  z-index: 1;
  float: right;
  display: grid;
  place-items: center;
  width: 1.875rem;
  height: 1.875rem;
  margin: 0 0 0.25rem 0.5rem;
  border: 1px solid var(--c-border);
  border-radius: 50%;
  background: var(--c-surface);
  color: var(--c-ink-soft);
  font: 600 1.125rem/1 var(--font-ui);
  cursor: pointer;
  transition: color var(--t-fast) var(--ease-out),
    border-color var(--t-fast) var(--ease-out),
    background var(--t-fast) var(--ease-out);
}

.project-expand__close:hover,
.project-expand__close:focus-visible {
  border-color: var(--c-border-orange);
  background: var(--c-orange-50);
  color: var(--c-orange-800);
}

@keyframes project-overlay-in {
  from {
    opacity: 0;
    transform: translate(-50%, -8px);
  }
  to {
    opacity: 1;
    transform: translate(-50%, 0);
  }
}

.project-expand__details .project-expand__story {
  gap: 0.625rem;
}

.project-expand__details .project-feature__intro {
  font-size: var(--fs-body-xs);
  line-height: 1.45;
}

/* The stack already appears in the summary; repeating every chip made the
   opened card unnecessarily tall. */
.project-expand__details .chip-set {
  display: none;
}

.project-expand__details .award {
  padding: 0.25rem 0.5rem;
  font-size: 0.5625rem;
}

.project-expand__details .project-feature__stats {
  margin-top: 0.75rem;
}

.project-expand__details .project-metric,
.project-expand__details .project-metric + .project-metric {
  gap: 0.2rem;
  padding: 0.625rem 0.4rem;
}

.project-expand__details .project-metric .num {
  font-size: 1.25rem;
}

.project-expand__details .project-metric > :last-child {
  font-size: 0.5625rem;
}

@media (max-width: 1100px) {
  .project-ledger {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 700px) {
  .project-ledger {
    grid-template-columns: minmax(0, 1fr);
  }

  .project-expand[open] {
    grid-column: 1;
  }

  .project-expand__summary,
  .project-expand[open] .project-expand__summary {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto 1fr auto auto;
  }

  .project-expand__summary .project-row__media,
  .project-expand[open] .project-row__media {
    grid-column: 1;
    grid-row: 1;
    width: 100%;
    min-height: 0;
    aspect-ratio: 16 / 9;
    border-right: 0;
    border-bottom: 1px solid var(--c-border);
  }

  .project-expand[open] .project-row__media img {
    object-fit: cover;
  }

  .project-expand__summary .project-row__body,
  .project-expand[open] .project-row__body {
    grid-column: 1;
    grid-row: 2;
    align-self: start;
    min-height: 0;
    padding: var(--s-2) var(--s-2) 0.625rem;
  }

  .project-expand__summary .project-row__stack,
  .project-expand[open] .project-row__stack {
    grid-column: 1;
    grid-row: 3;
    padding: 0 var(--s-2) 0.75rem;
  }

  .project-expand__toggle,
  .project-expand[open] .project-expand__toggle {
    grid-column: 1;
    grid-row: 4;
    margin: 0 var(--s-2) var(--s-2);
  }

  .project-expand__details {
    padding: var(--s-2);
  }
}

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

  .project-expand:hover,
  .project-expand[open] {
    top: 0;
  }

  .project-expand[open] .project-expand__summary {
    transform: none;
  }

  .project-expand__details {
    animation: none;
    transform: translateX(-50%);
  }
}

/* Horizontal project showcase: artwork left, project context right. */
.project-ledger {
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-3);
}

.project-ledger__head {
  margin-bottom: calc(var(--s-1) * -1);
}

.project-expand,
.project-expand:last-child,
.project-expand:hover,
.project-expand[open] {
  top: 0;
  overflow: hidden;
}

.project-expand__summary,
.project-expand[open] .project-expand__summary {
  grid-template-columns: minmax(240px, 0.82fr) minmax(0, 1.18fr);
  grid-template-rows: minmax(0, 1fr) auto auto;
  min-height: 250px;
  border-radius: inherit;
  background: var(--c-surface);
  box-shadow: none;
  transform: none;
}

.project-expand__summary .project-row__media,
.project-expand[open] .project-row__media {
  grid-column: 1;
  grid-row: 1 / 4;
  width: 100%;
  height: 100%;
  min-height: 250px;
  aspect-ratio: auto;
  border: 0;
  border-right: 1px solid var(--c-border);
  border-radius: 0;
}

.project-expand__summary .project-row__body,
.project-expand[open] .project-row__body {
  grid-column: 2;
  grid-row: 1;
  align-self: end;
  min-height: 0;
  padding: var(--s-4) var(--s-4) var(--s-2);
}

.project-expand__summary .project-row__title .title {
  font-size: clamp(1.25rem, 2.1vw, 1.75rem);
}

.project-expand__summary .project-row__body > p {
  display: block;
  max-width: 56ch;
  overflow: visible;
  font-size: var(--fs-body-sm);
  -webkit-line-clamp: unset;
}

.project-expand__summary .project-row__stack,
.project-expand[open] .project-row__stack {
  grid-column: 2;
  grid-row: 2;
  padding: 0 var(--s-4) var(--s-2);
  font-size: var(--fs-label-sm);
}

.project-expand__toggle,
.project-expand[open] .project-expand__toggle {
  grid-column: 2;
  grid-row: 3;
  margin: 0 var(--s-4) var(--s-4);
}

.project-expand__details {
  position: static;
  width: auto;
  max-height: none;
  padding: var(--s-3) var(--s-4) var(--s-4);
  overflow: visible;
  border: 0;
  border-top: 1px solid var(--c-border);
  border-radius: 0;
  background: var(--c-orange-50);
  box-shadow: none;
  transform: none;
  animation: project-details-in var(--t-med) var(--ease-out) both;
}

.project-expand__close {
  position: static;
}

@media (max-width: 700px) {
  .project-expand__summary,
  .project-expand[open] .project-expand__summary {
    grid-template-columns: 104px minmax(0, 1fr);
    grid-template-rows: auto auto auto;
    min-height: 178px;
  }

  .project-expand__summary .project-row__media,
  .project-expand[open] .project-row__media {
    grid-column: 1;
    grid-row: 1 / 4;
    width: 100%;
    height: 100%;
    min-height: 178px;
    aspect-ratio: auto;
    border-right: 1px solid var(--c-border);
    border-bottom: 0;
  }

  .project-expand__summary .project-row__body,
  .project-expand[open] .project-row__body {
    grid-column: 2;
    grid-row: 1;
    align-self: start;
    padding: var(--s-2) var(--s-2) 0.625rem;
  }

  .project-expand__summary .project-row__title .title {
    font-size: 1rem;
  }

  .project-expand__summary .project-row__body > p {
    display: -webkit-box;
    overflow: hidden;
    font-size: var(--fs-body-xs);
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
  }

  .project-expand__summary .project-row__stack,
  .project-expand[open] .project-row__stack {
    grid-column: 2;
    grid-row: 2;
    padding: 0 var(--s-2) 0.625rem;
  }

  .project-expand__toggle,
  .project-expand[open] .project-expand__toggle {
    grid-column: 2;
    grid-row: 3;
    margin: 0 var(--s-2) var(--s-2);
  }

  .project-expand__details {
    padding: var(--s-2);
  }
}

/* Professional project cards — 8px spacing grid, restrained depth, clear hierarchy. */
.project-ledger {
  gap: var(--s-3);
}

.project-ledger__head {
  margin-bottom: 0;
  padding: 0 0 var(--s-2);
}

.project-expand--static,
.project-expand--static:hover {
  --project-media-width: 27.5rem;
  --project-media-height: 19rem;
  top: 0;
  display: grid;
  grid-template-columns: var(--project-media-width) minmax(0, 1fr);
  grid-template-rows: auto auto 1fr;
  min-height: var(--project-media-height);
  overflow: hidden;
  border: 1px solid var(--c-border);
  border-radius: var(--r-card);
  background: var(--c-surface);
  box-shadow: var(--shadow-card);
  transition: border-color var(--t-med) var(--ease-out),
    box-shadow var(--t-med) var(--ease-out);
}

.project-expand--static:hover {
  border-color: var(--c-border-strong);
  box-shadow: var(--shadow-md);
}

.project-expand--static .project-expand__summary {
  display: contents;
  cursor: default;
}

.project-expand--static .project-row__media {
  position: relative;
  grid-column: 1;
  grid-row: 1 / 4;
  align-self: start;
  width: var(--project-media-width);
  height: var(--project-media-height);
  min-height: 0;
  overflow: hidden;
  border: 0;
  border-radius: 0;
  background: var(--c-surface-sunk);
}

.project-expand--static .project-row__media::after {
  content: "";
  position: absolute;
  inset: 0 0 0 auto;
  width: 4.5rem;
  background: linear-gradient(90deg, transparent, var(--c-surface));
  pointer-events: none;
}

.project-expand--static .project-row__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  transition: transform var(--t-slow) var(--ease-out);
}

.project-expand--static:hover .project-row__media img {
  transform: scale(1.025);
}

.project-expand--static .project-row__body {
  grid-column: 2;
  grid-row: 1;
  align-self: start;
  min-height: 0;
  padding: var(--s-4) var(--s-4) var(--s-1);
}

.project-expand--static .project-row__title {
  align-items: center;
  gap: var(--s-1);
}

.project-expand--static .project-row__title .title {
  font-size: clamp(1.25rem, 1.8vw, 1.625rem);
  line-height: 1.15;
}

.project-expand--static .project__index {
  font-size: var(--fs-label-sm);
}

.project-expand--static .project-row__tag {
  padding: 0.25rem 0.5rem;
}

/* The richer introduction below is used instead of repeating the teaser. */
.project-expand--static .project-row__body > p {
  display: none;
}

.project-expand--static .project-row__stack {
  grid-column: 2;
  grid-row: 2;
  align-self: start;
  margin: 0 var(--s-4);
  padding: var(--s-1) 0 var(--s-2);
  border-bottom: 1px solid var(--c-border);
  font-size: var(--fs-label-sm);
  line-height: 1.5;
}

.project-expand--static .project-row__stack span {
  display: inline;
  margin: 0 var(--s-1) 0 0;
}

.project-expand--static .project-expand__details {
  position: static;
  grid-column: 2;
  grid-row: 3;
  align-self: start;
  width: auto;
  max-height: none;
  padding: var(--s-2) var(--s-4) var(--s-4);
  overflow: visible;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  transform: none;
  animation: none;
}

.project-expand--static .project-expand__story {
  gap: var(--s-2);
  max-width: 62ch;
}

.project-expand--static .project-feature__intro {
  display: -webkit-box;
  margin: 0;
  overflow: hidden;
  font-size: var(--fs-body-sm);
  line-height: 1.55;
  color: var(--c-ink-soft);
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
}

.project-expand--static .project-feature__stats,
.project-expand--static .chip-set,
.project-expand--static .project-expand__toggle,
.project-expand--static .project-expand__close {
  display: none;
}

.project-expand--static .project-feature__awards,
.project-expand--static .project-feature__actions {
  gap: var(--s-1) var(--s-2);
}

.project-expand--static .project-feature__actions .link {
  min-height: 2rem;
  padding: 0.375rem 0;
}

/* Alternate the visual rhythm on larger screens without changing reading order. */
.project-expand--static:nth-of-type(even) {
  grid-template-columns: minmax(0, 1fr) var(--project-media-width);
}

.project-expand--static:nth-of-type(even) .project-row__media {
  grid-column: 2;
}

.project-expand--static:nth-of-type(even) .project-row__media::after {
  inset: 0 auto 0 0;
  background: linear-gradient(90deg, var(--c-surface), transparent);
}

.project-expand--static:nth-of-type(even) .project-row__body,
.project-expand--static:nth-of-type(even) .project-row__stack,
.project-expand--static:nth-of-type(even) .project-expand__details {
  grid-column: 1;
}

@media (max-width: 1024px) {
  .project-expand--static,
  .project-expand--static:hover {
    --project-media-width: 20rem;
    --project-media-height: 17.5rem;
  }

  .project-expand--static .project-row__body {
    padding: var(--s-3) var(--s-3) var(--s-1);
  }

  .project-expand--static .project-row__stack {
    margin-inline: var(--s-3);
  }

  .project-expand--static .project-expand__details {
    padding: var(--s-2) var(--s-3) var(--s-3);
  }
}

@media (max-width: 700px) {
  .project-ledger {
    gap: var(--s-2);
  }

  .project-ledger__head {
    gap: var(--s-1);
    padding-bottom: var(--s-1);
  }

  .project-expand--static,
  .project-expand--static:hover,
  .project-expand--static:nth-of-type(even) {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto auto auto;
    min-height: 0;
  }

  .project-expand--static .project-row__media,
  .project-expand--static:nth-of-type(even) .project-row__media {
    grid-column: 1;
    grid-row: 1;
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
  }

  .project-expand--static .project-row__media::after,
  .project-expand--static:nth-of-type(even) .project-row__media::after {
    inset: auto 0 0;
    width: 100%;
    height: 3.5rem;
    background: linear-gradient(180deg, transparent, var(--c-surface));
  }

  .project-expand--static .project-row__body,
  .project-expand--static:nth-of-type(even) .project-row__body {
    grid-column: 1;
    grid-row: 2;
    padding: var(--s-3) var(--s-3) var(--s-1);
  }

  .project-expand--static .project-row__title .title {
    font-size: 1.125rem;
  }

  .project-expand--static .project-row__stack,
  .project-expand--static:nth-of-type(even) .project-row__stack {
    grid-column: 1;
    grid-row: 3;
    margin-inline: var(--s-3);
    padding: var(--s-1) 0 var(--s-2);
  }

  .project-expand--static .project-expand__details,
  .project-expand--static:nth-of-type(even) .project-expand__details {
    grid-column: 1;
    grid-row: 4;
    padding: var(--s-2) var(--s-3) var(--s-3);
  }

  .project-expand--static .project-feature__intro {
    font-size: var(--fs-body-xs);
    -webkit-line-clamp: 4;
  }

  .project-expand--static .award {
    white-space: normal;
  }
}

@media (prefers-reduced-motion: reduce) {
  .project-expand--static,
  .project-expand--static .project-row__media img {
    transition: none;
  }

  .project-expand--static:hover .project-row__media img {
    transform: none;
  }
}

/* Compact media proportions; content remains the visual focus. */
.section--projects .project__index {
  display: none;
}

.project-expand--static,
.project-expand--static:hover {
  --project-media-width: 27rem;
  --project-media-height: 17rem;
}

@media (max-width: 1024px) {
  .project-expand--static,
  .project-expand--static:hover {
    --project-media-width: 21rem;
    --project-media-height: 16rem;
  }
}

@media (min-width: 701px) {
  .project-expand--static .project-row__media,
  .project-expand--static:nth-of-type(even) .project-row__media {
    align-self: stretch;
    height: 100%;
    min-height: var(--project-media-height);
  }
}

@media (max-width: 700px) {
  .project-expand--static .project-row__media,
  .project-expand--static:nth-of-type(even) .project-row__media {
    aspect-ratio: 2 / 1;
  }
}

/* =================== 12  SKILLS AND CERTIFICATIONS ======================= */

.cap {
  gap: var(--s-2);
}

.cap__icon {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border-radius: var(--r-control);
  background: var(--c-orange-50);
  border: 1px solid var(--c-border-orange);
  color: var(--c-orange-ink);
}

.cap__icon svg {
  width: 20px;
  height: 20px;
}

.certs {
  margin-top: var(--s-3);
}

.cert {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.75rem;
  border-radius: var(--r-nested);
  background: var(--c-surface-sunk);
  border: 1px solid var(--c-border);
  min-width: 0;
  transition: border-color var(--t-fast) var(--ease-out),
    background var(--t-fast) var(--ease-out);
}

.cert:hover {
  border-color: var(--c-border-orange);
  background: var(--c-orange-50);
}

.cert__badge {
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: 50%;
  padding: 5px;
  object-fit: contain;
  background: #fff;
  border: 1px solid var(--c-border);
}

/* Databricks ships its symbol on a 16:9 canvas with wide transparent margins,
   so `contain` fits the empty canvas rather than the mark and the logo lands
   about a third smaller than its neighbours. Dropping the inset for that one
   badge brings the optical weight back in line. */
.cert__badge--tight {
  padding: 1px;
}

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

.cert__name {
  font-size: var(--fs-body-xs);
  font-weight: 600;
  color: var(--c-ink);
  line-height: 1.3;
}

.cert__issuer {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-ink-muted);
}

/* ======================= 13  CONTACT AND FOOTER ========================== */

.section--contact {
  padding-bottom: var(--s-8);
}

.contact {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
  gap: clamp(var(--s-4), 6vw, var(--s-10));
  align-items: start;
}

.contact__copy {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  max-width: 46rem;
  min-width: 0;
  padding-block: var(--s-2);
}

.contact__title {
  display: grid;
  gap: 0.02em;
}

.contact__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.625rem;
}

/* The stage owns reveal and perspective so the inner card can tilt freely. */
.contact-card-stage {
  align-self: center;
  width: 100%;
  perspective: 1000px;
  perspective-origin: 50% 45%;
}

.contact-card {
  --contact-rx: 0deg;
  --contact-ry: 0deg;
  --contact-mx: 50%;
  --contact-my: 35%;
  --contact-lift: 0px;
  --contact-gloss: 0;

  align-self: auto;
  width: 100%;
  isolation: isolate;
  transform-style: preserve-3d;
  transform: translate3d(0, 0, var(--contact-lift))
    rotateX(var(--contact-rx)) rotateY(var(--contact-ry));
  background-color: var(--c-deep-2);
  background-image: linear-gradient(165deg, var(--c-deep-2), var(--c-deep));
  border-color: var(--c-deep-border);
  box-shadow: var(--shadow-lg);
  gap: 0;
  transition: transform 620ms var(--ease-out),
    box-shadow var(--t-med) var(--ease-out),
    border-color var(--t-med) var(--ease-out);
  will-change: transform;
}

.contact-card.is-live {
  transition: transform 120ms var(--ease-out),
    box-shadow var(--t-med) var(--ease-out),
    border-color var(--t-med) var(--ease-out);
}

.contact-card.is-active,
.contact-card:focus-within {
  border-color: var(--c-border-orange);
  box-shadow: 0 34px 64px -28px rgba(124, 45, 18, 0.58),
    0 0 0 1px rgba(249, 115, 22, 0.22);
}

.contact-card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  pointer-events: none;
  background: radial-gradient(
    520px circle at var(--contact-mx) var(--contact-my),
    rgba(255, 255, 255, 0.2),
    rgba(255, 224, 190, 0.06) 30%,
    transparent 64%
  );
  opacity: var(--contact-gloss);
  transition: opacity 220ms var(--ease-out);
}

.contact-card > * {
  position: relative;
  z-index: 1;
}

@media (prefers-reduced-motion: reduce) {
  .contact-card,
  .contact-card.is-live {
    transform: none;
  }
}

.contact-card .label {
  color: var(--c-orange-300);
}

.contact-card__head {
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--c-deep-border);
}

.contact-card__identity {
  min-width: 0;
}

.contact-card__name {
  margin-top: 0.375rem;
  color: var(--c-on-deep);
  font-family: var(--font-display);
  font-size: var(--fs-title);
  font-weight: 600;
  line-height: var(--lh-heading);
  letter-spacing: -0.01em;
}

.contact-card__role {
  margin-top: 0.25rem;
  color: var(--c-on-deep-soft);
  font-family: var(--font-ui);
  font-size: var(--fs-body-xs);
  line-height: 1.4;
}

.contact-card__links {
  display: grid;
  gap: 0.5rem;
  padding-top: var(--s-3);
}

.contact-row {
  display: grid;
  grid-template-columns: 2.25rem minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.75rem;
  min-height: 3.75rem;
  padding: 0.625rem 0.75rem;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--r-nested);
  background:
    linear-gradient(135deg, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0.02) 50%, rgba(0, 0, 0, 0.04) 100%),
    rgba(255, 255, 255, 0.035);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.06) inset,
    0 2px 4px rgba(0, 0, 0, 0.2),
    0 4px 8px rgba(0, 0, 0, 0.1);
  transform: translateY(0);
  transition: background var(--t-fast) var(--ease-out),
    border-color var(--t-fast) var(--ease-out),
    transform var(--t-fast) var(--ease-out),
    box-shadow var(--t-fast) var(--ease-out);
}

a.contact-row:hover,
a.contact-row:focus-visible {
  border-color: var(--c-border-orange);
  background:
    linear-gradient(135deg, rgba(249, 115, 22, 0.12) 0%, rgba(249, 115, 22, 0.06) 50%, rgba(0, 0, 0, 0.06) 100%),
    rgba(249, 115, 22, 0.09);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.08) inset,
    0 4px 12px rgba(249, 115, 22, 0.25),
    0 8px 16px rgba(0, 0, 0, 0.15);
  transform: translateY(-2px);
}

.contact-row__icon {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--r-control);
  background:
    linear-gradient(145deg, rgba(249, 115, 22, 0.22) 0%, rgba(249, 115, 22, 0.12) 50%, rgba(234, 88, 12, 0.18) 100%),
    rgba(249, 115, 22, 0.16);
  color: var(--c-orange-200);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.15) inset,
    0 2px 4px rgba(0, 0, 0, 0.3),
    0 1px 2px rgba(249, 115, 22, 0.2);
  transform: translateZ(0);
  transition: transform var(--t-fast) var(--ease-out),
    box-shadow var(--t-fast) var(--ease-out);
}

a.contact-row:hover .contact-row__icon,
a.contact-row:focus-visible .contact-row__icon {
  transform: translateY(-1px) scale(1.05);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.2) inset,
    0 4px 8px rgba(249, 115, 22, 0.35),
    0 2px 4px rgba(0, 0, 0, 0.25);
}

.contact-row__body {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
}

.contact-row__key {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  font-weight: var(--fw-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-on-deep-faint);
}

.contact-row__val {
  color: var(--c-on-deep);
  font-family: var(--font-ui);
  font-size: var(--fs-body-xs);
  font-weight: 500;
  line-height: 1.35;
  overflow-wrap: anywhere;
  transition: color var(--t-fast) var(--ease-out);
}

a.contact-row:hover .contact-row__val,
a.contact-row:focus-visible .contact-row__val {
  color: var(--c-orange-200);
}

.contact-row__arrow {
  display: inline-block;
  color: var(--c-orange-300);
  font-family: var(--font-ui);
  transition: transform var(--t-fast) var(--ease-out);
}

a.contact-row:hover .contact-row__arrow,
a.contact-row:focus-visible .contact-row__arrow {
  transform: translate(2px, -1px);
}

.footer {
  position: relative;
  z-index: var(--z-content);
  border-top: 1px solid var(--c-border);
  background: var(--c-bg-tint);
}

.footer__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  flex-wrap: wrap;
  max-width: var(--measure);
  margin: 0 auto;
  padding: var(--s-4) var(--gutter);
}

.footer__links {
  display: flex;
  gap: var(--s-2);
  flex-wrap: wrap;
}

.footer__links a {
  font-family: var(--font-mono);
  font-size: var(--fs-label-md);
  font-weight: var(--fw-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-ink-muted);
  transition: color var(--t-fast) var(--ease-out);
}

.footer__links a:hover {
  color: var(--c-orange-ink);
}

.to-top {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

/* ============================= 14  REVEALS =============================== */

/* Gated behind .js so the page is readable with scripts off. */

.js [data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--t-slow) var(--ease-out) var(--delay, 0ms),
    transform var(--t-slow) var(--ease-out) var(--delay, 0ms);
}

.js [data-reveal].is-inview {
  opacity: 1;
  transform: none;
}

/* Masked line reveal for display headlines. */
.reveal-mask {
  display: block;
  overflow: hidden;
  padding-bottom: 0.06em;
}

.js .reveal-mask > span {
  display: block;
  transform: translateY(105%);
  transition: transform var(--t-slow) var(--ease-out) var(--delay, 0ms);
}

.js [data-reveal].is-inview .reveal-mask > span {
  transform: none;
}

.js [data-reveal] .reveal-mask > span {
  will-change: transform;
}

/* ============================ 15  RESPONSIVE ============================= */

@media (max-width: 1180px) {
  .nav__links {
    gap: 0;
  }

  .nav__link {
    padding: 0.5rem 0.6875rem;
  }

  .nav__link-idx {
    display: none;
  }
}

@media (max-width: 1024px) {
  :root {
    --measure: 900px;
  }

  .hero {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-8);
    justify-items: center;
    text-align: left;
  }

  /* Once the hero stacks, the card is 800-1000px down the page and scrolls
     through the middle of the viewport, so no ribbon length keeps its end
     off-screen: the top would come into view as you scroll. Short lace and a
     fade, which reads as running out of sight rather than being cut off. */
  .id {
    --lace-h: 120px;
    --lace-seat: 16px;
    --id-offset-x: 0px;
  }

  .id__strap {
    -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 30px);
    mask-image: linear-gradient(180deg, transparent 0, #000 30px);
  }

  .hero__copy {
    width: 100%;
  }

  .lede {
    max-width: none;
  }

  .work-grid,
  .grid--3 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .grid--4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .contact {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-6);
  }
}

/* Below this the pill nav cannot hold six labels honestly, so it becomes a
   drawer rather than shrinking the type until it lies about being readable. */
@media (max-width: 900px) {
  .nav__links,
  .nav__cta {
    display: none;
  }

  .nav__toggle {
    display: grid;
  }

  .nav.is-open .nav__drawer {
    display: block;
  }
}

@media (max-width: 760px) {
  :root {
    --nav-h: 60px;
  }

  .hero {
    gap: var(--s-4);
  }

  .hero__stage {
    margin-top: calc(0px - var(--s-12) - var(--s-2));
  }

  .grid--2,
  .grid--3,
  .work-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .hero__meta {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.875rem;
  }

  .tl-panel {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-2);
  }

  .tl-panel__side {
    padding-right: 0;
    padding-bottom: var(--s-2);
    border-right: 0;
    border-bottom: 1px solid var(--c-border);
  }

  .scrub__node-year {
    font-size: 0.625rem;
  }

  .section__head {
    margin-bottom: var(--s-4);
  }

  /* On a phone the hero is a tall stack and the cue is the last thing before
     the fold, so it moves to the right edge and stands upright: word first,
     then a rail whose dot travels downward. Same direction as the gesture it
     is asking for, and it sits under the thumb rather than across the text. */
  .scroll-cue {
    flex-direction: column;
    align-self: flex-end;
    gap: 0.5rem;
  }

  .scroll-cue__text {
    writing-mode: vertical-rl;
    letter-spacing: 0.22em;
  }

  .scroll-cue__line {
    order: 2;
    flex: none;
    width: 1px;
    height: 56px;
    background: linear-gradient(180deg, var(--c-primary), transparent);
  }

  .scroll-cue__line::after {
    width: 100%;
    height: 40%;
    animation-name: cue-down;
  }
}

@media (max-width: 560px) {
  /* Keep one complete node readable at a time on narrow screens while the
     graph remains horizontally explorable instead of collapsing to cards. */
  .flow__bar,
  .flow__foot {
    padding-inline: var(--s-2);
  }

  .flow__jump {
    width: 100%;
  }

  .flow__chip {
    flex: 1;
  }

  .flow__canvas {
    gap: 0.75rem;
    padding: var(--s-3) var(--s-2) var(--s-4);
  }

  .flow__col--nodes {
    width: min(252px, calc(100vw - 5rem));
  }

  .flow__col--gate {
    width: 96px;
  }

  .fnode {
    padding: 0.75rem;
  }

  .flow__legend {
    gap: 0.75rem;
  }

  /* The brand is text-only now, so the name stays and only the secondary role
     line drops. Hiding the whole block would leave the home link blank. */
  .nav__role {
    display: none;
  }

  .grid--4 {
    grid-template-columns: minmax(0, 1fr);
  }

  .edu__stats,
  .project__stats {
    grid-template-columns: minmax(0, 1fr);
  }

  .hero__actions .btn,
  .contact__actions .btn {
    flex: 1 1 100%;
  }

  .contact-card {
    padding: var(--s-2);
  }

  .contact-row {
    grid-template-columns: 2rem minmax(0, 1fr) auto;
    gap: 0.625rem;
    min-height: 3.5rem;
    padding: 0.625rem;
  }

  .contact-row__icon {
    width: 2rem;
    height: 2rem;
  }

  .footer__inner {
    justify-content: flex-start;
  }
}

/* --- Organizations: two-dimensional canvas refinement ---------------- */

/* Keep the workflow compact while preserving a larger pannable surface
   behind it. The outer frame is narrower and shorter; overflow remains
   available in both axes for mouse, keyboard, and touch navigation. */
.flow {
  width: 100%;
  margin-inline: 0;
}

.flow__viewport {
  height: clamp(28rem, 58vh, 36rem);
  overflow: auto;
  overscroll-behavior: contain;
}

.flow__canvas {
  align-items: center;
  gap: var(--s-6);
  min-height: 48rem;
  padding: var(--s-6) var(--s-8);
}

.flow__col {
  gap: var(--s-3);
}

.flow__col--nodes {
  width: 268px;
}

.flow__col--gate {
  width: 128px;
}

.fnode {
  gap: var(--s-1);
  padding: 0.75rem;
}

.fnode__idx {
  display: none;
}

.fnode.is-collapsible {
  cursor: pointer;
}

.fnode.is-collapsible:focus-visible {
  outline: 2px solid var(--c-orange-400);
  outline-offset: 3px;
}

.fnode.is-collapsible .fnode__idx {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--c-deep-border);
  font-size: 0;
  color: var(--c-on-deep-orange);
}

.fnode.is-collapsible .fnode__idx::after {
  content: "+";
  font-family: var(--font-mono);
  font-size: 0.875rem;
  line-height: 1;
}

.fnode.is-collapsible.is-expanded .fnode__idx::after {
  content: "−";
}

.fnode.is-collapsible .fnode__note {
  display: block;
  max-height: 0;
  margin-block: calc(var(--s-1) * -1);
  overflow: hidden;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  pointer-events: none;
}

.fnode.is-collapsible.is-expanded .fnode__note {
  max-height: 16rem;
  margin-block: 0;
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  pointer-events: auto;
}

.fnode.is-expanded {
  border-color: var(--c-border-orange);
  box-shadow: 0 20px 40px -22px rgba(0, 0, 0, 0.95), var(--glow-primary);
}

@media (max-width: 760px) {
  .flow__viewport {
    height: min(60vh, 32rem);
    min-height: 24rem;
  }

  .flow__canvas {
    min-height: 44rem;
    padding: var(--s-4) var(--s-3);
  }
}

@media (max-width: 560px) {
  .flow__canvas {
    gap: var(--s-2);
    min-height: 42rem;
    padding: var(--s-3) var(--s-2);
  }

  .flow__col--nodes {
    width: min(260px, calc(100vw - 5rem));
  }

  .flow__col--gate {
    width: 104px;
  }

  .fnode {
    padding: 0.75rem;
  }
}

/* --- Reduced motion --------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  .js [data-reveal] {
    opacity: 1;
    transform: none;
  }

  .js .reveal-mask > span {
    transform: none;
  }

  /* No lap around the card. The frame still lights up so the hover is legible,
     it just resolves as an even ring instead of a travelling head. */
  .card--lift::after {
    animation: none;
    background: linear-gradient(135deg, var(--c-primary), var(--c-accent));
  }

  /* The ID stops tilting outright; the sheen still resolves statically.
     The lace has to be pinned alongside it: JS sets --lift on hover whatever
     the motion preference, so leaving the straps and crimp reading it would
     float them off a card that is no longer moving. */
  .id__card,
  .id.is-live .id__card {
    animation: none;
    transform: none;
  }

  .id__strap--l {
    transform: rotate(calc(var(--lace-open) * -1)) scaleX(var(--lace-turn));
  }

  .id__strap--r {
    transform: rotate(var(--lace-open)) scaleX(var(--lace-turn));
  }

  .id__crimp {
    transform: translateZ(6px);
  }
}

/* --- Coarse pointers: no tilt, but keep the shine ---------------------- */

@media (hover: none) {
  .id__hit {
    cursor: default;
  }
}

/* --- Print ------------------------------------------------------------ */

@media print {
  .nav,
  .atmos,
  .skip-link,
  .scroll-cue {
    display: none !important;
  }

  body {
    background: #fff;
  }
}

/* ========================= QUICK CONTACT DIALOG ======================== */

.contact-dialog {
  width: min(34rem, calc(100% - 2rem));
  max-width: none;
  margin: auto;
  padding: 0;
  border: 0;
  border-radius: var(--r-card);
  background: transparent;
  color: var(--c-ink);
  overflow: visible;
}

.contact-dialog::backdrop {
  background: rgba(25, 28, 33, 0.58);
  backdrop-filter: blur(6px);
}

.contact-dialog[open] {
  animation: contact-dialog-in 220ms var(--ease-out) both;
}

.contact-dialog__panel {
  position: relative;
  padding: clamp(1.5rem, 5vw, 2.5rem);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-card);
  background: var(--c-surface);
  box-shadow: var(--shadow-xl);
}

.contact-dialog__close {
  position: absolute;
  top: 0.875rem;
  right: 0.875rem;
  display: grid;
  width: 2.25rem;
  height: 2.25rem;
  place-items: center;
  border: 1px solid var(--c-border);
  border-radius: var(--r-control);
  background: var(--c-surface-2);
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease-out),
    background var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out);
}

.contact-dialog__close:hover {
  transform: rotate(4deg);
  border-color: var(--c-border-orange);
  background: var(--c-orange-50);
}

.contact-dialog__close svg {
  width: 1.125rem;
  height: 1.125rem;
}

.contact-dialog__eyebrow {
  display: block;
  padding-right: 2.5rem;
  color: var(--c-orange-800);
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.contact-dialog__title {
  margin-top: 0.625rem;
  font-family: var(--font-display);
  font-size: var(--fs-display-md);
  line-height: var(--lh-display);
}

.contact-dialog__intro {
  margin-top: 0.625rem;
  color: var(--c-ink-soft);
  font-size: var(--fs-body-md);
}

.contact-dialog__email {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin-top: var(--s-3);
  padding: 1rem;
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-control);
  background: var(--c-orange-50);
  color: var(--c-ink);
  font-family: var(--font-ui);
  font-size: clamp(0.875rem, 3.5vw, 1rem);
  font-weight: 600;
  overflow-wrap: anywhere;
  transition: border-color var(--t-fast) var(--ease-out),
    box-shadow var(--t-fast) var(--ease-out);
}

.contact-dialog__email:hover,
.contact-dialog__email:focus-visible {
  border-color: var(--c-primary);
  box-shadow: var(--glow-primary);
}

.contact-dialog__email-label {
  color: var(--c-orange-800);
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.contact-dialog__actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.625rem;
  margin-top: 0.875rem;
}

@keyframes contact-dialog-in {
  from {
    opacity: 0;
    transform: translateY(12px) scale(0.98);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@media (max-width: 560px) {
  .contact-dialog__actions {
    grid-template-columns: 1fr;
  }
}

@media (prefers-reduced-motion: reduce) {
  .contact-dialog[open] {
    animation: none;
  }
}