/* ============================================================
   Dalyana Vergilio de Souza — Landing page
   Design tokens → base → layout → components → sections → responsive
   ============================================================ */

/* ---------- Design tokens ---------- */
:root {
  /* Brand palette */
  --off-white: #FAF8F5;
  --off-white-2: #F1ECE4;   /* warm light panel */
  --yellow: #F3C200;
  --purple: #67386C;
  --gray: #444441;
  --black: #000000;

  /* Semantic */
  --bg: var(--off-white);
  --ink: #1c1c1a;
  --ink-soft: #56564f;
  --line: #e5ded4;
  --line-dark: rgba(250, 248, 245, 0.16);

  /* Typography */
  --font-display: "Playfair Display", Georgia, "Times New Roman", serif;
  --font-body: "Lato", system-ui, -apple-system, "Segoe UI", sans-serif;

  --fs-hero: clamp(2.6rem, 5.6vw, 4.6rem);
  --fs-h2: clamp(1.9rem, 3.6vw, 3rem);
  --fs-h3: clamp(1.25rem, 1.8vw, 1.55rem);
  --fs-lead: clamp(1.05rem, 1.5vw, 1.2rem);
  --fs-body: 1rem;
  --fs-small: 0.82rem;
  --fs-micro: 0.72rem;

  /* Spacing */
  --sp-1: 0.5rem;  --sp-2: 0.75rem; --sp-3: 1rem;  --sp-4: 1.5rem;
  --sp-5: 2rem;    --sp-6: 3rem;    --sp-7: 4rem;  --sp-8: 6rem;

  /* Layout */
  --container: 1180px;
  --gutter: clamp(1.25rem, 4vw, 3rem);
  --section-y: clamp(3.5rem, 8vw, 7rem);
  /* Clearance for the fixed floating navbar (top gap + bar height + breathing room);
     sections and #main use this to sit clear of it. Redefined for desktop below. */
  --header-h: 88px;

  /* Borders / radius / shadow */
  --radius: 14px;
  --radius-sm: 8px;
  --border: 1px solid var(--line);
  --shadow-sm: 0 1px 2px rgba(28, 28, 26, 0.05);
  --shadow-md: 0 14px 40px -18px rgba(28, 28, 26, 0.28);

  /* Motion */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --t-fast: 0.18s var(--ease);
  --t: 0.32s var(--ease);
}

/* ---------- Reset / base ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; scroll-padding-top: var(--header-h); }

body {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.65;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

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

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

h1, h2, h3 { font-family: var(--font-display); font-weight: 600; line-height: 1.1; letter-spacing: -0.01em; }

::selection { background: var(--yellow); color: var(--black); }

:focus-visible {
  outline: 2px solid var(--purple);
  outline-offset: 3px;
  border-radius: 3px;
}
.section-dark :focus-visible { outline-color: var(--yellow); }

/* ---------- Layout helpers ---------- */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section-dark { background: var(--black); color: var(--off-white); }

.skip-link {
  position: absolute;
  left: var(--sp-3); top: -60px;
  z-index: 1001; /* above the floating navbar so a focused skip-link is never hidden */
  background: var(--black); color: var(--off-white);
  padding: 0.6rem 1rem; border-radius: var(--radius-sm);
  transition: top var(--t-fast);
}
.skip-link:focus { top: var(--sp-3); }

/* The navbar is now fixed/floating (out of flow) — reserve its clearance up top */
#main { padding-top: var(--header-h); }

/* ---------- Signature ---------- */
.sig, .badge-sig {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.02em;
}
.sig-dot { color: var(--yellow); }

/* ---------- Section titles / kickers ---------- */
.section-kicker {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  margin: 0 0 var(--sp-5);
  display: flex; width: fit-content; align-items: center; gap: 0.6rem;
}
.kicker-yellow { color: var(--yellow); }
.kicker-purple { color: var(--purple); }

.section-title { font-size: var(--fs-h2); margin-bottom: var(--sp-6); max-width: 20ch; }
.section-title.dark-title { color: var(--ink); }
.section-dark .section-title { color: var(--off-white); }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; gap: 0.55rem;
  font-family: var(--font-body); font-weight: 700; font-size: 0.95rem;
  padding: 0.85rem 1.6rem; border-radius: 20px;
  min-height: 50px;
  border: 1.5px solid transparent;
  transition: transform var(--t-fast), background var(--t-fast), color var(--t-fast), box-shadow var(--t-fast);
  cursor: pointer;
}
.btn-ico { width: 20px; height: 20px; transition: transform var(--t-fast); }
.btn:hover .btn-ico { transform: translateX(3px); }
.btn:active { transform: translateY(1px); }

.btn-primary { background: var(--yellow); color: var(--black); box-shadow: var(--shadow-sm); }
.btn-primary:hover { background: #ffce1a; transform: translateY(-2px); box-shadow: var(--shadow-md); }

.btn-secondary { background: transparent; color: var(--purple); border-color: var(--purple); }
.btn-secondary:hover { background: var(--purple); color: var(--off-white); border-color: var(--purple); transform: translateY(-2px); box-shadow: var(--shadow-md); }
.section-dark .btn-secondary { color: var(--off-white); border-color: rgba(250,248,245,0.5); }
.section-dark .btn-secondary:hover { background: var(--off-white); color: var(--black); }

/* ============================================================
   HEADER
   ============================================================ */
/* Floating pill navbar: fixed, inset from every edge, never a full-width band */
.site-header {
  position: fixed; top: 12px; left: 0; right: 0; z-index: 1000;
  width: calc(100% - 24px); max-width: 1400px; margin-inline: auto;
  border-radius: 14px;
  background: rgba(250, 248, 245, 0.86);
  border: 1px solid rgba(68, 68, 65, 0.12);
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  box-shadow: 0 12px 35px rgba(0, 0, 0, 0.08);
  transition: box-shadow var(--t), backdrop-filter var(--t), height var(--t), border-color var(--t);
}
.site-header.is-scrolled {
  box-shadow: 0 16px 44px rgba(0, 0, 0, 0.12);
  border-color: rgba(68, 68, 65, 0.16);
}
.header-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-4); height: 64px; padding-inline: 16px;
}
.brand { display: inline-flex; align-items: center; }
.brand .sig { font-size: 1.5rem; }

/* Nav */
.site-nav { display: flex; align-items: center; }
.nav-menu { list-style: none; padding: 0; display: flex; gap: clamp(1rem, 2.4vw, 2rem); }
.nav-menu a {
  position: relative; font-weight: 400; font-size: 0.95rem; color: var(--ink-soft);
  padding: 0.4rem 0; transition: color var(--t-fast);
}
.nav-menu a::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 2px; width: 0;
  background: var(--yellow); transition: width var(--t);
}
.nav-menu a:hover { color: var(--ink); }
.nav-menu a:hover::after { width: 100%; }
/* Active section: same subtle underline, persistent rather than hover-only */
.nav-menu a.is-active { color: var(--ink); }
.nav-menu a.is-active::after { width: 100%; }

.nav-toggle {
  display: none; width: 44px; height: 44px; border: none; background: none;
  cursor: pointer; flex-direction: column; gap: 5px; align-items: center; justify-content: center;
}
.nav-toggle-bar { width: 22px; height: 2px; background: var(--ink); transition: transform var(--t), opacity var(--t); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bar:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Language switcher — small refined capsule, separated from the nav links */
.lang-toggle {
  display: inline-flex; align-items: center; gap: 0.3rem;
  font-weight: 700; font-size: 0.8rem;
  padding: 0.3rem 0.55rem; border-radius: 100px;
  background: rgba(68, 68, 65, 0.055);
  border: 1px solid rgba(68, 68, 65, 0.10);
}
.lang-btn {
  background: none; border: none; cursor: pointer; padding: 0.25rem 0.3rem;
  color: var(--ink-soft); letter-spacing: 0.04em; transition: color var(--t-fast);
  min-height: 32px;
}
.lang-btn.is-active { color: var(--ink); text-decoration: underline; text-decoration-color: var(--yellow); text-decoration-thickness: 2px; text-underline-offset: 4px; }
.lang-btn:hover { color: var(--ink); }
.lang-sep { color: var(--line); }

@media (min-width: 900px) {
  :root { --header-h: 112px; } /* 20px top gap + ~76px bar + breathing room */
  .site-header {
    top: 20px; width: calc(100% - 48px); border-radius: 18px;
  }
  .site-header.is-scrolled { height: auto; }
  .site-header.is-scrolled .header-inner { height: 72px; }
  .header-inner { height: 76px; padding-inline: 32px; transition: height var(--t); }
  /* True centring: nav links sit in the middle column regardless of the width of
     the logo vs. the language capsule on either side. */
  .header-inner { display: grid; grid-template-columns: 1fr auto 1fr; }
  .brand { justify-self: start; }
  .site-nav { justify-self: center; }
  .lang-toggle { justify-self: end; }
}

/* ============================================================
   HERO
   ============================================================ */
.hero { padding-block: clamp(2.5rem, 6vw, 5rem) var(--section-y); }
.hero-grid { display: grid; grid-template-columns: 1fr; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
.eyebrow {
  color: var(--purple); font-weight: 700; font-size: var(--fs-small);
  letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: var(--sp-5);
  display: inline-flex; align-items: center; gap: 0.6rem;
}
.hero-title { font-size: clamp(2.3rem, 4.4vw, 3.6rem); font-weight: 700; line-height: 1.08; margin-bottom: var(--sp-5); max-width: 16ch; }
.hero-support { font-size: var(--fs-lead); color: var(--ink-soft); max-width: 46ch; margin-bottom: 0; }

/* ---------- Hero typewriter line ---------- */
.hero-typed {
  font-family: var(--font-body); font-size: var(--fs-lead); font-weight: 500;
  color: var(--ink-soft); line-height: 1.4; max-width: 46ch;
  margin-top: calc(-1 * var(--sp-3)); margin-bottom: var(--sp-4);
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.4em;
}
.typed { position: relative; display: inline-block; }
.typed-sizer { visibility: hidden; white-space: nowrap; padding-right: 6px; }
.typed-live { position: absolute; left: 0; top: 0; white-space: nowrap; }
.typed-text { color: var(--purple); }
.typed-caret {
  display: inline-block; width: 2px; height: 1em; margin-left: 3px;
  background: var(--purple); border-radius: 1px; vertical-align: -0.12em;
  animation: typed-blink 1.05s steps(1, end) infinite;
}
@keyframes typed-blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .typed-caret { display: none; } }
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-bottom: var(--sp-6); }
.hero-actions .btn { min-width: 224px; justify-content: center; }
.hero-location { display: flex; align-items: center; gap: 0.5rem; color: var(--ink-soft); font-size: 0.9rem; margin-top: var(--sp-4); }
.hero-location .pin { width: 18px; height: 18px; color: var(--purple); flex: none; }
.dot-sep { color: var(--line); }

.hero-visual { display: flex; justify-content: center; }

/* ---------- Hero marquee — seamless certifications & ecosystems ticker ---------- */
.marquee-band { width: 100%; margin-top: clamp(2rem, 4vw, 3.25rem); }
.marquee-head {
  display: flex; align-items: center; gap: clamp(0.85rem, 2vw, 1.6rem);
  margin-bottom: clamp(0.85rem, 1.6vw, 1.2rem);
}
.marquee-rule { flex: 1; height: 1px; background: linear-gradient(90deg, transparent, #d8d0c2 55%, transparent); }
.marquee-eyebrow {
  font-family: var(--font-body); font-weight: 700; font-size: var(--fs-small);
  letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink-soft); white-space: nowrap;
}
.marquee-viewport {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
}
.marquee-track {
  display: flex; width: max-content;
  animation: hero-marquee 42s linear infinite;
  will-change: transform;
}
/* Two identical lists; shifting the track by exactly one list (-50%) loops seamlessly */
@keyframes hero-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.marquee-list { display: flex; margin: 0; padding: 0; list-style: none; }
.marquee-item {
  display: inline-flex; align-items: center; gap: 0.6rem;
  margin-right: clamp(2.25rem, 4.5vw, 4rem); white-space: nowrap;
  color: var(--gray); opacity: 0.68; transition: opacity var(--t-fast);
}
.marquee-ico { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; color: inherit; flex: none; }
.marquee-ico svg { width: 100%; height: 100%; display: block; }
/* Brand logos: muted greyscale by default, true colour on hover (premium logo-strip feel) */
.marquee-logo { width: 21px; height: 21px; object-fit: contain; display: block; filter: grayscale(1); transition: filter var(--t-fast); }
.marquee-label { font-family: var(--font-body); font-weight: 700; font-size: 1rem; letter-spacing: 0.01em; }
@media (hover: hover) {
  .marquee-viewport:hover .marquee-track { animation-play-state: paused; }
  .marquee-item:hover { opacity: 1; }
  .marquee-item:hover .marquee-logo { filter: grayscale(0); }
}
/* Marquee adapted for the dark "What I Do" section */
.section-dark .marquee-eyebrow { color: var(--yellow); }
.section-dark .marquee-rule { background: linear-gradient(90deg, transparent, rgba(250, 248, 245, 0.3) 55%, transparent); }
.section-dark .marquee-item { color: rgba(250, 248, 245, 0.72); opacity: 1; }
.section-dark .marquee-logo { filter: brightness(0) invert(0.85); opacity: 0.82; }
@media (hover: hover) {
  .section-dark .marquee-item:hover { color: var(--off-white); }
  .section-dark .marquee-item:hover .marquee-logo { filter: none; opacity: 1; }
}
@media (max-width: 640px) {
  .marquee-band { margin-top: 1.75rem; }
  .marquee-item { margin-right: clamp(1.5rem, 6vw, 2.25rem); gap: 0.5rem; }
  .marquee-ico { width: 19px; height: 19px; }
  .marquee-label { font-size: 0.88rem; }
}

/* ---------- Hero portrait — editorial 4:5 photo block ---------- */
.hero-portrait {
  position: relative;
  width: 100%; max-width: 300px; margin-inline: auto;
  aspect-ratio: 4 / 5;
}
/* soft cream/yellow accent block, gently offset down-right, behind the photo */
.hero-portrait::before {
  content: ""; position: absolute; z-index: 0; inset: 0;
  transform: translate(24px, 24px);
  border-radius: 40px;
  background: rgba(243, 194, 0, 0.14);
}
.hero-portrait-img {
  position: relative; z-index: 1;
  width: 100%; height: 100%;
  object-fit: cover; object-position: center top;
  border-radius: 38px;
  display: block;
}
/* the purple outline sits AROUND the photo with an even gap (not glued to it) */
.hero-portrait::after {
  content: ""; position: absolute; z-index: 2; inset: -13px;
  border: 1.5px solid var(--purple);
  border-radius: 48px;
  pointer-events: none;
}
/* ============================================================
   WHAT I DO
   ============================================================ */
.what { padding-block: var(--section-y); }
.what-grid { display: grid; grid-template-columns: 1fr; gap: 0; }
.what-col { padding: var(--sp-5) 0; border-top: 1px solid var(--line-dark); }
.what-col:first-child { border-top: none; }
.what-ico { display: inline-flex; margin-bottom: var(--sp-3); }
.what-ico svg { width: 40px; height: 40px; }
.what-heading { font-size: var(--fs-h3); color: var(--off-white); margin-bottom: 0.35rem; }
.expertise-label {
  font-weight: 700; font-size: var(--fs-micro); letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--yellow); margin-bottom: var(--sp-3);
}
.what-desc { color: rgba(250, 248, 245, 0.78); max-width: 40ch; margin-bottom: 0; }

/* ============================================================
   SELECTED WORK
   ============================================================ */
.work { padding-block: var(--section-y); background: var(--off-white); }
/* Carousel — one large slide at a time */
.work-carousel { position: relative; }
/* height is JS-synced to the active slide (initWorkCarousel) so the viewport
   only ever shows one slide's worth of height — without this the flex track
   below would make every slide as tall as the tallest (the stacked academic
   workflow on mobile), leaving the shorter slides full of dead space. */
.work-viewport { overflow: hidden; border-radius: var(--radius); border: var(--border); background: #fff; transition: height 0.45s var(--ease); }
/* align-items:flex-start (not the flex default `stretch`) lets each slide keep
   its own natural height so it can be measured individually for the height
   sync above; stretch would report every slide at the tallest one's height. */
.work-track { display: flex; align-items: flex-start; transition: transform 0.55s var(--ease); will-change: transform; }
.work-slide {
  flex: 0 0 100%; min-width: 0;
  display: grid; grid-template-columns: 1fr;
}
.work-slide-visual { position: relative; overflow: hidden; min-height: 210px; }
.work-slide-visual svg { width: 100%; height: 100%; display: block; }
/* Ready for a real image per project (fills the left panel). Positioned absolute
   (out of flow) so the image's own intrinsic aspect ratio can't inflate the grid
   row's auto height — it should always take whatever height the row ends up at,
   never dictate it. */
/* object-position biased toward the bottom: the signature line sits right at the
   image's bottom edge, so it's the first thing lost to cover-cropping at panel
   ratios narrower than the image's own — the bold top title bar can absorb a
   little more crop without becoming unreadable, the thin signature line can't. */
.work-slide-visual img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 88%; display: block; }
.work-slide-body {
  padding: clamp(1.4rem, 2.6vw, 1.75rem);
  display: flex; flex-direction: column; justify-content: flex-start; gap: clamp(0.7rem, 1.3vw, 0.85rem);
}
.work-slide-body .project-title { font-size: clamp(1.4rem, 3vw, 2.1rem); }
.work-slide-body .project-desc { font-size: 1rem; }
.work-slide-body .project-outputs { margin-top: 0.4rem; }
/* Tags always sit at the card's bottom edge, regardless of how much text sits above
   them, instead of trailing the description at a variable height. */
.work-slide-body .chips { margin-top: auto; }

/* Controls */
.work-arrow {
  position: absolute; top: 50%; z-index: 3;
  width: 48px; height: 48px; border-radius: 50%;
  background: #fff; border: 1px solid var(--line); color: var(--ink);
  display: grid; place-items: center; cursor: pointer;
  box-shadow: var(--shadow-md);
  transition: background var(--t-fast), color var(--t-fast), transform var(--t-fast);
}
.work-arrow svg { width: 22px; height: 22px; }
.work-arrow:hover { background: var(--ink); color: var(--off-white); }
.work-prev { left: 0; transform: translate(-45%, -50%); }
.work-next { right: 0; transform: translate(45%, -50%); }
.work-prev:hover { transform: translate(-45%, -50%) scale(1.06); }
.work-next:hover { transform: translate(45%, -50%) scale(1.06); }
.work-arrow:focus-visible { outline: 2px solid var(--purple); outline-offset: 2px; }

.work-dots { display: flex; justify-content: center; gap: 0.55rem; margin-top: var(--sp-4); }
.work-dot {
  width: 9px; height: 9px; border-radius: 50%; border: none; padding: 0;
  background: #d3c9ba; cursor: pointer; transition: background var(--t-fast), transform var(--t-fast);
}
.work-dot.is-active { background: var(--purple); transform: scale(1.3); }
.work-dot:hover { background: var(--purple); }
.project-label {
  align-self: flex-start; font-weight: 700; font-size: var(--fs-micro);
  letter-spacing: 0.1em; text-transform: uppercase; white-space: nowrap;
  padding: 0.34rem 0.72rem; border-radius: 100px;
}
/* Solid theme colours for the label pill */
.pl-anonymized { background: var(--gray); color: var(--off-white); }
.pl-academic { background: var(--purple); color: var(--off-white); }
.pl-public { background: var(--yellow); color: var(--black); }
/* Title in the brand purple (not flat black) so the text block isn't all grey/ink —
   matches the purple already used for section kickers elsewhere on the page. */
.project-title { font-size: var(--fs-h3); color: var(--purple); }
.project-context { color: var(--ink-soft); font-size: 0.92rem; font-style: italic; }
.project-desc { color: var(--ink); font-size: 0.95rem; }
.project-outputs { font-size: 0.85rem; color: var(--ink-soft); margin-top: auto; padding-top: var(--sp-2); }
.mini-label {
  display: block; font-weight: 700; font-size: var(--fs-micro); letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink); opacity: 0.55; margin-bottom: 0.15rem;
}
.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; padding-top: 0.25rem; }
/* Light purple tint (same family as the status pills) instead of flat grey/off-white,
   so the tag row reads as a deliberate accent rather than neutral filler. */
.chip {
  font-size: var(--fs-micro); font-weight: 700; letter-spacing: 0.03em;
  color: var(--purple); border: 1px solid rgba(103, 56, 108, 0.18); border-radius: 100px;
  padding: 0.24rem 0.6rem; background: rgba(103, 56, 108, 0.08);
}

/* Project 3 — two real deck slides composed as an overlapping pair, on the
   warm off-white panel colour (not a random accent) so it stays on-brand. */
.pv-public { background: var(--off-white-2); }
.deck-collage { position: relative; width: 100%; height: 100%; }
/* Each slide keeps its native 16:9 ratio and is never cropped (object-fit:
   contain): sized and offset so both sit fully inside the panel with no
   overflow, echoing a loose "deck preview" stack rather than a single cover.
   Scoped with an extra class (.pv-public) so this out-specifies the generic
   .work-slide-visual img rule (full-bleed cover) used by other slides. */
.pv-public .deck-slide {
  position: absolute; inset: auto; height: auto; aspect-ratio: 16 / 9; object-fit: contain;
  background: #fff; border: var(--border); border-radius: 10px;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.16);
}
.pv-public .deck-slide-1 { top: 9%; left: 4%; width: 60%; z-index: 1; }
.pv-public .deck-slide-2 { right: 3%; bottom: 7%; width: 64%; z-index: 2; }

/* Project 2 — HR-Flow case study, laid out as a 3×2 grid:
     chat      | workflow | workflow   <- primary row, 2/3 of the height
     storage   | notify   | stack      <- secondary row, 1/3 of the height
   01 (chat) and 02 (workflow) are the primary story and get equal height
   and most of the width; 03/04 sit directly under 01/02 respectively, and
   the tool stack fills the space under the right half of 02 — its own
   bordered card, no step number, since it isn't a workflow stage.
   .flow-panel is position:absolute (out of flow), same reasoning as the
   other two project visuals: its own content must never dictate the grid
   row's auto-height, it should always just fill whatever height that row
   ends up being. */
/* contain:paint is a stronger guarantee than overflow:hidden alone — it forces
   the browser to never paint this element's descendants outside its box,
   which matters here since .flow-panel stacks several position:absolute /
   shadowed sub-panels that could otherwise be promoted to their own
   compositing layer and escape a plain overflow clip. */
.pv-academic { background: var(--off-white-2); contain: paint; }
.flow-panel--grid {
  position: absolute; inset: 0; padding: clamp(0.75rem, 1.8vw, 1.15rem);
  display: grid; gap: clamp(0.55rem, 1.3vw, 0.85rem);
  grid-template-columns: 0.85fr 1fr 1fr;
  grid-template-rows: 2fr 1fr;
  grid-template-areas: "chat workflow workflow" "storage notify stack";
}
/* justify-content:center — the secondary-row cells (storage/notify/stack)
   don't all need the same height to render their content, so without this
   the shorter ones sit flush at the top of their grid row, leaving invisible
   slack at the bottom; that slack silently breaks true vertical centering
   between cards (e.g. the tech-stack card centers on the row, not on
   storage's actual card, if storage itself isn't centered too). Centering
   every cell's content within its own row makes any two cards' visual
   centers match by construction, not by a hand-tuned offset. Chat/workflow
   have no slack to redistribute (their image fills via flex:1 1 auto), so
   this is a no-op for them. */
.flow-cell { display: flex; flex-direction: column; justify-content: center; min-height: 0; min-width: 0; }
.flow-cell--chat { grid-area: chat; }
.flow-cell--workflow { grid-area: workflow; }
.flow-cell--storage { grid-area: storage; }
.flow-cell--notify { grid-area: notify; }
.flow-cell--stack { grid-area: stack; }

.flow-step { display: flex; align-items: center; gap: 0.35rem; flex: 0 0 auto; margin-bottom: 0.3rem; }
.flow-step-num {
  width: 18px; height: 18px; border-radius: 50%; background: var(--purple); color: var(--off-white);
  font-size: 0.62rem; font-weight: 700; display: flex; align-items: center; justify-content: center; flex: none;
}
.flow-step-label { font-size: 0.7rem; font-weight: 700; color: var(--ink); letter-spacing: 0.01em; }
/* Storage/notify are the secondary tier — visibly smaller step markers. */
.flow-cell--storage .flow-step, .flow-cell--notify .flow-step { gap: 0.3rem; margin-bottom: 0.2rem; }
.flow-cell--storage .flow-step-num, .flow-cell--notify .flow-step-num { width: 14px; height: 14px; font-size: 0.5rem; }
.flow-cell--storage .flow-step-label, .flow-cell--notify .flow-step-label { font-size: 0.58rem; }

/* No card here on purpose: these two screenshots already read as self-contained
   UI mockups (their own window chrome / canvas edge), so they float directly
   on the panel's warm background instead of sitting inside another white card
   — only the built "storage" / "notify" mockups below get real card treatment. */
.flow-shot { position: relative; flex: 1 1 auto; min-height: 0; overflow: hidden; }
/* contain (not cover): these are informational screenshots — cropping their
   text/UI detail would defeat the point of showing them. Scoped with
   .pv-academic (as with .pv-public above) to out-specify the generic
   .work-slide-visual img full-bleed rule used by project 1's slide. */
.pv-academic .flow-shot img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; object-position: 50% 50%; }

.flow-store-panel {
  flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column;
  background: #fff; border: var(--border); border-radius: 8px;
  box-shadow: 0 8px 18px rgba(0, 0, 0, 0.07); padding: clamp(0.35rem, 0.8vw, 0.55rem);
}
/* Storage's card sizes to its own content instead of flex-growing to fill the
   cell — a flex-grow child eats all free space itself, leaving nothing for
   the parent's justify-content:center to distribute, which is exactly what
   was throwing off true vertical alignment against the tech-stack card. */
.flow-cell--storage .flow-store-panel { flex: 0 0 auto; }
.flow-store-head {
  display: flex; align-items: center; gap: 0.32rem; flex: 0 0 auto;
  font-weight: 700; font-size: 0.56rem; color: var(--ink); margin-bottom: 0.2rem; white-space: nowrap;
}
/* Storage (Airtable) has more headroom than notify's tight two-line cards —
   give it a bit more air between the header and the table rows. */
.flow-store-airtable .flow-store-head { margin-bottom: 0.55rem; }
.pv-academic .flow-store-ico { position: static; width: 12px; height: 12px; flex: none; object-fit: contain; }
.flow-store-table { width: 100%; border-collapse: collapse; }
.flow-store-table th {
  text-align: left; font-size: 0.46rem; font-weight: 700; letter-spacing: 0.03em;
  text-transform: uppercase; color: var(--gray); padding-bottom: 0.18rem;
}
.flow-store-table td { font-size: 0.54rem; color: var(--ink); padding: 0.16rem 0; border-top: 1px solid var(--line); white-space: nowrap; }
.flow-fit {
  display: inline-block; font-size: 0.44rem; font-weight: 700; letter-spacing: 0.02em;
  padding: 0.07rem 0.34rem; border-radius: 100px; white-space: nowrap;
}
.flow-fit--high { background: rgba(103, 56, 108, 0.12); color: var(--purple); }
.flow-fit--medium { background: rgba(243, 194, 0, 0.22); color: #7a5f00; }
.flow-fit--low { background: var(--off-white-2); color: var(--gray); border: 1px solid var(--line); }
.flow-fit--flag { background: transparent; color: var(--gray); border: 1px dashed #cfc6b8; }

/* Notify stacks two compact notification cards (Slack for the recruiter,
   email for the candidate) — each just a name/title + one meta line. */
.flow-notify-stack { display: flex; flex-direction: column; gap: clamp(0.3rem, 0.7vw, 0.4rem); flex: 1 1 auto; min-height: 0; }
.flow-notify-stack .flow-store-panel { flex: 1 1 50%; }
.flow-slack-msg, .flow-email-msg {
  background: var(--off-white-2); border-radius: 5px; padding: 0.3rem 0.42rem;
  flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; justify-content: center;
}
.flow-slack-title, .flow-email-title { font-size: 0.5rem; font-weight: 700; color: var(--ink); margin: 0; }
.flow-slack-name { font-size: 0.54rem; font-weight: 700; color: var(--purple); margin: 0.08rem 0 0; }
.flow-slack-meta, .flow-email-meta { font-size: 0.46rem; color: var(--gray); margin: 0.06rem 0 0; }

/* Tool stack cell — deliberately set apart (own bordered card, no step
   number) since it's metadata about the project, not a workflow stage.
   Icons wrap in a compact grid rather than a single row, since this cell
   is narrower than the full panel width it used to span. */
/* Dark card on purpose: every other block on this panel is white-on-beige, so
   a same-toned "tech stack" card reads as more filler than content. Dark +
   yellow kicker instead borrows the site's own "dark section" language (see
   the black What I Do section) and gives the tool list real visual weight. */
/* align-self:center centers the (label + card) block within the row — same
   treatment .flow-cell--storage now gets (see .flow-cell--storage
   .flow-store-panel above). With both blocks centered on the same row, their
   overall centers already match; the remaining piece is making the label's
   own footprint identical on both sides (below), so the two *visible cards*
   — not just the blocks — end up centered against each other too. */
.flow-cell--stack { align-self: center; }
/* Matches .flow-cell--storage .flow-step exactly: same line-height (the
   14px the secondary-tier step-number circle sets) and same margin-bottom.
   If this label's own footprint ever differs from that row's, the black
   card's centre will drift from the white card's centre again — the two
   are only guaranteed to line up because both "label + gap" footprints are
   identical, independent of either card's own height (see the maths in the
   session notes: centering two blocks of equal label-offset on the same row
   always aligns their inner boxes, regardless of box height). */
.flow-tools-label {
  font-size: 0.48rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink); line-height: 14px; margin-bottom: 0.2rem;
}
.flow-tools-box { background: var(--black); border-radius: 8px; padding: clamp(0.35rem, 0.7vw, 0.5rem); }
.flow-tools {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 0.32rem 0.6rem;
}
.flow-cell--stack .flow-tool { color: var(--off-white); font-size: 0.58rem; }
.pv-academic .flow-tool img { position: static; width: 15px; height: 15px; flex: none; object-fit: contain; }
/* OpenAI's and Voiceflow's marks are solid black — invisible on this dark card
   without inverting them, same fix already used for single-colour logos in
   the Tools section. */
.flow-cell--stack .flow-tool img[src*="openai"],
.flow-cell--stack .flow-tool img[src*="Voiceflow"] { filter: brightness(0) invert(1); }

/* ============================================================
   CREDENTIALS
   ============================================================ */
.credentials { padding-block: var(--section-y); background: var(--off-white-2); }
.cred-layout {
  display: grid; gap: clamp(1.5rem, 3vw, 2.2rem);
  grid-template-columns: 1fr;
}
.cred-group-label {
  font-weight: 700; font-size: var(--fs-micro); letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--gray); margin-bottom: var(--sp-3);
}
.status-pill {
  display: inline-block; font-weight: 700; font-size: var(--fs-micro);
  letter-spacing: 0.06em; text-transform: uppercase; padding: 0.25rem 0.6rem;
  border-radius: 100px; margin-bottom: 0.5rem;
}
.status-certified { background: var(--yellow); color: var(--black); }
.status-current { background: rgba(103, 56, 108, 0.12); color: var(--purple); }
.status-completed { background: var(--purple); color: var(--off-white); }
.status-prep { background: rgba(243, 194, 0, 0.16); color: var(--black); }

/* A. Earned certifications — three identical cards */
.earned-grid {
  list-style: none; padding: 0; margin: 0;
  display: grid; gap: clamp(1rem, 2vw, 1.5rem);
  grid-template-columns: repeat(2, 1fr);
}
.earned-card {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: 0.7rem;
  background: #fff; border: var(--border); border-radius: var(--radius);
  padding: var(--sp-4); box-shadow: var(--shadow-md);
}
.earned-card .status-pill { margin: 0; }
.badge-link { display: inline-flex; align-items: center; justify-content: center; border-radius: var(--radius-sm); transition: transform var(--t-fast); }
.badge-link:hover { transform: scale(1.03); }
/* Fixed badge band so the name and verify link line up across all three cards */
.earned-card .badge-link { height: 84px; }
.earned-badge { height: 78px; width: auto; max-width: 100%; object-fit: contain; }
/* Digital Leader art fills its whole frame; trim its height so its ring matches the other Google badge */
.earned-badge--tight { height: 70px; }
/* Not-yet-earned badge in the certifications row: greyed to signal "in preparation" */
.earned-badge--prep { filter: grayscale(1) opacity(0.55); }
.earned-card .cred-name { font-size: 1.05rem; line-height: 1.25; margin: 0; min-height: 2.5em; display: flex; align-items: center; justify-content: center; }
.cred-name { font-size: 1.2rem; margin: 0.15rem 0 0.4rem; }
.verify-link { color: var(--purple); font-weight: 700; font-size: 0.88rem; }
.verify-link:hover { text-decoration: underline; }
/* Invisible same-size stand-in for diplomas with no RNCP link yet — keeps
   every .edu-card the same height (visibility:hidden reserves its layout
   space, unlike display:none, which would collapse it and re-introduce the
   misalignment between cards that have a trailing link and those that don't). */
.verify-link-spacer { visibility: hidden; }

/* Education + In preparation row */
.cred-education-block { margin-top: var(--sp-4); }
.edu-grid {
  display: grid; gap: clamp(1rem, 2vw, 1.5rem);
  grid-template-columns: repeat(2, 1fr);
}

/* B. Education — same vertical, centered card language as earned-card, so the
   four diplomas read as one consistent grid with the certifications above. */
.edu-card {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  /* flex-start (not center): the pill/icon/name/institution/year must all start at
     the same height across every card, whether or not a card has a trailing RNCP
     link — centering the whole block would push shorter cards' content down. */
  justify-content: flex-start; gap: 0.5rem;
  background: #fff; border: var(--border); border-radius: var(--radius);
  padding: var(--sp-4); box-shadow: var(--shadow-md);
}
.edu-ico svg { width: 36px; height: 36px; flex: none; }
.edu-card .cred-name { font-size: 1.05rem; line-height: 1.25; margin: 0; min-height: 2.5em; display: flex; align-items: flex-start; justify-content: center; }
.edu-institution { color: var(--ink); font-weight: 700; font-size: 0.92rem; margin: 0; }
.edu-meta { color: var(--ink-soft); font-size: 0.85rem; margin: 0; }
.edu-logo { width: 150px; height: auto; opacity: 0.92; }

/* C. In preparation (lower emphasis) */
.prep-grid { list-style: none; padding: 0; display: grid; gap: var(--sp-3); grid-template-columns: 1fr; }
.prep-card {
  display: flex; align-items: center; gap: var(--sp-3);
  background: rgba(255, 255, 255, 0.5); border: 1px dashed #cfc6b8;
  border-radius: var(--radius-sm); padding: var(--sp-3);
}
.prep-card img {
  width: 48px; height: 48px; object-fit: contain; flex: none;
  filter: grayscale(1) opacity(0.55); transition: filter var(--t);
}
.prep-card:hover img { filter: grayscale(0.4) opacity(0.85); }
/* Certified item: full-colour badge, solid card (like an earned credential) */
.prep-card--color { background: #fff; border-style: solid; border-color: var(--line); }
.prep-card--color img, .prep-card--color:hover img { filter: none; }
.prep-card--color .prep-name { color: var(--ink); }
.prep-name { font-weight: 700; font-size: 0.9rem; color: var(--gray); margin-bottom: 0.35rem; }

/* ============================================================
   TOOLS
   ============================================================ */
.tools { padding-block: var(--section-y); background: var(--off-white); }

/* Category cards (AI Assistants featured dark; the rest light) */
.tools-cats {
  display: grid; grid-template-columns: 1fr;
  gap: clamp(1rem, 1.8vw, 1.5rem);
}
.tool-cat {
  --accent: var(--purple);
  position: relative;
  background: #fff; border: 1px solid #EFE8DD; border-radius: 18px;
  padding: clamp(1rem, 1.5vw, 1.35rem);
  box-shadow: 0 2px 22px -14px rgba(28, 28, 26, 0.28);
}
.tool-cat--workflow { --accent: #C68A16; }
.tool-cat--dark {
  --accent: var(--yellow);
  background: linear-gradient(150deg, #17130c 0%, #050505 62%);
  border: none; color: var(--off-white); overflow: hidden;
}
/* Subtle dotted glow in the featured card's corner */
.tool-cat--dark::after {
  content: ""; position: absolute; top: -50px; right: -50px; width: 190px; height: 190px;
  background: radial-gradient(circle, rgba(243, 194, 0, 0.4) 1px, transparent 1.6px);
  background-size: 11px 11px;
  -webkit-mask-image: radial-gradient(circle at center, #000 22%, transparent 68%);
          mask-image: radial-gradient(circle at center, #000 22%, transparent 68%);
  opacity: 0.55; pointer-events: none;
}
.tool-cat-ico { display: block; width: 26px; height: 26px; color: var(--accent); margin-bottom: 0.65rem; position: relative; z-index: 1; }
.tool-cat-ico svg { width: 100%; height: 100%; display: block; }
.tool-cat-title {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.02rem, 1.2vw, 1.2rem); line-height: 1.2; margin: 0 0 0.9rem;
  position: relative; z-index: 1;
}
.tool-cat--dark .tool-cat-title { color: var(--off-white); }
.tool-cat-title::after {
  content: ""; display: block; width: 26px; height: 2.5px; border-radius: 2px;
  margin-top: 0.55rem; background: var(--accent);
}

.tool-cat-grid {
  list-style: none; padding: 0; margin: 0;
  display: grid; gap: clamp(0.5rem, 0.8vw, 0.7rem);
  grid-template-columns: repeat(2, 1fr);
  position: relative; z-index: 1;
}
.tool-cat-grid--three { grid-template-columns: repeat(3, 1fr); }

/* Tile (shared) — compact; a fixed 2-line caption box keeps every tile the same height */
.tool {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.4rem; text-align: center;
  padding: 0.75rem 0.5rem;
  background: #fff; border: var(--border); border-radius: var(--radius-sm);
  transition: border-color var(--t-fast), transform var(--t-fast), box-shadow var(--t-fast);
}
.tool:hover { border-color: var(--purple); transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.tool img { object-fit: contain; display: block; }
.tool-logo { width: 27px; height: 27px; }
.tool-name {
  font-weight: 700; font-size: 0.79rem; color: var(--gray); line-height: 1.2;
  min-height: 2.4em; display: flex; align-items: center; justify-content: center;
}
/* KNIME & NotebookLM ship only as wordmarks; the wordmark self-labels (no caption) */
.tool--wordmark .tool-wordmark { width: auto; height: auto; max-height: 17px; max-width: 74%; }

/* Dark-card tiles + logos that need to read on black */
.tool-cat--dark .tool { background: rgba(255, 255, 255, 0.04); border-color: rgba(255, 255, 255, 0.10); }
.tool-cat--dark .tool:hover { border-color: rgba(255, 255, 255, 0.28); background: rgba(255, 255, 255, 0.06); }
.tool-cat--dark .tool-name { color: rgba(250, 248, 245, 0.92); }
.tool-cat--dark .tool-logo--invert { filter: brightness(0) invert(1); }

/* ---------- Category flip cards ----------
   Both faces share one grid cell → identical dimensions, no reflow. The existing
   card becomes the back; a minimal cover (icon + title) is the front. */
.tool-flip { position: relative; perspective: 1400px; cursor: pointer; border-radius: 18px; }
.tool-flip:focus-visible { outline: 2px solid var(--purple); outline-offset: 3px; }
.tool-flip-inner {
  display: grid; height: 100%;
  transform-style: preserve-3d;
  transition: transform 850ms cubic-bezier(0.22, 1, 0.36, 1);
}
.tool-flip-face { grid-area: 1 / 1; -webkit-backface-visibility: hidden; backface-visibility: hidden; }
.tool-flip-back { transform: rotateY(180deg); }
@media (hover: hover) {
  .tool-flip:hover .tool-flip-inner { transform: rotateY(180deg); }
}
.tool-flip.is-flipped .tool-flip-inner { transform: rotateY(180deg); }
/* Minimal cover: centre the existing icon + title, drop the underline */
.tool-flip-front { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; }
.tool-flip-front .tool-cat-ico { margin-bottom: 0.7rem; }
.tool-flip-front .tool-cat-title { margin: 0; }
.tool-flip-front .tool-cat-title::after { display: none; }
@media (prefers-reduced-motion: reduce) {
  .tool-flip-inner { transition: none; }
}

/* ============================================================
   ABOUT
   ============================================================ */
.about { padding-block: var(--section-y); background: var(--off-white-2); }
.about-grid { display: grid; grid-template-columns: 1fr; gap: clamp(2rem, 5vw, 4rem); align-items: start; }
.about-portrait {
  position: relative;
  width: clamp(200px, 40vw, 280px);
  aspect-ratio: 1 / 1;
  margin-inline: auto;
}
.about-portrait img {
  width: 100%; height: 100%;
  border-radius: 50%;
  object-fit: cover; object-position: center top; /* same crop as the header: whole head visible */
  border: 1.5px solid var(--line);
}
.about-copy p { color: var(--ink-soft); margin-bottom: var(--sp-3); max-width: 52ch; }
/* Slightly smaller than --fs-lead so the sentence settles at 3 lines, not 4,
   within the narrower middle column created by the portrait + facts columns. */
.about-copy .about-lead { font-family: var(--font-display); font-size: clamp(1rem, 1.3vw, 1.125rem); color: var(--ink); font-style: italic; }
.fact-list { list-style: none; padding: 0; margin-top: var(--sp-4); display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem 1.2rem; }
.fact-list li { position: relative; padding-left: 1.4rem; font-weight: 700; font-size: 0.92rem; color: var(--gray); }
.fact-list li::before {
  content: ""; position: absolute; left: 0; top: 0.5em; width: 8px; height: 8px;
  background: var(--yellow); transform: rotate(45deg);
}

/* About — facts as an airy icon list: soft badge icons + thin dividers between rows */
.about-facts {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column;
}
.about-facts li {
  display: flex; align-items: center; gap: 0.9rem;
  padding-block: clamp(0.65rem, 1.3vw, 0.9rem);
  border-bottom: 1px solid rgba(68, 68, 65, 0.12);
}
.about-facts li:first-child { padding-top: 0; }
.about-facts li:last-child { padding-bottom: 0; border-bottom: none; }
/* Soft lavender tile + bold purple icon — on-brand, quieter than a solid
   dark badge, so this list reads as a light "quick facts" strip rather than
   competing with the section's darker blocks elsewhere on the page. */
.fact-ico {
  flex: none; width: 38px; height: 38px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(103, 56, 108, 0.12); border-radius: 12px; color: var(--purple);
}
.fact-ico svg { width: 19px; height: 19px; display: block; }
.fact-body { display: flex; flex-direction: column; gap: 0.45rem; }
.fact-body--inline { flex-direction: row; align-items: center; flex-wrap: nowrap; gap: 0.5rem; }
.fact-body--inline .fact-langs { flex: none; }
.fact-label { color: var(--ink); font-weight: 700; font-size: 0.95rem; line-height: 1.35; white-space: nowrap; }

/* Multilingual — compact language pills replacing the old flag icons */
.fact-langs { display: inline-flex; gap: 0.3rem; }
.lang-pill {
  font-family: var(--font-body); font-size: 0.66rem; font-weight: 700; letter-spacing: 0.03em;
  color: var(--ink); border: var(--border); border-radius: var(--radius-sm);
  padding: 0.22rem 0.42rem; background: #fff; line-height: 1; box-shadow: var(--shadow-sm);
}

/* ============================================================
   CONTACT — slim single-row bar: "CONTACT" kicker + inline links
   ============================================================ */
.contact { padding-block: clamp(1.75rem, 3.5vw, 2.5rem); }
/* column-gap (not padding-inline) supplies the spacing between items —
   padding-left on every-child-but-the-first only works while the row stays
   a single line; the moment it wraps (mobile), each item becomes its own
   row and that left padding reappears as a stray indent with nothing to
   its left justifying it. gap has no such "which side" ambiguity: it only
   ever sits between two items, in a row or stacked, so every item's own
   left edge stays flush regardless of wrapping. */
.contact-links {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-wrap: wrap; align-items: center;
  column-gap: 1.25rem; row-gap: 0.6rem;
}
.contact-links li {
  display: flex; align-items: center; gap: 0.65rem;
  color: rgba(250, 248, 245, 0.9); font-size: 0.95rem;
}
.contact-links li:not(:last-child) {
  padding-right: 1.25rem;
  border-right: 1px solid rgba(250, 248, 245, 0.18);
}
.contact-links .ci { width: 18px; height: 18px; color: var(--yellow); flex: none; }
.contact-links a { border-bottom: 1px solid transparent; transition: border-color var(--t-fast), color var(--t-fast); }
.contact-links a:hover { border-bottom-color: var(--yellow); }
/* Mobile: links are already stacked one per line, so the divider (meant to
   separate items sitting side by side) has nothing left to divide — drop
   it and its padding rather than leave a stray line to the right of each. */
@media (max-width: 640px) {
  .contact-links li:not(:last-child) { padding-right: 0; border-right: none; }
}

/* ============================================================
   FOOTER
   ============================================================ */
.site-footer { background: #0a0a09; color: rgba(250, 248, 245, 0.7); padding-block: var(--sp-3); border-top: 1px solid rgba(250, 248, 245, 0.12); }
.footer-inner { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3) var(--sp-5); justify-content: space-between; }
.badge-sig { color: var(--off-white); font-size: 1.3rem; }
.footer-copy { font-size: 0.85rem; }
.footer-legal-link { color: inherit; border-bottom: 1px solid transparent; transition: border-color var(--t-fast), color var(--t-fast); }
.footer-legal-link:hover { color: var(--off-white); border-bottom-color: var(--yellow); }
.footer-meta { font-size: var(--fs-micro); letter-spacing: 0.08em; text-transform: uppercase; color: rgba(250,248,245,0.45); }
/* Mobile: the copyright and the Legal & Privacy link were free-flowing
   inline text, so they'd wrap wherever the line happened to run out of
   room — sometimes splitting "Legal &" from "Privacy" mid-phrase. Force
   two clean, deliberate lines instead of a run-on paragraph. */
@media (max-width: 640px) {
  .footer-copy { display: flex; flex-direction: column; gap: 0.3rem; }
  .footer-sep { display: none; }
}

/* ============================================================
   REVEAL ANIMATION
   ============================================================ */
.reveal { opacity: 0; transform: translateY(22px); transition: opacity 0.6s var(--ease), transform 0.6s var(--ease); }
.reveal.is-visible { opacity: 1; transform: none; }

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (min-width: 640px) {
  .earned-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 768px) {
  .what-grid { grid-template-columns: repeat(3, 1fr); gap: clamp(1.5rem, 3vw, 2.5rem); }
  .what-col { padding: 0 clamp(1rem, 2vw, 1.75rem); border-top: none; border-left: 1px solid var(--line-dark); }
  .what-col:first-child { border-left: none; padding-left: 0; }
  .edu-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 820px) {
  .work-slide { grid-template-columns: 2.3fr 1fr; }
  .work-slide-visual { min-height: 368px; }
  /* Shared min-height is set inline by equalizeSlideHeights() in script.js,
     recomputed from the tallest slide's actual height at the current width —
     a fixed px value here would either clip content at narrow widths or force
     every slide needlessly tall at wide ones. */
}

/* Below 820px the slides stack (image above text). The academic slide's full
   5-cell workflow, laid out vertically, made it several times taller than the
   other two slides. On mobile we collapse it to a single compact preview of the
   workflow diagram (the project's headline artefact) inside the same short
   visual band the other slides use — so all three slides read at the same size.
   The full breakdown remains on desktop, where there's room for it. */
@media (max-width: 819px) {
  /* Harmonise all three slides to one height so the carousel doesn't resize as
     you swipe: a fixed visual band (same for every slide) + a shared slide
     min-height set to the tallest slide's natural height (FR "Professional").
     Tags follow the description tightly rather than pinning to the bottom, so
     the leftover space lands as uniform padding beneath the card rather than a
     gap between the text and the tags. */
  .work-slide { min-height: 688px; grid-template-rows: auto 1fr; }
  .work-slide-visual { height: 230px; min-height: 230px; }
  .work-slide-body .project-desc { font-size: 0.95rem; line-height: 1.5; }
  .work-slide-body .chips { margin-top: 0.55rem; }

  /* Professional slide: the intro graphic already has its own navy border and
     white backing, so show it whole (contain) instead of cover-cropping it —
     cover was clipping the top border and bleeding grey into the band. */
  .pv-enterprise { background: var(--off-white-2); }
  .pv-enterprise img {
    object-fit: contain; object-position: center;
    padding: clamp(0.75rem, 3vw, 1.15rem);
  }

  /* Academic slide: collapse the full 5-cell workflow to a single compact
     preview of the workflow diagram (the project's headline artefact), centred
     in the same band, so it reads at the same size as the other two. The full
     breakdown remains on desktop, where there's room for it. */
  .pv-academic .flow-panel--grid {
    /* stays absolute-positioned to fill the fixed band (not static flow), then
       centres the single remaining cell instead of running the 3×2 grid. */
    display: flex; align-items: center; justify-content: center;
    grid-template-columns: none; grid-template-rows: none; grid-template-areas: none;
    gap: 0; padding: clamp(0.75rem, 3vw, 1.15rem);
  }
  .pv-academic .flow-cell--chat,
  .pv-academic .flow-cell--storage,
  .pv-academic .flow-cell--notify,
  .pv-academic .flow-cell--stack { display: none; }
  .pv-academic .flow-cell--workflow { width: 100%; margin: 0; }
  .pv-academic .flow-cell--workflow .flow-step { display: none; }
  .pv-academic .flow-shot--workflow {
    position: relative; flex: none; height: auto; aspect-ratio: 16 / 9;
    background: #fff; border: var(--border); border-radius: 10px;
    overflow: hidden; box-shadow: 0 16px 36px rgba(0, 0, 0, 0.14);
  }
  .pv-academic .flow-shot--workflow img { padding: 5px; }
}

@media (min-width: 900px) {
  .hero-grid {
    /* Text column widened so the title and typed line fit on one line each; the
       portrait has plenty of slack (it's sized by height, not by this column). */
    grid-template-columns: 1.45fr 0.55fr;
    grid-template-rows: auto auto;
    grid-template-areas: "lead visual" "cta meta";
    column-gap: clamp(2rem, 5vw, 4rem);
    row-gap: 0;
  }
  /* min-width:0 stops each item's own content (e.g. the 224px-wide buttons) from
     forcing a floor wider than its fr share, which otherwise throws off the
     column split and the photo's right-edge alignment with the header. */
  .hero-lead { grid-area: lead; min-width: 0; }
  .hero-cta { grid-area: cta; min-width: 0; }
  .hero-visual { grid-area: visual; min-width: 0; }
  /* Location sits under the photo, roughly level with the CTA row but settled toward
     its bottom (near the buttons' baseline) with real air above it, so it reads as a
     calm, separate detail rather than a label stuck to the photo's edge. */
  .hero-location { grid-area: meta; align-self: end; justify-self: end; margin-top: 0; padding-right: 13px; padding-bottom: 0.3rem; }
  /* Desktop hierarchy: bigger title and typed line, tighter CTA-to-location spacing.
     Both are forced to a single line (nowrap) at a fluid size that keeps them fitting
     the widened text column across the desktop range. */
  .hero-title {
    font-size: clamp(2.9rem, 1.4vw + 2.7rem, 4.85rem);
    white-space: nowrap; max-width: none;
  }
  .hero-typed {
    font-size: clamp(1.5rem, 0.9vw + 1.2rem, 2.15rem);
    flex-wrap: nowrap; max-width: none; margin-bottom: var(--sp-5);
  }
  .hero-actions { margin-bottom: 0.85rem; }
}

/* The 3-column About layout (portrait | copy | facts) needs real room to breathe —
   below this width the two "auto" side columns leave almost nothing for the copy
   text, so About stays single-column (stacked) until there's enough space. */
@media (min-width: 1180px) {
  /* Facts column is "auto": it always sizes to fit its own (nowrap) longest label,
     so it never overflows; the copy column takes whatever space remains. */
  .about-grid { grid-template-columns: auto minmax(0, 1fr) auto; }
  .about-facts { border-left: 1px solid var(--line); padding-left: clamp(1.25rem, 2.5vw, 2rem); }
}

@media (min-width: 1024px) {
  .earned-grid { grid-template-columns: repeat(4, 1fr); }
  .edu-grid { grid-template-columns: repeat(4, 1fr); }
}

/* Mobile nav drawer + balanced bar (logo left · EN/FR · burger right) */
@media (max-width: 899px) {
  /* Rebalance the bar: brand on the left, then the language capsule and the
     burger grouped to the right (margin-left:auto on the capsule eats the free
     space) — instead of the burger landing in the centre, which is what the
     default space-between flow produced with the nav sitting between them. */
  .header-inner { justify-content: flex-start; gap: 0.55rem; }
  .brand { order: 1; }
  .lang-toggle { order: 2; margin-left: auto; }
  .site-nav { order: 3; }

  .nav-toggle { display: flex; order: 2; }
  .nav-menu {
    position: fixed; inset: 84px 12px auto 12px;
    flex-direction: column; gap: 0; align-items: stretch;
    background: var(--off-white); border: 1px solid var(--line); border-radius: 16px;
    padding: var(--sp-3) var(--gutter) var(--sp-5);
    box-shadow: var(--shadow-md);
    transform: translateY(-8px); opacity: 0; visibility: hidden;
    transition: opacity var(--t), transform var(--t), visibility var(--t);
  }
  .nav-menu.is-open { transform: translateY(0); opacity: 1; visibility: visible; }
  .nav-menu li { border-bottom: 1px solid var(--line); }
  .nav-menu a { display: block; padding: 0.9rem 0; font-size: 1.05rem; min-height: 48px; }
  .nav-menu a::after { display: none; }

  /* Hero: compress the vertical rhythm so the whole intro (name → photo) fits
     the first screen on a standard phone, and scales down gracefully on shorter
     ones. Heights are viewport-relative (dvh) rather than fixed, so tall and
     short phones both get a sensible layout instead of one tuned to a single
     device. */
  #main { padding-top: 82px; }
  .hero { padding-block: clamp(0.75rem, 3vw, 1.5rem) clamp(1.75rem, 6vw, 2.75rem); }
  /* Generous, even rhythm between the intro elements for a calmer, more premium
     feel; the smaller photo below frees the vertical room this needs. */
  .hero-grid { gap: clamp(1.15rem, 4.2vw, 1.85rem); align-items: start; }
  .eyebrow { margin-bottom: clamp(0.85rem, 3.2vw, 1.2rem); }
  .hero-title { margin-bottom: clamp(0.7rem, 2.8vw, 1rem); line-height: 1.06; }
  .hero-typed { margin-top: 0; margin-bottom: 0; }
  .hero-actions { margin-bottom: 0; gap: clamp(0.7rem, 2.6vw, 0.95rem); }

  /* Reorder so "Paris, France" sits beneath the photo as a quiet closing detail
     at the foot of the mobile hero, rather than wedged between the buttons and
     the portrait. */
  .hero-lead { order: 1; }
  .hero-cta { order: 2; }
  .hero-visual { order: 3; margin-top: 0; }
  .hero-location { order: 4; margin-top: 0; justify-content: center; }

  /* Smaller photo (see rhythm note above): still a balanced face + upper-body
     crop, sized off the available height so it shrinks on short phones and
     breathes on tall ones, always within sensible bounds. */
  .hero-portrait {
    width: auto; height: clamp(190px, 29dvh, 285px);
    max-width: min(74vw, 260px); aspect-ratio: 4 / 4.5;
    margin-inline: auto;
  }
  .hero-portrait-img { object-position: center 12%; }
}

@media (max-width: 420px) {
  .fact-list { grid-template-columns: 1fr; }
  .cred-earned { flex-direction: column; text-align: center; align-items: center; }
  .btn { width: 100%; justify-content: center; }
  .hero-actions { flex-direction: column; align-items: stretch; }
}

/* ============================================================
   FULL-SCREEN SECTIONS (desktop) — one section per viewport + scroll snap
   ============================================================ */
@media (min-width: 900px) {
  /* Magnetic but not trapping: snaps near a boundary, lets tall sections scroll freely */
  html { scroll-snap-type: y proximity; }

  /* One section = the viewport minus the sticky header */
  .hero, .what, .work, .credentials, .tools {
    min-height: calc(100vh - var(--header-h));
    display: flex; flex-direction: column;
    scroll-snap-align: start;
  }
  /* Hero: fills to the fold (no next-section bleed on load); content sits at its
     natural, tighter height and is centred within that full space. */
  .hero { justify-content: center; padding-block: 80px; }
  .hero-grid { flex: 0 1 auto; width: 100%; align-content: center; align-items: stretch; }
  /* Portrait sits only in the "lead" row: its bottom edge lands exactly where the
     CTA row begins, and it hugs the right edge (aligned with the EN/FR toggle above).
     min-height keeps the portrait a sensible size even though the lead block (no
     longer padded out by the removed support sentence) is now fairly short. */
  .hero-visual { position: relative; min-height: clamp(300px, 30vw, 390px); }
  .hero-portrait {
    position: absolute; right: 13px; bottom: 0; left: auto; top: auto; transform: none;
    height: 92%; width: auto; max-width: 100%; aspect-ratio: 4 / 5; margin: 0;
  }

  /* Content sections: the kicker (section title) stays pinned top-left at the same
     height across every section; the main content fills and centers below it. */
  .what, .work, .credentials, .tools { justify-content: flex-start; }
  .what > .container,
  .tools > .container { flex: 1; display: flex; flex-direction: column; }
  .what-grid,
  .tools-cats { flex: 1; align-content: center; }

  /* Tools: 2×2 of equal-size cards; AI Assistants runs its 4 tiles on one row so
     every card is a single tile-row and they all match height */
  .tools-cats { grid-template-columns: 1fr 1fr; align-items: stretch; }
  .tool-cat--ai .tool-cat-grid { grid-template-columns: repeat(4, 1fr); }

  /* Work/Credentials/Tools: the generous base --section-y padding (designed pre full-
     screen layout) pushes these past one viewport; trim it so content fits without
     being cut off, matching the harmonized top gap used by every other section. */
  .work, .credentials, .tools { padding-block: clamp(1.5rem, 3vw, 2.5rem); }

  /* Work: the carousel's own vertical rhythm (kicker→carousel, carousel→dots) is
     tuned for a scrolling page, not a one-viewport slot; tighten it here so a full
     slide (image + title + description + chips) fits without pushing past the fold. */
  .work .section-kicker { margin-bottom: var(--sp-3); }
  .work-dots { margin-top: var(--sp-2); }

  /* Credentials: four earned-certification cards + four education cards is more
     rows than this slot was tuned for; tighten its own padding and internal
     spacing (independent of Work/Tools, which have no headroom left to give) so
     nothing pushes past the fold. */
  .credentials { padding-block: clamp(1rem, 2vw, 1.75rem); }
  .earned-card { padding: var(--sp-3) var(--sp-4); }
  .cred-education-block { margin-top: var(--sp-3); }
  .edu-card { padding: 1rem 1.25rem; }

  /* About + Contact: compact, content-sized (not forced to fill a screen); Contact is a slim bar.
     No snap point on About so the tail of the page (Contact + footer) stays reachable at the bottom. */
  .about { padding-block: clamp(2rem, 4vw, 3rem); }
  .contact { padding-block: clamp(1.75rem, 3vw, 2.5rem); }
}

/* ============================================================
   LEGAL & PRIVACY PAGE
   A deliberately quiet secondary page: no floating navbar, no cards, no
   marketing hierarchy — reuses the same tokens/typography as the landing
   page but at editorial (not promotional) density.
   ============================================================ */
.legal-main { padding-block: clamp(2rem, 4vw, 3rem) clamp(2.5rem, 5vw, 3.5rem); }
.legal-back {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--font-body); font-weight: 700; font-size: 0.9rem; color: var(--ink);
  margin-bottom: clamp(1.75rem, 3.5vw, 2.5rem);
}
.legal-back-ico { width: 18px; height: 18px; color: var(--yellow); flex: none; transition: transform var(--t-fast); }
.legal-back:hover .legal-back-ico { transform: translateX(-3px); }

.legal-head { margin-bottom: clamp(1.75rem, 3.5vw, 2.5rem); }
.legal-title { font-family: var(--font-display); font-weight: 700; font-size: clamp(2rem, 3.4vw, 2.8rem); line-height: 1.12; margin: 0; }
.legal-rule { display: block; width: 44px; height: 3px; background: var(--yellow); border-radius: 2px; margin: var(--sp-3) 0; }
.legal-updated { font-size: var(--fs-small); color: var(--ink-soft); margin: 0 0 var(--sp-3); }
.legal-intro { font-size: var(--fs-lead); color: var(--ink); line-height: 1.6; margin: 0; }

/* One full-width column: the numbered blocks read straight down the page, each
   spanning the container edge to edge. The page scrolls freely, so spacing is
   set for comfortable reading rather than squeezed to fit a single viewport. */
.legal-grid { display: grid; grid-template-columns: 1fr; }
.legal-col { display: flex; flex-direction: column; }
.legal-section {
  display: flex; gap: var(--sp-4);
  padding-block: clamp(1.5rem, 2.8vw, 2.1rem);
  border-top: 1px solid var(--line);
}
/* The body sits beside the number and takes all remaining width (flex:0 1 auto
   would otherwise shrink it to its content, leaving the right edge ragged). */
.legal-section > div { flex: 1; min-width: 0; }
/* Only the very first block skips the divider — the two .legal-col wrappers are
   a layout artefact of the markup, not a visual break in the stacked list. */
.legal-col:first-child > .legal-section:first-child { border-top: none; padding-top: 0; }
.legal-num {
  font-family: var(--font-display); font-weight: 700; color: var(--purple);
  font-size: 1.15rem; flex: none; line-height: 1.4;
}
.legal-section h3 { font-family: var(--font-display); font-weight: 700; font-size: var(--fs-h3); margin: 0 0 var(--sp-3); color: var(--ink); }
.legal-section p { font-size: 0.95rem; color: var(--ink-soft); line-height: 1.65; margin: 0 0 var(--sp-3); }
.legal-section p:last-child { margin-bottom: 0; }
.legal-section a { color: var(--purple); font-weight: 600; }
.legal-section a:hover { text-decoration: underline; }
.legal-label { font-weight: 700; color: var(--ink); font-size: 0.85rem; margin: 0 0 0.3rem; }
.legal-two { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); }
.legal-two > div:first-child { padding-right: var(--sp-4); border-right: 1px solid var(--line); }

/* Desktop: the page is meant to be read in one screenful, no scrolling. Running
   full width (rather than two columns) already puts most paragraphs on a single
   line, so the height left to claw back is mostly spacing — tightened here,
   plus the title/date collapsed onto one row. Mobile keeps the airier spacing
   above, where scrolling is expected. */
@media (min-width: 860px) {
  /* Pin the footer to the viewport bottom instead of leaving dead space below
     it: the page becomes a full-height flex column and the content block is
     centred in whatever room is left above the footer. */
  .legal-page { min-height: 100dvh; display: flex; flex-direction: column; }
  .legal-page .legal-main {
    flex: 1 1 auto; display: flex; flex-direction: column; justify-content: center;
  }
  .legal-page .site-footer { flex: none; }

  /* --air grows with the window's own height, past a 720px-ish floor: the
     extra room a taller window has to give goes mostly into gaps between
     blocks (the shares below), not into dead margin above/below the content
     from the centering above. Never negative, capped so very tall windows
     don't get absurd gaps. */
  .legal-page { --air: clamp(0px, calc(72dvh - 470px), 300px); }

  /* The top block (back link, title, date, intro) now gets a fair share of
     --air too, instead of most of it going to the section separators only —
     the whole page should read as evenly airy, top included. */
  .legal-main { padding-block: calc(0.2rem + var(--air) * 0.06) calc(0.3rem + var(--air) * 0.08); }
  .legal-back { margin-bottom: calc(0.35rem + var(--air) * 0.09); }

  /* Title and date stacked (date moves under the title), each on its own line. */
  .legal-head { margin-bottom: calc(0.3rem + var(--air) * 0.09); }
  .legal-title { font-size: 1.45rem; line-height: 1.15; }
  .legal-rule { display: none; }
  .legal-updated { margin: calc(0.2rem + var(--air) * 0.07) 0 0; }
  .legal-intro { margin-top: calc(0.2rem + var(--air) * 0.07); font-size: 0.96rem; line-height: 1.35; }

  /* Room before/after the separator line between sections, and between each
     section's title and its body text. */
  .legal-section { gap: var(--sp-3); padding-block: calc(0.16rem + var(--air) * 0.032); }
  .legal-section h3 { font-size: 1.02rem; margin: 0 0 calc(0.12rem + var(--air) * 0.03); }
  .legal-section p { font-size: 0.86rem; line-height: 1.35; margin: 0 0 calc(0.12rem + var(--air) * 0.0037); }
  .legal-two { gap: var(--sp-3); }
  .legal-two > div:first-child { padding-right: var(--sp-3); }

  .legal-page .site-footer { padding-block: 0.22rem; }
}

@media (max-width: 560px) {
  .legal-two { grid-template-columns: 1fr; gap: var(--sp-4); }
  .legal-two > div:first-child { padding-right: 0; border-right: none; padding-bottom: var(--sp-3); border-bottom: 1px solid var(--line); }
}

/* ============================================================
   REDUCED MOTION
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
  .reveal { opacity: 1; transform: none; }

  /* Marquee: stop scrolling, show one static, horizontally scrollable list */
  .marquee-track { animation: none !important; transform: none; width: 100%; }
  .marquee-viewport { overflow-x: auto; }
  .marquee-list[aria-hidden="true"] { display: none; }
  .marquee-item { opacity: 1; }
}
