/* ===================================================
   SITE CHROME — shared by every page
   Theme + weather tokens, nav, cursor, grain, the
   Vancouver scene, and the shared page-hero band.
   Base tokens / footer / buttons live in styles.css
   Page-specific styles live in landing / case-study / pages
   =================================================== */

/* ==============================
   SCENE + WEATHER TOKENS
   Scoped to `body` so every page inherits them.
   ============================== */
body {
  --sky-1: #9EC4E4;
  --sky-2: #C6DCEF;
  --ridge-far: #A8BED2;
  --ridge-near: #8BA5BE;
  --city: #7B93A6;
  --city-deep: #5E778C;
  --window: rgba(255, 232, 180, 0);   /* windows unlit in daylight */
  --drop: rgba(96, 124, 152, 0.5);
  --wave-back: #7E9CB8;
  --wave-front: var(--bg);
  --glass: rgba(255, 255, 255, 0.72);
  --glass-line: rgba(255, 255, 255, 0.55);
  --grain-opacity: 0.22;
}

html[data-theme="dark"] {
  --bg: #12151A;
  --fg: #F2EFE9;
  --text: #B4B9C1;
  --muted: #838995;
  --accent: #93B8DD;
  --accent-warm: #E9A188;
  --card: #191E26;
  --card-border: rgba(255, 255, 255, 0.09);
  --card-shadow: 0 2px 14px rgba(0, 0, 0, 0.35);
  --card-shadow-hover: 0 22px 60px rgba(0, 0, 0, 0.5);
  --surface-invert: #20262F;
  --on-invert: #F2EFE9;
  --on-invert-muted: rgba(242, 239, 233, 0.52);
  --on-invert-line: rgba(242, 239, 233, 0.16);
}

/* Rain: greyer sky, the city hazes out and recedes.
   Declared BEFORE the dark rules so that at equal specificity dark still
   wins for dark+sun; dark+rain is handled by its own two-attribute rule. */
html[data-weather="rain"] body {
  --sky-1: #90A5B7;
  --sky-2: #BAC7D2;
  --ridge-far: #A7B4C0;
  --ridge-near: #8E9DAC;
  --city: #8695A4;
  --city-deep: #6E7F90;
  --wave-back: #8496A6;
}

html[data-theme="dark"] body {
  --sky-1: #1E2A3A;
  --sky-2: #161C26;
  --ridge-far: #26344A;
  --ridge-near: #1B2534;
  --city: #10161F;
  --city-deep: #080C12;
  --window: rgba(255, 214, 140, 0.9);  /* the city switches on at night */
  --drop: rgba(190, 214, 238, 0.45);
  --wave-back: #0E141C;
  --wave-front: var(--bg);
  --glass: rgba(30, 36, 46, 0.72);
  --glass-line: rgba(255, 255, 255, 0.1);
  --grain-opacity: 0.14;
}

html[data-theme="dark"][data-weather="rain"] body {
  --sky-1: #141C26;
  --sky-2: #0F141B;
  --ridge-far: #212C3B;
  --ridge-near: #18212C;
  --city: #141B25;
  --city-deep: #0C1118;
  --wave-back: #0C1117;
}

/* ==============================
   FILM GRAIN
   ============================== */
.grain {
  position: fixed;
  inset: -120px;
  z-index: 9000;
  pointer-events: none;
  opacity: var(--grain-opacity);
  mix-blend-mode: soft-light;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grainShift 6s steps(6) infinite;
}

@keyframes grainShift {
  0% { transform: translate(0, 0); }
  20% { transform: translate(-12px, 8px); }
  40% { transform: translate(10px, -14px); }
  60% { transform: translate(-8px, -6px); }
  80% { transform: translate(14px, 10px); }
  100% { transform: translate(0, 0); }
}

/* ==============================
   CURSOR
   ============================== */
.cursor-dot,
.cursor-ring {
  position: fixed;
  top: 0; left: 0;
  z-index: 9500;
  pointer-events: none;
  border-radius: 50%;
  opacity: 0;
  will-change: transform;
}

.cursor-dot {
  width: 6px; height: 6px;
  margin: -3px 0 0 -3px;
  background: var(--accent);
}

.cursor-ring {
  width: 34px; height: 34px;
  margin: -17px 0 0 -17px;
  border: 1.5px solid var(--accent);
  transition: width 0.28s var(--ease-out), height 0.28s var(--ease-out),
    margin 0.28s var(--ease-out), background 0.28s var(--ease-out),
    opacity 0.25s linear, border-color 0.28s var(--ease-out);
}

.cursor-ring.is-hot {
  width: 58px; height: 58px;
  margin: -29px 0 0 -29px;
  background: rgba(61, 90, 128, 0.1);
}

body.cursor-ready .cursor-dot { opacity: 1; }
body.cursor-ready .cursor-ring { opacity: 0.55; }

/* only take over the cursor where a fine pointer exists */
@media (hover: hover) and (pointer: fine) {
  body.cursor-ready,
  body.cursor-ready a,
  body.cursor-ready button { cursor: none; }
  /* text fields still need a caret */
  body.cursor-ready input,
  body.cursor-ready textarea { cursor: auto; }
}

/* ==============================
   NAV — floating pill
   ============================== */
.nav-hero {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
  padding: 20px var(--page-pad);
}

.nav-hero .nav-tag { justify-self: start; }
.nav-hero .nav-utils { justify-self: end; }

.nav-hero .nav-pill {
  margin: 0;
  justify-self: center;
  width: auto;
  max-width: none;
  gap: 6px;
  padding: 6px;
  background: var(--glass);
  border: 1px solid var(--glass-line);
  box-shadow: 0 10px 34px rgba(23, 42, 66, 0.12);
  backdrop-filter: blur(18px) saturate(1.5);
  -webkit-backdrop-filter: blur(18px) saturate(1.5);
  transition: box-shadow 0.4s var(--ease-out), transform 0.4s var(--ease-out);
}

.nav-hero.is-stuck .nav-pill {
  box-shadow: 0 14px 44px rgba(23, 42, 66, 0.2);
}

.nav-avatar {
  width: 38px; height: 38px;
  border-radius: 50%;
  overflow: hidden;
  flex-shrink: 0;
  border: 1.5px solid var(--glass-line);
  transition: transform 0.35s var(--ease-out);
}
.nav-avatar img { width: 100%; height: 100%; object-fit: cover; }
.nav-avatar:hover { transform: scale(1.08) rotate(-4deg); }

.nav-hero .nav-links { gap: 2px; }

.nav-hero .nav-links a {
  padding: 9px 16px;
  color: var(--text);
  position: relative;
}

.nav-hero .nav-links a:hover {
  background: rgba(127, 127, 127, 0.12);
  color: var(--fg);
}

.nav-hero .nav-links a.active {
  background: var(--surface-invert);
  color: var(--on-invert);
}

/* duplicate of the "Work with me" CTA — only shown once the CTA hides */
.nav-hero .nav-links .nav-link-contact { display: none; }

.nav-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 18px;
  border-radius: var(--radius-pill);
  background: var(--surface-invert);
  color: var(--on-invert);
  font: 600 13px/1 var(--sans);
  letter-spacing: 0.01em;
  white-space: nowrap;
  transition: transform 0.3s var(--ease-out), box-shadow 0.3s var(--ease-out);
}
.nav-cta svg {
  width: 15px; height: 15px;
  fill: none; stroke: currentColor;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
.nav-cta:hover { box-shadow: 0 8px 22px rgba(0, 0, 0, 0.22); }

/* In dark mode an inverted pill is nearly invisible against the pill it
   sits in, so the primary actions switch to the accent instead. */
html[data-theme="dark"] .nav-cta,
html[data-theme="dark"] .btn-primary {
  background: var(--accent);
  color: #12151A;
}
html[data-theme="dark"] .nav-hero .nav-links a.active {
  background: rgba(147, 184, 221, 0.16);
  color: var(--accent);
}

/* location tag, top-left */
.nav-tag {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font: 500 11px/1 var(--mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text);
  padding: 8px 4px;
  transition: color 0.25s var(--ease-out);
}
.nav-tag svg {
  width: 15px; height: 15px;
  fill: none; stroke: currentColor;
  stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round;
}
.nav-tag:hover { color: var(--accent); }

/* utility buttons, top-right */
.nav-utils { display: flex; gap: 8px; }

.util-btn {
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--glass-line);
  background: var(--glass);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  color: var(--fg);
  cursor: pointer;
  transition: transform 0.3s var(--ease-out), border-color 0.3s var(--ease-out), color 0.3s var(--ease-out);
}
.util-btn svg {
  width: 17px; height: 17px;
  fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.util-btn:hover { transform: translateY(-2px); border-color: var(--accent); color: var(--accent); }

/* motion button reads as "off" when paused */
html[data-motion="off"] #motionToggle { opacity: 0.45; }

/* umbrella lights up while it's raining */
#weatherToggle[aria-pressed="true"] {
  color: var(--accent);
  border-color: var(--accent);
}

/* sun / moon swap */
#themeToggle .icon-sun { display: none; }
html[data-theme="dark"] #themeToggle .icon-moon { display: none; }
html[data-theme="dark"] #themeToggle .icon-sun { display: block; }

/* ==============================
   THE SCENE
   Markup is injected by site.js so it lives in exactly one place.
   ============================== */
.scene {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  /* fallback sky if the injected markup never arrives */
  background: linear-gradient(180deg, var(--sky-1) 0%, var(--sky-2) 62%, var(--sky-2) 100%);
}

/* The sky holds its colour all the way down — the cream waves below
   are what transitions into the page background, so they read as a
   cut-paper edge instead of dissolving into it. */
.scene-sky {
  position: absolute;
  inset: -10%;
  background:
    linear-gradient(180deg, var(--sky-1) 0%, var(--sky-2) 62%, var(--sky-2) 100%);
}

/* soft light bloom that drifts */
.scene-glow {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(48vw 40vw at 78% 12%, rgba(255, 233, 190, 0.55), transparent 62%),
    radial-gradient(40vw 34vw at 14% 30%, rgba(255, 255, 255, 0.45), transparent 65%);
  animation: bloom 16s ease-in-out infinite alternate;
}

html[data-theme="dark"] .scene-glow {
  background:
    radial-gradient(48vw 40vw at 78% 12%, rgba(147, 184, 221, 0.16), transparent 62%),
    radial-gradient(40vw 34vw at 14% 30%, rgba(233, 161, 136, 0.08), transparent 65%);
}

/* no warm bloom in a downpour */
html[data-weather="rain"] .scene-glow {
  opacity: 0.28;
  transition: opacity 1s var(--ease-out);
}

@keyframes bloom {
  from { transform: translate3d(-2%, -1%, 0) scale(1); opacity: 0.9; }
  to   { transform: translate3d(3%, 2%, 0) scale(1.08); opacity: 1; }
}

/* halftone sun, sitting over the North Shore */
.sun {
  position: absolute;
  top: 12%;
  right: clamp(10px, 3vw, 56px);
  width: clamp(110px, 13vw, 190px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: repeating-linear-gradient(
    to bottom,
    var(--accent-warm) 0 5px,
    transparent 5px 11px
  );
  opacity: 0.6;
  animation: sunTurn 44s linear infinite;
  /* only opacity transitions — `translate` is owned by the parallax loop */
  transition: opacity 0.9s var(--ease-out);
}

html[data-theme="dark"] .sun { opacity: 0.3; }
html[data-weather="rain"] .sun { opacity: 0; }

@keyframes sunTurn {
  to { transform: rotate(360deg); }
}

/* ---- skyline layers ---- */
.layer {
  position: absolute;
  left: -5%;
  bottom: 0;
  width: 110%;   /* overscan so parallax never exposes an edge */
  pointer-events: none;
}
.layer svg { display: block; width: 100%; height: 100%; }

/* preserveAspectRatio="none" on both: these are rectangles, triangles and
   ridgelines, all of which stretch gracefully on ultrawide displays. */
.mountains { height: clamp(200px, 42vh, 420px); }
.skyline   { height: clamp(220px, 38vh, 380px); }

.ridge-far  { fill: var(--ridge-far); }
.ridge-near { fill: var(--ridge-near); }
.city-body  { fill: url(#cityGrad); }
.city-top { stop-color: var(--city); }
.city-bot { stop-color: var(--city-deep); }

/* second pass over the same shapes, filled with the window pattern */
.city-windows { fill: url(#winPat); }
.win-cell { fill: var(--window); }

.ridge-far, .ridge-near, .win-cell {
  transition: fill 0.9s var(--ease-out);
}
.city-top, .city-bot { transition: stop-color 0.9s var(--ease-out); }

/* ---- rain ---- */
.rain {
  position: absolute;
  inset: -12% -18%;
  overflow: hidden;
  transform: rotate(10deg);
  opacity: 0;
  transition: opacity 1s var(--ease-out);
  pointer-events: none;
}
html[data-weather="rain"] .rain { opacity: 1; }

.drop {
  position: absolute;
  top: -14%;
  width: 1.6px;
  border-radius: 2px;
  background: linear-gradient(to bottom, transparent, var(--drop));
  animation: fall linear infinite;
}

@keyframes fall {
  to { transform: translateY(128vh); }
}

/* frozen drops would just sit off-screen, so drop the layer entirely */
html[data-motion="off"] .rain { display: none; }

/* the inlet in front of the city — kept short so the skyline reads above it */
.waves {
  position: absolute;
  left: 0; bottom: -1px;
  width: 200%;
  height: clamp(80px, 11vh, 130px);
  animation: waveRoll 30s linear infinite;
}
.wave-back  { fill: var(--wave-back); }
.wave-front { fill: var(--wave-front); }

@keyframes waveRoll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* Compact scene for inner-page hero bands: same city, less of it.
   The skyline height is a variable so .page-hero can reserve exactly
   that much bottom padding — see --skyline-h below. */
.scene--compact .mountains { height: clamp(110px, 20vh, 190px); }
.scene--compact .skyline   { height: var(--skyline-h, clamp(120px, 22vh, 200px)); }
.scene--compact .waves     { height: clamp(52px, 7vh, 84px); }
.scene--compact .sun {
  width: clamp(74px, 8vw, 120px);
  top: 20%;
}

/* ==============================
   SHARED PAGE HERO (inner pages)
   ============================== */
.page-hero {
  --skyline-h: clamp(120px, 22vh, 200px);
  position: relative;
  overflow: hidden;
  isolation: isolate;
  /* The skyline is anchored to the hero's bottom edge, so the bottom
     padding has to clear it or body copy lands on the buildings.
     Deriving it from the same variable keeps the two in step at every
     viewport instead of leaving two numbers to drift apart. */
  padding: 152px var(--page-pad) calc(var(--skyline-h) + 34px);
  display: flex;
  align-items: center;
}

.page-hero-inner {
  position: relative;
  z-index: 2;
  max-width: 1200px;
  margin: 0 auto;
  width: 100%;
}

.page-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font: 500 11px/1 var(--mono);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text);
  margin-bottom: 20px;
}

.page-eyebrow { margin-bottom: 24px; }
.page-eyebrow a { color: var(--accent); transition: color 0.2s; }
.page-eyebrow a:hover { color: var(--fg); }
.page-eyebrow .sep { color: var(--muted); }

.page-title {
  /* 1.02 was fine for a single line but far too tight once it wraps */
  font: 600 clamp(36px, 6.4vw, 82px)/1.08 var(--display);
  letter-spacing: -0.04em;
  margin-bottom: 26px;
  max-width: 15ch;
  /* keeps a single short word from orphaning onto its own line */
  text-wrap: balance;
}

.page-lead {
  font: 400 clamp(15px, 1.2vw, 17px)/1.8 var(--sans);
  color: var(--text);
  max-width: 620px;
  text-wrap: pretty;
}

/* the serif italic accent used across every heading on the site */
.page-title em,
.section-head h2 em,
.exp-head h2 em,
.footer-cta-content h2 em,
.cs-section h2 em,
.about-card h2 em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
  letter-spacing: -0.01em;
}

.status-dot {
  width: 8px; height: 8px;
  background: #4CAF50;
  border-radius: 50%;
  flex-shrink: 0;
  animation: pulse 2.5s ease-in-out infinite;
  box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.15);
}

/* ==============================
   SHARED BUTTONS
   ============================== */
.btn-ghost {
  display: inline-flex;
  align-items: center;
  padding: 15px 30px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--card-border);
  background: var(--glass);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: var(--fg);
  font: 600 14px/1 var(--sans);
  letter-spacing: 0.02em;
  transition: border-color 0.3s var(--ease-out), color 0.3s var(--ease-out), transform 0.3s var(--ease-out);
}
.btn-ghost:hover { border-color: var(--accent); color: var(--accent); }

@keyframes riseIn {
  from { opacity: 0; transform: translateY(26px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ==============================
   PROJECT META / CHIPS
   Used on the landing stack and in case-study heroes.
   ============================== */
.project-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 18px;
  flex-wrap: wrap;
}

.project-chip {
  padding: 5px 12px;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  font: 500 11px/1 var(--sans);
  letter-spacing: 0.03em;
}

.chip-community { background: #E8F5E9; color: #2E7D32; }
.chip-ux        { background: #F3E5F5; color: #7B1FA2; }
.chip-mobile    { background: #E3F2FD; color: #1565C0; }
.chip-feature   { background: #FFF3E0; color: #E65100; }
.chip-brand     { background: #FBE9E7; color: #C5432A; }
.chip-product   { background: #E8EAF6; color: #37469B; }
.chip-soon {
  background: transparent;
  color: var(--muted);
  border-color: var(--card-border);
  border-style: dashed;
}

/* dark equivalents — tinted grounds keep the label legible */
html[data-theme="dark"] .chip-community { background: rgba(76, 175, 80, 0.16);  color: #92D399; }
html[data-theme="dark"] .chip-ux        { background: rgba(156, 39, 176, 0.18); color: #D7A8E4; }
html[data-theme="dark"] .chip-mobile    { background: rgba(33, 150, 243, 0.18); color: #91C5F0; }
html[data-theme="dark"] .chip-feature   { background: rgba(255, 152, 0, 0.18);  color: #F0BB74; }
html[data-theme="dark"] .chip-brand     { background: rgba(224, 122, 95, 0.18); color: #EDA791; }
html[data-theme="dark"] .chip-product   { background: rgba(92, 107, 192, 0.22); color: #AAB4EC; }

.project-num {
  font: 700 11px/1 var(--mono);
  color: var(--muted);
  margin-left: auto;
}

.project-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font: 600 13px/1 var(--sans);
  color: var(--accent);
  transition: gap 0.3s var(--ease-out);
}
.project-link.muted { color: var(--muted); }
.project-link .arrow { transition: transform 0.3s var(--ease-out); }
a:hover > .project-link,
a:hover .project-link { gap: 14px; }
a:hover .project-link .arrow { transform: translateX(3px); }

/* ==============================
   REVEAL ON SCROLL
   ============================== */
.reveal {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.8s var(--ease-out), transform 0.8s var(--ease-out);
}
.reveal.visible { opacity: 1; transform: translateY(0); }

/* ==============================
   FOOTER TWEAKS
   ============================== */
.footer { position: relative; z-index: 1; }
.footer-cta-card { padding: 64px 56px; }
.footer-bottom { padding-top: 28px; }

/* ==============================
   MOTION OFF
   ============================== */
html[data-motion="off"] .grain,
html[data-motion="off"] .scene-glow,
html[data-motion="off"] .sun,
html[data-motion="off"] .waves,
html[data-motion="off"] .status-dot {
  animation: none !important;
}

@media (prefers-reduced-motion: reduce) {
  .grain, .scene-glow, .sun, .waves, .status-dot {
    animation: none !important;
  }
  .rain { display: none; }
  .reveal { transition-duration: 0.01ms; }
}

/* ==============================
   RESPONSIVE — NAV
   ============================== */
@media (max-width: 960px) {
  .nav-hero { padding: 14px var(--page-pad); }
  .nav-hero .nav-links a { padding: 8px 12px; font-size: 12px; }
  .nav-cta { padding: 9px 14px; font-size: 12px; }
  .nav-cta svg { display: none; }
  .footer-cta-card { padding: 44px 32px; }
}

/* This nav carries an avatar and a CTA, so it has to collapse well before
   the 600px breakpoint the shared nav in styles.css uses. */
@media (max-width: 820px) {
  .nav-hero { grid-template-columns: auto 1fr auto; }
  .nav-tag { display: none; }
  .nav-hero .nav-pill { justify-self: center; position: relative; }

  .nav-hero .menu-toggle { display: block; }

  .nav-hero .nav-links {
    display: none;
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    flex-direction: column;
    min-width: 190px;
    padding: 8px;
    background: var(--card);
    border: 1px solid var(--card-border);
    border-radius: var(--radius);
    box-shadow: 0 16px 44px rgba(0, 0, 0, 0.14);
  }
  .nav-hero .nav-links.show { display: flex; }
  .nav-hero .nav-links a {
    padding: 12px 16px;
    font-size: 14px;
    border-radius: var(--radius-sm);
  }

  .page-hero { padding-top: 128px; }
}

/* The sun collides with the headline once titles start wrapping. */
@media (max-width: 700px) {
  .sun { top: 8%; right: 6%; }
  .mountains { height: clamp(150px, 30vh, 260px); }
  .skyline { height: clamp(150px, 26vh, 240px); }
  .scene--compact .mountains { height: clamp(96px, 17vh, 150px); }
  /* the hero's bottom padding follows this automatically */
  .page-hero { --skyline-h: clamp(104px, 19vh, 160px); }
  /* inner-page titles run the full width here — nowhere left to put it */
  .scene--compact .sun { display: none; }
}

@media (max-width: 560px) {
  /* the CTA is the only route to Contact on wider screens, so once it
     goes the dropdown has to carry a Contact link instead */
  .nav-cta { display: none; }
  .nav-hero .nav-links .nav-link-contact { display: block; }
  .nav-hero .nav-pill { padding: 6px; }
  .page-hero { padding-top: 114px; }
  .page-title { max-width: none; margin-bottom: 20px; }
}
