/* ==========================================================================
   MARKCAPTURES — global stylesheet
   Shared by every page. Anything used on more than one page lives here;
   page-specific layout stays in that page's own <style> block, which loads
   after this file and can therefore override any rule below.
   ========================================================================== */

/* ---------- theme tokens ----------
   dark is the site default (homepage, coming-soon). Pages that want the
   light treatment set data-theme="light" on <html> — the overlay colors
   stay the same in both, since the mobile nav overlay is always light. */
:root {
  --bg: #0a0a09;
  --fg: #f2efea;
  --muted: #8a8680;
  --accent: #ff4d23;
  --overlay-bg: #d9d9d9;
  --overlay-fg: #0a0a09;
  /* hairline for dividers — flips with the theme so rules stay visible */
  --rule: rgba(242, 239, 234, 0.15);
  /* page-transition panel — always the opposite of the page it slides over,
     or it sweeps up invisibly. See "page transitions" below. */
  --veil: #d9d9d9;
  /* ...except when it rises over the open mobile nav, which is grey on every
     page. Then it has to be dark regardless of the theme underneath. */
  --veil-on-nav: #0a0a09;
  /* shutter cursor diameter. Published as a token because the homepage's
     hover caption sits beside the cursor and measures its offset from this,
     so the two can't drift apart. */
  --cursor-size: 1.25rem;
}
:root[data-theme="light"] {
  --bg: #d9d9d9;
  --fg: #0a0a09;
  --muted: #6b6b68;
  --rule: rgba(10, 10, 9, 0.15);
  --veil: #0a0a09;
}

/* ---------- reset / base ---------- */
* { box-sizing: border-box; }
* {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* ---------- webfonts ----------
   Shipped in their original .otf/.ttf form; /font-sources keeps a copy of
   the same masters outside the deployed folder, one directory per family.

   Canela = display serif (brand, nav, headings).
   Lunchtype22 = body sans (paragraphs, links, UI text), three weights.

   font-display: block, not swap. With swap the browser paints the fallback
   sans immediately and reflows the whole page when the real font lands —
   that is the visible "jump". block hides the text for a short period
   instead, so it paints once, in the right font. Every page also preloads
   the two fonts used above the fold, which keeps that period near zero. */
@font-face {
  font-family: 'Canela';
  src: url('fonts/canela/Canela-Regular-Trial.otf') format('opentype');
  font-weight: 400;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: 'Lunchtype22';
  src: url('fonts/lunchtype22/lunchtype22-light.ttf') format('truetype');
  font-weight: 300;
  font-style: normal;
  font-display: block;
}
@font-face {
  font-family: 'Lunchtype22';
  src: url('fonts/lunchtype22/lunchtype22-regular.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: block;
}
@font-face {
  font-family: 'Lunchtype22';
  src: url('fonts/lunchtype22/lunchtype22-medium.ttf') format('truetype');
  font-weight: 500;
  font-style: normal;
  font-display: block;
}

/* fluid type scale — mirrors the Webflow prototype's breakpoint-tiered
   html font-size, so every rem-based value site-wide scales the same way */
html {
  font-size: calc(0.625rem + 0.41666666666666663vw);
}
@media screen and (max-width: 1440px) {
  html { font-size: calc(0.8126951092611863rem + 0.20811654526534862vw); }
}
@media screen and (max-width: 479px) {
  html { font-size: calc(0.7497384937238494rem + 0.41841004184100417vw); }
}

html, body {
  margin: 0; padding: 0; min-height: 100%;
  background: var(--bg); color: var(--fg);
  font-family: 'Lunchtype22', ui-sans-serif, -apple-system, "Helvetica Neue", Arial, sans-serif;
  /* matches the reference setting for Lunchtype22: the light weight with a
     hair of letter-spacing. Set here so every page inherits the same body
     voice; headings override with Canela. */
  font-weight: 300;
  letter-spacing: 0.01em;
}

a { color: inherit; text-decoration: none; }
::selection { background: var(--accent); color: var(--bg); text-shadow: none; }

/* ---------- utilities ---------- */

/* visually hidden but still read by search engines and screen readers —
   clipped rather than display:none so it stays in the accessibility tree */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* standard document-page layout: pins the footer to the bottom of the
   viewport when content is short, pushes it down when content is long.
   Full-viewport pages (e.g. the WebGL homepage) simply don't use it. */
body.page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
body.page > main { flex: 1 0 auto; }
body.page > .site-footer { flex: none; }

/* ---------- custom cursor ----------
   The shutter icon replaces the native pointer on devices with a real mouse
   — but ONLY on the WebGL homepage, which opts in with body.custom-cursor.
   Everywhere else the native cursor stays, because the sub-pages are ordinary
   documents: there the pointer's job is to say "this is a link", "this is a
   button", "you can type here", and a single unchanging shutter says none of
   those. The homepage has no such affordances to communicate — the whole
   viewport is one draggable canvas — so there it costs nothing and reads as
   part of the work.

   Positioned fixed unconditionally so it never occupies layout space on touch
   devices, where the media query below doesn't apply. */
#custom-cursor {
  position: fixed; top: 0; left: 0; z-index: 50; pointer-events: none;
  /* around 20px on a desktop display: close to the footprint of a native
     pointer, so the shutter reads as a cursor rather than as a graphic
     sitting on the page, but still big enough to show the aperture blades.
     Only ever shown on fine-pointer devices (see the media query below),
     which is why there is no separate mobile value */
  width: var(--cursor-size); height: var(--cursor-size);
  transform: translate(-50%, -50%);
  opacity: 0;
}
@media (hover: hover) and (pointer: fine) {
  body.custom-cursor,
  body.custom-cursor a,
  body.custom-cursor button { cursor: none; }
  body.custom-cursor #custom-cursor { transition: opacity .2s ease; }
  body.custom-cursor #custom-cursor.visible { opacity: 1; }
  /* overflow: visible for the same reason as .hamburger svg — the aperture is
     drawn tangent to its viewBox, so clipping shaves the curve's antialiasing */
  #custom-cursor svg { width: 100%; height: 100%; display: block; overflow: visible; }
  #custom-cursor path { fill: var(--fg); }
  body.nav-open #custom-cursor path { fill: var(--overlay-fg); }
}

/* ---------- native cursor affordances ----------
   For every page that does NOT opt into the shutter above. Links and text
   fields already carry their own cursors; buttons and <summary> do not — the
   UA default for both is the plain arrow, which makes a button look inert.
   The body.custom-cursor rules above are more specific, so the homepage is
   unaffected by these. */
button, summary, label[for], [role="button"] { cursor: pointer; }
button:disabled { cursor: not-allowed; }

/* ---------- top bar ---------- */
nav.topbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 40;
  display: flex; align-items: center; justify-content: space-between;
  padding: 1rem 2%; pointer-events: none;
}
nav.topbar .brand, nav.topbar .navlinks, nav.topbar .hamburger { pointer-events: auto; }

.brand {
  font-family: 'Canela', Georgia, 'Times New Roman', serif;
  font-size: 2.18rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--fg); text-decoration: none;
  transition: color .4s ease;
}
/* crossfades to the overlay's foreground as the nav slides open, and waits
   for the overlay to finish sliding away again before fading back */
body.nav-open .brand { color: var(--overlay-fg); transition-delay: 0s; }
body:not(.nav-open) .brand { transition-delay: .5s; }

/* hover behavior mirrors the Webflow prototype's nav interactions:
   hovering one item fades its own bullet in (300ms ease) while dimming
   the sibling items to 50% opacity (200ms ease-out), both ways */
.navlinks { display: flex; align-items: center; gap: 1rem; list-style: none; margin: 0; padding: 0; }
.navlinks li {
  display: flex; align-items: center; gap: 0.45rem;
  transition: opacity .15s ease-out;
}
.navlinks:has(li:hover) li:not(:hover) { opacity: 0.5; }
.navlinks .bullet {
  width: 8px; height: 8px; background: var(--fg); flex: none;
  opacity: 0; transition: opacity .3s ease;
}
.navlinks li:hover .bullet { opacity: 1; }
.navlinks a {
  font-family: 'Canela', Georgia, 'Times New Roman', serif;
  color: var(--fg); text-decoration: none;
  font-size: 1.25rem; letter-spacing: 0.12em; text-transform: uppercase;
}
.navlinks a:focus-visible { opacity: 0.7; }
.navlinks a[aria-current="page"] { opacity: 0.5; }

/* mobile menu toggle — collapses .navlinks at <=767px. Shutter/aperture
   icon spins 720deg (ease-in-out, "slow-fast-slow") on every click; fill
   color crossfades between the closed/open nav states over 0.4s */
.hamburger {
  display: none;
  width: 2.25rem; height: 2.25rem;
  background: none; border: none; padding: 0; cursor: pointer;
}
/* overflow: visible because the aperture is drawn tangent to its own viewBox
   (bbox runs -0.011..152.011 in a 0..152 box). The root <svg> clips at its
   viewport by default, and since the shape's edge runs *along* that boundary
   rather than crossing it, the clip shaves the antialiasing off a wide stretch
   of the curve — a sub-pixel overshoot that reads as a flat cut. Letting it
   paint past the edge costs nothing: the overshoot is 0.0026px at this size. */
.hamburger svg { width: 100%; height: 100%; display: block; overflow: visible; }
.hamburger path {
  fill: var(--fg);
  transition: fill .4s ease;
  /* The spin is applied to this <path>, NOT to the <svg> above, and that
     distinction is the whole ballgame. The outer <svg> is a replaced element:
     its CSS box is in px (2.25rem = 36px) while its contents are in viewBox
     units (152), and browsers disagree about how a percentage or `center`
     origin maps across that boundary — which is why the same markup span in
     place on iOS but orbited in Chrome. Inside the SVG there is no such
     ambiguity: lengths here are user units, so 76px means user unit 76, the
     artwork's exact centre in a 0 0 152 152 viewBox. transform-box is stated
     explicitly (it is also the default) so a future change to fill-box can't
     silently reinterpret the origin below. */
  transform-box: view-box;
  transform-origin: 76px 76px;
}
.hamburger[aria-expanded="true"] path { fill: var(--overlay-fg); transition-delay: 0s; }
.hamburger:not([aria-expanded="true"]) path { transition-delay: .5s; }
.hamburger.spin path { animation: shutter-spin 1s ease-in-out; }
@keyframes shutter-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(720deg); }
}

/* ---------- mobile nav overlay ---------- */
.nav-overlay {
  position: fixed; inset: 0; z-index: 35;
  background: var(--overlay-bg);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2rem;
  transform: translateY(-100%);
  transition: transform .4s ease-in;
}
.nav-overlay.open { transform: translateY(0); transition-timing-function: ease-out; transition-delay: 0s; }
.nav-overlay:not(.open) { transition-delay: .5s; }

.nav-overlay a {
  font-family: 'Canela', Georgia, 'Times New Roman', serif;
  color: var(--overlay-fg); text-decoration: none;
  font-size: 2rem; letter-spacing: 0.06em; text-transform: uppercase;
  opacity: 0;
  transform: translateY(0);
  transition: opacity .4s ease, transform .4s ease;
}
.nav-overlay.open a { opacity: 1; transform: translateY(-3rem); }
.nav-overlay.open:has(a:hover) a:not(:hover) {
  opacity: 0.5;
  transition: opacity .15s ease-out;
}

/* links stagger in top-to-bottom on open, and back out bottom-to-top */
.nav-overlay.open a:nth-child(1) { transition-delay: .3s; }
.nav-overlay.open a:nth-child(2) { transition-delay: .4s; }
.nav-overlay.open a:nth-child(3) { transition-delay: .5s; }
.nav-overlay.open a:nth-child(4) { transition-delay: .6s; }
.nav-overlay:not(.open) a:nth-child(1) { transition-delay: .3s; }
.nav-overlay:not(.open) a:nth-child(2) { transition-delay: .2s; }
.nav-overlay:not(.open) a:nth-child(3) { transition-delay: .1s; }
.nav-overlay:not(.open) a:nth-child(4) { transition-delay: 0s; }

/* ---------- footer ---------- */
.site-footer {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 1rem;
  padding: 2rem 2%;
  border-top: 1px solid var(--rule);
  font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--muted);
}
.site-footer p { margin: 0; }
.site-footer a { color: var(--fg); }
.footer-contact { display: flex; gap: 1.25rem; }

/* ---------- page transitions ----------
   A solid panel covers every same-site navigation in two beats: it arrives
   over the outgoing page, then — once the new page is in — fades out in
   place so that page appears to fade in. The fade out is always the same;
   how the panel *arrives* is chosen per navigation by main.js:

     from below   default. Entering a grey sub-page, and what the mobile nav
                  uses, mirroring the way that overlay unfolds downward.
     from above   .veil-from-top — returning to the dark homepage, so going
                  back reads as the reverse motion of going in.
     no travel    .veil-crossfade — between two sub-pages, which already
                  share a background. A sliding panel there would announce
                  movement that isn't happening, so the panel is that shared
                  grey and simply fades up, reading as the content itself
                  crossfading rather than anything sliding over it.

   Color is --veil, which inverts with the theme: grey over the dark
   homepage, near-black over the grey sub-pages. A panel the same color as
   the surface it's covering would arrive invisibly — except in the crossfade
   case, where matching it is exactly the point. When the mobile nav is open
   the surface being covered is that overlay rather than the page, so main.js
   reaches for --veil-on-nav instead. main.js also carries the chosen color
   into the incoming page (as an inline override), so the panel stays a
   single color end to end instead of jumping at the navigation boundary.

   It's a ::after on <html>, not a real element, so it can paint on the very
   first frame of the incoming page — before any script has run. An injected
   div would flash the new page's content first, which is the whole thing
   this is meant to hide. Sits above the topbar (40) but below the custom
   cursor (50) so the cursor stays visible throughout. */
html.is-leaving::after,
html.is-entering::after {
  content: '';
  position: fixed; inset: 0; z-index: 45;
  background: var(--veil);
  pointer-events: none;
}

/* outgoing page — one of three arrivals, see the note above */
html.is-leaving::after {
  animation: veil-sweep-in .4s ease-out both;
}
html.is-leaving.veil-from-top::after {
  animation: veil-sweep-down .4s ease-out both;
}
html.is-leaving.veil-crossfade::after {
  animation: veil-fade-in .4s ease-out both;
}

/* incoming page: holds covered from first paint until main.js adds
   .is-revealing, then fades out in place, revealing the page underneath.
   Slightly longer than the slide — a fade has no travel to track, so at
   .4s it reads as abrupt next to the sweep that preceded it. */
html.is-entering::after { transform: translateY(0); }
html.is-entering.is-revealing::after {
  animation: veil-fade-out .5s ease-out both;
}

@keyframes veil-sweep-in {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}
@keyframes veil-sweep-down {
  from { transform: translateY(-100%); }
  to   { transform: translateY(0); }
}
@keyframes veil-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes veil-fade-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* main.js already declines to intercept clicks here, so this only catches
   a veil left mid-flight by a preference change */
@media (prefers-reduced-motion: reduce) {
  html.is-leaving::after,
  html.is-entering::after { display: none; }
}

/* ---------- shared breakpoints ---------- */
@media (max-width: 767px) {
  .navlinks { display: none; }
  .hamburger { display: block; }
  .brand { font-size: 1.7rem; }

  .site-footer {
    flex-direction: column; text-align: center; gap: 0.6rem;
    padding: 2rem 1.25rem 2.5rem;
    /* 0.75rem lands near 10px once the fluid scale bottoms out on a phone,
       which is below the size uppercase letterspaced text stays readable at.
       Still the quiet footer voice, just legible */
    font-size: 0.9rem;
  }
  /* contact first, "site by" and copyright last on mobile */
  .footer-contact { flex-direction: column; gap: 0.4rem; order: -1; }
  .footer-credit { order: 1; }
  .footer-copyright { order: 2; }
}
@media (max-width: 479px) {
  .brand { font-size: 1.45rem; }
}
