/* ==========================================================================
   BEAU BARRE — ARCHITECTURE PORTFOLIO
   Design System & Shared Styles
   ========================================================================== */

/* -------------------------------------------------------------------------
   1. FONTS
   ------------------------------------------------------------------------- */
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,300;0,400;0,600;0,700;1,300;1,400;1,700&display=swap');

/* -------------------------------------------------------------------------
   2. DESIGN TOKENS
   ------------------------------------------------------------------------- */
:root {
  /* ---- Color palette -----------------------------------------------
     Strict 3-background / 3-font-color system, used everywhere on the
     site — every element's text color is determined solely by which of
     the three backgrounds it sits on:

       Background                          → Font color
       ------------------------------------------------------------
       --color-bg (light green, default)   → --color-charcoal (lighter brown)
       --color-surface (beige)             → --color-black (dark brown)
       --color-bg-inverse (dark green)     → --color-text-inverse (beige)

     --color-accent/--color-accent-dim/--color-gray-light/-lighter remain
     for purely decorative use (borders, underlines, hover accents) —
     never for body text or headings.
  --------------------------------------------------------------------- */
  --color-bg:        #C1C6B5;   /* Light Moss — primary background (light green) */
  --color-surface:    #EBE6DD;   /* Alabaster — beige background (nav, cards, badges, alt sections) */
  --color-black:      #392F27;   /* deep brown — font color on beige backgrounds (7.5:1 on bg) */
  --color-charcoal:   #453A30;   /* lighter brown — font color on light-green backgrounds (6.3:1 on bg) */
  --color-gray:        #594C41;   /* Smoked — reserved, not used for body text */
  --color-gray-light:  #AEB59E;   /* muted green-tan — borders, dividers */
  --color-gray-lighter: #D3D8C6;  /* faint borders / hairlines on the green bg */
  --color-accent:      #3B4A41;   /* Juniper — decorative borders/underlines/hover accents only */
  --color-accent-dim:  #8DA085;   /* Moss — richer green for hover/fill states */

  --color-bg-inverse: #3B4A41;   /* Juniper — nav (mobile panel), footer, dark sections */
  --color-text-inverse: #EBE6DD; /* Alabaster — the ONLY font color on dark-green backgrounds */

  /* ---- Typography -----------------------------------------------------
     Single unified typeface (Roboto Mono) — hierarchy comes entirely from
     weight now: 700 bold for display/hero text, 600 semibold for headings,
     buttons, and badges, 400 normal for body copy and UI labels, 300 light
     for intros (.lede) and secondary/muted text.
  --------------------------------------------------------------------- */
  --font-display: 'Roboto Mono', 'SFMono-Regular', Consolas, monospace;
  --font-body: 'Roboto Mono', 'SFMono-Regular', Consolas, monospace;
  --font-mono: 'Roboto Mono', 'SFMono-Regular', Consolas, monospace;

  /* ---- Spacing scale (rem) -------------------------------------------- */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;
  --sp-7: 3rem;
  --sp-8: 4rem;
  --sp-9: 6rem;
  --sp-10: 8rem;
  --sp-11: 10rem;

  /* ---- Layout ----------------------------------------------------------- */
  --container-max: 1440px;
  --container-pad: clamp(1.5rem, 4vw, 4rem);
  --nav-height: 84px;

  /* ---- Motion ------------------------------------------------------------ */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
  --dur-fast: 0.25s;
  --dur-mid: 0.55s;
  --dur-slow: 0.9s;
}

/* -------------------------------------------------------------------------
   3. RESET
   ------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

body, h1, h2, h3, h4, p, figure, blockquote, dl, dd {
  margin: 0;
}

ul[class], ol[class] { list-style: none; margin: 0; padding: 0; }

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

input, button, textarea, select { font: inherit; color: inherit; }

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

body {
  background: var(--color-bg);
  color: var(--color-charcoal);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* overflow-x alone would make the UA compute overflow-y: auto (per the
     CSS overflow spec, when only one axis is a non-visible keyword), which
     turns body into its own scroll container and breaks position: sticky
     for descendants (used on the About page's title bands). Setting
     overflow-y: visible explicitly keeps the normal document scroller
     while still clipping any accidental horizontal overflow. */
  overflow-x: hidden;
  overflow-y: visible;
}

/* -------------------------------------------------------------------------
   4. TYPOGRAPHY SYSTEM
   ------------------------------------------------------------------------- */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--color-charcoal);
  line-height: 1.08;
  letter-spacing: -0.01em;
}

h1 { font-weight: 700; }

strong, b { font-weight: 700; }

.display-1 {
  font-size: clamp(2.75rem, 7vw, 6.5rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
}

.display-2 {
  font-size: clamp(2.25rem, 5vw, 4.25rem);
  font-weight: 700;
  line-height: 1.04;
  letter-spacing: -0.015em;
}

h2, .h2 {
  font-size: clamp(1.9rem, 3.4vw, 2.75rem);
}

h3, .h3 {
  font-size: clamp(1.35rem, 2vw, 1.75rem);
  font-weight: 600;
}

h4, .h4 {
  font-size: 1.1rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-family: var(--font-body);
}

.lede {
  font-size: clamp(1.1rem, 1.6vw, 1.4rem);
  line-height: 1.55;
  color: var(--color-charcoal);
  font-weight: 400;
}

p { color: var(--color-charcoal); font-weight: 400; }

.text-gray { color: var(--color-charcoal); }
.text-accent { color: var(--color-accent); }

.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-charcoal);
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
}

.eyebrow::before {
  content: '';
  width: 28px;
  height: 1px;
  background: var(--color-accent);
  display: inline-block;
}

.meta-label {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-charcoal);
}

.serif-italic {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
}

/* -------------------------------------------------------------------------
   5. LAYOUT HELPERS
   ------------------------------------------------------------------------- */
.container {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}

.section {
  padding-block: var(--sp-10);
}

.section--tight { padding-block: var(--sp-6); }

/* Full-viewport section — makes a section occupy one full screen's height,
   with its content vertically centered, so each section can be brought
   fully into view on its own as the page is scrolled. Used on the About
   page. scroll-snap-align only takes effect where an ancestor opts into
   scroll-snap-type (see .snap-sections below), so this is harmless
   elsewhere. */
.section--full {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  scroll-snap-align: start;
  scroll-margin-top: var(--nav-height);
}

.snap-sections {
  scroll-snap-type: y proximity;
}

.section--alt {
  background: var(--color-surface);
  color: var(--color-black);
}
.section--alt h1, .section--alt h2, .section--alt h3, .section--alt h4,
.section--alt .eyebrow {
  color: var(--color-black);
}

.section--dark {
  background: var(--color-bg-inverse);
  color: var(--color-text-inverse);
}
.section--dark h1, .section--dark h2, .section--dark h3, .section--dark h4,
.section--dark .eyebrow {
  color: var(--color-text-inverse);
}
.section--dark p { color: var(--color-text-inverse); }

.grid {
  display: grid;
  gap: var(--sp-6);
}

.grid-12 { grid-template-columns: repeat(12, 1fr); }

@media (max-width: 900px) {
  .grid-12 { grid-template-columns: repeat(6, 1fr); }
}
@media (max-width: 560px) {
  .grid-12 { grid-template-columns: repeat(2, 1fr); }
}

.hairline {
  border: none;
  border-top: 1px solid var(--color-gray-lighter);
  margin: 0;
}

.section-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--sp-6);
  margin-bottom: var(--sp-8);
  flex-wrap: wrap;
}

.section-head__title { max-width: 640px; }
.section-head__title h2 { margin-top: var(--sp-3); }

/* -------------------------------------------------------------------------
   6. BUTTONS & LINKS
   ------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: var(--sp-4) var(--sp-6);
  border: 1px solid var(--color-black);
  background: transparent;
  color: var(--color-black);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-soft), color var(--dur-fast) var(--ease-soft), border-color var(--dur-fast) var(--ease-soft);
  white-space: nowrap;
}

.btn:hover, .btn:focus-visible {
  background: var(--color-black);
  color: var(--color-bg);
}

.btn--primary {
  background: var(--color-black);
  color: var(--color-bg);
}
.btn--primary:hover, .btn--primary:focus-visible {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: #fff;
}

.btn--accent {
  border-color: var(--color-accent);
  color: var(--color-accent);
}
.btn--accent:hover, .btn--accent:focus-visible {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: #fff;
}

.btn--ghost-light {
  border-color: rgba(235, 230, 221,0.5);
  color: var(--color-text-inverse);
}
.btn--ghost-light:hover, .btn--ghost-light:focus-visible {
  background: var(--color-text-inverse);
  color: var(--color-black);
}

.btn svg { width: 14px; height: 14px; }

.text-link {
  position: relative;
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-black);
  padding-bottom: 3px;
  background-image: linear-gradient(var(--color-accent), var(--color-accent));
  background-size: 0% 1px;
  background-repeat: no-repeat;
  background-position: left bottom;
  transition: background-size var(--dur-fast) var(--ease-soft), color var(--dur-fast) var(--ease-soft);
}
.text-link:hover, .text-link:focus-visible {
  background-size: 100% 1px;
  color: var(--color-accent);
}

/* -------------------------------------------------------------------------
   7. NAVIGATION
   ------------------------------------------------------------------------- */
.site-header {
  /* NOTE: intentionally has no backdrop-filter/filter/transform of its own —
     any of those on this element would make it a "containing block" for its
     position:fixed descendants (like the mobile .nav panel below), causing
     them to size against this 84px-tall bar instead of the full viewport.
     The blur is applied via ::before instead, which is safe. */
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  height: var(--nav-height);
  display: flex;
  align-items: center;
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-fast) var(--ease-soft);
}

.site-header::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  /* Dark green (Juniper) — matches --color-bg-inverse, translucent so the
     blur still reads through on scroll. */
  background: rgba(59, 74, 65, 0.92);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
}

.site-header.is-scrolled {
  border-bottom-color: rgba(235, 230, 221, 0.16);
}

.site-header__inner {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.brand {
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--color-text-inverse);
  display: flex;
  flex-direction: column;
  line-height: 1.1;
}
.brand span {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(235, 230, 221, 0.65);
  font-weight: 400;
}

.nav {
  display: flex;
  align-items: center;
  gap: var(--sp-8);
}

.nav__links {
  display: flex;
  gap: var(--sp-7);
}

.nav__links a {
  position: relative;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-inverse);
  padding-bottom: 6px;
}

.nav__links a::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  height: 1px;
  width: 100%;
  background: var(--color-accent-dim);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-fast) var(--ease-soft);
}

.nav__links a:hover::after,
.nav__links a:focus-visible::after,
.nav__links a[aria-current="page"]::after {
  transform: scaleX(1);
}

.nav__links a[aria-current="page"] {
  color: var(--color-text-inverse);
  font-weight: 600;
}

.nav__cta {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
}

/* Nav's Download Resume button sits on the dark green header bar, so it
   needs the light-on-dark button treatment rather than the default
   dark-on-light .btn styling. */
.site-header .nav__cta .btn {
  border-color: rgba(235, 230, 221, 0.5);
  color: var(--color-text-inverse);
}
.site-header .nav__cta .btn:hover, .site-header .nav__cta .btn:focus-visible {
  background: var(--color-text-inverse);
  color: var(--color-black);
}

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 32px;
  height: 32px;
  background: none;
  border: none;
  cursor: pointer;
  z-index: 110;
}
.nav-toggle span {
  display: block;
  height: 1px;
  width: 100%;
  /* Beige, to read against the dark green header bar behind it when the
     mobile menu is closed. */
  background: var(--color-text-inverse);
  transition: transform var(--dur-fast) var(--ease-soft), opacity var(--dur-fast) var(--ease-soft);
}
/* Once the off-canvas mobile menu is open, the toggle (now an "X") sits
   above the beige mobile nav panel instead of the dark header, so it needs
   to flip back to dark for contrast. */
.nav.is-open ~ .nav-toggle span {
  background: var(--color-black);
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

@media (max-width: 900px) {
  .nav-toggle { display: flex; }
  .nav {
    position: fixed;
    inset: 0;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
    gap: var(--sp-8);
    background: var(--color-surface);
    padding: var(--sp-10) var(--container-pad);
    transform: translateX(100%);
    transition: transform var(--dur-mid) var(--ease);
  }
  .nav.is-open { transform: translateX(0); }
  .nav__links {
    flex-direction: column;
    gap: var(--sp-6);
  }
  /* The off-canvas panel itself is beige (--color-surface, set above), so
     its links and CTA button need the dark-on-light treatment rather than
     the light-on-dark colors used for the same elements on the desktop
     dark-green header bar. */
  .nav__links a { font-size: 1.6rem; font-weight: 600; font-family: var(--font-display); text-transform: none; letter-spacing: 0; color: var(--color-black); }
  .nav__links a[aria-current="page"] { color: var(--color-black); }
  .nav__cta { flex-direction: column; align-items: flex-start; }
  .site-header .nav__cta .btn {
    border-color: var(--color-black);
    color: var(--color-black);
  }
  .site-header .nav__cta .btn:hover, .site-header .nav__cta .btn:focus-visible {
    background: var(--color-black);
    color: var(--color-bg);
  }
}

/* -------------------------------------------------------------------------
   8. HERO
   ------------------------------------------------------------------------- */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  padding-top: calc(var(--nav-height) + var(--sp-8));
  padding-bottom: var(--sp-9);
  overflow: hidden;
}

.hero__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
}
.hero__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.06);
  animation: heroZoom 16s var(--ease-soft) forwards;
}
@keyframes heroZoom {
  to { transform: scale(1); }
}
.hero__bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(30, 29, 27,0.38) 0%, rgba(30, 29, 27,0.46) 50%, rgba(30, 29, 27,0.62) 100%);
}

/* ---- Hero slideshow variant (Home) — split layout ------------------------
   A fixed info panel on the left (name + rotating caption) and a
   fixed-size rotating picture on the right. Every slide shares this same
   two-panel template — only what's inside each panel changes. ----------- */
.hero--slideshow {
  text-align: left;
  /* Panels handle their own padding. On desktop this is moot (the panels
     are position:absolute and fill the hero regardless), but on mobile
     they switch to position:relative and would otherwise double up with
     this padding, leaving gaps above/below the panels. */
  padding: 0;
}

.hero__info-panel {
  position: absolute;
  inset: 0;
  /* Asymmetric split (roughly 40/60) rather than an even 50/50 — gives the
     imagery more presence (it's a portfolio, the pictures are the point)
     and keeps the text column from floating in a wide half-empty panel on
     large screens. */
  right: 60%;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  background: var(--color-surface);
  padding: calc(var(--nav-height) + var(--sp-7)) var(--sp-8) var(--sp-8);
  overflow: hidden;
}

.hero__image-panel {
  position: absolute;
  inset: 0;
  left: 40%;
  z-index: 0;
  overflow: hidden;
}

.hero__slides {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.hero__slide {
  position: absolute;
  inset: 0;
  display: block;
  opacity: 0;
  visibility: hidden;
  transition: opacity 1.1s var(--ease-soft), visibility 0s linear 1.1s;
}
.hero__slide.is-active {
  opacity: 1;
  visibility: visible;
  z-index: 1;
  transition: opacity 1.1s var(--ease-soft), visibility 0s linear 0s;
}
.hero__slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  animation: heroZoom 16s var(--ease-soft) forwards;
}

/* Modifier for slides whose source image doesn't share the panel's aspect
   ratio (e.g. a tall/portrait render) — still fills the entire slide edge
   to edge, but anchors the crop to the top of the image so the top of the
   subject is always visible, trimming any excess off the bottom instead. */
.hero__slide--top img {
  object-position: top center;
}

/* Modifier for slides where the whole frame matters (e.g. a photo with a
   person on one side and content on the other) — shows the entire image
   with no cropping, letterboxed against a matching beige backdrop so
   nothing important is ever trimmed off the sides. */
.hero__slide--contain img {
  object-fit: contain;
  background: var(--color-surface);
}

/* On desktop the image panel sits behind the fixed header (so the picture
   can bleed edge-to-edge), which eats into the top letterbox of a
   contain-fit image and makes plain 50%/50% centering read as bottom-heavy.
   Nudge the image down by half the header height so the letterbox visible
   above and below the picture reads as equal. Mobile stacks the image panel
   below the header in normal flow, so it keeps the default centering. */
@media (min-width: 801px) {
  .hero__slide--contain img {
    object-position: 50% calc(50% + (var(--nav-height) / 2));
  }
}

/* "Featured projects" slide: three vertical images side by side, each its
   own link to a different project. A thin gap (filled with beige) separates
   the columns so they read as one cohesive strip rather than three
   unrelated photos jammed together. */
.hero__slide--triptych {
  display: flex;
  gap: 2px;
  background: var(--color-surface);
}
.hero__triptych-col {
  position: relative;
  flex: 1 1 33.3333%;
  height: 100%;
  overflow: hidden;
  display: block;
}
/* Shifts the visible crop toward the left side of the source image — use
   on a triptych image whose interesting detail (e.g. the staggered
   balconies on this building's left facade) sits left of center, so a
   narrow column crop doesn't cut it off. */
.hero__triptych-col img.is-pos-left {
  object-position: 15% center;
}
.hero__triptych-col::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(20, 24, 20, 0);
  transition: background var(--dur-fast) var(--ease-soft);
  pointer-events: none;
}
.hero__triptych-col:hover::after,
.hero__triptych-col:focus-visible::after {
  background: rgba(20, 24, 20, 0.18);
}

/* Small labeled tag centered along the bottom of each triptych image,
   naming the project it links to. */
.hero__triptych-label {
  position: absolute;
  z-index: 1;
  left: 50%;
  bottom: var(--sp-7);
  transform: translateX(-50%);
  max-width: 90%;
  padding: 5px var(--sp-3);
  background: var(--color-surface);
  color: var(--color-black);
  font-family: var(--font-mono);
  font-size: 0.76rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-align: center;
  line-height: 1.3;
  pointer-events: none;
}

.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(30, 29, 27,0) 65%, rgba(30, 29, 27,0.55) 100%);
}

/* ---- Left panel: name mark (top) + rotating caption (bottom) -----------
   NOTE: the name/submark block (.hero__content, .hero__name, .hero__submark)
   is no longer shown here — the name lives in the nav bar now — but the
   rules are kept in case a future slideshow variant wants them back. ----- */
.hero--slideshow .hero__content {
  align-items: flex-start;
  text-align: left;
  max-width: none;
  margin-inline: 0;
}

.hero--slideshow .hero__name {
  font-size: clamp(2.25rem, 3.6vw, 4rem);
  line-height: 1;
}

.hero__submark {
  margin-top: var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  font-weight: 400;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: rgba(57, 47, 39, 0.75);
}

.hero__captions {
  position: relative;
  /* Desktop: sized to the tallest caption (see main.js) so the centered
     info-panel box never changes height between slides — the title always
     lands in the same spot, whether or not that slide has a description. */
  min-height: var(--captions-max-h, 5.5rem);
  max-width: 34rem;
  transition: min-height 0.6s var(--ease-soft);
}
.hero__caption {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.5s var(--ease-soft), visibility 0s linear 0.5s;
}
.hero__caption.is-active {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.5s var(--ease-soft), visibility 0s linear 0s;
}

/* Short accent rule above every caption's title — echoes the underline
   that marks the active nav link, so the hero reads as part of the same
   design system instead of a big empty block of color. */
.hero__caption::before {
  content: '';
  display: block;
  width: 44px;
  height: 2px;
  margin-bottom: var(--sp-4);
  background: var(--color-accent-dim);
}

.hero__slide-title {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.75rem, 2.7vw, 2.75rem);
  color: var(--color-black);
  line-height: 1.15;
}

.hero__slide-meta {
  display: block;
  margin-top: var(--sp-3);
  font-family: var(--font-mono);
  font-size: 0.85rem;
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(57, 47, 39, 0.75);
}

.hero__slide-desc {
  display: block;
  margin-top: var(--sp-4);
  max-width: 40ch;
  font-size: 1.05rem;
  line-height: 1.6;
  color: rgba(57, 47, 39, 0.75);
}

.hero__cta {
  margin-top: var(--sp-5);
}

/* ---- Slideshow controls: arrows at the edges of the picture panel,
   vertically centered; dots centered along its bottom. ------------------- */
.hero__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  width: 44px;
  height: 44px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-accent-dim);
  background: rgba(59, 74, 65, 0.35);
  color: var(--color-accent-dim);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-soft), border-color var(--dur-fast) var(--ease-soft), color var(--dur-fast) var(--ease-soft);
}
.hero__arrow:hover, .hero__arrow:focus-visible {
  background: var(--color-accent-dim);
  border-color: var(--color-accent-dim);
  color: var(--color-bg-inverse);
}
.hero__arrow--prev { left: var(--sp-5); }
.hero__arrow--next { right: var(--sp-5); }

.hero__dots {
  position: absolute;
  z-index: 2;
  left: 50%;
  bottom: var(--sp-6);
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.hero__dot {
  width: 5px;
  height: 5px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(235, 230, 221, 0.28);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-soft), transform var(--dur-fast) var(--ease-soft);
}
.hero__dot.is-active {
  background: rgba(235, 230, 221, 0.7);
  transform: scale(1.3);
}

@media (max-width: 800px) {
  .hero--slideshow {
    justify-content: flex-start;
    /* Let the section size to its content (info panel + 4:3 image) rather
       than being forced to fill the viewport, which would otherwise leave
       dead empty space below the image on shorter slides. */
    min-height: auto;
  }
  .hero__info-panel {
    position: relative;
    inset: auto;
    right: auto;
    width: 100%;
    padding: calc(var(--nav-height) + var(--sp-6)) var(--sp-5) var(--sp-6);
  }
  .hero__image-panel {
    position: relative;
    inset: auto;
    left: auto;
    width: 100%;
    aspect-ratio: 4 / 3;
  }
  /* Mobile: the info panel is in normal document flow (image sits below
     it). Size to the tallest caption across all slides (same value used
     on desktop) so the box height — and therefore the image's vertical
     position — stays constant as slides change, instead of the picture
     jumping up/down to match each slide's own caption length. Short
     captions leave a small gap before the image, which is the tradeoff. */
  .hero__captions { min-height: var(--captions-max-h, 5.5rem); max-width: none; }
  .hero__arrow {
    width: 36px;
    height: 36px;
    font-size: 1.2rem;
  }
  .hero__arrow--prev { left: var(--sp-3); }
  .hero__arrow--next { right: var(--sp-3); }
  .hero__dots { bottom: var(--sp-4); }
  .hero__triptych-label {
    bottom: var(--sp-6);
    max-width: 94%;
    padding: 4px var(--sp-2);
    font-size: 0.64rem;
    letter-spacing: 0.02em;
    line-height: 1.25;
    white-space: normal;
  }
}

.hero__content {
  position: relative;
  z-index: 1;
  color: var(--color-text-inverse);
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 900px;
}

.hero__eyebrow {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(235, 230, 221,0.75);
  margin-bottom: var(--sp-5);
  display: block;
}

.hero__name {
  font-size: clamp(3.25rem, 9vw, 8.5rem);
  font-weight: 700;
  color: var(--color-text-inverse);
  line-height: 0.95;
  letter-spacing: -0.02em;
}

.hero__role {
  font-family: var(--font-mono);
  font-size: clamp(0.85rem, 1.4vw, 1.05rem);
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(235, 230, 221,0.85);
  margin-top: var(--sp-5);
}

.hero__desc {
  max-width: 620px;
  margin-top: var(--sp-6);
  font-size: clamp(1.05rem, 1.5vw, 1.3rem);
  font-weight: 400;
  color: rgba(235, 230, 221,0.92);
  line-height: 1.55;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-4);
  margin-top: var(--sp-8);
}

.hero__scroll {
  position: absolute;
  left: 50%;
  bottom: var(--sp-6);
  transform: translateX(-50%);
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  color: rgba(235, 230, 221,0.7);
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.hero__scroll::after {
  content: '';
  width: 1px;
  height: 40px;
  background: rgba(235, 230, 221,0.5);
  animation: scrollLine 2.4s ease-in-out infinite;
}
@keyframes scrollLine {
  0%, 100% { transform: scaleY(0.4); transform-origin: top; }
  50% { transform: scaleY(1); transform-origin: top; }
}

@media (max-width: 700px) {
  .hero__scroll { display: none; }
}

/* -------------------------------------------------------------------------
   9. PAGE / SECTION HEADERS (interior pages)
   ------------------------------------------------------------------------- */
.page-header {
  padding-top: calc(var(--nav-height) + var(--sp-9));
  padding-bottom: var(--sp-8);
}

.page-header__index {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 400;
  color: var(--color-charcoal);
  letter-spacing: 0.08em;
}

.page-header h1 { margin-top: var(--sp-4); }

.page-header__desc {
  max-width: 680px;
  margin-top: var(--sp-5);
}

/* -------------------------------------------------------------------------
   10. PROJECT CARDS (Home — featured)
   ------------------------------------------------------------------------- */
.project-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-7) var(--sp-6);
}

@media (max-width: 800px) {
  .project-grid { grid-template-columns: 1fr; }
}

.project-card {
  display: block;
  position: relative;
  opacity: 0;
  transform: translateY(28px);
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
}
.project-card.in-view { opacity: 1; transform: translateY(0); }

.project-card__frame {
  position: relative;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background: var(--color-surface);
}
.project-card__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease);
}
.project-card:hover .project-card__frame img { transform: scale(1.06); }

/* Dark scrim + "View Project" reveal on hover — portfolio-grid style */
.project-card__frame::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background: rgba(30, 29, 27, 0);
  transition: background var(--dur-fast) var(--ease-soft);
}
.project-card:hover .project-card__frame::before { background: rgba(30, 29, 27, 0.34); }

.project-card__frame::after {
  content: 'View Project';
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 2;
  transform: translate(-50%, calc(-50% + 10px));
  color: #fff;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: var(--sp-3) var(--sp-5);
  border: 1px solid rgba(255, 255, 255, 0.7);
  white-space: nowrap;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-soft), transform var(--dur-fast) var(--ease-soft);
}
.project-card:hover .project-card__frame::after {
  opacity: 1;
  transform: translate(-50%, -50%);
}

.project-card__number {
  position: absolute;
  top: var(--sp-4);
  left: var(--sp-4);
  z-index: 2;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  color: #fff;
  background: rgba(30, 29, 27,0.55);
  padding: var(--sp-1) var(--sp-3);
  letter-spacing: 0.08em;
}

.project-card__meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-top: var(--sp-5);
  gap: var(--sp-4);
}

.project-card__title {
  font-size: clamp(1.3rem, 2vw, 1.65rem);
  font-weight: 600;
}

.project-card__tags {
  display: flex;
  gap: var(--sp-3);
  margin-top: var(--sp-2);
  flex-wrap: wrap;
}

.tag {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-black);
  background: var(--color-surface);
  padding: 3px var(--sp-2);
}

.project-card__desc {
  margin-top: var(--sp-3);
  color: var(--color-charcoal);
  font-weight: 400;
  max-width: 46ch;
}

.project-card__arrow {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-black);
  transition: transform var(--dur-fast) var(--ease-soft);
}
.project-card:hover .project-card__arrow { transform: translateX(6px); color: var(--color-accent); }

/* -------------------------------------------------------------------------
   11. ABOUT TEASER (Home)
   ------------------------------------------------------------------------- */
.about-teaser {
  display: grid;
  grid-template-columns: 0.9fr 1.1fr;
  gap: var(--sp-9);
  align-items: center;
}
@media (max-width: 900px) {
  .about-teaser { grid-template-columns: 1fr; gap: var(--sp-7); }
}
.about-teaser__frame {
  aspect-ratio: 3 / 4;
  overflow: hidden;
  background: var(--color-surface);
}
.about-teaser__frame img { width: 100%; height: 100%; object-fit: cover; }

/* -------------------------------------------------------------------------
   12. SKILLS / INTERESTS
   ------------------------------------------------------------------------- */
.skills-list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  border-top: 1px solid var(--color-gray-lighter);
}
@media (max-width: 700px) {
  .skills-list { grid-template-columns: 1fr; }
}

.skill-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: var(--sp-5) 0;
  border-bottom: 1px solid var(--color-gray-lighter);
  gap: var(--sp-4);
}
.skill-row:nth-child(odd) { padding-right: var(--sp-7); }
.skill-row:nth-child(even) { padding-left: var(--sp-7); }
@media (max-width: 700px) {
  .skill-row:nth-child(odd), .skill-row:nth-child(even) { padding-inline: 0; }
}

.skill-row__index {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--color-charcoal);
}
.skill-row__name {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
}
.skill-row__desc {
  color: var(--color-charcoal);
  font-weight: 400;
  font-size: 0.9rem;
  text-align: right;
  max-width: 30ch;
}
@media (max-width: 560px) {
  .skill-row { flex-direction: column; gap: var(--sp-2); }
  .skill-row__desc { text-align: left; }
}

/* -------------------------------------------------------------------------
   13. FOOTER
   ------------------------------------------------------------------------- */
.site-footer {
  background: var(--color-bg-inverse);
  color: var(--color-text-inverse);
  padding-block: var(--sp-9) var(--sp-6);
}

.footer-top {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: var(--sp-8);
  padding-bottom: var(--sp-9);
  border-bottom: 1px solid rgba(235, 230, 221,0.14);
}
@media (max-width: 800px) {
  .footer-top { grid-template-columns: 1fr; gap: var(--sp-7); }
}

.footer-cta h2 { color: var(--color-text-inverse); max-width: 12ch; }
.footer-cta p { margin-top: var(--sp-4); color: var(--color-text-inverse); max-width: 42ch; }
.footer-cta .btn { margin-top: var(--sp-6); }

.footer-col h4 { color: rgba(235, 230, 221,0.55); margin-bottom: var(--sp-5); }
.footer-col ul { display: flex; flex-direction: column; gap: var(--sp-3); }
.footer-col a {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  font-weight: 400;
  color: var(--color-text-inverse);
  transition: color var(--dur-fast) var(--ease-soft), padding-left var(--dur-fast) var(--ease-soft);
}
.footer-col a:hover { color: rgba(235, 230, 221, 0.7); padding-left: 4px; }

.footer-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: var(--sp-6);
  flex-wrap: wrap;
  gap: var(--sp-4);
}
.footer-bottom p {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 400;
  color: rgba(235, 230, 221,0.6);
  letter-spacing: 0.04em;
}

/* -------------------------------------------------------------------------
   14. SCROLL REVEAL (generic)
   ------------------------------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
}
.reveal.in-view { opacity: 1; transform: translateY(0); }
.reveal-delay-1 { transition-delay: 0.08s; }
.reveal-delay-2 { transition-delay: 0.16s; }
.reveal-delay-3 { transition-delay: 0.24s; }
.reveal-delay-4 { transition-delay: 0.32s; }

/* -------------------------------------------------------------------------
   15. FIGURES / CAPTIONS (used across About / Projects / Extracurricular)
   ------------------------------------------------------------------------- */
figure { margin: 0; }
figcaption {
  margin-top: var(--sp-3);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 400;
  letter-spacing: 0.03em;
  color: var(--color-charcoal);
  display: flex;
  justify-content: space-between;
  gap: var(--sp-3);
}

.media-frame {
  overflow: hidden;
  background: var(--color-surface);
}
.media-frame img { width: 100%; height: 100%; object-fit: cover; }

/* -------------------------------------------------------------------------
   16. UTILITY
   ------------------------------------------------------------------------- */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: var(--sp-4);
  top: -60px;
  background: var(--color-bg-inverse);
  color: var(--color-text-inverse);
  padding: var(--sp-3) var(--sp-5);
  z-index: 1000;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 600;
  transition: top var(--dur-fast) var(--ease-soft);
}
.skip-link:focus { top: var(--sp-4); }

::selection { background: var(--color-accent); color: #fff; }

a:focus-visible, button:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}
