/* ===========================================================
   Cinematic Silver Portfolio
   =========================================================== */

:root{
  /* Neutral near-black, not the navy the gold version used -- the silver
     reads as tinted against anything with a hue in it. */
  --bg:            #0a0a0c;
  --bg-raise:      #121216;
  --bg-card:       rgba(255,255,255,0.03);
  --line:          rgba(var(--accent-rgb),0.16);
  --line-soft:     rgba(255,255,255,0.08);
  --text:          #f5f6f8;
  --text-dim:      rgba(245,246,248,0.62);
  --text-mute:     rgba(245,246,248,0.34);

  /* Accent. Every glow, border and hover in the file resolves through
     these four, so re-theming is a four-line edit rather than a hunt. */
  --accent-rgb:    201,204,211;
  /* One knob for the whole hero light rig -- beam, floor glow and both
     hazes scale off it. Raise for a brighter stage, lower for a subtler one. */
  --beam:          1.35;
  --accent-1:      #f2f4f7;
  --accent-2:      #c9ccd3;
  --accent-3:      #8b8f99;
  --accent-grad:   linear-gradient(120deg, #ffffff, #cbced5 55%, #8b8f99);
  --font-display:  'Archivo Black', 'Inter', sans-serif;
  --font-body:     'Inter', sans-serif;
  --font-mono:     'JetBrains Mono', monospace;
  --ease:          cubic-bezier(.19,1,.22,1);
  --container:     1180px;
}

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

html{ scroll-behavior:smooth; background:var(--bg); }

body{
  background:var(--bg);
  color:var(--text);
  font-family:var(--font-body);
  overflow-x:hidden;
  -webkit-font-smoothing:antialiased;
}

a{ color:inherit; text-decoration:none; }
img{ max-width:100%; display:block; }
ul{ list-style:none; }

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

.gradient-text{
  background:var(--accent-grad);
  -webkit-background-clip:text;
  background-clip:text;
  -webkit-text-fill-color:transparent;
  /* when the span wraps, give each line its own sweep — otherwise the
     gradient stretches across both and the last line lands on the dim end */
  -webkit-box-decoration-break:clone;
          box-decoration-break:clone;
}

/* film grain */
.noise-overlay{
  position:fixed; inset:0; z-index:9998; pointer-events:none;
  opacity:0.035; mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

.scroll-progress{
  position:fixed; top:0; left:0; height:2px; width:0%;
  background:var(--accent-grad); z-index:9999; transition:width .1s linear;
}

/* reveal helpers driven by JS/GSAP */
.reveal-mask{ overflow:hidden; display:block; }
.reveal-up{ display:inline-block; transform:translateY(115%); }
.reveal-el{
  opacity:0; transform:translateY(40px);
  transition:opacity .8s var(--ease), transform .8s var(--ease);
}
.reveal-el.in-view{ opacity:1; transform:translateY(0); }

/* ============== NAV ============== */
#navbar{
  position:fixed; top:0; left:0; width:100%; z-index:1000;
  display:flex; align-items:center; justify-content:space-between;
  padding:1.6rem 2.5rem;
  transition:all .4s var(--ease);
  border-bottom:1px solid transparent;
}
#navbar.scrolled{
  padding:1rem 2.5rem;
  background:rgba(10,14,23,0.82);
  backdrop-filter:blur(16px);
  border-bottom:1px solid var(--line-soft);
}
/* The full name is 14 characters where the monogram was 2, so it is set
   smaller and tighter than the old "MA." — at 1.2rem/.05em Archivo Black it
   ran ~210px and crowded the links on the way down to the burger. */
.nav-logo{
  font-family:var(--font-display);
  font-size:clamp(.82rem, 1.05vw, .98rem);
  letter-spacing:.02em; white-space:nowrap;
}
.nav-links{ display:flex; gap:2.4rem; }
.nav-link{
  font-family:var(--font-mono); font-size:.72rem; letter-spacing:.14em;
  text-transform:uppercase; color:var(--text-dim); position:relative; padding-bottom:4px;
  transition:color .3s ease;
}
.nav-link::after{
  content:''; position:absolute; left:0; bottom:0; width:0; height:1px;
  background:var(--accent-grad); transition:width .35s var(--ease);
}
.nav-link:hover, .nav-link.active{ color:var(--text); }
.nav-link:hover::after, .nav-link.active::after{ width:100%; }

.nav-cta{
  font-family:var(--font-mono); font-size:.72rem; letter-spacing:.1em;
  border:1px solid var(--line); border-radius:40px; padding:.6rem 1.3rem;
  color:var(--accent-1); transition:all .3s ease; display:flex; align-items:center; gap:.4rem;
}
.nav-cta:hover{ background:rgba(var(--accent-rgb),0.1); border-color:var(--accent-2); }

.nav-burger{
  display:none; flex-direction:column; gap:5px; background:none; border:none; cursor:pointer; z-index:1001;
}
.nav-burger span{ width:24px; height:1.5px; background:var(--text); transition:.3s var(--ease); }
.nav-burger.active span:nth-child(1){ transform:translateY(6.5px) rotate(45deg); }
.nav-burger.active span:nth-child(2){ opacity:0; }
.nav-burger.active span:nth-child(3){ transform:translateY(-6.5px) rotate(-45deg); }

.mobile-menu{
  position:fixed; inset:0; background:rgba(8,10,17,0.97); backdrop-filter:blur(20px);
  z-index:999; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2rem;
  opacity:0; visibility:hidden; transition:all .4s var(--ease);
}
.mobile-menu.active{ opacity:1; visibility:visible; }
.mobile-link{ font-family:var(--font-display); font-size:1.6rem; color:var(--text-dim); }
.mobile-link:hover{ color:var(--accent-2); }

/* ============== HERO ============== */
.hero{
  position:relative; min-height:100vh; display:flex; align-items:center;
  overflow:hidden; padding:7rem 2.5rem 4rem;
}
.hero-bg{ position:absolute; inset:0; z-index:0; }

/* Light rig.  Every emitter blends additively -- where two overlap they add
   up like real light instead of one painting flat grey over the other. */
.spotlight-cone, .key-glow, .floor-glow, .haze{ mix-blend-mode:screen; }

/* The shaft.  Wide soft shoulders around a narrow core, then blurred hard:
   a sharp-edged wedge reads as a shape, a blurred one reads as light. */
.spotlight-cone{
  position:absolute; top:-14%; right:6%; width:66%; height:132%;
  background:conic-gradient(from 200deg at 50% 0%,
    transparent 4deg,
    rgba(var(--accent-rgb),calc(0.04 * var(--beam))) 10deg,
    rgba(var(--accent-rgb),calc(0.26 * var(--beam))) 17deg,
    rgba(var(--accent-rgb),calc(0.34 * var(--beam))) 21deg,
    rgba(var(--accent-rgb),calc(0.09 * var(--beam))) 31deg,
    transparent 47deg);
  filter:blur(20px);
}

/* Key light sitting behind the figure, so he is lit rather than pasted on. */
.key-glow{
  position:absolute; top:8%; right:13%; width:38%; height:76%;
  background:radial-gradient(ellipse at 50% 42%,
    rgba(var(--accent-rgb),calc(0.20 * var(--beam))) 0%,
    rgba(var(--accent-rgb),calc(0.07 * var(--beam))) 38%,
    transparent 68%);
  filter:blur(46px);
}

.floor-glow{
  position:absolute; bottom:-9%; right:1%; width:54%; height:36%;
  background:radial-gradient(ellipse at center, rgba(var(--accent-rgb),calc(0.26 * var(--beam))), transparent 72%);
  filter:blur(38px);
}

.haze{ position:absolute; border-radius:50%; filter:blur(80px); opacity:.6; }
.haze-1{ width:460px; height:460px; top:6%; right:12%; background:radial-gradient(circle, rgba(var(--accent-rgb),calc(0.16 * var(--beam))), transparent 70%); }
.haze-2{ width:340px; height:340px; bottom:4%; left:6%; background:radial-gradient(circle, rgba(var(--accent-rgb),calc(0.07 * var(--beam))), transparent 70%); }

/* Corners pulled down.  Without this the beam has nothing to be brighter
   than -- contrast is what makes a glow look like a glow. */
.hero-vignette{
  position:absolute; inset:0; pointer-events:none;
  background:radial-gradient(ellipse at 64% 42%, transparent 26%, rgba(0,0,0,0.50) 78%, rgba(0,0,0,0.72) 100%);
}

.hero-inner{
  position:relative; z-index:2; max-width:var(--container); margin:0 auto; width:100%;
  display:grid; grid-template-columns:1.15fr .85fr; gap:3rem; align-items:center;
}

/* The tag centres over the headline, not over the column.  Wrapping just the
   tag and the headline gives the head a box exactly as wide as the longest
   headline line (each line is its own block, so fit-content hugs the widest).
   Shrinking .hero-copy instead looked right on desktop but hugged the
   description on mobile, where the description is the wider element.

   The headline size lives here rather than on .hero-title because the tag
   needs it too -- see the margin-right on .hero-tag. */
.hero-head{
  width:fit-content; max-width:100%;
  --hero-title-fs:clamp(2.6rem, 5.6vw, 4.6rem);
}

/* Centred on "I BUILD AI", the line directly beneath it, not on the wider
   "THAT SHIPS." that sets the box width.  The gap between the two lines is a
   fixed fraction of the display font (same face, same strings), so trimming
   that difference off the right of the tag's box walks its centre onto line
   one at every breakpoint. */
.hero-tag{
  display:flex; align-items:center; justify-content:center;
  font-family:var(--font-mono); font-size:.72rem; letter-spacing:.16em;
  color:var(--accent-1); margin-bottom:1.6rem;
  margin-right:calc(var(--hero-title-fs) * 1.393);
}

.hero-title{
  font-family:var(--font-display); text-transform:uppercase;
  font-size:var(--hero-title-fs); line-height:1.02; margin-bottom:1.6rem;
}
.hero-title .reveal-mask{ margin-bottom:.05em; }

.hero-desc{
  font-size:1.05rem; color:var(--text-dim); max-width:480px; line-height:1.75; margin-bottom:2.4rem;
}

.hero-cta{ display:flex; gap:1rem; flex-wrap:wrap; }

.btn-gold{
  display:inline-flex; align-items:center; gap:.6rem;
  background:var(--accent-grad); color:#161006; font-weight:700;
  font-family:var(--font-mono); font-size:.78rem; letter-spacing:.06em;
  padding:.95rem 1.7rem; border-radius:50px; transition:all .35s var(--ease);
  box-shadow:0 0 0 rgba(var(--accent-rgb),0);
}
.btn-gold:hover{ transform:translateY(-3px); box-shadow:0 14px 40px rgba(var(--accent-rgb),0.28); }

.btn-ghost{
  display:inline-flex; align-items:center; gap:.6rem;
  border:1px solid var(--line-soft); color:var(--text);
  font-family:var(--font-mono); font-size:.78rem; letter-spacing:.06em;
  padding:.95rem 1.7rem; border-radius:50px; transition:all .35s var(--ease);
}
.btn-ghost:hover{ border-color:var(--accent-2); background:rgba(var(--accent-rgb),0.06); transform:translateY(-3px); }

.hero-visual{
  position:relative; width:100%; aspect-ratio:3/4.6; max-width:440px; margin-left:auto;
}
.frame-glow{ display:none; }
.hero-floor-shadow{
  position:absolute; left:50%; bottom:4%; width:52%; height:22px;
  transform:translateX(-50%) scale(0.5); opacity:0;
  background:radial-gradient(ellipse, rgba(0,0,0,0.55) 0%, rgba(0,0,0,0.25) 45%, transparent 75%);
  filter:blur(3px); z-index:1; pointer-events:none;
}

/* Real walking clip.  There is no static-photo stand-in any more: if the
   video cannot load, the hero simply reveals its copy and shows no figure.
   Background is truly removed (rembg, per-frame), but relying on the
   browser's native alpha-video support proved inconsistent (renders fine
   in some engines, shows solid black in others — e.g. Safari has no
   reliable transparent-webm support). So instead: the mp4 packs an
   ordinary color frame on top and its alpha mask (as grayscale) stacked
   below — a normal, universally-supported video, no alpha codec needed.
   JS (initHeroWalk in script.js) decodes both halves each frame via a
   hidden <video> + canvas and composites them manually, so transparency
   works identically in every browser. The video itself stays invisible;
   only the canvas paints. */
.hero-walk-video{
  position:absolute; width:1px; height:1px; opacity:0; overflow:hidden; pointer-events:none;
}
.hero-walk-canvas{
  position:absolute; inset:0; width:100%; height:100%;
  filter:none;
  opacity:0; z-index:1;
  transform-origin:50% 100%;
  will-change:transform;
}
.hero-visual.has-video .hero-walk-canvas{ opacity:1; }
.hero-visual-vignette{
  display:none;
}

.scroll-cue{
  position:absolute; left:2.5rem; bottom:2rem; z-index:2;
  display:flex; flex-direction:column; align-items:center; gap:.7rem; opacity:0;
}
.scroll-cue span{ writing-mode:vertical-lr; font-family:var(--font-mono); font-size:.65rem; letter-spacing:.2em; color:var(--text-mute); }
.scroll-cue-line{ width:1px; height:52px; background:rgba(255,255,255,0.12); position:relative; overflow:hidden; }
.scroll-cue-dot{ position:absolute; top:-30%; left:-1px; width:3px; height:30%; background:var(--accent-2); animation:cueMove 2.2s ease-in-out infinite; }
@keyframes cueMove{ 0%{top:-30%;} 100%{top:110%;} }

/* ============== SECTIONS SHARED ============== */
.section{ position:relative; padding:8rem 0; }
.section-header{ margin-bottom:4rem; max-width:760px; }
.eyebrow{
  display:inline-block; font-family:var(--font-mono); font-size:.72rem;
  letter-spacing:.2em; color:var(--accent-2); margin-bottom:1.1rem;
}
.section-title{
  font-family:var(--font-display); text-transform:uppercase;
  font-size:clamp(1.8rem,3.6vw,2.6rem); line-height:1.18; color:var(--text);
}
.section-title.huge{ font-size:clamp(2.1rem,4.6vw,3.4rem); line-height:1.1; }
.contact-lead{ margin-top:1.4rem; color:var(--text-dim); font-size:1.02rem; max-width:520px; line-height:1.7; }

/* ============== ABOUT ============== */
.about-grid{ display:grid; grid-template-columns:.85fr 1.15fr; gap:4.5rem; align-items:center; }
.about-portrait{ position:relative; }
.portrait-frame{
  position:relative; aspect-ratio:3/4; border-radius:6px; overflow:hidden;
  border:1px solid var(--line); box-shadow:0 30px 70px rgba(0,0,0,0.45);
}
.portrait-frame::after{
  content:''; position:absolute; inset:0;
  background:linear-gradient(200deg, transparent 55%, rgba(var(--accent-rgb),0.18));
  pointer-events:none;
}
.portrait-frame img{
  width:100%; height:100%; object-fit:cover; object-position:50% 8%;
  filter:grayscale(22%) brightness(0.88) contrast(1.14) saturate(0.9) sepia(6%);
}
.about-text{ color:var(--text-dim); font-size:1.02rem; line-height:1.85; margin:1.3rem 0; max-width:560px; }

.stats-row{ display:grid; grid-template-columns:repeat(4,1fr); gap:1.2rem; margin-top:2.4rem; }
.stat-tile{
  border:1px solid var(--line-soft); border-radius:10px; padding:1.3rem 1rem;
  background:var(--bg-card); text-align:left;
}
.stat-num{
  font-family:var(--font-display); font-size:1.9rem;
  background:var(--accent-grad); -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent;
}
.stat-plus{ font-family:var(--font-display); font-size:1.5rem; color:var(--accent-2); }
.stat-sub{ display:block; font-family:var(--font-mono); font-size:.68rem; letter-spacing:.06em; color:var(--text-mute); margin-top:.4rem; text-transform:uppercase; }

/* ============== PROJECTS ============== */
.project-list{ display:flex; flex-direction:column; gap:1.6rem; }
.project-card{
  border:1px solid var(--line-soft); border-radius:14px; padding:2.4rem;
  background:linear-gradient(160deg, rgba(var(--accent-rgb),0.035), transparent 60%);
  transition:border-color .35s ease, transform .35s var(--ease);
}
.project-card:hover{ border-color:var(--line); transform:translateY(-4px); }
.project-card-top{ display:flex; justify-content:space-between; font-family:var(--font-mono); font-size:.7rem; letter-spacing:.1em; color:var(--accent-2); margin-bottom:1rem; }
.project-status{ color:var(--text-mute); }
.project-title{ font-family:var(--font-display); font-size:1.5rem; letter-spacing:.02em; margin-bottom:1rem; }
.project-desc{ color:var(--text-dim); line-height:1.75; max-width:760px; margin-bottom:1.6rem; }
.project-meta{ display:flex; gap:3rem; margin-bottom:1.6rem; flex-wrap:wrap; }
.meta-col{ display:flex; flex-direction:column; gap:.3rem; }
.meta-label{ font-family:var(--font-mono); font-size:.65rem; letter-spacing:.14em; color:var(--text-mute); }
.meta-val{ font-size:.92rem; color:var(--text); }

.tag-row{ display:flex; flex-wrap:wrap; gap:.55rem; }
.tag{
  font-family:var(--font-mono); font-size:.68rem; letter-spacing:.05em;
  border:1px solid var(--line); border-radius:5px; padding:.4rem .7rem; color:var(--accent-1);
}

/* ============== TECH MATRIX ============== */
.matrix-grid{ display:grid; grid-template-columns:repeat(2,1fr); gap:1.4rem; }
.matrix-panel{
  border:1px solid var(--line-soft); border-radius:14px; padding:2rem;
  background:var(--bg-card);
}
.matrix-panel h3{ font-family:var(--font-display); font-size:1.15rem; letter-spacing:.03em; margin-bottom:.7rem; color:var(--accent-1); }
.matrix-panel p{ color:var(--text-dim); font-size:.92rem; margin-bottom:1.3rem; line-height:1.6; }

/* ============== CERTIFICATIONS ============== */
/* Certificates run as a continuous marquee rather than a grid. The track
   holds two identical copies of the card set and translates by exactly -50%,
   so the moment the first copy has fully left the frame the second sits
   pixel-for-pixel where the first began and the loop is seamless. Duration
   is set in JS from the measured track width, so adding or removing a
   certificate changes the loop length, never the scroll speed. */
.cert-marquee{
  position:relative;
  /* bleed past the container so cards enter and exit at the window edge */
  margin-inline:calc(50% - 50vw);
  padding:.6rem 0 1rem;
  overflow:hidden;
}
/* The scrolling lives on an inner element, not on .cert-marquee itself: the
   edge fades below are absolutely positioned, and inside a scroll container
   they would scroll away with the cards instead of staying at the edges. */
.cert-scroller{
  overflow-x:auto; overflow-y:hidden;
  /* a horizontal swipe should move the cards, not trigger back-navigation */
  overscroll-behavior-x:contain;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;
}
.cert-scroller::-webkit-scrollbar{ display:none; }
.cert-marquee::before,
.cert-marquee::after{
  content:''; position:absolute; top:0; bottom:0; width:12%; z-index:2;
  pointer-events:none;
}
.cert-marquee::before{ left:0;  background:linear-gradient(90deg, var(--bg) 10%, transparent); }
.cert-marquee::after { right:0; background:linear-gradient(270deg, var(--bg) 10%, transparent); }

/* The drift is JS moving the scroller's scrollLeft, not a transform animation
   on this element, so a visitor can swipe or trackpad through the row at any
   moment instead of waiting for a card to come round.

   It also fixes the black bands that showed up on a phone. A transform
   animation makes the browser composite the whole track as one layer -- 9402
   x 799 device pixels at dpr 2, past the 4096/8192 texture ceiling many phone
   GPUs have -- and tiles that fail to rasterise paint black. A native scroller
   only ever rasterises what is on screen, however wide the content is. */
.cert-track{ display:flex; gap:1.4rem; width:max-content; }

.cert-track .cert-card{ flex:0 0 clamp(230px, 24vw, 300px); }

.cert-hint{
  margin-top:1rem; text-align:center;
  font-family:var(--font-mono); font-size:.68rem; letter-spacing:.08em;
  color:var(--text-mute);
}

/* Anyone who asked the OS for less motion keeps the scroller but gets no
   drift (initCertMarquee reads the same query), so snapping is worth having --
   with the drift running it would fight it. */
@media (prefers-reduced-motion: reduce){
  .cert-scroller{ scroll-snap-type:x proximity; }
  .cert-track .cert-card{ scroll-snap-align:center; }
}

.cert-hint .hint-manual, .cert-hint .hint-touch{ display:none; }
@media (hover: none){
  .cert-hint .hint-hover{ display:none; }
  .cert-hint .hint-touch{ display:inline; }
}
/* Last, so it wins on a touch device that also asks for reduced motion --
   there the track is a manual scroller, not a marquee. */
@media (prefers-reduced-motion: reduce){
  .cert-hint .hint-hover, .cert-hint .hint-touch{ display:none; }
  .cert-hint .hint-manual{ display:inline; }
}
.cert-card{
  display:flex; flex-direction:column; border:1px solid var(--line-soft); border-radius:14px;
  overflow:hidden; background:var(--bg-card);
  transition:border-color .35s ease, transform .35s var(--ease);
}
.cert-card:hover{ border-color:var(--line); transform:translateY(-4px); }
.cert-thumb{ aspect-ratio:4/3; overflow:hidden; background:#000; }
.cert-thumb img{
  width:100%; height:100%; object-fit:cover; display:block;
  filter:grayscale(10%) brightness(0.92) contrast(1.06);
  transition:transform .5s var(--ease);
}
.cert-card:hover .cert-thumb img{ transform:scale(1.05); }
.cert-body{ padding:1.3rem 1.4rem .4rem; flex:1; }
.cert-issuer{ font-family:var(--font-mono); font-size:.64rem; letter-spacing:.1em; color:var(--accent-2); text-transform:uppercase; }
.cert-title{ font-family:var(--font-display); font-size:.94rem; font-weight:normal; letter-spacing:.01em; line-height:1.35; margin:.6rem 0; color:var(--text); }
.cert-date{ display:block; font-family:var(--font-mono); font-size:.68rem; color:var(--text-mute); margin-bottom:1rem; }
.cert-view{
  display:flex; align-items:center; justify-content:space-between; gap:.4rem;
  padding:.85rem 1.4rem; border-top:1px solid var(--line-soft);
  font-family:var(--font-mono); font-size:.68rem; letter-spacing:.06em; color:var(--text-dim);
  transition:color .3s ease;
}
.cert-card:hover .cert-view{ color:var(--accent-1); }

/* ============== TIMELINE ============== */
.timeline{ position:relative; padding-left:2.6rem; }
.timeline-line{ position:absolute; left:5px; top:6px; bottom:6px; width:1px; background:linear-gradient(var(--accent-2), transparent 95%); opacity:.4; }
.timeline-item{ position:relative; padding-bottom:3rem; }
.timeline-item:last-child{ padding-bottom:0; }
.timeline-node{
  position:absolute; left:-2.6rem; top:.35rem; width:11px; height:11px; border-radius:50%;
  background:var(--bg); border:2px solid var(--accent-2); box-shadow:0 0 0 4px rgba(var(--accent-rgb),0.08);
}
.timeline-date{ font-family:var(--font-mono); font-size:.7rem; letter-spacing:.1em; color:var(--accent-2); }
.timeline-item h3{ font-family:var(--font-display); font-size:1.15rem; margin:.5rem 0 .3rem; letter-spacing:.02em; }
.timeline-org{ display:block; font-size:.85rem; color:var(--text-mute); margin-bottom:.6rem; }
.timeline-item p{ color:var(--text-dim); max-width:600px; line-height:1.7; font-size:.94rem; }
.timeline-cert{
  display:inline-flex; align-items:center; gap:.35rem; margin-top:.8rem;
  font-family:var(--font-mono); font-size:.7rem; letter-spacing:.06em; color:var(--accent-2);
  border-bottom:1px solid transparent; transition:border-color .3s ease, color .3s ease;
}
.timeline-cert:hover{ color:var(--accent-1); border-color:var(--accent-2); }

/* Project cards carry an outbound link where the work is publicly visible;
   same affordance as the timeline's certificate link. */
.project-link{
  display:inline-flex; align-items:center; gap:.35rem; margin-top:1.4rem;
  font-family:var(--font-mono); font-size:.72rem; letter-spacing:.06em; color:var(--accent-2);
  border-bottom:1px solid transparent; transition:border-color .3s ease, color .3s ease;
}
.project-link:hover{ color:var(--accent-1); border-color:var(--accent-2); }

/* ============== CONTACT ============== */
.contact-grid{ display:grid; grid-template-columns:.8fr 1.2fr; gap:4rem; margin-top:3.5rem; }
.contact-info{ display:flex; flex-direction:column; gap:1.6rem; }
.contact-info-row{ display:flex; flex-direction:column; gap:.35rem; border-bottom:1px solid var(--line-soft); padding-bottom:1.1rem; }
.contact-info-row a{ font-size:1rem; transition:color .3s ease; }
.contact-info-row a:hover{ color:var(--accent-2); }

.terminal-form{
  border:1px solid var(--line); border-radius:12px; overflow:hidden;
  background:rgba(255,255,255,0.02); box-shadow:0 30px 70px rgba(0,0,0,0.35);
}
.terminal-bar{
  display:flex; align-items:center; gap:.4rem; padding:.85rem 1.2rem;
  border-bottom:1px solid var(--line-soft); background:rgba(255,255,255,0.02);
  font-family:var(--font-mono); font-size:.72rem; color:var(--text-mute);
}
.terminal-bar span{ width:9px; height:9px; border-radius:50%; background:rgba(255,255,255,0.15); }
.terminal-bar em{ margin-left:.6rem; font-style:normal; }

/* The bottom padding is not optional: without it the "// PAYLOAD" label butts
   straight against the inputs above while every other gap in the form is
   1.6rem, and stacked on mobile that reads as one run-on field. */
.form-row{ display:grid; grid-template-columns:1fr 1fr; gap:1.4rem; padding:1.6rem 1.6rem 1.6rem; }
.form-field{ padding:0 1.6rem 1.6rem; display:flex; flex-direction:column; gap:.6rem; }
.form-row .form-field{ padding:0; }
.form-field label{ font-family:var(--font-mono); font-size:.68rem; letter-spacing:.1em; color:var(--accent-2); }
.form-field input, .form-field textarea{
  background:rgba(255,255,255,0.02); border:1px solid var(--line-soft); border-radius:6px;
  color:var(--text); font-family:var(--font-mono); font-size:.88rem; padding:.8rem .9rem;
  outline:none; transition:border-color .3s ease; resize:none;
}
.form-field input:focus, .form-field textarea:focus{ border-color:var(--accent-2); box-shadow:0 0 0 3px rgba(var(--accent-rgb),0.08); }
.form-field input::placeholder, .form-field textarea::placeholder{ color:var(--text-mute); }

.submit-btn{ margin:0 1.6rem 1.6rem; border:none; cursor:pointer; }

/* ============== FOOTER ============== */
.footer{
  border-top:1px solid var(--line-soft); padding:3rem 2.5rem; text-align:center;
  display:flex; flex-direction:column; align-items:center; gap:.6rem;
}
.footer-logo{ font-family:var(--font-body); font-size:1.3rem; font-weight:400; color:var(--text-dim); }
.footer p{ color:var(--text-dim); font-size:.9rem; }
.footer-year{ font-family:var(--font-mono); font-size:.7rem; color:var(--text-mute); letter-spacing:.1em; }

/* ============== RESPONSIVE ============== */
/* Between the widest the links stay visible and the burger breakpoint there
   is now a full name where a 2-letter monogram used to sit, and at ~1000px
   the logo, the links and the CTA met with 0px between them. Buying the room
   back from the link gaps and the bar padding keeps all seven links rather
   than dropping to the burger earlier. */
@media (max-width: 1120px) and (min-width: 981px){
  #navbar{ padding-left:1.6rem; padding-right:1.6rem; }
  #navbar.scrolled{ padding-left:1.6rem; padding-right:1.6rem; }
  .nav-links{ gap:1.45rem; }
  .nav-cta{ padding:.55rem 1rem; }
}

@media (max-width: 980px){
  .hero-inner{ grid-template-columns:1fr; }
  .hero-visual{ max-width:320px; margin:2.5rem auto 0; order:-1; }
  .about-grid, .contact-grid{ grid-template-columns:1fr; gap:2.5rem; }
  .matrix-grid{ grid-template-columns:1fr; }
  .stats-row{ grid-template-columns:repeat(2,1fr); }
  .nav-links, .nav-cta{ display:none; }
  .nav-burger{ display:flex; }
  .form-row{ grid-template-columns:1fr; }
}

@media (max-width: 560px){
  .section{ padding:5.5rem 0; }
  .container{ padding:0 1.4rem; }

  /* Section heads on a phone: a step smaller, and centred over the content
     they introduce. The old narrow-screen override raised the floor rather
     than lowering the size, so at 375px these actually rendered LARGER
     (33.6px) than at 500px (28.8px). */
  /* 7.2vw, not 7.4: at 7.4 "ARCHITECTURAL" measured 275px against a 275px
     column at 320px wide -- it fit, but with nothing to spare. */
  .section-title, .section-title.huge{ font-size:clamp(1.35rem, 7.2vw, 1.85rem); }
  .section-header{ text-align:center; }
  .section-header .contact-lead{ margin-inline:auto; }

  /* About is the one section whose eyebrow and title are not wrapped in a
     .section-header, so they are centred directly. The eyebrow has to become
     a block first -- centring an inline-block is the parent's job, and this
     parent also holds the body copy, which stays left-aligned. */
  #about .eyebrow{ display:block; }
  #about .eyebrow, #about .section-title{ text-align:center; }

  /* Every other section title is one phrase left to wrap where it likes. This
     one carries authored <br> breaks, and at the shared size its longest line,
     "FROM INTELLIGENCE", outruns the column -- so it re-wrapped and left "TO"
     and "FROM" stranded alone. Sized to that line instead: 6.8vw clears it
     from 320px up, where the width available is (100vw - 2.8rem of container
     padding). */
  #about .section-title{ font-size:clamp(1rem, 6.8vw, 1.85rem); }
  .project-card{ padding:1.6rem; }
  .cert-track .cert-card{ flex:0 0 78vw; }
  .cert-marquee::before, .cert-marquee::after{ width:8%; }
  .stats-row{ grid-template-columns:1fr 1fr; }
  #navbar{ padding:1.2rem 1.4rem; }
}

/* ==================================================================
   MOBILE
   Everything below is inside a max-width query, so the desktop
   layout is untouched by all of it.

   The hero was the problem: 1250px of it on an 844px phone, so the
   headline, the buttons and the whole of the copy started below the
   fold — you landed on a figure and a stray line of small type. The
   walk itself was fine, it was the space around it that wasn't.

   The figure cannot simply be made bigger: the canvas crop is locked
   to 3/4.6 (widening it would stretch him), and he already fills the
   frame vertically — measured headroom above his head runs from 14.6%
   at the start down to 2.4% by 7s, so there is nothing to crop away.
   Height is what buys size, and height is what has to fit. So the
   space is taken back from the padding and the gaps instead.
   ================================================================== */
@media (max-width: 980px){
  /* svh, not vh: on a phone vh includes the browser chrome that is
     actually covering the page, which is what made this overflow worse
     than it measured. */
  .hero{ padding:5rem 1.6rem 2.5rem; min-height:100svh; }
  .hero-inner{ gap:0; }

  /* width:auto lets aspect-ratio derive the width from the height, so the
     3/4.6 the canvas is drawn at is preserved exactly — setting both
     dimensions would ignore aspect-ratio and stretch him. */
  .hero-visual{
    order:-1; width:auto; max-width:min(84vw, 330px);
    height:min(44svh, 420px);
    margin:0 auto .9rem;
  }

  /* Flush left on a phone. The tag is centred on the headline at desktop
     widths, but once the headline is only ~240px wide that centring reads as
     the whole hero being centre-aligned rather than as a deliberate
     relationship between two lines. */
  .hero-tag{
    font-size:.64rem; letter-spacing:.09em; margin-bottom:.9rem;
    justify-content:flex-start; margin-right:0;
  }
  .hero-head{ --hero-title-fs:clamp(2rem, 8.6vw, 2.75rem); }
  .hero-title{ margin-bottom:.9rem; }
  .hero-desc{ font-size:.94rem; line-height:1.62; margin-bottom:1.4rem; }
  .btn-gold, .btn-ghost{ padding:.85rem 1.35rem; font-size:.74rem; min-height:44px; }

  /* Both buttons stay on one row. At 375px they wanted 338px of a 330px row
     and wrapped, stranding "Download Résumé" on a line of its own -- and the
     extra row cost vertical space the hero cannot spare on a short phone.
     Sizing them from the viewport keeps the pair together down to 320px, and
     the horizontal padding is in em so it shrinks with the label instead of
     eating the saving. */
  .hero-cta{ gap:.7rem; flex-wrap:nowrap; }
  .hero-cta .btn-gold, .hero-cta .btn-ghost{
    flex:0 1 auto; min-width:0; white-space:nowrap;
    font-size:clamp(.58rem, 3.1vw, .78rem); padding:.85rem 1.4em; gap:.5em;
  }

  /* it sat at the bottom-left of a 1250px hero, i.e. far past the fold
     and out of line with the copy above it */
  .scroll-cue{ display:none; }

  /* Touch targets. Measured before: burger 15px tall, menu links 28px,
     the certificate links 16px and the email/phone links 20px — all well
     under the ~44px a fingertip actually needs. Padding only, so nothing
     moves on desktop and nothing here changes what the text looks like. */
  .nav-burger{ padding:15px 10px; margin-right:-10px; }
  .mobile-link{ padding:.5rem 1rem; }
  .nav-logo{ padding:1rem 0; }
  .timeline-cert{ display:inline-flex; align-items:center; min-height:44px; }
  .project-link{ display:inline-flex; align-items:center; min-height:44px; }
  .contact-info-row a{ display:inline-flex; align-items:center; min-height:44px; }
  .contact-info-row{ gap:0; }
}

@media (max-width: 560px){
  .hero{ padding:4.5rem 1.4rem 2rem; }
  .hero-visual{ max-width:min(88vw, 300px); height:min(42svh, 380px); }
  .hero-head{ --hero-title-fs:clamp(1.85rem, 9.2vw, 2.4rem); }
  .hero-desc{ font-size:.9rem; }
  .about-text{ font-size:.95rem; }
  .stats-row{ gap:.7rem; }
}

/* Short screens. On a 360x640 Android the copy plus a 269px figure came to
   762px against a 640px viewport, so the buttons fell below the fold again.
   The binding constraint here is viewport HEIGHT, not width, so it is asked
   about directly rather than inferred from another width breakpoint. */
@media (max-width: 980px) and (max-height: 700px){
  .hero{ padding:4rem 1.4rem 1rem; }
  .hero-visual{ height:min(35svh, 300px); margin-bottom:.5rem; }
  .hero-tag{ font-size:.58rem; margin-bottom:.6rem; }
  .hero-head{ --hero-title-fs:clamp(1.55rem, 7.4vw, 2rem); }
  .hero-title{ margin-bottom:.6rem; }
  .hero-desc{ font-size:.84rem; line-height:1.5; margin-bottom:.7rem; }
  .btn-gold, .btn-ghost{ padding:.7rem 1.15rem; font-size:.68rem; }
  /* The hero pair is deliberately left on the one-row sizing above. Shrinking
     it further here would buy no height -- min-height:44px is what sets the
     button height at these sizes, not the padding -- and would only make the
     labels smaller than they need to be. */
}

/* Mid-height phones — 360x800 (Galaxy S20-S24), 375x812 (iPhone X-13 mini).
   These are the two most common sizes there are, and the sweep found the
   buttons clearing the fold by 3px and 6px: they fit, but by luck rather
   than by design. The pixels are taken from the padding and the gaps only.
   The figure keeps its full 44svh — the walk is the point of the section,
   so it is the last thing that gets shrunk, not the first. */
@media (max-width: 980px) and (min-height: 701px) and (max-height: 880px){
  .hero{ padding-bottom:1.6rem; }
  .hero-visual{ margin-bottom:.7rem; }
  .hero-tag{ margin-bottom:.7rem; }
  .hero-title{ margin-bottom:.7rem; }
  .hero-desc{ margin-bottom:1.05rem; }
}

/* Very short screens — 320x568 (iPhone SE 1st gen / iPhone 5). Measured:
   the hero came to 602px against a 568px viewport and the buttons fell 18px
   below the fold. Placed before the landscape block on purpose, so landscape
   still wins its own figure height. */
@media (max-width: 980px) and (max-height: 620px){
  .hero-visual{ height:min(29svh, 300px); }
  .hero-tag{ margin-bottom:.45rem; }
  .hero-title{ margin-bottom:.45rem; }
}

/* Narrow screens. "TRANSMISSION." is a single unbreakable 302px word, and at
   the clamp's 2.1rem floor it stopped the page going below 343px wide at all
   — every phone narrower than that got a sideways scroll. The floor has to
   scale with the viewport rather than being a fixed rem value. */
@media (max-width: 400px){
  .section-title{ overflow-wrap:break-word; }
}

/* Landscape phone. Stacking a portrait 3/4.6 figure above the copy on a
   viewport ~400px tall leaves room for neither, so the two-column hero comes
   back — the same arrangement desktop uses, for the same reason. */
@media (max-width: 980px) and (orientation: landscape) and (max-height: 560px){
  .hero{ padding:4.5rem 1.6rem .8rem; }
  .hero-inner{ grid-template-columns:1.1fr .9fr; gap:1.6rem; align-items:center; }
  .hero-visual{ order:0; height:min(74svh, 330px); max-width:none; margin:0 0 0 auto; }
  .hero-head{ --hero-title-fs:clamp(1.4rem, 4.6vw, 2rem); }
  .hero-title{ margin-bottom:.45rem; }
  .hero-desc{ font-size:.82rem; line-height:1.5; margin-bottom:.5rem; }
  .hero-tag{ margin-bottom:.45rem; }
}
