/* ============================================================
   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 "Certified by" ticker — same .marquee-band component,
   badge-sized items ---------- */
/* min-width:0 is REQUIRED, at every width: this is a grid item wrapping a
   max-content track, and a grid item's default min-width:auto refuses to
   shrink below that content — so the hero column stretches to the full width
   of the ticker (measured 1639px on a 375px phone) and the whole page scrolls
   sideways. Zeroing the minimum lets the column take the grid's width and the
   .marquee-viewport's own overflow:hidden do the clipping, as intended. */
.hero-certs { min-width: 0; }
/* Tighter than the default .marquee-band spacing: this sits inside the hero's
   own tight one-screen budget, under content that already has its own rhythm,
   not under a section heading with room to spare. */
.hero-certs.marquee-band { margin-top: clamp(1.25rem, 3.5vw, 2rem); }
/* Desktop: a bit more air between the buttons and the ticker, so the two read as
   separate beats. Deliberately a fixed value rather than leftover height — the
   hero centres its whole block, and spare space belongs above and below it. */
@media (min-width: 900px) {
  .hero-certs.marquee-band { margin-top: 3rem; }
}
.hero-certs .marquee-eyebrow { font-size: 0.72rem; }
/* Real badge artwork carries its own brand colour and credibility — unlike the
   plain-stroke Focus Areas icons, it shouldn't be muted to 68%. Only the
   caption stays quiet (inherited --gray), so the badge itself stays legible
   and the row still reads as understated rather than a second hero. */
.marquee-item--badge { opacity: 1; gap: 0.65rem; margin-right: clamp(2rem, 4vw, 3rem); }
.marquee-badge { height: 38px; width: auto; object-fit: contain; flex: none; display: block; }
.marquee-item--badge .marquee-label { font-size: 0.95rem; font-weight: 700; letter-spacing: 0.01em; }
@media (hover: hover) {
  .marquee-item--badge:hover .marquee-label { color: var(--ink); }
}
@media (max-width: 640px) {
  /* No margin of its own on phones: the grid row-gap above already separates it
     from the buttons, and that pair was the one obvious pocket of slack left
     between the CTAs and the badges — reclaiming it is what stops the badge row
     landing against the phone's system bar. */
  .hero-certs.marquee-band { margin-top: 0; }
  .marquee-item--badge { margin-right: clamp(1.5rem, 6vw, 2.25rem); gap: 0.55rem; }
  .marquee-badge { height: 32px; }
  .marquee-item--badge .marquee-label { font-size: 0.86rem; }
}

/* ---------- 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); }

/* ============================================================
   PROFESSIONAL BACKGROUND + TOOLS & PLATFORMS
   ============================================================ */
.background { padding-block: var(--section-y); background: var(--off-white); }

/* ---------- Professional Background (top half) ---------- */
.bg-cards {
  list-style: none; margin: var(--sp-5) 0 0; padding: 0; position: relative;
  display: grid; grid-template-columns: 1fr; gap: var(--sp-3);
}
.bg-card {
  position: relative; display: flex; flex-direction: column; gap: 0.3rem;
  background: #fff; border: var(--border); border-radius: var(--radius);
  padding: var(--sp-4); box-shadow: var(--shadow-sm);
}
.bg-period { font-weight: 700; font-size: var(--fs-micro); letter-spacing: 0.08em; text-transform: uppercase; color: #C68A16; margin: 0; }
.bg-org { font-weight: 700; font-size: 0.85rem; color: var(--purple); margin: 0; }
.bg-role { font-size: 1.05rem; color: var(--ink); margin: 0; font-weight: 600; }
.bg-desc { font-size: 0.85rem; color: var(--ink-soft); line-height: 1.5; margin: 0; }
.bg-cv-link { display: inline-block; margin-top: var(--sp-4); }

@media (min-width: 900px) {
  .bg-cards { grid-template-columns: repeat(3, 1fr); gap: var(--sp-4); margin-top: 48px; }
  .bg-card::before {
    content: ""; position: absolute; top: -26px; left: 50%; transform: translateX(-50%);
    width: 9px; height: 9px; border-radius: 50%; background: #C68A16; z-index: 1;
  }
  .bg-cards::before {
    content: ""; position: absolute; top: -22px; left: 16.6667%; right: 16.6667%; height: 1px;
    background: var(--line);
  }
}

/* ---------- Tools & Platforms (bottom half, compact panel — no interaction) ---------- */
.tools-panel { background: linear-gradient(150deg, #17130c 0%, #050505 62%); border-radius: var(--radius); padding: var(--sp-4); margin-top: var(--sp-6); }
.tools-panel .section-kicker { margin-bottom: var(--sp-3); }
.tools-panel-grid { display: grid; grid-template-columns: 1fr; }
.tools-col { padding-block: var(--sp-3); border-top: 1px solid rgba(255, 255, 255, 0.10); }
.tools-col:first-child { border-top: none; padding-top: 0; }
.tools-col-ico { display: block; width: 22px; height: 22px; color: var(--yellow); margin-bottom: 0.5rem; }
.tools-col-ico svg { width: 100%; height: 100%; display: block; }
.tools-col-title { font-family: var(--font-display); font-weight: 600; font-size: 1rem; color: var(--off-white); margin: 0 0 0.4rem; }
.tools-col-list { font-size: 0.85rem; color: rgba(250, 248, 245, 0.78); line-height: 1.5; margin: 0; }

@media (min-width: 900px) {
  .tools-panel-grid { grid-template-columns: repeat(4, 1fr); }
  .tools-col { border-top: none; padding: 0 var(--sp-3); border-left: 1px solid rgba(255, 255, 255, 0.10); }
  .tools-col:first-child { border-left: none; padding-left: 0; }
  .tools-col:last-child { padding-right: 0; }
}

/* ============================================================
   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); }
/* Two-word statuses ("In preparation", "Current program") wrap their pill onto
   a second line in the narrow two-column card grid on phones, which breaks the
   even top rhythm the cards are tuned for. Both wordings live in the markup,
   each with its own i18n key, and only one is ever rendered — so the swap works
   in FR and EN alike and screen readers never see the duplicate. */
.pill-short { display: none; }
@media (max-width: 640px) {
  .pill-long { display: none; }
  .pill-short { display: inline; }
}

/* Every card in this section — badge certifications, AI trainings, diplomas —
   shares this ONE class and ONE box model, so "exactly the same dimensions" is
   true by construction rather than by keeping several rulesets in sync by hand
   (the latter is what caused repeated gap/min-height drift bugs earlier in this
   section). Content bands (icon / title / org line / link) each reserve a fixed
   height via min-height or the .verify-link-spacer trick below, so a card is
   always the same height whether or not a given band has real content. */
.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;
  /* justify-content: flex-start (not center) — every card's status pill must
     sit the same distance from the card's top edge (the Microsoft/Google
     cards' original, airier rhythm). Badge certifications have 4 content bands
     (pill/icon/title/link) while diplomas have 5 (they also show an
     institution line); the shared .earned-card min-height below still pins
     both to the same overall height, but any surplus from the shorter 4-band
     cards falls at the BOTTOM, after the link — never pushing the pill down. */
  justify-content: flex-start;
  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 icon band so the name and verify link line up across every card in the
   section, whether it holds an official badge <img> or a .cred-icon disc. */
.earned-card .badge-link { height: 84px; }
.earned-badge { height: 78px; width: auto; max-width: 100%; object-fit: contain; }
/* All four badge source images (Transformation Leader, Generative AI Leader,
   Digital Leader, Business Professional) are now tightly cropped to their own
   visible content, each filling ~99% of its own canvas — confirmed by
   pixel-scanning. With that parity in place, a single shared height makes
   them look the same size; no per-badge boost/offset is needed anymore. */
/* Not-yet-earned badge in the certifications row: greyed to signal "in preparation" */
.earned-badge--prep { filter: grayscale(1) opacity(0.55); }
/* Soft tinted disc + line icon, for cards with no official badge image to show
   (AI trainings, diplomas) — same device as .fact-ico (About), sized to sit
   inside the shared .badge-link band above. Brand colours only: purple accent,
   or the darker gold already used as the Tools "workflow" accent (never flat
   --yellow, illegible small on a light disc). */
.cred-icon {
  width: 68px; height: 68px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
}
.cred-icon svg { width: 40px; height: 40px; display: block; }
.cred-icon--purple { background: rgba(103, 56, 108, 0.12); color: var(--purple); }
.cred-icon--amber  { background: rgba(243, 194, 0, 0.18); color: #C68A16; }
.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; }
/* Organisation / institution line — "INR · 2026", "aivancity"… One reserved-
   height line under the title on every card; badge certifications that have no
   such text use an invisible spacer (see .verify-link-spacer) of the same class
   so their box height still matches every other card. */
.edu-institution { color: var(--ink-soft); font-weight: 400; font-size: 0.85rem; line-height: 1.3; min-height: 1.3em; margin: 0; }
.verify-link { color: var(--purple); font-weight: 700; font-size: 0.88rem; }
.verify-link:hover { text-decoration: underline; }
/* Invisible same-size stand-in for cards with no organisation line or no link
   yet — keeps every .earned-card the same height (visibility:hidden reserves
   its layout space, unlike display:none, which would collapse it and
   reintroduce the misalignment this class exists to prevent). */
.verify-link-spacer { visibility: hidden; }

/* Formal education — reuses .earned-grid/.earned-card directly; no separate
   ruleset (see comment above .earned-grid). */
.cred-education-block { margin-top: var(--sp-4); }

/* 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; }

/* ============================================================
   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; }
}

@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 auto;
    grid-template-areas: "lead visual" "cta meta" "certs certs";
    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; }
  /* Full-width row under both columns, so it reads as one calm base for the
     whole hero rather than belonging to either the text or the photo side.
     (min-width:0 is set unconditionally on .hero-certs — see the note there.) */
  .hero-certs { grid-area: certs; }
  /* 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: normal; 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); }
}

/* 1180px (= --container, the design's own max content width), not the usual
   1024px breakpoint: at 1024px a 4-column card is only ~230px wide, too narrow
   to reliably keep every credential/diploma title to 2 lines (measured 3-line
   wraps there). Below 1180px the grid stays 2 columns — wider cards, correct
   line count, natural scroll — matching how mobile is already allowed to
   scroll rather than force-fit content into an artificially narrow column. */
@media (min-width: 1180px) {
  /* Applies to every row in the section: badge certifications, AI trainings and
     diplomas all share .earned-grid now, so all become 4 columns together. */
  .earned-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.75rem, 6vw, 2.5rem); 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); }

  /* Centre the intro on phones, so name, title and typed line sit on the same
     axis as the portrait and the centred CTAs below.
     max-width is dropped because those caps (16ch / 46ch) are narrower than the
     phone column: the text would centre inside a narrow box still pinned left,
     which reads as off-centre.
     .hero-typed is a flex row, so it needs justify-content rather than
     text-align — and .typed-live is absolutely positioned inside the invisible
     .typed-sizer (the widest phrase, which reserves the width so the line does
     not jump as it types), so it has to be centred within that box too, else
     every shorter phrase would hug its left edge. */
  .hero-lead { text-align: center; }
  .hero-title { max-width: none; }
  .hero-typed { justify-content: center; max-width: none; }
  .typed-live { left: 50%; transform: translateX(-50%); }

  /* Reorder for mobile: name/title → portrait → CTAs → certifications ticker.
     The photo comes right after the identity block, before the buttons, rather
     than the reverse. */
  .hero-lead { order: 1; }
  /* The portrait's ::before shadow block bleeds 24px past the photo's own box
     (see .hero-portrait::before, translate(24px,24px)) — without this margin the
     grid gap is measured from the photo's box, not the shadow's visible edge, so
     the shadow appears glued to the CTA buttons below regardless of gap size. */
  .hero-visual { order: 2; margin-top: 0; margin-bottom: 24px; }
  .hero-cta { order: 3; }
  /* "Paris, France" is dropped on phones: its line plus the grid gap above it
     cost ~56px, and that is what lets the certifications ticker reach the first
     screen on a real phone (where the browser's own chrome already eats a large
     share of the height). It stays on desktop, where there is room for it, and
     the location is still published in the page's Person JSON-LD. */
  .hero-location { display: none; }
  /* Spacing comes from the base .hero-certs.marquee-band rule (and its <=640px
     step); only the running order belongs here. */
  .hero-certs { order: 4; }

  /* 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.
     23dvh (was 29dvh) is what buys the certs ticker its place in the first
     screen: everything else in the mobile hero measures ~533px, so on a
     standard 812px-tall phone the photo's budget is ~197px — 29dvh overshot it
     at 235px and pushed the ticker below the fold. Height-relative, so taller
     phones still get a larger photo without re-breaking the fit. */
  .hero-portrait {
    width: auto; height: clamp(170px, 23dvh, 260px);
    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 CTAs are capped rather than edge-to-edge: a button spanning the full
     gutter-to-gutter width reads like a mobile app form, not an editorial page.
     Centred and held to a comfortable measure, they keep the premium feel while
     staying an easy tap target. Scoped to the hero so nothing else changes. */
  .hero-actions { flex-direction: column; align-items: center; }
  .hero-actions .btn { max-width: 240px; }
}

/* ============================================================
   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, .background {
    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); the whole block
     (name → photo → buttons → certs ticker) is centred within that space, so
     spare height splits evenly above and below and the composition stays
     balanced on every screen — never glued to the fold, never floating.
     The gap between the buttons and the ticker is a fixed, deliberate value
     (see .hero-certs margin below), NOT leftover space: letting a stretched
     grid row absorb the slack instead blew that gap out to ~210px on a tall
     viewport. Padding stays trimmed (was 80px) to keep the hero compact on
     short screens like 1366x768. */
  .hero { justify-content: center; padding-block: 32px; }
  .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, .background { justify-content: flex-start; }
  .what > .container { flex: 1; display: flex; flex-direction: column; }
  .what-grid { flex: 1; align-content: center; }

  /* Work/Credentials/Background: 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, .background { padding-block: clamp(1.5rem, 3vw, 2.5rem); }

  /* Background+Tools: keep the combined block compact enough to fit one screen at
     typical desktop heights (~1440×900) — tighten further here if verification
     shows overflow, rather than touching the section's own base spacing. */
  .bg-cards { margin-top: 32px; }
  .bg-cv-link { margin-top: var(--sp-3); }
  .tools-panel { margin-top: var(--sp-4); padding: var(--sp-3) var(--sp-4); }
  .tools-panel .section-kicker { margin-bottom: 0.6rem; }
  .tools-col { padding-block: 0.6rem; }

  /* 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: three rows now (four badge certifications → two AI trainings →
     four diplomas), all sharing one .earned-card box model, in a slot tuned for
     two. Everything below buys back vertical space so the section still fits
     one screen: only padding, gaps and icon sizes are trimmed — no content is
     dropped and text sizes are untouched. Every rule here applies to all three
     rows at once (single shared class), so nothing can drift out of sync.
     Independent of Work/Tools, which have no headroom left to give. */
  .credentials { padding-block: clamp(0.3rem, 0.7vw, 0.5rem); }
  .credentials .section-kicker { margin-bottom: 1rem; }
  .credentials .cred-group-label { margin-bottom: 0.5rem; }
  .earned-grid { gap: 0.6rem; }
  .earned-card { padding: 0.85rem var(--sp-4); gap: 0.35rem; }
  .earned-card .badge-link { height: 56px; }
  .earned-badge { height: 48px; }
  .cred-icon { width: 40px; height: 40px; }
  .cred-icon svg { width: 22px; height: 22px; }
  /* 2.5em, not 2 — a genuine wrapped 2-line title (line-height 1.25) needs 2.5em;
     2em was silently too small and only "worked" because most titles are long
     enough to overflow it anyway. (.cred-compact below opts back out of this for
     the two titles that are always one line.) */
  .earned-card .cred-name { min-height: 2.5em; }
  /* Badge certifications have 4 content bands (pill/icon/title/link); diplomas
     have 5 (they also show an institution line) and are naturally taller as a
     result. This value pins both rows to the taller one; .earned-card's
     justify-content:flex-start (see base rule) keeps every pill the same
     distance from the card's top edge — the surplus on shorter (4-band) cards
     falls at the bottom instead. Re-measure after touching padding/gap above. */
  .earned-card:not(.cred-compact) { min-height: 194px; }
  .cred-education-block { margin-top: 0.7rem; }

  /* .cred-compact — the two AI-training cards, explicitly allowed to be shorter
     than the rest of the section (not "exactly the same dimensions"): with 8
     badge/diploma cards already needing a 2-line title band to avoid clipping,
     holding every one of the 10 cards to that same full height doesn't fit one
     screen. Their titles never wrap, so they don't need the 2-line band anyway —
     same width (grid column), same border/radius/shadow/content order, same icon
     size as the rest of the section, just tighter padding/gap and a 1-line title
     band to make up the difference. */
  .cred-compact { padding: 0.55rem var(--sp-4); gap: 0.3rem; }
  .cred-compact .badge-link { height: 44px; }
  .cred-compact .cred-icon { width: 38px; height: 38px; }
  .cred-compact .cred-icon svg { width: 22px; height: 22px; }
  .cred-compact .cred-name { min-height: 1.25em; }

  /* 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; }
}
