/* ============================================================
   Gaspard Beharel — personal site
   Tokens, layout and components for the four-screen one-pager.
   ============================================================ */

:root {
  --font-serif: 'Newsreader', Georgia, serif;
  --font-sans: 'Space Grotesk', sans-serif;
  --font-cursive: 'La Belle Aurore', cursive;
  --paper: #ffffff;
  --ink: #0a0a0a;
  --mut: #6b6b6b;
  --hair: rgba(10, 10, 10, .13);
  --hair-chip: rgba(10, 10, 10, .16);
  --num: #a3a09a;
  /* Vertical room taken by the down-arrow cue at the bottom of each screen
     (its bottom offset + height). Content is centred in the space ABOVE it. */
  --gh-cue-space: 88px;
}

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

html {
  -webkit-text-size-adjust: 100%;
  /* Mandatory so a touch flick that settles between two screens is always
     pulled to a full screen (no half-cut screen) — same feel on mobile as
     on desktop. Screens taller than the viewport (long text on small phones)
     stay freely scrollable: the spec treats an oversized section as its own
     valid snap zone, so mandatory only snaps at the boundaries between screens. */
  scroll-snap-type: y mandatory;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-serif);
  font-size: 19px;
  line-height: 1.62;
  font-weight: 400;
  overflow-x: hidden;
}

::selection { background: var(--ink); color: var(--paper); }

/* ---- Screens ------------------------------------------------ */
.gh-sec { scroll-snap-align: start; }

.gh-screen {
  position: relative;
  min-height: 100vh;   /* fallback for browsers without dvh */
  min-height: 100dvh;  /* real visible height — adapts to the browser toolbar */
  display: flex;
  align-items: center;
  /* Extra bottom padding = the cue zone, so the content is centred in the
     space between the top of the screen and the down-arrows, not the full
     height (which read as slightly too low). */
  padding: 80px 40px calc(80px + var(--gh-cue-space));
}

.gh-inner {
  max-width: 720px;
  margin: 0 auto;
  width: 100%;
}

.gh-inner--center { text-align: center; }

/* ---- Scroll-reveal ----------------------------------------- */
[data-reveal] {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 1s cubic-bezier(.2, .7, .2, 1),
              transform 1s cubic-bezier(.2, .7, .2, 1);
}
[data-reveal].gh-in { opacity: 1; transform: none; }

/* ---- Down-arrow cue ---------------------------------------- */
.gh-cue-wrap {
  position: absolute;
  bottom: 30px;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
}
.gh-cue-arrow {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  line-height: 0.5;
  color: var(--mut);
  cursor: pointer;
  text-decoration: none;
  border: none;
  padding: 8px;
  animation: gh-cue 2.4s ease-in-out infinite;
}
.gh-cue-arrow svg { display: block; }
.gh-cue-arrow svg.gh-cue-2 { opacity: .4; margin-top: -4px; }

@keyframes gh-cue {
  0%, 100% { transform: translateY(0); opacity: .35; }
  50%      { transform: translateY(16px); opacity: 1; }
}

/* ---- Language Switcher (Top Right) ------------------------ */
.gh-lang-switcher {
  position: fixed;
  top: 32px;
  right: 40px;
  z-index: 900;
  pointer-events: auto;
  animation: gh-fade-in 0.8s cubic-bezier(.2, .7, .2, 1) backwards;
  animation-delay: 0.2s;
}

.gh-lang-capsule {
  display: inline-flex;
  align-items: center;
  padding: 3px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--hair-chip);
  box-shadow: 0 4px 20px rgba(10, 10, 10, 0.05);
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
}

.gh-lang-capsule:hover {
  border-color: rgba(10, 10, 10, 0.28);
  box-shadow: 0 6px 24px rgba(10, 10, 10, 0.08);
}

.gh-lang-btn {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  line-height: 1;
  padding: 6px 11px;
  border-radius: 999px;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: all 0.22s cubic-bezier(.2, .8, .2, 1);
  user-select: none;
}

.gh-lang-btn--active {
  background: var(--ink);
  color: var(--paper);
  box-shadow: 0 2px 8px rgba(10, 10, 10, 0.22);
  cursor: default;
}

.gh-lang-btn--link {
  color: var(--mut);
  background: transparent;
  cursor: pointer;
}

.gh-lang-btn--link:hover {
  color: var(--ink);
  background: rgba(10, 10, 10, 0.06);
}

.gh-lang-btn--link:active {
  transform: scale(0.96);
}

/* ---- CV download FAB (top left, mirrors the switcher) ------ */
.gh-cv {
  position: fixed;
  top: 32px;
  left: 40px;
  z-index: 900;
  animation: gh-fade-in 0.8s cubic-bezier(.2, .7, .2, 1) backwards;
  animation-delay: 0.3s;
}
.gh-cv-btn {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--hair-chip);
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 4px 20px rgba(10, 10, 10, 0.05);
  color: var(--mut);
  cursor: pointer;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: all 0.22s cubic-bezier(.2, .8, .2, 1);
  user-select: none;
}
.gh-cv-btn svg {
  width: 18px;
  height: 18px;
  display: block;
  transition: transform 0.22s cubic-bezier(.2, .8, .2, 1);
}
.gh-cv-btn:hover {
  color: var(--ink);
  border-color: rgba(10, 10, 10, 0.28);
  box-shadow: 0 6px 24px rgba(10, 10, 10, 0.08);
  transform: translateY(-2px);
}
.gh-cv-btn:hover svg { transform: scale(1.08); }
.gh-cv-btn:active { transform: scale(0.96); }

.gh-cv-pop {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  min-width: 176px;
  padding: 10px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--hair-chip);
  box-shadow: 0 14px 40px rgba(10, 10, 10, 0.14);
  opacity: 0;
  transform: translateY(-4px) scale(0.98);
  transform-origin: top left;
  pointer-events: none;
  transition: opacity 0.22s ease, transform 0.22s cubic-bezier(.2, .8, .2, 1);
}
.gh-cv.is-open .gh-cv-pop {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
.gh-cv-pop-title {
  margin: 2px 6px 0;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--ink);
}
.gh-cv-pop-hint {
  margin: 1px 6px 6px;
  font-family: var(--font-sans);
  font-size: 9.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--num);
}
.gh-cv-opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 7px 8px;
  border-radius: 9px;
  text-decoration: none;
  font-family: var(--font-sans);
  color: var(--ink);
  transition: background 0.18s ease;
}
.gh-cv-opt:hover { background: rgba(10, 10, 10, 0.055); }
.gh-cv-opt-name { font-size: 12px; font-weight: 500; }
.gh-cv-opt-tag {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--num);
}

/* ---- Handwritten hero notes (point at FAB & GitHub dock) --- */
/* Cursive = the same hand that writes the name; slight rotation and
   a late entrance make them feel annotated after the fact. They fade
   away off the hero together with the Rails colophon (main.js §4). */
.gh-hero-note {
  position: fixed;
  z-index: 900;
  display: flex;
  align-items: flex-start;
  gap: 7px;
  font-family: var(--font-cursive);
  font-size: 18px;
  line-height: 1;
  color: var(--ink);
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.45s ease, transform 0.45s ease;
  animation: gh-fade-in 0.9s cubic-bezier(.2, .7, .2, 1) backwards;
  animation-delay: 0.85s;
}
.gh-hero-note--cv {
  top: 82px;
  left: 58px;
  transform: rotate(-2deg);
}
.gh-hero-note--cv svg { margin-top: -4px; }
.gh-hero-note--projects {
  bottom: 84px;
  right: 52px;
  transform: rotate(1.4deg);
}
.gh-hero-note--projects svg { margin-top: 16px; }
.gh-hero-note--hidden { opacity: 0; }
/* While the download popover is open, the resume note steps aside —
   the popover would otherwise land on top of it. Fades back when closed. */
.gh-cv.is-open ~ .gh-hero-note--cv { opacity: 0; }

/* ---- Built-with Rails Badge (Bottom Left) ----------------- */
.gh-rails-badge {
  position: fixed;
  bottom: 5px;
  left: 5px;
  z-index: 900;
  text-decoration: none;
  animation: gh-fade-in 0.8s cubic-bezier(.2, .7, .2, 1) backwards;
  animation-delay: 0.25s;
  transition: opacity 0.45s ease, transform 0.45s ease;
}

/* Hidden once the reader starts scrolling (see main.js). */
.gh-rails-badge--hidden {
  opacity: 0;
  transform: translateY(10px);
  pointer-events: none;
}

.gh-rails-capsule {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  /* No oval outline — just a faint frosted wash so scrolling text stays
     readable behind the badge. */
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.gh-rails-badge:hover .gh-rails-label {
  color: var(--ink);
}

.gh-rails-logo {
  height: 12px;
  width: auto;
  display: block;
  transition: transform 0.25s cubic-bezier(.2, .8, .2, 1);
}

.gh-rails-badge:hover .gh-rails-logo {
  transform: scale(1.06);
}

.gh-rails-label {
  font-family: var(--font-sans);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: #9a9a9a;
  transition: color 0.25s ease;
}

/* ---- Persistent Social Dock (Bottom Right) ---------------- */
.gh-social-dock {
  position: fixed;
  bottom: 32px;
  right: 40px;
  z-index: 900;
  pointer-events: auto;
  animation: gh-fade-in 0.8s cubic-bezier(.2, .7, .2, 1) backwards;
  animation-delay: 0.35s;
}

.gh-social-capsule {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--hair-chip);
  box-shadow: 0 4px 20px rgba(10, 10, 10, 0.05);
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
}

.gh-social-capsule:hover {
  border-color: rgba(10, 10, 10, 0.28);
  box-shadow: 0 6px 24px rgba(10, 10, 10, 0.08);
}

.gh-social-btn {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: none;
  background: transparent;
  color: var(--mut);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  text-decoration: none;
  transition: all 0.22s cubic-bezier(.2, .8, .2, 1);
  user-select: none;
}

.gh-social-btn svg {
  width: 18px;
  height: 18px;
  display: block;
  transition: transform 0.22s cubic-bezier(.2, .8, .2, 1);
}

.gh-social-btn:hover {
  color: var(--ink);
  background: rgba(10, 10, 10, 0.07);
  transform: translateY(-2px);
}

.gh-social-btn:hover svg {
  transform: scale(1.08);
}

.gh-social-btn:active {
  transform: scale(0.94);
}

@keyframes gh-fade-in {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ============================================================
   01 — Hero
   ============================================================ */
.gh-hero {
  position: relative;
  min-height: 100vh;   /* fallback for browsers without dvh */
  min-height: 100dvh;  /* real visible height — adapts to the browser toolbar */
  padding: 34px 40px 26px;
}
.gh-hero-stack {
  position: absolute;
  top: 44%;
  left: 40px;
  right: 40px;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(30px, 4.2vw, 50px);
}
.gh-portrait {
  width: clamp(92px, 12vw, 128px);
  height: clamp(92px, 12vw, 128px);
  border-radius: 50%;
  overflow: hidden;
  box-shadow: 0 0 0 1px var(--hair);
  background: var(--paper);
  display: block;
  flex: none;
  transition: box-shadow 0.45s ease;
}
.gh-portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 26%;
  filter: grayscale(1) contrast(1.03);
  display: block;
  transition: filter 0.55s cubic-bezier(.2, .8, .2, 1),
              transform 0.55s cubic-bezier(.2, .8, .2, 1);
}
/* Color reveal: the graduation portrait blooms into color on hover,
   with a whisper of zoom — a small moment of life in the monochrome hero. */
.gh-portrait:hover {
  box-shadow: 0 0 0 1px rgba(10, 10, 10, 0.28), 0 6px 24px rgba(10, 10, 10, 0.10);
}
.gh-portrait:hover img {
  filter: grayscale(0) contrast(1.02);
  transform: scale(1.04);
}

/* ---- Hero Journey Capsule (FR → CN → JP → US) ------------- */
.gh-hero-journey {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  /* Lift above the name's letter spans: .gh-l's ±0.45em padding (with
     negative margin) extends its hit area over the capsule and would
     otherwise steal the hover.
     👇 margin-bottom = extra distance between the flags capsule and the
     name; growing it pushes capsule + portrait up and the name down,
     because the whole stack re-centers around its middle. */
  z-index: 1;
  margin: -2px 0 clamp(8px, 1.1vw, 14px);
  animation: gh-fade-in 0.9s cubic-bezier(.2, .7, .2, 1) backwards;
  animation-delay: 0.15s;
}

.gh-journey-capsule {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 13px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--hair-chip);
  box-shadow: 0 4px 18px rgba(10, 10, 10, 0.04);
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
}

.gh-journey-capsule:hover {
  border-color: rgba(10, 10, 10, 0.24);
  box-shadow: 0 6px 22px rgba(10, 10, 10, 0.07);
}

.gh-journey-step {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  cursor: default;
  transition: transform 0.2s ease;
  user-select: none;
}

.gh-journey-step:hover {
  transform: translateY(-1.5px) scale(1.18);
}

.gh-journey-flag {
  width: 17.6px;
  height: 12px;
  border-radius: 3px;
  overflow: hidden;
  display: inline-flex;
  box-shadow: 0 1px 4px rgba(10, 10, 10, 0.10);
  flex-shrink: 0;
  -webkit-mask-image: -webkit-radial-gradient(white, black);
  isolation: isolate;
  box-sizing: border-box;
}

/* Japan is white-on-white; a hairline border delimits its rounded shape. */
.gh-flag-jp {
  border: 1px solid rgba(10, 10, 10, 0.18);
}

.gh-journey-flag svg {
  width: 100%;
  height: 100%;
  display: block;
}

.gh-journey-code {
  font-family: var(--font-sans);
  font-size: 8.8px;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--mut);
  line-height: 1;
  transition: color 0.2s ease;
}

.gh-journey-step:hover .gh-journey-code {
  color: var(--ink);
}

.gh-journey-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--num);
  opacity: 0.7;
}

.gh-journey-arrow svg {
  width: 8.8px;
  height: 8.8px;
  display: block;
}

/* Styled by class (not #id) so the infinite-loop clone — which keeps its
   classes but has its ids stripped — retains the handwritten font. */
.gh-name {
  position: relative;
  text-align: center;
  margin: 0;
  /* Base cushion for the cursive side-bearings (esp. the g descender) in case
     the JS auto-fit doesn't run; the script overrides this with exact values. */
  padding: 0 0.22em;
  font-family: var(--font-cursive);
  font-weight: 400;
  font-size: 5.6em;
  line-height: 1.02;
  letter-spacing: normal;
  color: var(--ink);
  white-space: nowrap;
}
.gh-l, .gh-name-char {
  display: inline-block;
  animation: gh-breathe 3.4s ease-in-out infinite;
  /* iOS Safari rasterizes each animated span into a compositing layer sized
     to its border box, clipping the cursive ink that overhangs it (the g's
     descender loop on the left, the h/r swashes on the right). Padding
     enlarges the painted box; the negative margins cancel it in layout. */
  padding: 0.45em;
  margin: -0.45em;
}
.gh-name-gap { display: inline-block; width: 0.32em; }

@keyframes gh-breathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.05); }
}

/* ---- Hero tagline (student · maker · relentless) ------------ */
/* Italic serif — the language of the site's quotes, not the UI's
   small caps. The two nouns stay quiet gray; the temperament word
   carries the weight. Hugs the name with a negative pull against
   the stack's gap, and arrives last during the entrance cascade. */
.gh-hero-tag {
  /* 👇 DISTANCE TO THE NAME: actual gap = hero stack gap (30–50px
     depending on screen width) + this margin. Raise the numbers to
     push the tagline further from the name; use negatives to pull it
     closer. */
  margin: clamp(10px, 1.6vw, 18px) 0 0;
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 300;
  font-size: clamp(15px, 2vw, 21px);
  letter-spacing: 0.01em;
  line-height: 1;
  color: var(--mut);
  text-align: center;
  white-space: nowrap;
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 0.55em;
  animation: gh-fade-in 0.9s cubic-bezier(.2, .7, .2, 1) backwards;
  animation-delay: 0.5s;
}
.gh-hero-tag .gh-tag-dot {
  color: var(--num);
  font-weight: 300;
}
.gh-hero-tag strong {
  font-style: italic;
  font-weight: 400;
  color: var(--ink);
}

/* ============================================================
   02 — Présentation
   ============================================================ */
.gh-eyebrow {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--mut);
  margin: 0 0 34px;
}
.gh-eyebrow--tight { margin-bottom: 22px; }

.gh-lead { margin: 0 0 28px; color: var(--mut); }
/* Class-based (not #id) so the infinite-loop clones — whose ids are
   stripped — render exactly like the originals. */
.gh-body { margin: 0; color: var(--ink); }
.gh-body + .gh-body { margin-top: 26px; }

/* Closing "the ask" line — sans (UI voice) against the serif story,
   so it reads as a purposeful statement, not another paragraph. */
.gh-cta {
  margin: 34px 0 0;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.01em;
  line-height: 1.55;
  color: var(--ink);
}
.gh-cta .gh-num { color: var(--num); font-weight: 400; }

/* Inline emlyon quick-link badge, sitting in the lead sentence. */
.gh-school {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  color: inherit;
  margin-left: 0px;
  margin-right: 2px; /* breathing room between the badge and the comma after it */
  vertical-align: middle;
  position: relative;
  top: -2px; /* nudge the badge up so it sits optically centered on the line */
}
.gh-school-badge {
  width: 1.8em;
  height: 1.8em;
  display: block;
  border-radius: 6px;
  box-shadow: 0 2px 8px rgba(10, 10, 10, 0.08), 0 0 12px rgba(219, 10, 32, 0.30);
  transform-origin: center;
  transition: transform 0.25s cubic-bezier(.2, .8, .2, 1), box-shadow 0.25s ease;
}
.gh-school:hover .gh-school-badge {
  transform: scale(1.5) rotate(-2deg);
  box-shadow: 0 7px 20px rgba(10, 10, 10, 0.18), 0 0 24px rgba(219, 10, 32, 0.45);
}
.gh-school:active .gh-school-badge {
  transform: scale(1.08);
}
.gh-school:focus-visible {
  outline: none;
}
.gh-school:focus-visible .gh-school-badge {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

.gh-num { color: var(--num); }

/* ============================================================
   03 — Builder (projects)
   ============================================================ */
.gh-intro {
  margin: 0 0 44px;
  width: 100%;
  color: var(--ink);
  font-size: 21px;
  line-height: 1.6;
}

/* --- Project row: story left, live terminal right ------------ */
.gh-proj {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.12fr);
  gap: clamp(20px, 4vw, 52px);
  align-items: center;
  padding: clamp(28px, 5.5vh, 54px) 0;
  border-top: 1px solid var(--hair);
}
.gh-proj-title {
  margin: 0 0 6px;
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: clamp(21px, 2.6vw, 27px);
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.gh-proj-tag {
  margin: 0 0 14px;
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 300;
  font-size: 15.5px;
  line-height: 1.4;
  color: var(--mut);
}
.gh-proj-desc {
  margin: 0 0 14px;
  font-size: 16px;
  line-height: 1.62;
  color: var(--ink);
}
.gh-proj-meta {
  margin: 0 0 14px;
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--num);
}
.gh-proj-link {
  display: inline-block;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid rgba(10, 10, 10, 0.28);
  padding-bottom: 1px;
  transition: border-color 0.25s ease;
}
.gh-proj-link:hover { border-color: var(--ink); }
.gh-proj-vis { margin: 0; min-width: 0; }

/* --- Simulated terminal -------------------------------------- */
.gh-term {
  position: relative;
  border-radius: 10px;
  background: #141413;
  box-shadow: 0 10px 32px rgba(10, 10, 10, 0.16),
              0 2px 6px rgba(10, 10, 10, 0.08),
              inset 0 0 0 1px rgba(255, 255, 255, 0.05);
  overflow: hidden;
  transition: box-shadow 0.3s ease, transform 0.3s cubic-bezier(.2, .8, .2, 1);
}
.gh-proj:hover .gh-term {
  transform: translateY(-3px);
  box-shadow: 0 16px 44px rgba(10, 10, 10, 0.22),
              0 3px 8px rgba(10, 10, 10, 0.10),
              inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
.gh-term-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
  background: rgba(255, 255, 255, 0.02);
}
.gh-term-dots i {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #3d3c38;
  margin-right: 5px;
}
.gh-term-name {
  font-family: var(--font-sans);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  color: #7f7d78;
}
.gh-term-body {
  padding: 14px 16px 16px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  font-size: 12px;
  line-height: 1.8;
  color: #d9d6cf;
}
.gh-term-body p {
  margin: 0 0 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.t-dim { color: #6f6d68; }
.t-em { color: #fff; font-weight: 600; letter-spacing: 0.04em; }
.t-row {
  display: grid;
  /* ch = monospace character cells; total + gaps stays inside the panel,
     so the ≈/yr column can never collide with the right edge. */
  grid-template-columns: 5ch 7ch 9ch 7ch 6ch 2ch;
  align-items: baseline;
  gap: 8px;
  padding: 2px 0;
  white-space: nowrap;
  transition: color 0.3s ease;
}
.t-cell { flex: none; }
.t-state { font-weight: 600; }
.t-row.is-hot { color: #ff6b62; }
.t-row.is-hot .t-em { color: #ff8a80; }
.t-foot { color: #6f6d68; margin-top: 6px; }
.t-alert { display: none; color: #ff6b62; text-shadow: 0 0 12px rgba(255, 80, 70, 0.45); }
.t-alert.is-on { display: block; animation: gh-alert 2.6s ease both; }
/* "No window right now" — the quiet state, shown in calm gray. */
.t-alert.calm { color: #6f6d68; text-shadow: none; animation: none; }
@keyframes gh-alert {
  from { opacity: 0; transform: translateY(3px); }
  12%  { opacity: 1; transform: none; }
}
.t-log p { display: flex; gap: 10px; align-items: baseline; }
.t-up { color: #6fbf88; font-style: normal; }
.t-dn { color: #d76d63; font-style: normal; }
.t-caret {
  display: inline-block;
  width: 7px;
  height: 13px;
  background: #d9d6cf;
  vertical-align: -2px;
  animation: gh-caret 1.1s steps(1) infinite;
}
@keyframes gh-caret { 50% { opacity: 0; } }
.gh-term-cap {
  margin-top: 8px;
  font-family: var(--font-sans);
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--num);
  text-align: right;
}

/* ============================================================
   04 — Contact
   ============================================================ */
.gh-quote {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 300;
  font-size: clamp(26px, 4vw, 42px);
  line-height: 1.45;
  letter-spacing: -0.01em;
  margin: 0 auto 64px;
  white-space: nowrap;
  color: var(--ink);
}

.gh-links {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 28px;
  font-family: var(--font-sans);
  font-size: 14px;
  letter-spacing: 0.02em;
}
a.gh-link {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid rgba(10, 10, 10, .28);
  padding-bottom: 1px;
  transition: border-color .25s ease;
}
a.gh-link:hover { border-color: var(--ink); }

.gh-signature {
  margin-top: 72px;
  font-family: var(--font-cursive);
  font-size: clamp(38px, 6vw, 60px);
  line-height: 1;
  color: var(--ink);
}

/* ============================================================
   Mobile
   ============================================================ */
@media (max-width: 600px) {
  /* Compact everything so each screen (content + arrows) fits even the small
     toolbar-shown viewport of an iPhone SE — no overflow, no hidden arrows. */
  :root { --gh-cue-space: 58px; }

  .gh-screen { padding: 20px 26px calc(14px + var(--gh-cue-space)); }

  /* Smaller, closer down-arrows */
  .gh-cue-wrap { bottom: 20px; }
  .gh-cue-arrow { padding: 6px; }
  .gh-cue-arrow svg { width: 30px; height: 17px; }

  /* Narrower on mobile so the cursive "g" descender can never reach the left
     edge — safe even if the JS auto-fit doesn't run (e.g. an iOS quirk). */
  .gh-name { font-size: clamp(28.8px, 12.1vw, 50.4px); }

  /* Tagline: keep the (longer) French line inside 320px viewports */
  .gh-hero-tag { font-size: 13.5px; gap: 0.45em; }

  /* Eyebrows: less space above/below (this is the big top margin) */
  .gh-eyebrow { margin-bottom: 16px; }
  .gh-eyebrow--tight { margin-bottom: 14px; }

  /* Projects eyebrow: clear the fixed CV download button in the top-left
     and vertically align with the button's center (button is 32px at top:18px) */
  #gh-p3 .gh-eyebrow { padding-left: 40px; padding-top: 6px; }

  /* Screen 2 — compact the two paragraphs.
     Target the body paragraphs only, NOT the .gh-eyebrow (keeps it 12px). */
  /* Class selectors (not #gh-p2 …) so the loop clones — ids stripped —
     get the same mobile sizing as the real screens (no visible re-adjust
     when the loop teleports back). */
  .gh-lead, .gh-body { font-size: 14.5px; line-height: 1.4; }
  .gh-lead { margin: 0 0 12px; }
  .gh-body + .gh-body { margin-top: 16px; }
  .gh-cta { font-size: 13px; margin-top: 22px; line-height: 1.5; }

  /* Screen 3 — projects: single column, compact terminal */
  .gh-intro { font-size: 15px; line-height: 1.4; margin-bottom: 20px; }
  .gh-proj {
    grid-template-columns: 1fr;
    gap: 18px;
    padding: 26px 0;
  }
  .gh-proj-desc { font-size: 14.5px; line-height: 1.5; }
  .gh-term-body { font-size: 10.5px; padding: 11px 12px 12px; }
  .gh-term-bar { padding: 8px 10px; }
  .t-row { gap: 3px; }

  /* Screen 4 — tighten */
  .gh-quote { margin: 0 auto 40px; white-space: normal; }
  .gh-signature { margin-top: 44px; }

  /* Language Switcher & Social Dock mobile adjustments */
  .gh-lang-switcher { top: 18px; right: 18px; }
  .gh-lang-btn { font-size: 10px; padding: 5px 9px; }

  /* Rails badge mobile adjustment (bottom left, same as desktop) */
  .gh-rails-badge { bottom: 5px; left: 5px; }
  .gh-rails-capsule { gap: 5px; padding: 5px 10px; }
  .gh-rails-label { font-size: 9px; }
  .gh-rails-logo { height: 10.4px; }

  /* CV FAB mobile adjustment (mirrors switcher corner) */
  .gh-cv { top: 18px; left: 18px; }
  .gh-cv-btn { width: 32px; height: 32px; }
  .gh-cv-btn svg { width: 16px; height: 16px; }

  /* Handwritten notes on small screens */
  .gh-hero-note { font-size: 15px; gap: 5px; }
  .gh-hero-note--cv { top: 60px; left: 28px; }
  .gh-hero-note--projects { bottom: 68px; right: 26px; }

  .gh-social-dock { bottom: 18px; right: 18px; }
  .gh-social-capsule { gap: 2px; padding: 3px; }
  .gh-social-btn { width: 32px; height: 32px; }
  .gh-social-btn svg { width: 16px; height: 16px; }

  /* Journey Capsule mobile adjustment */
  .gh-journey-capsule {
    gap: 5.04px;
    padding: 2.7px 8.1px;
  }
  .gh-journey-flag {
    width: 12.96px;
    height: 8.64px;
    border-radius: 2.25px;
  }
  .gh-journey-code {
    font-size: 6.84px;
  }
  .gh-journey-arrow svg {
    width: 6.48px;
    height: 6.48px;
  }
}

/* Respect reduced-motion preferences */
@media (prefers-reduced-motion: reduce) {
  .gh-l, .gh-name-char, .gh-cue-arrow, .gh-lang-switcher, .gh-social-dock, .gh-hero-journey, .gh-rails-badge, .gh-hero-tag, .gh-cv, .gh-hero-note { animation: none; }
  .gh-lang-btn, .gh-social-btn, .gh-social-btn svg, .gh-journey-step, .gh-rails-logo, .gh-rails-capsule, .gh-rails-badge, .gh-portrait, .gh-portrait img, .gh-cv-btn, .gh-cv-btn svg, .gh-cv-pop, .gh-cv-opt, .gh-hero-note, [data-reveal] { transition: none; }
}
