/* ============================================================
   Mohamed Khaled Portfolio — Main Stylesheet
   style.css | Part of CV-Web project
   ============================================================ */

/* ─── Self-hosted fonts (Syne + DM Sans) ───
   Replaces the external Google Fonts request. font-display: swap means
   text is visible immediately in fallback fonts while custom fonts load. */
@font-face { font-family: 'Syne'; font-style: normal; font-weight: 400; font-display: swap; src: url('../assets/fonts/syne-400.woff2') format('woff2'); }
@font-face { font-family: 'Syne'; font-style: normal; font-weight: 500; font-display: swap; src: url('../assets/fonts/syne-500.woff2') format('woff2'); }
@font-face { font-family: 'Syne'; font-style: normal; font-weight: 600; font-display: swap; src: url('../assets/fonts/syne-600.woff2') format('woff2'); }
@font-face { font-family: 'Syne'; font-style: normal; font-weight: 700; font-display: swap; src: url('../assets/fonts/syne-700.woff2') format('woff2'); }
@font-face { font-family: 'Syne'; font-style: normal; font-weight: 800; font-display: swap; src: url('../assets/fonts/syne-800.woff2') format('woff2'); }
@font-face { font-family: 'DM Sans'; font-style: normal; font-weight: 300; font-display: swap; src: url('../assets/fonts/dm-sans-300.woff2') format('woff2'); }
@font-face { font-family: 'DM Sans'; font-style: normal; font-weight: 400; font-display: swap; src: url('../assets/fonts/dm-sans-400.woff2') format('woff2'); }
@font-face { font-family: 'DM Sans'; font-style: normal; font-weight: 500; font-display: swap; src: url('../assets/fonts/dm-sans-500.woff2') format('woff2'); }
@font-face { font-family: 'DM Sans'; font-style: normal; font-weight: 600; font-display: swap; src: url('../assets/fonts/dm-sans-600.woff2') format('woff2'); }
@font-face { font-family: 'DM Sans'; font-style: italic; font-weight: 300; font-display: swap; src: url('../assets/fonts/dm-sans-300i.woff2') format('woff2'); }

/* ─── CSS Variables ─── */
:root {
  --indigo: #3730a3;
  --indigo-light: #6366f1;
  --pink: #ec4899;
  --cyan: #06b6d4;
  --amber: #f59e0b;
  --bg: #06060f;
  --bg2: #0d0d1f;
  --bg3: #12122a;
  --surface: rgba(255,255,255,0.03);
  --surface2: rgba(255,255,255,0.06);
  --border: rgba(255,255,255,0.08);
  --text: #e2e8f0;
  --text-dim: #94a3b8;
  --text-faint: #7d8c9f;  /* WCAG AA passes on --bg (~5.5:1); still visibly dimmer than --text-dim */
  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-lg: 20px;
  --transition: 0.3s ease;
}

/* ─── Reset & Base ─── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; font-size: 16px; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: 'DM Sans', sans-serif;
  overflow-x: hidden;
  cursor: none;
  line-height: 1.6;
}
img { max-width: 100%; display: block; }
a { text-decoration: none; color: inherit; }
button { font-family: inherit; }
ul { list-style: none; }

/* ─── Skip-to-content link ───
   Hidden off-screen until focused. Tab on page-load reveals it so keyboard
   and screen-reader users can jump straight past the navbar to main content. */
.skip-link {
  position: fixed; top: -100px; left: 12px;
  padding: 0.85rem 1.5rem;
  background: linear-gradient(135deg, var(--indigo-light), var(--pink));
  color: #fff; font-weight: 600; font-size: 0.92rem;
  border-radius: 0 0 10px 10px;
  z-index: 100002;  /* above lightbox + cursor */
  box-shadow: 0 8px 30px rgba(0,0,0,0.5);
  transition: top 0.2s ease;
}
.skip-link:focus,
.skip-link:focus-visible { top: 0; outline: none; }

/* ─── Keyboard focus rings ───
   `:focus-visible` only fires for keyboard navigation — mouse clicks won't
   trigger it, so the rings stay invisible during normal pointer use but
   appear cleanly when someone tabs through the page. */
a:focus-visible,
button:focus-visible,
.cf-btn:focus-visible,
.cert-card:focus-visible,
.edu-card-link:focus-visible,
.cert-toggle:focus-visible,
.lang-badge:focus-visible,
.project-card:focus-visible,
.exp-card:focus-visible,
.hamburger:focus-visible,
.pc-channel:focus-visible,
.pc-cta:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 3px;
  border-radius: 6px;
}
/* Lightbox close/prev/next buttons sit on a dark backdrop — ring tinted
   pink for contrast and to match the existing hover language */
.lb-close:focus-visible,
.lb-prev:focus-visible,
.lb-next:focus-visible {
  outline: 2px solid var(--pink);
  outline-offset: 3px;
}

/* ─── Noise Overlay ─── */
body::before {
  content: '';
  position: fixed; inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.05'/%3E%3C/svg%3E");
  pointer-events: none; z-index: 9998; opacity: 0.4;
}

/* ─── Scrollbar ─── */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb {
  background: linear-gradient(var(--indigo-light), var(--pink));
  border-radius: 3px;
}

/* ─── Custom Cursor ─── */
/* z-index sits above the lightbox (99990) and its controls (99991)
   so the cursor stays visible while a certificate is open. */
#cursor-dot {
  position: fixed; width: 8px; height: 8px;
  background: var(--cyan); border-radius: 50%;
  pointer-events: none; z-index: 100000;
  transform: translate(-50%, -50%);
  box-shadow: 0 0 10px var(--cyan), 0 0 20px var(--cyan);
}
#cursor-ring {
  position: fixed; width: 36px; height: 36px;
  border: 2px solid rgba(99,102,241,0.6); border-radius: 50%;
  pointer-events: none; z-index: 99999;
  transform: translate(-50%, -50%);
  transition: width 0.3s, height 0.3s, border-color 0.3s;
}

/* ─── Loading Screen ─── */
#loader {
  position: fixed; inset: 0;
  background: var(--bg);
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  z-index: 99999;
  /* Shortened from 0.6s for snappier hand-off; pairs with 1.0s JS timeout (was 2.2s).
     Total loader runtime ~1.3s instead of 2.8s — recovers ~2 s of Speed Index. */
  transition: opacity 0.3s ease, visibility 0.3s ease;
}
#loader.hidden { opacity: 0; visibility: hidden; }
.loader-initials {
  font-family: 'Syne', sans-serif;
  font-size: clamp(4rem, 12vw, 8rem);
  font-weight: 800;
  background: linear-gradient(135deg, var(--indigo-light), var(--pink), var(--cyan), var(--amber));
  background-size: 300% 300%;
  -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
  animation: gradShift 2s ease infinite;
}
.loader-bar-wrap {
  margin-top: 3rem; width: 200px; height: 3px;
  background: var(--surface2); border-radius: 2px; overflow: hidden;
}
.loader-bar {
  height: 100%;
  background: linear-gradient(90deg, var(--indigo-light), var(--pink), var(--cyan));
  border-radius: 2px;
  /* Match the JS dismiss timeout (1.0s) so the bar visibly completes before fade */
  animation: loadFill 1s ease forwards;
}

/* ─── Particle Canvas ─── */
#particle-canvas {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
}

/* ─── Navigation ─── */
nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 1000;
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  background: rgba(6,6,15,0.7);
  border-bottom: 1px solid var(--border);
  transform: translateY(-100%);
  animation: slideDown 0.8s 2.2s ease forwards;
}
.nav-inner {
  max-width: 1200px; margin: 0 auto; padding: 1.1rem 2rem;
  display: flex; align-items: center; justify-content: space-between;
}
.nav-logo {
  font-family: 'Syne', sans-serif; font-weight: 800; font-size: 1.2rem;
  background: linear-gradient(90deg, var(--indigo-light), var(--cyan));
  -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
}
.nav-links { display: flex; gap: 1.75rem; }
.nav-links a {
  color: var(--text-dim); font-size: 0.88rem; font-weight: 500;
  position: relative; padding-bottom: 3px; transition: color var(--transition);
}
.nav-links a::after {
  content: ''; position: absolute; bottom: 0; left: 0;
  width: 0; height: 2px;
  background: linear-gradient(90deg, var(--pink), var(--cyan));
  transition: width var(--transition);
}
.nav-links a:hover,
.nav-links a.active { color: var(--text); }
.nav-links a:hover::after,
.nav-links a.active::after { width: 100%; }
/* Mobile menu — same active-state tint as desktop nav */
.mobile-menu a.active { color: var(--text); background: rgba(99,102,241,0.08); }
.hamburger { display: none; flex-direction: column; gap: 5px; cursor: pointer; background: none; border: none; padding: 4px; }
.hamburger span { display: block; width: 24px; height: 2px; background: var(--text-dim); transition: var(--transition); border-radius: 2px; }
.mobile-menu {
  display: none; flex-direction: column; gap: 1rem;
  background: rgba(6,6,15,0.97); backdrop-filter: blur(20px);
  padding: 1.5rem 2rem; border-top: 1px solid var(--border);
}
.mobile-menu.open { display: flex; }
.mobile-menu a { color: var(--text-dim); font-size: 1rem; font-weight: 500; transition: color 0.2s; }
.mobile-menu a:hover { color: var(--text); }

/* ─── Layout ─── */
section { position: relative; z-index: 1; }
.container { max-width: 1200px; margin: 0 auto; padding: 0 2rem; }
.section-label {
  font-size: 0.72rem; font-weight: 600; letter-spacing: 0.15em;
  text-transform: uppercase; color: var(--pink); margin-bottom: 0.6rem;
}
.section-title {
  font-family: 'Syne', sans-serif;
  font-size: clamp(2rem, 4vw, 3rem); font-weight: 800; line-height: 1.1; margin-bottom: 1rem;
}
.section-title span {
  background: linear-gradient(135deg, var(--indigo-light), var(--cyan));
  -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
}

/* ─── Hero ─── */
#hero { min-height: 100vh; display: flex; align-items: center; padding: 8rem 0 4rem; }
.hero-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 4rem; align-items: center; }
.hero-name {
  font-family: 'Syne', sans-serif;
  font-size: clamp(2.5rem, 5vw, 4.5rem); font-weight: 800; line-height: 1.05; margin-bottom: 1rem;
  /* Reserve 2 lines of height (always "Mohamed<br>Khaled") so a Syne font
     swap can't change the box height and shift hero-left vertically. em
     scales with font-size so this stays correct across the clamp range. */
  min-height: 2.1em;
  background: linear-gradient(135deg, #fff 0%, var(--indigo-light) 40%, var(--pink) 70%, var(--cyan) 100%);
  background-size: 300% auto;
  -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
  animation: shimmer 4s linear infinite, fadeUp 0.8s 1.0s ease both;
}
.hero-subtitle {
  color: var(--text-dim); font-size: 1.05rem; line-height: 1.65; margin-bottom: 2rem;
  /* Reserve enough vertical space for the longest expected wrap (3 lines
     on desktop) so a DM Sans 600 swap on the bold span can't reflow the
     paragraph height and shift the column. Mobile overrides below to 4
     lines. Without this, phase 8b/8c showed 0.03–0.07 CLS originating here. */
  min-height: 5em;
  animation: fadeUp 0.8s 1.1s ease both;
}
.hero-subtitle strong { color: var(--amber); font-weight: 600; }
.hero-btns {
  display: flex; gap: 1rem; flex-wrap: wrap;
  animation: fadeUp 0.8s 1.2s ease both;
}
.btn-primary {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.85rem 2rem; border-radius: var(--radius-md);
  background: linear-gradient(135deg, var(--indigo-light), var(--pink));
  color: #fff; font-weight: 600; font-size: 0.95rem; border: none; cursor: pointer;
  transition: transform 0.2s, box-shadow 0.2s;
  box-shadow: 0 0 30px rgba(99,102,241,0.3);
}
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 8px 40px rgba(99,102,241,0.5); }
.btn-outline {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.85rem 2rem; border-radius: var(--radius-md);
  border: 1px solid var(--border); background: var(--surface);
  color: var(--text); font-weight: 600; font-size: 0.95rem; cursor: pointer;
  transition: border-color 0.2s, background 0.2s;
}
.btn-outline:hover { border-color: var(--indigo-light); background: rgba(99,102,241,0.1); }

/* Hero Card */
.hero-card-wrap {
  display: flex; justify-content: center; align-items: center;
  position: relative; animation: fadeUp 0.8s 1.3s ease both;
}
.hero-orb {
  position: absolute; width: 350px; height: 350px;
  background: radial-gradient(circle, rgba(99,102,241,0.3) 0%, rgba(236,72,153,0.15) 50%, transparent 70%);
  border-radius: 50%; filter: blur(60px); animation: orbFloat 6s ease-in-out infinite;
}
.hero-ring {
  /* Enlarged so it sits clearly outside the card and doesn't bisect the role text */
  position: absolute; width: 420px; height: 420px;
  border: 2px dashed rgba(99,102,241,0.4); border-radius: 50%;
  animation: spin 20s linear infinite;
}
.hero-card {
  position: relative; z-index: 2;
  background: rgba(13,13,31,0.8); backdrop-filter: blur(20px);
  border: 1px solid var(--border); border-radius: 24px; padding: 2.5rem;
  width: 280px; text-align: center;
  box-shadow: 0 25px 60px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.08);
  animation: float3d 6s ease-in-out infinite;
}
.card-avatar {
  width: 80px; height: 80px; border-radius: 50%; margin: 0 auto 1rem;
  background: linear-gradient(135deg, var(--indigo), var(--pink));
  display: flex; align-items: center; justify-content: center;
  font-family: 'Syne', sans-serif; font-size: 1.5rem; font-weight: 800; color: #fff;
  box-shadow: 0 0 30px rgba(99,102,241,0.5);
}
.card-name { font-family: 'Syne', sans-serif; font-size: 1.1rem; font-weight: 700; color: #fff; margin-bottom: 0.25rem; }
.card-role { color: var(--cyan); font-size: 0.8rem; font-weight: 500; margin-bottom: 1rem; }
/* New compact "currently at" badge under the role */
.card-status {
  display: inline-flex; align-items: center; gap: 0.45rem;
  font-size: 0.72rem; font-weight: 600; color: var(--text-dim);
  background: rgba(16,185,129,0.08); border: 1px solid rgba(16,185,129,0.25);
  padding: 0.3rem 0.7rem; border-radius: 999px; margin-bottom: 1.1rem;
}
.card-status-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: #10b981; box-shadow: 0 0 8px rgba(16,185,129,0.7);
  animation: pulseGlow 1.6s ease-in-out infinite;
}
/* Top credentials shown as gradient-bordered pills */
.card-creds {
  display: flex; flex-wrap: wrap; gap: 0.4rem; justify-content: center;
  margin-bottom: 0.9rem;
}
.cred-pill {
  font-family: 'Syne', sans-serif; font-size: 0.72rem; font-weight: 700;
  padding: 0.35rem 0.7rem; border-radius: 999px;
  background: linear-gradient(135deg, rgba(99,102,241,0.18), rgba(236,72,153,0.18));
  border: 1px solid rgba(99,102,241,0.35);
  color: #fff; letter-spacing: 0.02em;
}
.card-meta {
  font-size: 0.72rem; color: rgba(255,255,255,0.85);
  font-weight: 500; letter-spacing: 0.05em;
}

/* ─── Marquee ─── */
.marquee-section {
  padding: 2rem 0; overflow: hidden;
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
}
.marquee-track { display: flex; gap: 3rem; width: max-content; animation: marquee 22s linear infinite; }
.marquee-item {
  display: flex; align-items: center; gap: 0.75rem;
  color: var(--text-faint); font-size: 0.9rem; font-weight: 500; white-space: nowrap;
}
.marquee-item span { color: var(--indigo-light); }

/* ─── Skills ───
   Section now hosts only the Skills Timeline chart (js/skills-timeline.js).
   The legacy %-bars and Technology-Stack chip cloud were removed in favor
   of a single, story-led visualization. Their styles (.skill-bar-*,
   .bar-pink/cyan/amber/mix, .chip*, .chips-col) were dropped along with them. */
#skills { padding: 7rem 0; }
.skills-intro {
  color: var(--text-dim);
  max-width: 640px;
  margin: 1rem 0 0;
  font-size: 0.98rem;
  line-height: 1.65;
}

/* ─── Experience ─── */
#experience { padding: 7rem 0; background: linear-gradient(180deg, transparent, rgba(13,13,31,0.5) 50%, transparent); }
.timeline { margin-top: 3rem; position: relative; }
.timeline::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: linear-gradient(180deg, var(--indigo-light), var(--pink), var(--cyan), var(--amber));
}
.timeline-item {
  padding-left: 2.5rem; padding-bottom: 3rem; position: relative;
  opacity: 0; transform: translateX(-30px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}
.timeline-item.visible { opacity: 1; transform: translateX(0); }
.timeline-dot {
  position: absolute; left: -5px; top: 0;
  width: 12px; height: 12px; border-radius: 50%;
  background: linear-gradient(135deg, var(--indigo-light), var(--pink));
  box-shadow: 0 0 15px rgba(99,102,241,0.5);
}
.exp-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 1.75rem;
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
  position: relative; overflow: hidden;
}
.exp-card:hover {
  transform: translateX(8px);
  box-shadow: 0 20px 40px rgba(0,0,0,0.3), 0 0 0 1px rgba(99,102,241,0.3);
  border-color: rgba(99,102,241,0.3);
}
.exp-card::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: linear-gradient(180deg, var(--indigo-light), var(--pink)); border-radius: 3px 0 0 3px;
}
.exp-header { display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.75rem; }
.exp-title { font-family: 'Syne', sans-serif; font-size: 1.15rem; font-weight: 700; color: #fff; }
.exp-date { font-size: 0.78rem; color: var(--amber); font-weight: 600; background: rgba(245,158,11,0.1); padding: 0.25rem 0.75rem; border-radius: 100px; border: 1px solid rgba(245,158,11,0.3); }
.exp-company { color: var(--cyan); font-size: 0.9rem; font-weight: 600; margin-bottom: 1rem; }
.exp-desc { display: flex; flex-direction: column; gap: 0.5rem; }
.exp-desc li { color: var(--text-dim); font-size: 0.9rem; line-height: 1.6; padding-left: 1rem; position: relative; }
.exp-desc li::before { content: '→'; position: absolute; left: 0; color: var(--pink); font-size: 0.8rem; }
.exp-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 1.25rem; }
.exp-chip {
  font-size: 0.72rem; font-weight: 600; padding: 0.25rem 0.65rem; border-radius: 6px;
  background: rgba(99,102,241,0.15); color: var(--indigo-light); border: 1px solid rgba(99,102,241,0.3);
}

/* ─── Projects ─── */
#projects { padding: 7rem 0; }
.projects-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 1.5rem; margin-top: 3rem; }
.project-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 1.75rem; position: relative; overflow: hidden;
  display: flex; flex-direction: column;
  opacity: 0; transform: translateY(30px);
  transition: opacity 0.5s ease, transform 0.5s ease, box-shadow var(--transition);
}
.project-card.visible { opacity: 1; transform: translateY(0); }
.project-card::before {
  content: ''; position: absolute; top: 0; left: -100%; right: -100%; height: 2px;
  background: linear-gradient(90deg, transparent, var(--pink), var(--cyan), transparent);
  transition: left 0.5s ease;
}
.project-card:hover::before { left: 0; right: 0; }
.project-card:hover { transform: translateY(-8px); box-shadow: 0 30px 60px rgba(0,0,0,0.4), 0 0 0 1px rgba(6,182,212,0.2); }
.project-emoji { font-size: 2.5rem; margin-bottom: 1rem; }
.project-name { font-family: 'Syne', sans-serif; font-size: 1rem; font-weight: 700; color: #fff; margin-bottom: 0.5rem; }
.project-desc { font-size: 0.85rem; color: var(--text-dim); line-height: 1.6; margin-bottom: 1rem; }
.project-tags { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: auto; }
.project-tag {
  font-size: 0.7rem; font-weight: 600; padding: 0.2rem 0.55rem; border-radius: 6px;
  background: rgba(6,182,212,0.1); color: var(--cyan); border: 1px solid rgba(6,182,212,0.25);
}
/* Clickable project card (PingPair site, GitHub repos) — mirrors .pub-card-link */
.project-card-link { display: block; text-decoration: none; color: inherit; }
.project-link-hint {
  font-size: 0.72rem; font-weight: 600; color: var(--cyan);
  letter-spacing: 0.04em; margin-top: 1rem;
}
.project-card-link:hover .project-link-hint { text-decoration: underline; }

/* ─── Certificates ─── */
#certificates { padding: 7rem 0; background: linear-gradient(180deg, transparent, rgba(13,13,31,0.5) 50%, transparent); }
/* ─── Certificates toggle (collapsible gallery) ─── */
.cert-toggle {
  display: inline-flex; align-items: center; gap: 0.6rem;
  margin: 0 0 1rem; padding: 0.85rem 1.75rem;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 999px; color: var(--text);
  font-family: inherit; font-size: 0.92rem; font-weight: 600;
  cursor: pointer;
  transition: border-color 0.25s ease, box-shadow 0.25s ease, transform 0.2s ease, background 0.25s ease;
}
.cert-toggle:hover {
  border-color: rgba(99,102,241,0.5);
  background: rgba(99,102,241,0.06);
  box-shadow: 0 10px 30px rgba(0,0,0,0.25);
  transform: translateY(-2px);
}
.cert-toggle-count {
  background: linear-gradient(135deg, var(--indigo-light), var(--pink));
  color: #fff; font-size: 0.74rem; font-weight: 700;
  padding: 0.18rem 0.6rem; border-radius: 999px;
}
.cert-toggle-icon {
  display: inline-block; transition: transform 0.3s ease;
  color: var(--cyan); font-size: 0.95rem;
}
.cert-toggle[aria-expanded="true"] .cert-toggle-icon { transform: rotate(180deg); }
.cert-collapsible[hidden] { display: none; }
.cert-collapsible {
  margin-top: 2rem;
  animation: certSectionFade 0.45s ease both;
}
@keyframes certSectionFade {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}

.cert-filters { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 2.5rem; }
.cf-btn {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.5rem 1.1rem; border-radius: 100px;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--text-dim); font-size: 0.82rem; font-weight: 600; cursor: pointer;
  transition: all 0.2s;
}
.cf-btn:hover { border-color: var(--indigo-light); color: var(--text); }
.cf-btn.active { background: linear-gradient(135deg, var(--indigo-light), var(--pink)); border-color: transparent; color: #fff; }
.cf-count { background: rgba(255,255,255,0.2); border-radius: 100px; padding: 0 6px; font-size: 0.7rem; }
.cert-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1.25rem; }
.cert-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); overflow: hidden; cursor: pointer;
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
  opacity: 0; animation: certFadeIn 0.4s ease forwards; position: relative;
}
.cert-card:hover {
  transform: translateY(-6px);
  box-shadow: 0 20px 40px rgba(0,0,0,0.4), 0 0 0 1px rgba(99,102,241,0.4);
  border-color: rgba(99,102,241,0.4);
}
.cert-thumb { width: 100%; aspect-ratio: 16/10; overflow: hidden; background: #111; position: relative; }
.cert-thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.4s ease; display: block; }
.cert-card:hover .cert-thumb img { transform: scale(1.05); }
.cert-thumb-overlay { position: absolute; inset: 0; background: linear-gradient(to bottom, transparent 60%, rgba(6,6,15,0.8)); }
.cert-body { padding: 1rem 1.1rem 1.1rem; }
.cert-emoji { font-size: 1.3rem; margin-bottom: 0.4rem; }
.cert-title { font-family: 'Syne', sans-serif; font-size: 0.9rem; font-weight: 700; color: #fff; line-height: 1.3; margin-bottom: 0.3rem; }
.cert-issuer { font-size: 0.78rem; color: var(--cyan); font-weight: 600; }
.cert-date { font-size: 0.72rem; color: var(--text-faint); margin-top: 0.2rem; }
.cert-expand-hint {
  position: absolute; top: 0.6rem; right: 0.6rem;
  background: rgba(0,0,0,0.5); backdrop-filter: blur(4px);
  border-radius: 6px; padding: 0.2rem 0.5rem;
  font-size: 0.65rem; color: rgba(255,255,255,0.7);
  opacity: 0; transition: opacity 0.2s;
}
.cert-card:hover .cert-expand-hint { opacity: 1; }

/* ─── Lightbox ─── */
.lightbox-overlay {
  position: fixed; inset: 0; z-index: 99990;
  background: rgba(0,0,0,0.92); backdrop-filter: blur(8px);
  display: none; align-items: center; justify-content: center; padding: 2rem;
}
.lightbox-overlay.open { display: flex; }
.lb-inner {
  max-width: 900px; width: 100%; background: var(--bg2);
  border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden;
  box-shadow: 0 40px 100px rgba(0,0,0,0.8); animation: lbIn 0.3s ease;
}
.lb-inner img { width: 100%; display: block; max-height: 70vh; object-fit: contain; background: #000; }
.lb-info { padding: 1.25rem 1.5rem; }
.lb-title { font-family: 'Syne', sans-serif; font-size: 1.1rem; font-weight: 700; color: #fff; margin-bottom: 0.25rem; }
.lb-meta { font-size: 0.85rem; color: var(--cyan); }
.lb-close {
  position: fixed; top: 1.5rem; right: 1.5rem; width: 44px; height: 44px; border-radius: 50%;
  background: rgba(255,255,255,0.1); border: 1px solid var(--border);
  color: #fff; font-size: 1.1rem; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.2s; z-index: 99991;
}
.lb-close:hover { background: rgba(236,72,153,0.3); }
.lb-prev, .lb-next {
  position: fixed; top: 50%; transform: translateY(-50%);
  width: 48px; height: 48px; border-radius: 50%;
  background: rgba(255,255,255,0.1); border: 1px solid var(--border);
  color: #fff; font-size: 1.8rem; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.2s; z-index: 99991;
}
.lb-prev { left: 1rem; }
.lb-next { right: 1rem; }
.lb-prev:hover, .lb-next:hover { background: rgba(99,102,241,0.4); }

/* ─── Education ─── */
#education { padding: 7rem 0; }
.edu-lang-grid { display: grid; grid-template-columns: 2fr 1fr; gap: 3rem; margin-top: 3rem; }
.edu-cards { display: flex; flex-direction: column; gap: 1.5rem; }
.edu-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 1.25rem 1.5rem;
  display: flex; gap: 1.25rem; align-items: flex-start;
  opacity: 0; transform: translateX(-30px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}
.edu-card.visible { opacity: 1; transform: translateX(0); }
/* Clickable variant — opens the matching cert in the lightbox */
.edu-card-link {
  cursor: pointer; position: relative;
  transition: opacity 0.6s ease, transform 0.6s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}
.edu-card-link:hover {
  border-color: rgba(99,102,241,0.45);
  box-shadow: 0 10px 30px rgba(0,0,0,0.25);
}
.edu-card-link.visible:hover { transform: translateX(6px); }
.edu-view-hint {
  position: absolute; right: 1rem; top: 50%; transform: translateY(-50%);
  font-size: 0.72rem; font-weight: 600; color: var(--cyan);
  background: rgba(6,182,212,0.12); border: 1px solid rgba(6,182,212,0.3);
  padding: 0.25rem 0.55rem; border-radius: 999px;
  opacity: 0; transition: opacity 0.2s ease;
  pointer-events: none;
}
.edu-card-link:hover .edu-view-hint { opacity: 1; }
.edu-icon {
  width: 48px; height: 48px; border-radius: 10px; flex-shrink: 0;
  background: linear-gradient(135deg, var(--indigo), var(--indigo-light));
  display: flex; align-items: center; justify-content: center; font-size: 1.3rem;
}
.edu-degree { font-family: 'Syne', sans-serif; font-size: 0.95rem; font-weight: 700; color: #fff; margin-bottom: 0.2rem; }
.edu-school { color: var(--cyan); font-size: 0.82rem; font-weight: 600; margin-bottom: 0.2rem; }
.edu-year { color: var(--text-faint); font-size: 0.78rem; }
.pub-card {
  background: rgba(245,158,11,0.05); border: 1px solid rgba(245,158,11,0.2);
  border-radius: var(--radius-md); padding: 1.5rem; margin-top: 1.5rem;
  opacity: 0; transform: translateX(-30px);
  transition: opacity 0.6s ease 0.2s, transform 0.6s ease 0.2s;
}
.pub-card.visible { opacity: 1; transform: translateX(0); }
/* Linked publication card — opens DOI in a new tab */
.pub-card-link {
  display: block; text-decoration: none;
  transition: opacity 0.6s ease 0.2s, transform 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}
.pub-card-link:hover {
  border-color: rgba(245,158,11,0.55);
  background: rgba(245,158,11,0.08);
  box-shadow: 0 10px 30px rgba(0,0,0,0.25);
}
.pub-card-link.visible:hover { transform: translateY(-2px); }
.pub-label { font-size: 0.7rem; font-weight: 600; color: var(--amber); letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: 0.5rem; }
.pub-title { font-size: 0.9rem; color: var(--text); line-height: 1.5; margin-bottom: 0.5rem; }
.pub-journal { font-size: 0.8rem; color: var(--text-dim); }
.lang-section h3 { font-family: 'Syne', sans-serif; font-size: 1.1rem; font-weight: 700; margin-bottom: 1.5rem; }
.lang-badges { display: flex; flex-direction: column; gap: 1rem; }
.lang-badge {
  padding: 1rem 1.25rem; border-radius: var(--radius-md);
  background: var(--surface); border: 1px solid var(--border);
  display: flex; justify-content: space-between; align-items: center;
  opacity: 0; transform: translateX(30px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}
.lang-badge.visible { opacity: 1; transform: translateX(0); }
.lang-name { font-weight: 600; font-size: 0.95rem; display: inline-flex; align-items: center; gap: 0.55rem; }
/* Inline SVG flags — render identically on all platforms (Windows, Mac, iOS, Android, Linux).
   Avoids the regional-indicator-letter fallback that Windows shows for flag emojis. */
.lang-flag {
  width: 22px; height: auto; flex-shrink: 0;
  border-radius: 2px;
  box-shadow: 0 0 0 1px rgba(255,255,255,0.08);
}
/* Language tier badges. Foreground colors are LIGHTER tints (Tailwind 200-300
   range) instead of the saturated CSS variable colors — those failed WCAG AA
   contrast against the tinted-same-hue backgrounds (Lighthouse 13.0.2 audit
   `color-contrast` flagged all three). The lighter shades preserve the
   color-coding intent (indigo/cyan/amber tier hues) while clearing 4.5:1. */
.lang-level { font-size: 0.75rem; font-weight: 600; padding: 0.25rem 0.75rem; border-radius: 100px; }
.level-native  { background: rgba(99,102,241,0.2); color: #c7d2fe; border: 1px solid rgba(99,102,241,0.4); }
.level-fluent  { background: rgba(6,182,212,0.2);  color: #a5f3fc; border: 1px solid rgba(6,182,212,0.4); }
.level-basic   { background: rgba(245,158,11,0.2); color: #fde68a; border: 1px solid rgba(245,158,11,0.4); }

/* ─── Contact ─── */
#contact { padding: 7rem 0 5rem; }

/* ─── Personal Card ───
   A single unified vCard-style block: photo + identity in a header row,
   compact 2×2 grid of contact channels, single Download-CV CTA at the bottom.
   Replaced the old vertical contact-cards list to feel like one cohesive
   identity card rather than a scrollable directory. */
.personal-card {
  max-width: 720px;
  margin: 3rem auto 0;
  padding: 2.25rem 2rem;
  background:
    linear-gradient(135deg, rgba(99,102,241,0.04), rgba(236,72,153,0.04)) ,
    var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: 0 30px 80px rgba(0,0,0,0.35);
  position: relative;
  overflow: hidden;
}
/* Soft gradient accent ribbon at the top edge */
.personal-card::before {
  content: '';
  position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: linear-gradient(90deg, var(--indigo-light), var(--pink), var(--cyan));
  opacity: 0.85;
}

/* Header: avatar + identity stack */
.pc-header {
  display: flex;
  align-items: center;
  gap: 2rem;
  padding-bottom: 1.5rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 1.5rem;
}
/* Avatar wrap reuses the existing .contact-avatar-wrap / .contact-ripple /
   .contact-avatar visuals so the ripple animation continues to work. */
.contact-avatar-wrap {
  display: flex; align-items: center; justify-content: center;
  position: relative;
}
.contact-ripple {
  position: absolute;
  border-radius: 50%;
  border: 2px solid rgba(99,102,241,0.3);
  animation: rippleAnim 3s ease-out infinite;
}
.contact-ripple:nth-child(1) { width: 150px; height: 150px; animation-delay: 0s; }
.contact-ripple:nth-child(2) { width: 220px; height: 220px; animation-delay: 1s; }
.contact-ripple:nth-child(3) { width: 290px; height: 290px; animation-delay: 2s; }
.contact-avatar {
  width: 160px; height: 160px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--indigo), var(--pink));
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  box-shadow: 0 0 60px rgba(99,102,241,0.4);
  position: relative; z-index: 1;
  animation: pulseGlow 4s ease-in-out infinite;
  border: 3px solid transparent;
  background-clip: padding-box;
}
.contact-avatar img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}

/* Compact ripple sizing inside the personal card (smaller than default). */
.pc-avatar-wrap { flex: 0 0 auto; }
.pc-avatar-wrap .contact-ripple:nth-child(1) { width: 120px; height: 120px; }
.pc-avatar-wrap .contact-ripple:nth-child(2) { width: 170px; height: 170px; }
.pc-avatar-wrap .contact-ripple:nth-child(3) { width: 220px; height: 220px; }
.pc-avatar-wrap .contact-avatar { width: 130px; height: 130px; }

.pc-identity { flex: 1 1 auto; min-width: 0; }
.pc-name {
  font-family: 'Syne', sans-serif;
  font-size: clamp(1.5rem, 3.5vw, 2rem);
  font-weight: 700;
  margin: 0 0 0.3rem;
  background: linear-gradient(135deg, var(--indigo-light), var(--pink));
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}
.pc-role {
  color: var(--text-dim);
  font-size: 0.95rem;
  font-weight: 500;
  margin: 0 0 0.85rem;
  letter-spacing: 0.01em;
}
.pc-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.pc-meta-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.3rem 0.75rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-dim);
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: 999px;
}

/* Channel rows: compact icon + label/value pairs in a responsive 2-column grid */
.pc-channels {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.75rem;
}
.pc-channels li { margin: 0; }
.pc-channel {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.85rem 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: inherit;
  text-decoration: none;
  transition: transform 0.2s, border-color 0.2s, background 0.2s, box-shadow 0.2s;
}
.pc-channel:hover {
  transform: translateY(-2px);
  border-color: var(--indigo-light);
  background: var(--surface2);
  box-shadow: 0 8px 24px rgba(99,102,241,0.18);
}
.pc-channel:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 2px;
}
.pc-channel-icon {
  width: 38px; height: 38px;
  flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.05rem;
  border-radius: 10px;
}
.pc-channel-text {
  display: flex;
  flex-direction: column;
  min-width: 0;       /* let truncation work in the grid cell */
}
.pc-channel-label {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.pc-channel-val {
  font-size: 0.88rem;
  font-weight: 500;
  color: var(--text);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Download-CV CTA — single primary action below the channels */
.pc-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  margin: 1.5rem auto 0;
  padding: 0.85rem 1.75rem;
  background: linear-gradient(135deg, var(--indigo-light), var(--pink));
  color: #fff;
  font-weight: 600;
  font-size: 0.95rem;
  border-radius: 999px;
  text-decoration: none;
  box-shadow: 0 12px 28px rgba(99,102,241,0.35);
  transition: transform 0.2s, box-shadow 0.2s;
  width: fit-content;
}
.personal-card { display: flex; flex-direction: column; }
.pc-cta:hover {
  transform: translateY(-2px);
  box-shadow: 0 16px 36px rgba(99,102,241,0.45);
}
.pc-cta:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 3px;
}

/* Mobile: photo on top, identity centred, channels collapse to one column. */
@media (max-width: 600px) {
  .personal-card { padding: 1.75rem 1.25rem; margin-top: 2rem; }
  .pc-header {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 1.25rem;
  }
  .pc-meta { justify-content: center; }
  .pc-channels { grid-template-columns: 1fr; }
}

/* ─── Contact form (Web3Forms) ─────────────────────────────── */
.contact-form-wrap {
  margin-top: 4rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 2.5rem;
  max-width: 760px;
  margin-left: auto;
  margin-right: auto;
  position: relative;
  overflow: hidden;
}
/* Subtle gradient accent line at the top of the form card */
.contact-form-wrap::before {
  content: '';
  position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, var(--indigo-light), var(--pink), var(--cyan));
  opacity: 0.7;
}
.contact-form-title {
  font-family: 'Syne', sans-serif;
  font-size: 1.4rem; font-weight: 700; color: #fff;
  margin-bottom: 0.4rem;
}
.contact-form-intro {
  color: var(--text-dim); font-size: 0.92rem;
  margin-bottom: 1.75rem;
}

.contact-form { display: flex; flex-direction: column; gap: 1rem; }
.form-row {
  display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;
}
.form-field { display: flex; flex-direction: column; gap: 0.4rem; }
.form-label {
  font-size: 0.75rem; font-weight: 600;
  color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.08em;
}
.contact-form input[type="text"],
.contact-form input[type="email"],
.contact-form textarea {
  width: 100%;
  padding: 0.85rem 1rem;
  background: rgba(13,13,31,0.6);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text);
  font: inherit;
  font-size: 0.95rem;
  transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
}
.contact-form textarea {
  resize: vertical; min-height: 120px;
  font-family: inherit;  /* don't fall back to monospace */
}
.contact-form input::placeholder,
.contact-form textarea::placeholder {
  color: var(--text-faint);
}
.contact-form input:focus,
.contact-form textarea:focus {
  outline: none;
  border-color: var(--indigo-light);
  background: rgba(13,13,31,0.85);
  box-shadow: 0 0 0 3px rgba(99,102,241,0.15);
}
/* Validation visual feedback — only after the user has interacted */
.contact-form input:user-invalid,
.contact-form textarea:user-invalid {
  border-color: rgba(236,72,153,0.55);
}

/* Honeypot: visually + aurally hidden, but still in the DOM so bots fill it */
.hp-field {
  position: absolute !important;
  left: -9999px !important;
  width: 1px; height: 1px;
  opacity: 0; pointer-events: none;
}

.form-actions {
  display: flex; align-items: center; gap: 1.25rem; flex-wrap: wrap;
  margin-top: 0.5rem;
}
.form-submit {
  position: relative;
  min-width: 180px;
  justify-content: center;
}
.form-submit:disabled {
  opacity: 0.7; cursor: not-allowed;
  transform: none !important;
}
.form-submit-spinner {
  display: none;
  width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.3);
  border-top-color: #fff;
  animation: spin 0.7s linear infinite;
}
.form-submit.is-loading .form-submit-text { display: none; }
.form-submit.is-loading .form-submit-spinner { display: inline-block; }

.form-status {
  font-size: 0.88rem; font-weight: 500;
  margin: 0; min-height: 1.4em; line-height: 1.4;
}
.form-status-success { color: #34d399; }
.form-status-error   { color: #fb7185; }
.form-status-loading { color: var(--text-dim); }

/* Mobile: stack the name+email row */
@media (max-width: 600px) {
  .form-row { grid-template-columns: 1fr; }
  .contact-form-wrap { padding: 1.75rem 1.25rem; }
}

/* ─── Footer ─── */
footer {
  border-top: 1px solid var(--border); padding: 2rem;
  text-align: center; color: var(--text-faint); font-size: 0.85rem;
  position: relative; z-index: 1;
}
footer span { color: var(--pink); }

/* ─── Scroll Reveals ─── */
.reveal       { opacity: 0; transform: translateY(40px);  transition: opacity 0.7s ease, transform 0.7s ease; }
.reveal-left  { opacity: 0; transform: translateX(-40px); transition: opacity 0.7s ease, transform 0.7s ease; }
.reveal-right { opacity: 0; transform: translateX(40px);  transition: opacity 0.7s ease, transform 0.7s ease; }
.reveal.visible, .reveal-left.visible, .reveal-right.visible { opacity: 1; transform: none; }

/* ─── Keyframe Animations ─── */
@keyframes gradShift   { 0%,100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }
@keyframes loadFill    { from { width: 0; } to { width: 100%; } }
@keyframes slideDown   { to { transform: translateY(0); } }
/* Slide-up entry: keeps polish but never reduces opacity, so Lighthouse contrast
   audits never catch elements mid-fade (which previously dropped 4.15:1 readings). */
@keyframes fadeUp      { from { transform: translateY(30px); } to { transform: translateY(0); } }
@keyframes shimmer     { to { background-position: 300% center; } }
@keyframes shimmerBar  { to { transform: translateX(200%); } }
@keyframes pulseAnim   { 0%,100% { opacity:1; transform: scale(1); } 50% { opacity:0.5; transform: scale(0.8); } }
@keyframes spin        { to { transform: rotate(360deg); } }
@keyframes orbFloat    { 0%,100% { transform: scale(1) translate(0,0); } 50% { transform: scale(1.1) translate(10px,-10px); } }
@keyframes float3d     { 0%,100% { transform: translateY(0) rotateY(0deg) rotateX(0deg); } 25% { transform: translateY(-10px) rotateY(3deg) rotateX(2deg); } 50% { transform: translateY(-5px) rotateY(-2deg) rotateX(-1deg); } 75% { transform: translateY(-12px) rotateY(1deg) rotateX(3deg); } }
@keyframes marquee     { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes certFadeIn  { to { opacity: 1; } }
@keyframes lbIn        { from { transform: scale(0.92); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@keyframes rippleAnim  { 0% { transform: scale(0.8); opacity: 1; } 100% { transform: scale(1.2); opacity: 0; } }
@keyframes pulseGlow   { 0%,100% { box-shadow: 0 0 40px rgba(99,102,241,0.4); } 50% { box-shadow: 0 0 80px rgba(236,72,153,0.5); } }

/* ─── Skills Timeline Chart ───
   Single-purpose Skills section: a line chart showing growth over time.
   Built in js/skills-timeline.js using pure SVG (no chart library) to keep
   the bundle minimal and CSP-friendly. */

/* Host container — populated by JS on init. */
.skills-timeline-host {
  position: relative;        /* tooltip positions itself against this */
  margin-top: 2.5rem;
  min-height: 380px;
}
.st-chart-root {
  position: relative;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}
.st-chart-root.st-visible { opacity: 1; transform: translateY(0); }
.st-chart-root.st-no-anim { transition: none; }

/* The SVG itself scales with viewBox; cap height so it stays readable. */
.st-chart {
  width: 100%;
  height: auto;
  max-height: 460px;
  display: block;
  overflow: visible;          /* dots near the edges shouldn't get clipped */
  font-family: 'DM Sans', sans-serif;
}

/* Axes: faint horizontal gridlines, dimmer tick marks, dim labels. */
.st-grid {
  stroke: var(--border);
  stroke-width: 1;
  stroke-dasharray: 2 4;
}
.st-tick {
  stroke: var(--text-faint);
  stroke-width: 1;
  opacity: 0.5;
}
.st-axis-label {
  fill: var(--text-faint);
  font-size: 11px;
  font-weight: 500;
}

/* One <g> per skill — color and dash come from inline --skill-color. */
.st-skill { transition: opacity 0.25s; }
.st-skill.hidden { display: none; }

.st-line {
  fill: none;
  stroke: var(--skill-color);
  stroke-width: 2.25;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.95;
  transition: stroke-width 0.2s, opacity 0.2s;
  filter: drop-shadow(0 1px 6px color-mix(in srgb, var(--skill-color) 35%, transparent));
}
.st-skill:hover .st-line { stroke-width: 3; opacity: 1; }

.st-dot {
  fill: var(--bg);
  stroke: var(--skill-color);
  stroke-width: 2.5;
  cursor: pointer;
  transition: r 0.15s, fill 0.15s, transform 0.15s;
  transform-origin: center;
  transform-box: fill-box;
}
.st-dot:hover,
.st-dot:focus-visible {
  fill: var(--skill-color);
  outline: none;
}
.st-dot:focus-visible { stroke: var(--text); stroke-width: 2.5; }

/* Tooltip: positioned by JS in chartRoot coordinates. */
.st-tooltip {
  position: absolute;
  z-index: 5;
  left: 0; top: 0;
  max-width: 260px;
  padding: 0.65rem 0.85rem;
  background: var(--bg2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--skill-color, var(--indigo-light));
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 30px rgba(0,0,0,0.5);
  pointer-events: none;
  opacity: 0;
  transform: translateY(2px);
  transition: opacity 0.15s ease, transform 0.15s ease;
}
.st-tooltip.visible { opacity: 1; transform: translateY(0); }
.st-tooltip .tt-skill {
  font-family: 'Syne', sans-serif;
  font-weight: 600;
  font-size: 0.92rem;
  color: var(--text);
  margin-bottom: 0.15rem;
}
.st-tooltip .tt-meta {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--skill-color, var(--text-dim));
  margin-bottom: 0.35rem;
}
.st-tooltip .tt-note {
  font-size: 0.82rem;
  color: var(--text-dim);
  line-height: 1.45;
}

/* Legend: pill-style toggle buttons, one per skill. */
.st-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 1.25rem 0 0;
  padding: 0;
  list-style: none;
}
.st-legend-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.85rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-dim);
  font-size: 0.78rem;
  font-weight: 500;
  cursor: pointer;
  transition: color 0.2s, border-color 0.2s, background 0.2s, opacity 0.2s;
}
.st-legend-btn:hover { color: var(--text); border-color: var(--indigo-light); }
.st-legend-btn.active { color: var(--text); }
.st-legend-btn:not(.active) {
  opacity: 0.45;
  text-decoration: line-through;
}
.st-legend-btn:not(.active) .st-swatch { opacity: 0.4; }
.st-swatch {
  width: 22px;
  height: 3px;
  background: var(--skill-color);
  border-radius: 2px;
  display: inline-block;
  flex-shrink: 0;
}
.st-swatch-dashed {
  background: repeating-linear-gradient(
    90deg,
    var(--skill-color) 0 6px,
    transparent 6px 10px
  );
}

/* ─── Fallback "View as data table" ───
   The summary is styled as a prominent pill button so it reads as a
   real call-to-action rather than a small details-toggle.
   It's centered under the chart and uses the same gradient language
   as the primary buttons elsewhere on the site. */
.st-fallback {
  margin-top: 2rem;
  text-align: center;
}
.st-fallback-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.75rem 1.5rem;
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text);
  font-family: 'DM Sans', sans-serif;
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  user-select: none;
  list-style: none;     /* hide native marker on Firefox/Chrome */
  transition: background 0.2s, border-color 0.2s, transform 0.15s, box-shadow 0.2s;
}
.st-fallback-btn::-webkit-details-marker { display: none; }   /* Safari/Chrome */
.st-fallback-btn::marker { content: ''; }                      /* Firefox */
.st-fallback-btn:hover {
  background: linear-gradient(135deg, var(--indigo-light), var(--pink));
  border-color: transparent;
  color: #fff;
  transform: translateY(-2px);
  box-shadow: 0 10px 25px rgba(99,102,241,0.35);
}
.st-fallback-btn:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 3px;
}
.st-fb-icon {
  font-size: 1rem;
  line-height: 1;
  color: var(--cyan);
  transition: color 0.2s;
}
.st-fallback-btn:hover .st-fb-icon { color: #fff; }
.st-fb-chev {
  font-size: 0.85rem;
  line-height: 1;
  transition: transform 0.25s ease;
}
.st-fallback[open] .st-fb-chev { transform: rotate(180deg); }
/* When expanded, push the table into a styled card under the button. */
.st-fallback[open] .st-table {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.5rem 1rem;
  margin-top: 1.25rem;
  text-align: left;
}
.st-table {
  width: 100%;
  margin-top: 0.85rem;
  border-collapse: collapse;
  font-size: 0.85rem;
}
.st-table caption {
  text-align: left;
  font-size: 0.75rem;
  color: var(--text-faint);
  margin-bottom: 0.4rem;
}
.st-table th,
.st-table td {
  text-align: left;
  padding: 0.45rem 0.6rem;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
  color: var(--text-dim);
}
.st-table thead th {
  font-weight: 600;
  color: var(--text);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.st-table tbody th { font-weight: 500; color: var(--text); }

/* Narrow-screen tweaks for the timeline view. */
@media (max-width: 600px) {
  .skills-timeline-host { min-height: 320px; }
  .st-axis-label { font-size: 10px; }
  .st-legend-btn { font-size: 0.72rem; padding: 0.3rem 0.65rem; }
  .st-tooltip { max-width: 220px; }
}

/* ─── Responsive ─── */
@media (max-width: 1024px) {
  .projects-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 900px) {
  .hero-grid     { grid-template-columns: 1fr; }
  .hero-card-wrap { display: none; }
  .edu-lang-grid { grid-template-columns: 1fr; }
  .nav-links     { display: none; }
  .hamburger     { display: flex; }
}
@media (max-width: 600px) {
  .projects-grid { grid-template-columns: 1fr; }
  .container     { padding: 0 1.25rem; }
  .hero-btns     { flex-direction: column; }
  .lb-prev       { left: 0.25rem; }
  .lb-next       { right: 0.25rem; }
  /* Subtitle wraps to ~4 lines at this width; reserve the extra line
     so DM Sans 600 swapping on the bold span can't shift the column. */
  .hero-subtitle { min-height: 6.6em; }
}

/* ─── Mobile-only: skip hero entry animations to hit Lighthouse 100 ───
   On small screens, content visibility matters more than the staggered
   slide-in flair. Removing animation-delay + duration on hero elements
   lets the LCP element (hero name) paint at final position the moment
   FCP fires — ~600 ms saved on throttled-mobile CPU. Desktop animations
   are untouched. */
@media (max-width: 768px) {
  .hero-name,
  .hero-subtitle,
  .hero-btns,
  .hero-card-wrap {
    animation: none !important;
    transform: none !important;
  }
  #loader      { transition: opacity 0.2s ease, visibility 0.2s ease !important; }
  .loader-bar  { animation-duration: 0.6s !important; }
}

/* ─── Touch devices: use the native cursor ───
   The custom dot+ring cursor is a desktop-mouse effect. On touch devices it
   sits frozen because there's no mousemove stream, which looks broken.
   This block restores normal pointer behavior and hides the custom layers. */
@media (hover: none), (pointer: coarse) {
  body { cursor: auto; }
  #cursor-dot,
  #cursor-ring { display: none !important; }
}

/* ─── prefers-reduced-motion: respect OS-level animation opt-outs ───
   Users with vestibular disorders, migraine triggers, or low-power devices
   set this in their OS. We neutralize all decorative motion while keeping
   essential transitions (focus rings, hover color shifts) instant. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .reveal, .reveal-left, .reveal-right,
  .timeline-item, .project-card, .edu-card,
  .pub-card, .lang-badge { opacity: 1 !important; transform: none !important; }
  .st-chart-root  { opacity: 1 !important; transform: none !important; }
  .cli-overlay    { animation: none !important; }
  #particle-canvas,
  .hero-ring,
  .hero-orb,
  .contact-ripple { display: none !important; }
  #loader { display: none !important; }
  .marquee-track { animation: none !important; }
}

/* ─── Cisco IOS Terminal Easter Egg ───
   The full overlay + terminal frame for js/terminal.js.
   Triggered by the footer "> _ Open CLI" button or Ctrl+`.
   Black background, monospace, green text with amber prompt --
   the visual language of a Cisco IOS console.
   Kept as one section so cleanup is one delete if we ever
   sunset the feature. */

/* Hidden helper for the trigger glyph (so screen readers don't
   announce decorative chars like ">_") */
.cli-trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.45rem 0.95rem;
  margin-left: 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text-dim);
  font-family: 'JetBrains Mono', 'Consolas', 'Menlo', ui-monospace, monospace;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: color 0.2s, border-color 0.2s, background 0.2s, transform 0.15s;
}
.cli-trigger:hover {
  color: #0f0;
  border-color: #0f0;
  background: rgba(0, 255, 0, 0.06);
  transform: translateY(-1px);
}
.cli-trigger:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 3px;
}
.cli-trigger-icon {
  color: #0f0;
  font-weight: 700;
}

/* Full-screen modal backdrop. z-index above lightbox (99990) and cursor (100000)
   so nothing else can poke through. */
.cli-overlay {
  position: fixed;
  inset: 0;
  z-index: 100010;
  display: flex;
  align-items: flex-end;          /* slide up from the bottom */
  justify-content: center;
  background: rgba(0, 0, 0, 0.78);
  backdrop-filter: blur(8px) saturate(140%);
  -webkit-backdrop-filter: blur(8px) saturate(140%);
  animation: cli-fade-in 0.2s ease;
  /* Restore the native cursor inside the terminal. The site sets
     `body { cursor: none }` so the custom dot+ring cursor can take
     over for browsing, but inside a terminal the user wants to see
     where they're pointing/typing. The custom cursor (z-index 100000)
     is also drawn beneath this overlay (z-index 100010), so without
     this override there'd be no pointer at all. */
  cursor: default;
}
/* Text cursor over the typeable areas. */
.cli-screen,
.cli-input    { cursor: text; }
/* Make sure the trigger + close still feel like buttons. */
.cli-close    { cursor: pointer; }
.cli-overlay[hidden] { display: none !important; }

@keyframes cli-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Base frame — shared between the inline (hero) and overlay (modal)
   instances. Sizing/border-radius are scoped per-mode below so the same
   DOM tree can render edge-to-edge in the overlay AND in a card-shaped
   container inside the hero grid. */
.cli-frame {
  background: #0a0a0a;
  border: 1px solid #1a1a1a;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  font-family: 'JetBrains Mono', 'Consolas', 'Menlo', ui-monospace, monospace;
  /* Subtle scanline texture so it reads as "CRT" without being garish */
  position: relative;
}

/* Overlay-mode sizing: docked at the bottom of the viewport, no bottom
   edge so it reads as "rising from the dock". */
.cli-overlay .cli-frame {
  width: min(960px, 100vw);
  height: 70vh;
  max-height: 720px;
  border-bottom: none;
  border-radius: 12px 12px 0 0;
  box-shadow: 0 -20px 60px rgba(0, 255, 0, 0.06),
              0 -2px 0 rgba(0, 255, 0, 0.12);
}
.cli-frame::before {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    0deg,
    rgba(0, 255, 0, 0.02) 0,
    rgba(0, 255, 0, 0.02) 1px,
    transparent 1px,
    transparent 3px
  );
  pointer-events: none;
  mix-blend-mode: screen;
}

.cli-header {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.55rem 1rem;
  background: linear-gradient(180deg, #161616 0%, #0a0a0a 100%);
  border-bottom: 1px solid #1f1f1f;
  color: #888;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  user-select: none;
}
.cli-title { color: #0f0; font-weight: 600; }
.cli-close {
  background: none;
  border: 0;
  color: #888;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.15rem 0.5rem;
  border-radius: 4px;
  transition: color 0.15s, background 0.15s;
}
.cli-close:hover       { color: #ff5555; background: rgba(255, 85, 85, 0.1); }
.cli-close:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 2px;
}

.cli-screen {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 1rem 1.2rem 0.4rem;
  font-size: 0.85rem;
  line-height: 1.45;
  color: #0f0;
  scrollbar-color: #2a2a2a transparent;
  scrollbar-width: thin;
}
.cli-screen::-webkit-scrollbar       { width: 6px; }
.cli-screen::-webkit-scrollbar-thumb { background: #2a2a2a; border-radius: 3px; }
.cli-screen::-webkit-scrollbar-track { background: transparent; }

.cli-output { white-space: pre; }
.cli-line   { white-space: pre-wrap; word-break: break-word; min-height: 1.45em; }
.cli-line-input { color: #ddd; }
.cli-prompt-inline { color: #ffb000; font-weight: 600; }

.cli-prompt-line {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 1.2rem 0.85rem;
  background: #0a0a0a;
  border-top: 1px solid #1a1a1a;
  font-size: 0.85rem;
}
.cli-prompt {
  color: #ffb000;
  font-weight: 600;
  white-space: pre;
}
.cli-input {
  flex: 1 1 auto;
  background: transparent;
  border: 0;
  outline: 0;
  color: #0f0;
  font-family: inherit;
  font-size: inherit;
  caret-color: #0f0;
  padding: 0;
  /* Custom blinking caret on the input itself: rely on browser default,
     CSS can't reliably style the caret beyond color. The browser blink
     is what most terminals already feel like. */
}
.cli-input::placeholder { color: #2a2a2a; }
.cli-input::selection   { background: rgba(0, 255, 0, 0.3); color: #fff; }


/* Mobile overlay: terminal goes full-screen so the on-screen keyboard
   doesn't crowd the screen out of view. Scoped to .cli-overlay so the
   inline hero terminal keeps its card sizing on phones. */
@media (max-width: 600px) {
  /* dvh = "dynamic viewport height" — automatically excludes the area
     covered by the soft keyboard on every modern mobile browser
     (Chrome 108+, Safari 15.4+, Firefox 101+). With plain 100vh the
     prompt was getting pushed below the keyboard and visitors couldn't
     see what they were typing. Fallback to 100vh for old browsers
     where the dvh declaration is ignored. */
  .cli-overlay              { align-items: stretch; height: 100vh; height: 100dvh; }
  .cli-overlay .cli-frame   { width: 100vw; height: 100vh; height: 100dvh; max-height: none; border-radius: 0; }
  .cli-header               { padding: 0.85rem 1rem; font-size: 0.72rem; }
  .cli-title                { font-size: 0.7rem; }
  .cli-screen               { font-size: 0.78rem; }
  .cli-prompt-line          { font-size: 0.78rem; padding: 0.6rem 1rem 1rem; }
}

/* ─── Inline (hero) terminal ───
   Mounted into #hero-cli-mount by terminal.js on DOMContentLoaded.
   Replaces what used to be the floating profile card in the right
   column of the hero grid. The orb + ring siblings (carried over from
   the floating card) sit behind the frame so the column keeps its
   ambient glow. On <=900px the hero grid collapses to one column and
   the terminal stacks under the hero text. */
.hero-cli-wrap {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  /* Same staggered fade-in the floating card had, so the hero's entry
     animation rhythm doesn't change. */
  animation: fadeUp 0.8s 1.3s ease both;
}
.hero-cli-mount {
  position: relative;
  z-index: 2;            /* above .hero-orb (no z) and .hero-ring */
  width: min(520px, 100%);
  /* Reserve the slot's height before terminal.js mounts the cli-frame
     into it, so the hero column doesn't collapse on first paint and
     then jump open when JS runs. Matches .cli-inline .cli-frame height
     below. Without this we measured CLS = 0.021 on desktop. */
  min-height: 460px;
}
.cli-inline {
  position: relative;
  width: 100%;
}
.cli-inline .cli-frame {
  width: 100%;
  height: 460px;
  border-radius: 12px;
  /* Card-style shadow + faint green halo so it reads as "live console"
     instead of an empty box. */
  box-shadow: 0 25px 60px rgba(0, 0, 0, 0.5),
              0 0 0 1px rgba(0, 255, 0, 0.10),
              0 0 30px rgba(0, 255, 0, 0.06);
}
/* "Expand to fullscreen" affordance lives in the inline header in place
   of the overlay's close button. Clicking it opens the overlay instance. */
.cli-expand {
  background: none;
  border: 0;
  color: #888;
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.15rem 0.5rem;
  border-radius: 4px;
  transition: color 0.15s, background 0.15s;
}
.cli-expand:hover { color: #0f0; background: rgba(0, 255, 0, 0.08); }
.cli-expand:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 2px;
}

/* Tablet: hero collapses to one column; let the inline terminal go
   wider than the desktop card width and trim height a touch so it
   doesn't push the marquee too far down. */
@media (max-width: 900px) {
  .hero-cli-mount        { width: 100%; min-height: 400px; }
  .cli-inline .cli-frame { height: 400px; }
  /* The orb/ring background was sized for a centered 280px card.
     Now they wrap a wider terminal — keep them but let them sit
     behind without escaping. */
  .hero-cli-wrap .hero-orb,
  .hero-cli-wrap .hero-ring { display: none; }
}

/* Phone: shorter still, tighter type. */
@media (max-width: 600px) {
  .hero-cli-mount             { min-height: 340px; }
  .cli-inline .cli-frame      { height: 340px; }
  .cli-inline .cli-screen     { font-size: 0.78rem; padding: 0.85rem 1rem 0.4rem; }
  .cli-inline .cli-prompt-line{ font-size: 0.78rem; padding: 0.5rem 1rem 0.85rem; }
  .cli-inline .cli-header     { padding: 0.55rem 0.85rem; font-size: 0.7rem; }
}

/* ─── Utility classes (extracted from inline style="" attributes for CSP hardening) ───
   Moved out of HTML so style-src can eventually be tightened past 'unsafe-inline'.
   Keep behavior identical to the inline declarations they replace. */

/* Generic muted intro paragraph under a section title (Certificates, Contact, …). */
.section-intro {
  color: var(--text-dim);
  margin-bottom: 1.5rem;
  max-width: 500px;
}
/* Variant: flush against next element (Contact section) and slightly narrower. */
.section-intro-flush {
  margin-bottom: 0;
  max-width: 450px;
}

/* "Key Certifications" wrapper + heading inside the Education section. */
.key-certs-wrap {
  margin-top: 2rem;
}
.key-certs-heading {
  font-family: 'Syne', sans-serif;
  font-size: 1.1rem;
  font-weight: 700;
  margin-bottom: 1.25rem;
}

/* Smaller emoji size for the inline cert list (overrides .edu-icon default). */
.edu-icon-sm {
  font-size: 1rem;
}
