/* ============================================================
   ENGINEERING PORTFOLIO · SHARED STYLES
   Direction C "Technical Instrument" · Bronze
   Last edited: 25-06-2026 (added base a{color:var(--bronze)} so all inline links are bronze site-wide; bumped to ?v=32. Prior: 11-06-2026 color correction: all colors re-expressed in CSS oklch(L% C H), each family hue-locked — neutrals 260, bronze 77.5, status 160; L+C kept at measured values so look/contrast match, only hue drift fixed. Pure black/white literals left as-is)
   Design system: assets/design-system.html

   CACHE-BUST: every time you change this file, bump the ?v=N number on
   the style.css AND js/main.js links on EVERY page that uses them
   (index.html, projects.html, contact.html, ...) (e.g. ?v=1 -> ?v=2), then push. That
   forces visitors to fetch the new version instead of a stale cached copy.
   ============================================================ */

:root{
  /* dark sections (default identity) — neutrals hue-locked to 260 */
  --ink:oklch(16.8% 0.012 260);
  --surface:oklch(20.5% 0.019 260);
  --hairline:oklch(27.5% 0.023 260);
  --text:oklch(93.8% 0.010 260);
  --muted:oklch(61.6% 0.023 260);

  /* light sections (for future pages) — neutrals hue-locked to 260 (pure white left as-is) */
  --blueprint:oklch(95.4% 0.006 260);
  --light-surface:#FFFFFF;
  --light-hairline:oklch(89.8% 0.013 260);
  --light-text:oklch(15.8% 0.007 260);
  --light-muted:oklch(49.9% 0.023 260);

  /* machined-bronze accent (shared) — hue-locked to 77.5 */
  --bronze:oklch(66.4% 0.083 77.5);
  --bright:oklch(75.1% 0.081 77.5);
  --btn-ink:oklch(20.9% 0.028 77.5);
  --accent-rgb:176,141,87; /* bronze, already on hue 77.5; rgb form kept for the rgba() glow */
  --ok:oklch(71.3% 0.119 160); /* status green, hue-locked to 160 */

  /* frosted-glass surface: faint bronze top sheen over the exact .58 base.
     reads as glass even where the mobile renderer drops backdrop-filter */
  --glass:
    linear-gradient(180deg, oklch(75.1% 0.081 77.5 / .06), oklch(75.1% 0.081 77.5 / 0) 46%),
    oklch(20.5% 0.019 260 / .58);

  /* fonts */
  --font-display:"Saira SemiCondensed",sans-serif;
  --font-body:"Inter",sans-serif;
  --font-mono:"JetBrains Mono",monospace;

  /* easing */
  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);

  /* shared placement: distance from top of viewport to the page eyebrow.
     One source of truth so every page pins the eyebrow at the same spot. */
  --page-top:100px;
}

*{box-sizing:border-box;margin:0;padding:0;}

html,body{height:100%;}
body{
  background:var(--ink);
  color:var(--text);
  font-family:var(--font-body);
  -webkit-font-smoothing:antialiased;
  overflow:hidden;
  cursor:default;
}

/* base link color: bronze everywhere. nav, buttons, cards set their own color and override this. */
a{color:var(--bronze);}

/* ============================================================
   STAGE
   ============================================================ */
.stage{
  position:relative;
  width:100vw;
  height:100vh;
  overflow:hidden;
  isolation:isolate;
}

/* ---- Layer 0: blueprint grid ---- */
.grid{
  position:absolute; inset:-6%;
  background-image:
    linear-gradient(var(--hairline) 1px, transparent 1px),
    linear-gradient(90deg, var(--hairline) 1px, transparent 1px);
  background-size:
    64px 64px,
    64px 64px;
  opacity:0;
  -webkit-mask-image:radial-gradient(120% 120% at 64% 46%, #000 30%, transparent 74%);
          mask-image:radial-gradient(120% 120% at 64% 46%, #000 30%, transparent 74%);
  animation:gridIn 1.4s var(--ease-out) .15s forwards;
}
@keyframes gridIn{from{opacity:0;}to{opacity:.10;}}

/* ---- Layer 0: corner glow ---- */
.glow{
  position:absolute;
  width:54vw; height:54vw;
  right:-8vw; top:-10vw;
  background:radial-gradient(circle at center, rgba(var(--accent-rgb),.15), rgba(var(--accent-rgb),.04) 44%, transparent 68%);
  filter:blur(10px);
  opacity:0;
  animation:glowIn 2s var(--ease-out) .3s forwards;
  pointer-events:none;
}
@keyframes glowIn{from{opacity:0;}to{opacity:1;}}

/* ---- ghost numeral ---- */
.ghost-mark{
  position:absolute;
  font-family:var(--font-display);
  font-weight:800;
  font-size:min(56vh,44vw);
  line-height:.8;
  color:#fff;
  opacity:0;
  left:1vw; bottom:-6vh;
  letter-spacing:-.04em;
  z-index:2;
  animation:ghostIn 1.6s var(--ease-out) .5s forwards;
  pointer-events:none;
  user-select:none;
}
@keyframes ghostIn{from{opacity:0;}to{opacity:.02;}}

/* ============================================================
   TOP NAV
   ============================================================ */
nav{
  position:absolute; top:0; left:0; right:0;
  display:flex; align-items:center; justify-content:space-between;
  padding:30px clamp(28px,5vw,72px);
  z-index:10;
}
.nav-l{display:flex; align-items:center; gap:16px; opacity:0; animation:fadeDown .8s var(--ease) .2s forwards;}
.monogram{
  font-family:var(--font-display); font-weight:700; font-size:18px;
  letter-spacing:.02em;
  border:1px solid var(--hairline);
  padding:6px 10px 5px;
  position:relative;
  color:var(--text); text-decoration:none;
  background:var(--glass);
  -webkit-backdrop-filter:blur(22px) saturate(1.15);
          backdrop-filter:blur(22px) saturate(1.15);
}
.monogram::after{
  content:""; position:absolute; left:0; bottom:-1px; height:2px; width:60%;
  background:linear-gradient(90deg,var(--bronze),transparent);
}
.nav-sub{font-family:var(--font-mono); font-size:10px; letter-spacing:.22em; text-transform:uppercase; color:var(--muted);}
.nav-r{display:flex; align-items:center; gap:clamp(8px,1vw,12px); opacity:0; animation:fadeDown .8s var(--ease) .34s forwards;}
.nav-r a{
  font-family:var(--font-mono); font-size:11px; letter-spacing:.16em; text-transform:uppercase;
  color:oklch(82.4% 0.017 260); text-decoration:none; position:relative; overflow:hidden;
  padding:9px 17px; border:1px solid var(--hairline); background:var(--glass);
  -webkit-backdrop-filter:blur(22px) saturate(1.15);
          backdrop-filter:blur(22px) saturate(1.15);
  transition:color .25s var(--ease), border-color .25s var(--ease), background .25s var(--ease);
}
.nav-r a::after{content:"";position:absolute;left:0;bottom:0;height:2px;width:0;background:linear-gradient(90deg,var(--bronze),transparent);transition:width .3s var(--ease);}
.nav-r a:hover{color:var(--text); border-color:var(--bronze); background:oklch(29.1% 0.035 260 / .62);}
.nav-r a:hover::after{width:100%;}

/* ============================================================
   CENTERPIECE: geodesic FEA-mesh sphere (canvas)
   ============================================================ */
.instrument{
  position:absolute;
  right:clamp(2vw,6vw,130px); top:50%;
  width:min(42vw,520px); aspect-ratio:1;
  transform:translateY(-50%);
  z-index:3;
  opacity:0;
  animation:instrIn 1.8s var(--ease-out) .6s forwards;
}
@keyframes instrIn{
  from{opacity:0;transform:translateY(-50%) scale(.9);}
  to{opacity:1;transform:translateY(-50%) scale(1);}
}
.instrument canvas{width:100%;height:100%;display:block;}

/* ============================================================
   CONTENT (identity block)
   ============================================================ */
.content{
  position:absolute;
  left:clamp(28px,5vw,72px); top:var(--page-top);
  z-index:9; max-width:min(58vw,720px);
}
.eyebrow{
  display:flex; align-items:center; gap:14px;
  font-family:var(--font-mono); font-size:12px; letter-spacing:.2em; text-transform:uppercase;
  color:var(--muted);
  margin-bottom:24px;
  opacity:0; animation:fadeUp .9s var(--ease) .7s forwards;
}
.eyebrow .num{color:var(--bronze);}

h1{
  font-family:var(--font-display);
  font-weight:700;
  line-height:.9;
  letter-spacing:-.01em;
  font-size:clamp(46px,9vw,118px);
  text-transform:uppercase;
}
h1 .line{display:block;overflow:hidden;}
h1 .word{display:inline-block;transform:translateY(110%);opacity:0;}
h1 .word.go{animation:wordUp 1s var(--ease-out) forwards;}
h1 .accent{color:var(--bronze);}
@keyframes wordUp{to{transform:translateY(0);opacity:1;}}

.subhead{
  margin-top:26px;
  font-family:var(--font-body); font-weight:400;
  font-size:clamp(15px,1.5vw,19px); line-height:1.55;
  color:var(--muted); max-width:46ch;
  opacity:0; animation:fadeUp .9s var(--ease) 1.25s forwards;
}
.subhead b{color:var(--text);font-weight:600;}

.cta-row{
  margin-top:36px; display:flex; gap:16px; flex-wrap:wrap;
  opacity:0; animation:fadeUp .9s var(--ease) 1.4s forwards;
}
.btn{
  font-family:var(--font-mono); font-size:12px; letter-spacing:.14em; text-transform:uppercase;
  padding:15px 26px; border:1px solid var(--hairline);
  color:var(--text); text-decoration:none; background:transparent;
  transition:border-color .3s var(--ease),color .3s var(--ease),background .3s var(--ease);
  display:inline-flex; align-items:center; gap:10px;
}
.btn .arrow{transition:transform .3s var(--ease);}
.btn:hover .arrow{transform:translateX(4px);}
.btn.primary{background:var(--bronze);border-color:var(--bronze);color:var(--btn-ink);}
.btn.primary:hover{background:var(--bright);border-color:var(--bright);}
.btn.ghost{
  background:var(--glass);
  -webkit-backdrop-filter:blur(22px) saturate(1.15);
          backdrop-filter:blur(22px) saturate(1.15);
}
.btn.ghost:hover{border-color:var(--bronze);color:var(--bright);}

/* ============================================================
   TELEMETRY STRIP
   ============================================================ */
.telemetry{
  position:absolute; left:0; right:0; bottom:0;
  display:flex; align-items:stretch;
  border-top:1px solid var(--hairline);
  background:var(--glass);
  z-index:9;
  opacity:0; animation:fadeUp 1s var(--ease) 1.5s forwards;
  -webkit-backdrop-filter:blur(22px) saturate(1.15);
          backdrop-filter:blur(22px) saturate(1.15);
}
.tele{
  flex:1; padding:18px clamp(20px,3vw,40px);
  border-right:1px solid var(--hairline);
  display:flex; flex-direction:column; gap:6px;
}
.tele:last-child{border-right:none;}
.tele .k{font-family:var(--font-mono); font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--muted);}
.tele .v{font-family:var(--font-display); font-weight:600; font-size:clamp(15px,1.6vw,21px); color:var(--text); letter-spacing:.01em;}
.tele .v small{font-family:var(--font-mono);font-size:11px;color:var(--muted);font-weight:400;}
.status{display:flex;align-items:center;gap:9px;}
.status .led{
  width:8px;height:8px;border-radius:50%;background:var(--ok);
  box-shadow:0 0 0 0 oklch(71.3% 0.119 160 / .55);
  animation:ledOk 2.4s ease-out infinite;
}
@keyframes ledOk{
  0%{box-shadow:0 0 0 0 oklch(71.3% 0.119 160 / .55);}
  70%{box-shadow:0 0 0 10px oklch(71.3% 0.119 160 / 0);}
  100%{box-shadow:0 0 0 0 oklch(71.3% 0.119 160 / 0);}
}

/* ============================================================
   SHARED ANIMS
   ============================================================ */
@keyframes fadeUp{from{opacity:0;transform:translateY(22px);}to{opacity:1;transform:translateY(0);}}
@keyframes fadeDown{from{opacity:0;transform:translateY(-16px);}to{opacity:1;transform:translateY(0);}}
@keyframes fadeIn{from{opacity:0;}to{opacity:1;}}

/* ============================================================
   RESPONSIVE  (breakpoints: 1080 / 720 / 560 / 480)
   ============================================================ */
@media (max-width:1080px){
  .instrument{width:min(60vw,440px);right:-6vw;opacity:.5;}
  .content{max-width:78vw;}
}
@media (max-width:720px){
  body{overflow-y:auto;}
  .stage{height:auto; min-height:100svh; display:flex; flex-direction:column;}
  /* phone: center the mesh both axes (was anchored off the right edge).
     kill the desktop instrIn animation here: its forwards-filled keyframe
     locks transform:translateY(-50%), which overrode transform:none and rode
     the mesh up to the top. animation:none lets inset/margin centering hold.
     softened and dimmed so the text reads cleanly over it */
  .instrument{inset:0; margin:auto; transform:none; animation:none; width:min(82vw,440px); height:min(82vw,440px); opacity:.34; filter:blur(3px);}
  .content{
    position:static; transform:none; max-width:none;
    flex:1; display:flex; flex-direction:column; justify-content:flex-start;
    margin:0; padding:var(--page-top) 24px 44px;
  }
  .telemetry{position:static; flex-wrap:wrap;}
  .tele{flex:1 1 50%;border-bottom:1px solid var(--hairline);}
  .tele:nth-child(2n){border-right:none;}
  /* keep all three tabs reachable on phone, drop the decorative bits */
  .nav-sub{display:none;}
  .nav-r{gap:8px;}
  /* phone: kill backdrop-filter. iOS renders it then drops it on repaint,
     and over the bright corner glow the blur lightens the buttons toward
     white. A stable dark translucent panel instead: never white, no
     flicker, grid still reads faintly through. */
  .monogram, .nav-r a, .btn.ghost, .telemetry{
    background:oklch(20.0% 0.019 260 / .72);
    -webkit-backdrop-filter:none;
            backdrop-filter:none;
    border-color:oklch(63.9% 0.065 260 / .16);
  }
}
@media (max-width:560px){
  /* nav shrinks here, so the shared top offset shrinks with it (both pages) */
  :root{--page-top:88px;}
  nav{padding:22px 20px;}
  .instrument{opacity:.24;}
  .content{padding:var(--page-top) 20px 40px;}
  .subhead{margin-top:20px;}
  .cta-row{margin-top:28px;}
  .nav-r{gap:7px;}
  .nav-r a{font-size:10px;letter-spacing:.12em;padding:8px 12px;}
}
@media (max-width:480px){
  .tele{flex:1 1 50%;}
  h1{font-size:clamp(40px,13vw,64px);}
  .btn{flex:1 1 100%;justify-content:center;}
}

/* ============================================================
   CONTACT PAGE  (contact.html · body.page-contact)
   DESKTOP base here; PHONE layout in the media blocks below.
   Scoped to .page-contact / contact classes so the hero is untouched.
   ============================================================ */
/* the hero locks overflow:hidden; the contact page is content-led, so let
   it grow and scroll if it ever exceeds the viewport (scoped to this body) */
body.page-contact{overflow:hidden auto;}
body.page-contact .stage{height:auto; min-height:100vh;}

/* current-page tab */
.nav-r a.active{color:var(--text); border-color:var(--bronze); background:oklch(29.1% 0.035 260 / .62);}
.nav-r a.active::after{width:100%;}

/* DESKTOP/LAPTOP: top-anchored identity column, left-aligned like the hero.
   Eyebrow pinned --page-top below the top, the SAME shared token the hero's
   .content uses, so 04 and 01 land at an identical Y at every viewport height.
   Both pages pin from the top now: no vh, no bottom-anchoring, no drift. */
.contact-wrap{
  position:relative; z-index:9;
  min-height:100vh;
  display:flex; flex-direction:column; justify-content:flex-start;
  padding:var(--page-top) clamp(28px,5vw,72px) 56px;
  max-width:min(92vw,760px);
}
/* heading: a step below the hero name, same word-reveal (driven by main.js) */
h1.contact-h1{font-size:clamp(44px,7.5vw,92px);}
.contact-wrap .subhead{max-width:42ch; animation-delay:1.05s;}

/* ---- channel link cards (Email / LinkedIn / Instagram) ---- */
.channels{
  margin-top:30px;
  display:flex; flex-direction:column; gap:12px;
  max-width:min(92vw,560px);
}
.channel{
  display:grid; grid-template-columns:30px 1fr auto; align-items:center; gap:20px;
  padding:16px clamp(20px,2.4vw,28px);
  border:1px solid var(--hairline);
  background:var(--glass);
  -webkit-backdrop-filter:blur(22px) saturate(1.15);
          backdrop-filter:blur(22px) saturate(1.15);
  color:var(--text); text-decoration:none;
  position:relative; overflow:hidden;
  opacity:0; animation:fadeIn .8s var(--ease) forwards;
  transition:border-color .28s var(--ease), background .28s var(--ease), transform .28s var(--ease);
}
.channels .channel:nth-child(1){animation-delay:1.25s;}
.channels .channel:nth-child(2){animation-delay:1.38s;}
.channels .channel:nth-child(3){animation-delay:1.51s;}
.channel::after{content:"";position:absolute;left:0;bottom:0;height:2px;width:0;background:linear-gradient(90deg,var(--bronze),transparent);transition:width .3s var(--ease);}
.channel:hover{border-color:var(--bronze); background:oklch(29.1% 0.035 260 / .62); transform:translateX(5px);}
.channel:hover::after{width:100%;}
.channel:focus-visible{outline:2px solid var(--bright); outline-offset:3px;}
/* glowy bronze brand icon (replaces the old text label) */
.ch-ico{width:24px; height:24px; display:flex; align-items:center; justify-content:center; color:var(--bright);
  filter:drop-shadow(0 0 5px oklch(75.1% 0.081 77.5 / .45)); transition:filter .28s var(--ease);}
.ch-ico svg{width:100%; height:100%; display:block; fill:currentColor;}
.channel:hover .ch-ico{filter:drop-shadow(0 0 9px oklch(75.1% 0.081 77.5 / .8));}
.ch-v{font-family:var(--font-display); font-weight:600; font-size:clamp(16px,1.7vw,20px); letter-spacing:.01em; color:var(--text); overflow-wrap:anywhere;}
.channel:hover .ch-v{color:var(--bright);}
.ch-arrow{color:var(--bronze); font-size:18px; transition:transform .28s var(--ease);}
.channel:hover .ch-arrow{transform:translateX(4px);}

/* ---- PHONE layout: dedicated, not just a collapse ---- */
@media (max-width:720px){
  body.page-contact .stage{min-height:100svh;}
  .contact-wrap{
    min-height:0; flex:1; justify-content:flex-start;
    padding:var(--page-top) 24px 56px; max-width:none;
  }
  .channels{margin-top:32px; max-width:none;}
  /* match the hero's phone fix: kill backdrop-filter (iOS whitens it over
     the glow), use a stable dark panel instead */
  .channel{
    background:oklch(20.0% 0.019 260 / .72);
    -webkit-backdrop-filter:none;
            backdrop-filter:none;
    border-color:oklch(63.9% 0.065 260 / .16);
  }
}
@media (max-width:560px){
  .contact-wrap{padding:var(--page-top) 20px 48px;}
  h1.contact-h1{font-size:clamp(40px,12vw,60px);}
  /* icon + value + arrow stay in one row; long email wraps (overflow-wrap) */
  .channel{gap:14px; padding:16px 18px;}
  .ch-v{font-size:clamp(15px,4.6vw,18px);}
  .channel:hover{transform:none;}
}

/* ============================================================
   PROJECTS PAGE  (projects.html · body.page-projects)
   Card grid. Scoped to .page-projects / .proj-* so other pages
   are untouched. Header reuses the shared eyebrow + --page-top
   so 02 lands at the same spot as 01 / 04 (layout consistency rule).
   ============================================================ */
/* content-led page: let it grow and scroll past the viewport */
body.page-projects{overflow:hidden auto;}
body.page-projects .stage{height:auto; min-height:100vh;}

.projects-wrap{
  position:relative; z-index:9;
  min-height:100vh;
  padding:var(--page-top) clamp(28px,5vw,72px) 80px;
  /* full width: header text stays left (via padding), the card grid below
     centers itself on the page (see .proj-grid margin:auto) */
}
h1.projects-h1{font-size:clamp(44px,7.5vw,92px);}
.projects-wrap .subhead{max-width:54ch; animation-delay:1.05s;}
.proj-note{display:block; margin-top:8px; font-family:var(--font-mono); font-size:11px; letter-spacing:.14em; text-transform:uppercase; color:var(--bronze);}

/* the grid: centered on the page (margin auto), header above stays left.
   auto-fill + a 300px floor caps it at 3 columns in the 1100px band
   (4 cols would need 1272px), so it steps 3 -> 2 -> 1 cleanly */
.proj-grid{
  margin:40px auto 0;
  width:100%; max-width:1100px;
  display:grid;
  grid-template-columns:repeat(auto-fill, minmax(300px, 1fr));
  gap:clamp(16px,1.8vw,24px);
}

/* card: same frosted-glass + hover language as the contact channels */
.proj-card{
  display:flex; flex-direction:column;
  border:1px solid var(--hairline);
  background:var(--glass);
  -webkit-backdrop-filter:blur(22px) saturate(1.15);
          backdrop-filter:blur(22px) saturate(1.15);
  color:var(--text); text-decoration:none;
  position:relative; overflow:hidden;
  opacity:0; animation:fadeUp .8s var(--ease) forwards;
  transition:border-color .3s var(--ease), transform .3s var(--ease), box-shadow .3s var(--ease);
}
.proj-grid .proj-card:nth-child(1){animation-delay:1.20s;}
.proj-grid .proj-card:nth-child(2){animation-delay:1.30s;}
.proj-grid .proj-card:nth-child(3){animation-delay:1.40s;}
.proj-grid .proj-card:nth-child(4){animation-delay:1.50s;}
.proj-grid .proj-card:nth-child(5){animation-delay:1.60s;}
.proj-grid .proj-card:nth-child(6){animation-delay:1.70s;}
.proj-card:hover{
  border-color:var(--bronze);
  transform:translateY(-5px);
  box-shadow:0 16px 40px -18px oklch(66.4% 0.083 77.5 / .55);
}
.proj-card:focus-visible{outline:2px solid var(--bright); outline-offset:3px;}

/* ---- blueprint image zone ---- */
.proj-img{
  position:relative;
  aspect-ratio:5/4;
  border-bottom:1px solid var(--hairline);
  overflow:hidden;
  background:
    linear-gradient(oklch(66.4% 0.083 77.5 / .10) 1px, transparent 1px),
    linear-gradient(90deg, oklch(66.4% 0.083 77.5 / .10) 1px, transparent 1px),
    radial-gradient(120% 120% at 70% 20%, oklch(66.4% 0.083 77.5 / .12), transparent 70%),
    var(--surface);
  background-size:22px 22px, 22px 22px, 100% 100%, 100% 100%;
}
.schem{
  position:absolute; inset:0; margin:auto;
  width:58%; height:58%;
  stroke:var(--bright); stroke-width:1.5;
  stroke-linecap:round; stroke-linejoin:round;
  filter:drop-shadow(0 0 5px oklch(75.1% 0.081 77.5 / .45));
  transition:transform .35s var(--ease), filter .3s var(--ease);
}
.proj-card:hover .schem{transform:scale(1.07); filter:drop-shadow(0 0 10px oklch(75.1% 0.081 77.5 / .85));}
/* real photo / render filling a card thumbnail */
.proj-img img{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; display:block;
  transition:transform .4s var(--ease);
}
.proj-card:hover .proj-img img{transform:scale(1.05);}

/* ---- card body ---- */
.proj-body{
  display:flex; flex-direction:column;
  padding:18px clamp(18px,1.8vw,22px) 20px;
  position:relative;
}
.proj-tag{
  align-self:flex-start;
  font-family:var(--font-mono); font-size:10px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--bright);
  border:1px solid oklch(75.1% 0.081 77.5 / .4); border-radius:999px;
  padding:5px 11px; margin-bottom:14px;
  background:oklch(66.4% 0.083 77.5 / .08);
  box-shadow:0 0 12px -2px oklch(75.1% 0.081 77.5 / .4), inset 0 0 8px -4px oklch(75.1% 0.081 77.5 / .5);
}
.proj-name{
  font-family:var(--font-display); font-weight:600; font-size:clamp(19px,1.8vw,23px);
  letter-spacing:.01em; line-height:1.1; color:var(--text);
  transition:color .28s var(--ease);
}
.proj-card:hover .proj-name{color:var(--bright);}
.proj-desc{
  margin-top:9px;
  font-family:var(--font-body); font-size:14px; line-height:1.55; color:var(--muted);
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
}
.proj-arrow{
  margin-top:14px; align-self:flex-start;
  color:var(--bronze); font-size:18px;
  transition:transform .28s var(--ease);
}
.proj-card:hover .proj-arrow{transform:translateX(5px);}

/* ---- projects phone layout: keep the eyebrow on the shared offset ---- */
@media (max-width:720px){
  body.page-projects .stage{min-height:100svh;}
  .projects-wrap{padding:var(--page-top) 24px 56px; max-width:none;}
  .proj-grid{margin-top:32px;}
  /* match the hero/contact phone fix: drop backdrop-filter, stable dark panel */
  .proj-card{
    background:oklch(20.0% 0.019 260 / .72);
    -webkit-backdrop-filter:none;
            backdrop-filter:none;
    border-color:oklch(63.9% 0.065 260 / .16);
  }
}
@media (max-width:560px){
  .projects-wrap{padding:var(--page-top) 20px 48px;}
  h1.projects-h1{font-size:clamp(40px,12vw,60px);}
  /* true phones: one project per row, always (don't rely on auto-fill math) */
  .proj-grid{grid-template-columns:1fr;}
  .proj-card:hover{transform:none;}
}

/* ============================================================
   PROJECT PAGE  (projects/<name>/index.html · body.page-project)
   Per-project detail page. Fluid composition (no fixed template),
   but built from the shared atoms. Scoped to .page-project / .pp-*.
   Eyebrow + heading reuse --page-top so the lead block lands at the
   same Y as every other page (layout consistency rule).
   Placeholders (.pp-ph) stand in for real media until assets land.
   ============================================================ */
body.page-project{overflow:hidden auto;}
body.page-project .stage{height:auto; min-height:100vh;}

.pp-wrap{
  position:relative; z-index:9;
  width:100%; max-width:1040px; margin:0 auto;
  padding:var(--page-top) clamp(28px,5vw,72px) 96px;
}

/* back link above the eyebrow */
.pp-back{
  display:inline-flex; align-items:center; gap:8px;
  font-family:var(--font-mono); font-size:11px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--muted); text-decoration:none;
  margin-bottom:20px;
  opacity:0; animation:fadeUp .8s var(--ease) .55s forwards;
  transition:color .25s var(--ease);
}
.pp-back span{color:var(--bronze); transition:transform .25s var(--ease);}
.pp-back:hover{color:var(--text);}
.pp-back:hover span{transform:translateX(-4px);}

h1.pp-h1{font-size:clamp(42px,7.5vw,96px); max-width:14ch;}

.pp-lede{
  margin-top:24px; max-width:60ch;
  font-family:var(--font-body); font-weight:400;
  font-size:clamp(16px,1.7vw,21px); line-height:1.55; color:var(--muted);
  opacity:0; animation:fadeUp .9s var(--ease) 1.05s forwards;
}
.pp-lede b{color:var(--text); font-weight:600;}
.pp-note{display:block; margin-top:10px; font-family:var(--font-mono); font-size:11px; letter-spacing:.14em; text-transform:uppercase; color:var(--bronze);}

/* ---- placeholder media block ---- */
.pp-ph{
  position:relative; width:100%;
  display:flex; align-items:center; justify-content:center;
  margin:0; overflow:hidden;
  border:1px dashed oklch(75.1% 0.081 77.5 / .34);
  background:
    linear-gradient(oklch(66.4% 0.083 77.5 / .08) 1px, transparent 1px),
    linear-gradient(90deg, oklch(66.4% 0.083 77.5 / .08) 1px, transparent 1px),
    radial-gradient(120% 120% at 70% 18%, oklch(66.4% 0.083 77.5 / .10), transparent 70%),
    var(--surface);
  background-size:26px 26px, 26px 26px, 100% 100%, 100% 100%;
}
.pp-hero{margin-top:40px; opacity:0; animation:fadeUp .9s var(--ease) 1.2s forwards;}
/* real media (img / video) filling a media block: solid frame, no placeholder grid */
.pp-ph.pp-filled{border-style:solid; border-color:var(--hairline); background:var(--ink);}
.pp-ph img, .pp-ph video{position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block;}

/* ---- swipeable image carousel (any .pp-carousel) ---- */
.pp-carousel{cursor:pointer;}
.pp-carousel .pp-slide{opacity:0; transition:opacity .45s ease; z-index:1;}
.pp-carousel .pp-slide.is-active{opacity:1; z-index:2;}
.pp-carousel-btn{
  position:absolute; top:50%; transform:translateY(-50%); z-index:4;
  width:42px; height:42px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  border:1px solid var(--hairline); background:oklch(16.8% 0.012 260 / .5);
  color:var(--text); font-size:0; line-height:0;
  cursor:pointer; -webkit-backdrop-filter:blur(4px); backdrop-filter:blur(4px);
  transition:background .2s, border-color .2s;
}
/* chevron drawn from borders, so it stays perfectly centered at any size */
.pp-carousel-btn::before{
  content:""; display:block; width:9px; height:9px;
  border-top:2px solid var(--text); border-right:2px solid var(--text);
  transition:border-color .2s;
}
.pp-carousel-btn.pp-next::before{transform:translateX(-2px) rotate(45deg);}
.pp-carousel-btn.pp-prev::before{transform:translateX(2px) rotate(-135deg);}
.pp-carousel-btn:hover{background:oklch(16.8% 0.012 260 / .82); border-color:var(--bronze);}
.pp-carousel-btn:hover::before{border-color:var(--bronze);}
.pp-carousel-btn.pp-prev{left:14px;}
.pp-carousel-btn.pp-next{right:14px;}
.pp-carousel-dots{
  position:absolute; left:0; right:0; bottom:14px; z-index:4;
  display:flex; gap:8px; justify-content:center;
}
.pp-dot{
  width:7px; height:7px; padding:0; border-radius:50%; cursor:pointer;
  border:1px solid oklch(93.8% 0.010 260 / .5); background:transparent;
  transition:background .2s, border-color .2s;
}
.pp-dot.is-active{background:var(--bronze); border-color:var(--bronze);}
@media (max-width:560px){
  .pp-carousel-btn{width:36px; height:36px;}
}

/* ---- meta strip ---- */
.pp-meta{
  margin:24px 0 0;
  display:grid; grid-template-columns:repeat(4,1fr);
  border:1px solid var(--hairline);
  background:var(--glass);
  -webkit-backdrop-filter:blur(22px) saturate(1.15);
          backdrop-filter:blur(22px) saturate(1.15);
}
.pp-meta-cell{padding:16px clamp(14px,1.6vw,22px); border-right:1px solid var(--hairline); display:flex; flex-direction:column; gap:6px;}
.pp-meta-cell:last-child{border-right:none;}
.pp-meta dt{font-family:var(--font-mono); font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--muted);}
.pp-meta dd{font-family:var(--font-display); font-weight:600; font-size:clamp(14px,1.4vw,18px); color:var(--text); letter-spacing:.01em;}

/* ---- text sections ---- */
.pp-section{margin-top:clamp(40px,5vw,64px);}
.pp-h2{
  font-family:var(--font-display); font-weight:600; text-transform:uppercase;
  font-size:clamp(20px,2.2vw,28px); letter-spacing:.01em; color:var(--text);
  display:flex; align-items:baseline; gap:14px; margin-bottom:18px;
}
.pp-h2-num{font-family:var(--font-mono); font-size:13px; letter-spacing:.12em; color:var(--bronze);}
.pp-body{
  font-family:var(--font-body); font-size:clamp(15px,1.4vw,17px); line-height:1.7;
  color:oklch(77.4% 0.019 260); max-width:64ch; margin-top:14px;
}
.pp-body:first-of-type{margin-top:0;}
.pp-body b{color:var(--text); font-weight:600;}

.pp-steps{list-style:none; max-width:66ch; display:flex; flex-direction:column; gap:14px;}
.pp-steps li{
  position:relative; padding-left:26px;
  font-family:var(--font-body); font-size:clamp(15px,1.4vw,17px); line-height:1.6; color:oklch(77.4% 0.019 260);
}
.pp-steps li::before{
  content:""; position:absolute; left:0; top:.55em;
  width:8px; height:8px; border:1px solid var(--bronze); transform:rotate(45deg);
  background:oklch(66.4% 0.083 77.5 / .18);
}
.pp-steps b{color:var(--text); font-weight:600;}

/* ---- two-column block ---- */
.pp-cols{
  margin-top:clamp(40px,5vw,64px);
  display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.15fr);
  gap:clamp(24px,3vw,44px); align-items:center;
}
.pp-cols-text .pp-h2{margin-bottom:16px;}

/* ---- stats ---- */
.pp-stats{display:grid; grid-template-columns:repeat(4,1fr); gap:clamp(12px,1.6vw,20px);}
.pp-stat{
  border:1px solid var(--hairline); background:var(--surface);
  padding:20px clamp(14px,1.6vw,20px);
  display:flex; flex-direction:column; gap:8px;
}
.pp-stat-v{font-family:var(--font-display); font-weight:700; font-size:clamp(28px,3.6vw,42px); line-height:1; color:var(--bright);}
.pp-stat-k{font-family:var(--font-mono); font-size:10px; letter-spacing:.16em; text-transform:uppercase; color:var(--muted); line-height:1.4;}
.pp-stat-k small{display:block; color:var(--muted); opacity:.7;}

/* ---- gallery ---- */
.pp-gallery{display:grid; grid-template-columns:repeat(2,1fr); gap:clamp(16px,2vw,24px);}

/* ---- justified gallery: rows where each figure's width tracks its aspect ratio,
   so mixed ratios share a common row height instead of being boxed into equal cells ---- */
.pp-jgrid{display:flex; flex-direction:column; gap:clamp(16px,2vw,24px);}
.pp-jrow{display:flex; gap:clamp(16px,2vw,24px); align-items:flex-start;}
.pp-jrow > .pp-ph{flex-basis:0; min-width:0;}

/* ---- parts legend (section 02) ---- */
.pp-legend{display:grid; grid-template-columns:repeat(4,1fr); gap:clamp(12px,1.6vw,18px); margin-top:28px;}
.pp-legend-box{
  border:1px solid var(--hairline); background:var(--surface);
  padding:18px clamp(14px,1.6vw,20px);
  display:flex; flex-direction:column; gap:6px;
}
.pp-legend-n{font-family:var(--font-mono); font-size:12px; font-weight:700; letter-spacing:.12em; color:var(--bright);}
.pp-legend-t{font-family:var(--font-display); font-weight:700; font-size:18px; color:oklch(92% 0.01 260);}
.pp-legend-d{font-family:var(--font-body); font-size:14px; line-height:1.5; color:var(--muted);}

/* ---- fault tree analysis (section 06): framed light diagrams ---- */
.pp-fta-lead{
  margin-top:32px; background:#f3f5f7; border:1px solid var(--hairline);
  padding:clamp(16px,2.2vw,30px);
}
.pp-fta-lead img{display:block; width:100%; height:auto;}
.pp-fta-cap{
  font-family:var(--font-mono); font-size:12px; letter-spacing:.1em;
  color:var(--muted); margin-top:14px;
}
.pp-mosaic{columns:2; column-gap:clamp(12px,1.6vw,18px); margin-top:18px;}
.pp-mosaic figure{
  break-inside:avoid; margin:0 0 clamp(12px,1.6vw,18px);
  background:#f3f5f7; border:1px solid var(--hairline);
  padding:clamp(12px,1.6vw,22px);
}
.pp-mosaic img{display:block; width:100%; height:auto;}

/* ---- footer cta ---- */
.pp-cta{
  margin-top:clamp(48px,6vw,80px); padding-top:32px;
  border-top:1px solid var(--hairline);
  display:flex; gap:16px; flex-wrap:wrap;
}

/* ---- project page phone layout ---- */
@media (max-width:720px){
  body.page-project .stage{min-height:100svh;}
  .pp-wrap{padding:var(--page-top) 24px 72px; max-width:none;}
  .pp-cols{grid-template-columns:1fr; gap:24px;}
  .pp-meta{grid-template-columns:repeat(2,1fr);}
  .pp-meta-cell:nth-child(2n){border-right:none;}
  .pp-meta-cell:nth-child(1),.pp-meta-cell:nth-child(2){border-bottom:1px solid var(--hairline);}
  .pp-stats{grid-template-columns:repeat(2,1fr);}
  .pp-legend{grid-template-columns:repeat(2,1fr);}
  /* match the shared phone fix: drop backdrop-filter for a stable dark panel */
  .pp-meta{background:oklch(20.0% 0.019 260 / .72); -webkit-backdrop-filter:none; backdrop-filter:none; border-color:oklch(63.9% 0.065 260 / .16);}
}
@media (max-width:560px){
  .pp-wrap{padding:var(--page-top) 20px 56px;}
  h1.pp-h1{font-size:clamp(38px,12vw,58px);}
  .pp-gallery{grid-template-columns:1fr;}
  .pp-jrow{flex-direction:column;}
  .pp-jrow > .pp-ph{flex-basis:auto;}
  .pp-legend{grid-template-columns:1fr;}
  .pp-mosaic{columns:1;}
  .pp-cta .btn{flex:1 1 100%; justify-content:center;}
}

@media (prefers-reduced-motion:reduce){
  *{animation-duration:.001s!important;animation-iteration-count:1!important;}
  h1 .word{transform:none;opacity:1;}
  .pp-hero{opacity:1;}
}
