/* Page layer — everything the design system's tokens don't already cover.
   Values come from DS spacing tokens; nothing here redefines a token.

   Note on scope: the artboard's markup carries inline styles (already written
   against DS tokens), and inline styles outrank any stylesheet rule. So this
   file only handles what inline styles *can't* express — responsive steps,
   :hover/:focus, reveal state — and hover/active styling that would have to
   fight an inline declaration is set from app.js instead. */

:root {
  --page-gutter: var(--space-10);      /* 40px */
  --section-pad: var(--space-24);      /* 96px */
  --strip-pad: var(--space-12);        /* 48px */
  --footer-pad: var(--space-18);       /* 72px */
  --hero-pad-top: var(--space-32);     /* 128px */
  --hero-pad-bottom: var(--space-24);  /* 96px */
  --reveal-delay: 0ms;
}

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* Mobile touch polish: a custom (not default grey) tap flash on iOS, and
   manipulation to skip the ~300ms tap delay some mobile browsers still add. */
a, button { -webkit-tap-highlight-color: rgba(232,162,75,0.25); touch-action: manipulation; }

/* Back-to-top. Fixed bottom-right; app.js toggles [data-visible] past one
   viewport of scroll. --cookie-h (set by the consent banner while it's on
   screen) keeps it from sitting underneath that banner. */
#back-to-top {
  position: fixed;
  right: var(--space-5);
  bottom: calc(var(--space-5) + env(safe-area-inset-bottom, 0px) + var(--cookie-h, 0px));
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-card);
  color: var(--text-primary);
  border: 1px solid var(--border-soft);
  border-radius: 50%;
  box-shadow: 0 8px 20px -8px rgba(0,0,0,0.4);
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition: opacity 220ms ease, transform 220ms ease, bottom 220ms ease;
  z-index: 130;
}
#back-to-top[data-visible] { opacity: 1; visibility: visible; transform: none; }
@media (prefers-reduced-motion: reduce) { #back-to-top { transition: opacity 120ms linear; } }

/* Anchors land below the sticky header rather than under it. */
section[id] { scroll-margin-top: calc(var(--nav-height) + var(--space-4)); }

/* ── Skip link ── */
.skip-link {
  position: absolute;
  left: var(--space-4);
  top: -100px;
  z-index: calc(var(--z-nav) + 1);
  background: var(--surface-accent);
  color: var(--text-inverse);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-control);
  text-decoration: none;
  transition: top var(--duration-fast) var(--ease-out);
}
.skip-link:focus-visible { top: var(--space-4); }

/* ── Scroll reveal ──
   Gated on .js so the page still reads with JavaScript disabled or broken:
   without the class the content is simply visible. */
.js [data-reveal],
.js [data-count] {
  opacity: 0;
  transform: translateY(var(--space-5));
  transition:
    opacity var(--duration-slow) var(--ease-entrance) var(--reveal-delay),
    transform var(--duration-slow) var(--ease-entrance) var(--reveal-delay);
}
.js [data-reveal].is-in,
.js [data-count].is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .js [data-reveal],
  .js [data-count] { opacity: 1; transform: none; transition: none; }
}

/* ── Responsive steps ──
   The artboard is a 1160px composition; below that it reflows on its own
   (clamp() type, auto-fit grids). What it can't do is shrink its own padding. */
@media (max-width: 860px) {
  /* The nav wraps to a second row rather than hiding — on a multi-page site,
     hidden nav links mean no way to reach the other pages. */
  :root {
    --page-gutter: var(--space-8);
    --section-pad: var(--space-18);
    --hero-pad-top: var(--space-24);
    --hero-pad-bottom: var(--space-18);
    --footer-pad: var(--space-14);
  }
}

@media (max-width: 560px) {
  :root {
    --page-gutter: var(--space-6);
    --section-pad: var(--space-14);
    --strip-pad: var(--space-10);
    --hero-pad-top: var(--space-18);
    --hero-pad-bottom: var(--space-14);
    --footer-pad: var(--space-12);
  }
  /* The impact and thoughts grids read as a single rule-separated column. */
  #impact [data-count], #thoughts h3 { font-size: var(--text-2xl); }

  /* Header at phone width. Thoughts/About/Experience and the theme toggle
     collapse behind a menu button — the sticky header stays just the name
     and the CTA, matching the header on every other viewport width where
     that pair alone reads as the "floating" identity + action.
     !important because the generated markup carries inline sizing. */
  /* Name + CTA + menu button stay on one row: the wrapper goes nowrap, the
     name is allowed to shrink and ellipsize as a last resort, and every gap
     tightens up so the three pieces fit down to a 320px screen. */
  header > div { flex-wrap: nowrap !important; gap: var(--space-2) !important; }
  header a[href="index.html"] {
    font-size: 15px !important;
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  header nav { justify-content: flex-end !important; gap: var(--space-2) !important; flex: 0 0 auto; }

  header a[href="contact.html"] {
    order: 1;
    font-size: 13px !important;
    padding: var(--space-2) var(--space-3) !important;
  }
  #nav-toggle {
    display: inline-flex !important;
    order: 2;
    width: 36px !important;
    height: 36px !important;
  }

  /* #nav-links is `display:contents` by default (transparent — its links
     sit inline in nav on every wider viewport). At phone width it's the
     collapsible panel: hidden until #nav-toggle sets [data-menu-open] on
     the header, then it floats as its own overlay card below the header —
     `position:absolute` off the sticky header (a valid containing block),
     not stacked in normal flow, so it doesn't push the ticker/hero down
     and blend into them with no visual separation. */
  #nav-links { display: none !important; order: 3; }
  header[data-menu-open] #nav-links {
    display: flex !important;
    flex-direction: column;
    align-items: stretch;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    gap: var(--space-1);
    padding: var(--space-4) var(--page-gutter) calc(var(--space-4) + env(safe-area-inset-bottom, 0px));
    background: var(--surface-page);
    border-top: 1px solid var(--border-soft);
    box-shadow: 0 16px 32px -12px rgba(0,0,0,0.45);
    z-index: 150;
  }
  header nav a[data-navlink] { padding: var(--space-3) 0 !important; }
  #theme-toggle { padding: var(--space-3) var(--space-2) !important; align-self: flex-start; }

  /* Dimmed scrim behind the open panel — click it (or Escape, or a link)
     to close. Created once by app.js; [hidden] is display:none by default. */
  #nav-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.4);
    z-index: 140;
  }
}

/* Hero decoration. The globe draws its mesh in --text-primary, so it reads
   far heavier against the light theme's parchment than against charcoal;
   and on a phone it competes with the headline for the same pixels. */
[data-theme="executive-light"] #globe { opacity: 0.5; }

@media (max-width: 720px) {
  #globe { opacity: 0.28; }
  [data-theme="executive-light"] #globe { opacity: 0.22; }
}

/* ══ Impact band ═══════════════════════════════════════════════════════
   The session photo sits behind the figures. Same blending approach as the
   hero: a theme-coloured scrim rather than a border, so the photograph reads
   as part of the page. The numbers have to stay legible, so the scrim here is
   heavier than the hero's. */
.impact-media { position: absolute; inset: 0; z-index: 0; pointer-events: none; }

.impact-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 40%;
  opacity: 0.52;
}
[data-theme="executive-light"] .impact-bg { opacity: 0.26; }

.impact-scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom,
      var(--surface-sunken) 0%,
      color-mix(in oklab, var(--surface-sunken) 52%, transparent) 24%,
      color-mix(in oklab, var(--surface-sunken) 52%, transparent) 72%,
      var(--surface-sunken) 100%),
    linear-gradient(90deg,
      color-mix(in oklab, var(--surface-sunken) 80%, transparent) 0%,
      color-mix(in oklab, var(--surface-sunken) 42%, transparent) 55%,
      color-mix(in oklab, var(--surface-sunken) 24%, transparent) 100%);
}

@media (max-width: 900px) {
  .impact-bg { opacity: 0.3; }
  [data-theme="executive-light"] .impact-bg { opacity: 0.12; }
}

/* ── Print: a recruiter hitting Cmd-P should get the CV-shaped version ── */
@media print {
  header, #globe, .skip-link, #contact form, #theme-toggle { display: none !important; }
  section { padding: var(--space-6) 0 !important; border: 0 !important; }
  .js [data-reveal], .js [data-count] { opacity: 1 !important; transform: none !important; }
}

/* ══ Footer surface ════════════════════════════════════════════════════
   The footer's inline style asks for --surface-ink, which is a fixed
   near-black in every theme scope — including the light one, where the
   text tokens are also near-black. That made the light-theme footer
   unreadable (#1B1B1D on #0F1215). The footer now follows the theme:
   dark ink under the dark theme, the sunken parchment under the light
   one, with the existing hairline doing the separating. !important
   because the declaration it overrides is inline. */
[data-theme="executive-light"] footer {
  background: var(--surface-sunken) !important;
  border-top-color: var(--border-hairline);
}

/* ══ Photographic bands ════════════════════════════════════════════════
   Generalised from the impact band: a full-bleed photograph under a
   theme-coloured scrim, so the image reads as part of the page rather than
   a framed picture. .media-* carries text over it; .plate-* stands alone. */
.media-layer, .plate-layer {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
}
/* Header band: the photograph sits behind the top of a section only, not
   behind its whole contents, so it never stretches down a long page. */
.media-layer--top { bottom: auto; height: clamp(300px, 34vw, 440px); }
.media-bg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0.5;
}
[data-theme="executive-light"] .media-bg { opacity: 0.14; }

.media-scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(to bottom,
      var(--surface-page) 0%,
      color-mix(in oklab, var(--surface-page) 50%, transparent) 26%,
      color-mix(in oklab, var(--surface-page) 50%, transparent) 74%,
      var(--surface-page) 100%),
    linear-gradient(90deg,
      color-mix(in oklab, var(--surface-page) 88%, transparent) 0%,
      color-mix(in oklab, var(--surface-page) 46%, transparent) 58%,
      color-mix(in oklab, var(--surface-page) 20%, transparent) 100%);
}

/* Standalone plate: no text over it, so it can carry more of the image. */
.plate-bg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 42%; opacity: 0.86;
}
[data-theme="executive-light"] .plate-bg { opacity: 0.68; }
.plate-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(to bottom,
    var(--surface-page) 0%,
    transparent 30%,
    transparent 64%,
    var(--surface-page) 100%);
}

@media (max-width: 900px) {
  .media-bg { opacity: 0.28; }
  [data-theme="executive-light"] .media-bg { opacity: 0.1; }
  .plate-bg { opacity: 0.7; }
}
@media print { .media-layer, .plate-layer, .contact-portrait { display: none !important; } }
