/* ==========================================================================
   Jameson Thermitus — Portfolio
   Global styles: reset, typography, nav, footer, layout primitives
   ========================================================================== */

:root {
  --bg: #0a0a0a;
  --bg-elevated: #121212;
  --text: #f2f2f0;
  --text-dim: #a8a8a4;
  --text-faint: #6b6b68;
  --accent: #ffffff;
  --border: rgba(255, 255, 255, 0.1);
  --nav-height: 84px;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --font: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
  /* The logo wordmark (.hero-logo) is a raster PNG, not text set in a web
     font -- there's no serif actually loaded anywhere in the project (no
     @font-face, no Google Fonts link). This is the system serif stack,
     used only for the Portfolio landing tile names, kept consistent with
     the rest of the site's system-fonts-only approach (no web font
     dependency added just for this). */
  --font-serif: Georgia, "Times New Roman", Times, serif;
  /* Sampled directly from images/logo/logo-horizontal.png (rgb(26, 117, 187))
     -- a real accent color already used in the wordmark, not invented. */
  --accent-blue: #1a75bb;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-weight: 300;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

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

ul {
  list-style: none;
}

button {
  font-family: inherit;
  background: none;
  border: none;
  color: inherit;
  cursor: pointer;
}

/* ---- Layout primitives ---- */

.container {
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
  padding: 0 6vw;
}

.section {
  padding: 120px 0;
}

.eyebrow {
  font-size: 0.75rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-bottom: 16px;
}

.section-title {
  font-size: clamp(1.75rem, 3vw, 2.5rem);
  font-weight: 300;
  letter-spacing: -0.01em;
  margin-bottom: 24px;
}

.body-text {
  color: var(--text-dim);
  font-size: 1rem;
  max-width: 640px;
}

/* ---- Navigation ---- */

.site-nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  height: var(--nav-height);
  display: flex;
  align-items: center;
  padding: 0 6vw;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.55), rgba(0, 0, 0, 0));
  transition: background-color 0.4s var(--ease), backdrop-filter 0.4s var(--ease);
}

.site-nav.is-scrolled {
  background: rgba(10, 10, 10, 0.82);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}

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

.nav-logo {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  overflow: hidden; /* belt-and-suspenders: the img can never spill out of the nav row */
  z-index: 1001;
}

.nav-logo img {
  display: block;
  height: 40px; /* fixed — this is a wide horizontal lockup, so height (not max-width) is the dominant constraint */
  width: auto;
  max-width: 100%;
  object-fit: contain;
  /* the logo is a near-white mark; this keeps it legible in the transparent
     nav state over unpredictable photo content, same rationale as the
     text-shadow already used on .hero-mark */
  filter: drop-shadow(0 1px 6px rgba(0, 0, 0, 0.45));
}

@media (max-width: 860px) {
  .nav-logo img {
    height: 32px; /* leaves room next to the hamburger toggle */
  }
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 40px;
}

.nav-item {
  position: relative;
}

.nav-link {
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  transition: color 0.3s var(--ease);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 0;
}

.nav-link:hover,
.nav-link.is-active {
  color: var(--text);
}

.nav-caret {
  font-size: 0.6rem;
  transition: transform 0.3s var(--ease);
}

.nav-item.has-dropdown:hover .nav-caret {
  transform: rotate(180deg);
}

.nav-dropdown {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%) translateY(8px);
  min-width: 180px;
  background: rgba(15, 15, 15, 0.96);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 8px;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s var(--ease), transform 0.25s var(--ease), visibility 0.25s;
  backdrop-filter: blur(10px);
}

.nav-item.has-dropdown:hover .nav-dropdown,
.nav-item.has-dropdown:focus-within .nav-dropdown {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}

.nav-dropdown a {
  display: block;
  padding: 10px 14px;
  font-size: 0.8rem;
  letter-spacing: 0.05em;
  color: var(--text-dim);
  white-space: nowrap;
  border-radius: 3px;
  transition: background 0.2s var(--ease), color 0.2s var(--ease);
}

.nav-dropdown a:hover,
.nav-dropdown a.is-active {
  background: rgba(255, 255, 255, 0.06);
  color: var(--text);
}

/* ---- Nav social icons (last item in .nav-links -- top-right on desktop,
   folds to the bottom of the fullscreen mobile panel for free since it's
   just the last flex item in that column layout too) ---- */

.nav-socials {
  display: flex;
  align-items: center;
  gap: 14px;
}

.nav-social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 4px;
  color: var(--text-dim);
  transition: color 0.3s var(--ease);
}

.nav-social-link:hover {
  color: var(--text);
}

.nav-social-link svg {
  width: 18px;
  height: 18px;
}

.nav-toggle {
  display: none;
  z-index: 1001;
  width: 28px;
  height: 20px;
  position: relative;
}

.nav-toggle span {
  position: absolute;
  left: 0;
  width: 100%;
  height: 1px;
  background: var(--text);
  transition: transform 0.3s var(--ease), opacity 0.3s var(--ease), top 0.3s var(--ease);
}

.nav-toggle span:nth-child(1) { top: 0; }
.nav-toggle span:nth-child(2) { top: 9px; }
.nav-toggle span:nth-child(3) { top: 18px; }

.nav-toggle.is-open span:nth-child(1) { top: 9px; transform: rotate(45deg); }
.nav-toggle.is-open span:nth-child(2) { opacity: 0; }
.nav-toggle.is-open span:nth-child(3) { top: 9px; transform: rotate(-45deg); }

/* ---- Mobile nav ---- */

@media (max-width: 860px) {
  .nav-toggle {
    display: block;
  }

  /* The <ul class="nav-links"> is wrapped in a bare <nav> for semantics.
     That wrapper has no styling of its own, so at this width it still sat
     in .nav-inner's flex row as a 3rd item alongside .nav-logo and
     .nav-toggle -- even though .nav-links itself goes position:fixed
     below and renders full-screen, the empty wrapper box was still there,
     making space-between distribute across 3 items instead of 2 and
     pushing .nav-toggle in from the true right edge. display:none would
     hide .nav-links too (a display:none ancestor hides descendants
     regardless of their own position), so this uses display:contents
     instead -- the wrapper generates no box of its own (removing it from
     flex layout entirely), while .nav-links still renders exactly as
     before once it's fixed-positioned to fill the viewport. */
  .nav-inner > nav {
    display: contents;
  }

  .nav-links {
    position: fixed;
    inset: 0;
    flex-direction: column;
    justify-content: center;
    gap: 28px;
    background: rgba(8, 8, 8, 0.98);
    /* No backdrop-filter here (deliberately) -- this is a full-viewport
       panel revealed by a transform slide, and blurring everything newly
       exposed behind it in real time is expensive enough on real mobile
       hardware that the browser visibly lags getting the background fully
       opaque/blurred a beat after the (compositor-cheap) transform has
       already placed the nav links on screen -- the links render first,
       the backdrop looks like it's still catching up for about a second.
       The background alone is already 98% opaque, so removing the blur
       costs essentially no visible fidelity while removing the one
       expensive-to-composite thing that could lag behind the content. */
    transform: translateX(100%);
    transition: transform 0.4s var(--ease);
  }

  .nav-links.is-open {
    transform: translateX(0);
  }

  .nav-link {
    font-size: 1.1rem;
  }

  .nav-item.has-dropdown {
    display: flex;
    flex-direction: column;
    align-items: center;
  }

  .nav-dropdown {
    position: static;
    transform: none;
    opacity: 1;
    visibility: visible;
    background: none;
    border: none;
    backdrop-filter: none;
    display: none;
    text-align: center;
    padding: 4px 0 0;
  }

  .nav-item.has-dropdown.is-open .nav-dropdown {
    display: block;
  }

  /* Desktop's equivalent rotate is hover-driven (:hover above); on mobile
     there's no hover, so it's tied to the same .is-open class js/nav.js
     toggles for the dropdown itself, giving the same open/closed signal. */
  .nav-item.has-dropdown.is-open .nav-caret {
    transform: rotate(180deg);
  }

  .nav-dropdown a {
    padding: 10px 0;
    font-size: 0.95rem;
  }
}

/* ---- Footer ---- */

.site-footer {
  border-top: 1px solid var(--border);
  padding: 48px 6vw;
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  align-items: center;
  justify-content: space-between;
  color: var(--text-faint);
  font-size: 0.8rem;
  letter-spacing: 0.05em;
}

.site-footer a {
  color: var(--text-dim);
  transition: color 0.3s var(--ease);
}

.site-footer a:hover {
  color: var(--text);
}

.footer-social {
  display: flex;
  gap: 24px;
}

/* ---- Page hero (non-homepage pages) ---- */

.page-header {
  padding: calc(var(--nav-height) + 64px) 6vw 48px;
  border-bottom: 1px solid var(--border);
}

.page-header h1 {
  font-size: clamp(2rem, 5vw, 3.25rem);
  font-weight: 300;
  letter-spacing: -0.01em;
}

.page-header p {
  color: var(--text-dim);
  margin-top: 12px;
  max-width: 520px;
}

/* ---- Portfolio landing grid (portfolio/index.html) ----
   Full-bleed, edge-to-edge: the grid IS the page, no .container, no
   .page-header, no side padding on the section -- first/last tiles touch
   the viewport edges exactly. 4 tiles in a single row on desktop (one row
   is the right shape for exactly 4 categories, not a 2x2 stack); 2x2 at
   tablet/mobile, same "narrower cells, not fewer columns" rule used by
   .gallery-grid and .screenings-grid elsewhere on the site.

   This page is also a no-scroll, exactly-100vh layout: nav + grid + footer
   must add up to precisely window height, with zero leftover gap below
   the footer. Scoped to body.portfolio-landing-page (not bare body/main)
   so it can't affect any other page's height behavior -- the homepage
   hero, Movies, and the gallery pages all have their own, different
   height mechanics. */

body.portfolio-landing-page {
  display: flex;
  flex-direction: column;
  min-height: 100vh; /* min, not fixed height -- if the footer ever needs more room than the viewport has, the page grows instead of clipping content */
}

body.portfolio-landing-page main {
  /* display: flex (not just flex: 1 1 auto on its own) is required here --
     a flex ITEM's flex-grown main-axis size is real for layout purposes
     but is NOT treated as a definite size for CHILD PERCENTAGE HEIGHT
     resolution (the flexbox spec's "definite size for percentage
     children" guarantee only covers the CROSS axis / stretch, not the
     MAIN axis / grow-shrink). height: 100% on .portfolio-landing-grid
     silently fell back to content-based sizing because of exactly this --
     confirmed live: main measured a real, correct 762.8px via flex-grow,
     but the grid's height:100% still resolved to its old content-driven
     519.6px, completely ignoring it. Making main a flex column itself and
     using flex: 1 on the grid (below) sidesteps the issue entirely,
     since flex-grow participates directly in the layout algorithm rather
     than resolving a percentage against a parent size. */
  display: flex;
  flex-direction: column;
  flex: 1 1 auto; /* fills exactly the space between the fixed nav and the footer's natural height */
  min-height: 0; /* main is ALSO a flex item (of body) with its own default min-height: auto, which pulls a content-based floor from the grid's intrinsic size -- same fix as .portfolio-landing-grid's own min-height: 0 below, just one level up. Without this, main (and everything inside it) refuses to shrink past that floor regardless of how the grid itself is configured. */
}

.portfolio-landing-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr); /* desktop: one row of 4 */
  grid-template-rows: minmax(0, 1fr); /* bare 1fr is really minmax(auto, 1fr) -- an IMPLICIT content-based (aspect-ratio-driven) minimum the track won't shrink below, which silently defeated the shrink half of flex: 1 on short viewports. minmax(0, 1fr) removes that floor so the row can go all the way down to whatever height is actually available, not just grow beyond its content size. */
  gap: 2px; /* thin dark seam between tiles, not a visible margin -- background shows through as the "border" */
  background: #000;
  padding-top: var(--nav-height); /* .site-nav is position: fixed and overlaps in-flow content -- this offset pushes the grid's first row below it, same pattern as .page-header uses elsewhere. Horizontal edges stay untouched (0 side padding) so the grid is still full-bleed. */
  flex: 1 1 auto; /* grows to fill <main> (see the comment on body.portfolio-landing-page main above for why this is flex, not height: 100%) */
  min-height: 0; /* flex items default to min-height: auto, which can refuse to shrink below the (large) aspect-ratio-driven content size -- without this, flex: 1 wouldn't be able to shrink the grid on short viewports */
}

.portfolio-tile {
  position: relative;
  display: block;
  overflow: hidden; /* clips the hover-scaled img so it can't spill past the tile edge */
  /* No aspect-ratio here (deliberately -- see below). .portfolio-tile-img is
     position: absolute, so it contributes zero intrinsic size to this box;
     with no aspect-ratio either, the tile has NO preferred size of its own,
     so the grid's 1fr row/column tracks (below) can size it to exactly
     fill whatever space is actually available, growing OR shrinking.
     object-fit: cover on the photo means whatever size that ends up being
     always looks intentional, never distorted.

     aspect-ratio: 4/5 used to be set here as the tile's "preferred" shape,
     on the theory that it'd be overridden once both width and height were
     pinned by the grid tracks -- confirmed live that's wrong: CSS Grid's
     fr tracks have no real shrink mechanism (unlike flexbox's
     flex-shrink). A track's base size comes from its items' content
     contribution, and fr only ever distributes LEFTOVER positive space on
     top of that base -- minmax(0, 1fr) does NOT override it. With
     aspect-ratio still set, the tile's aspect-ratio-derived preferred
     size (e.g. 435.6px tall at a 348.5px column width) became that base
     size, so the grid could grow past it but never shrink below it --
     exactly the "stuck at the old height regardless of viewport" bug this
     was fixed for. Removing it was the actual fix, not the flex/min-height
     changes above (those were real and necessary too, just not
     sufficient on their own). */
}

.portfolio-tile-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover; /* fills the tile completely, no letterboxing */
  transition: transform 0.6s var(--ease);
}

.portfolio-tile:hover .portfolio-tile-img {
  transform: scale(1.05); /* image only -- .portfolio-tile-name is a sibling, so it never moves */
}

.portfolio-tile-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.28); /* flat dark wash, not a top/bottom gradient -- the name sits at dead center, so legibility needs to hold uniformly across the whole tile, not just one edge */
}

.portfolio-tile-name {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-family: var(--font-serif);
  font-size: clamp(1.5rem, 3vw, 2.5rem);
  font-weight: 400;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #fff;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.85), 0 6px 28px rgba(0, 0, 0, 0.6); /* two layers -- a tight dark edge plus a wide soft falloff -- so it stays legible against both bright and dark spots in the photo underneath */
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  .portfolio-tile-img {
    transition: none;
  }
}

@media (max-width: 1024px) {
  .portfolio-landing-grid {
    grid-template-columns: repeat(2, 1fr); /* 2x2 */
    grid-template-rows: repeat(2, minmax(0, 1fr)); /* both rows share the fill-height space evenly, no implicit content-based floor -- see the base rule's comment */
  }
}

@media (max-width: 640px) {
  .portfolio-landing-grid {
    grid-template-columns: repeat(2, 1fr); /* same as tablet -- not dropping to 1 column */
    grid-template-rows: repeat(2, minmax(0, 1fr));
    gap: 2px;
  }

  .portfolio-tile-name {
    font-size: 1.3rem;
    letter-spacing: 0.12em;
  }
}

/* ---- Travel landing grid (travel/index.html) ----
   Reuses .portfolio-tile/.portfolio-tile-img/.portfolio-tile-overlay/
   .portfolio-tile-name exactly as-is (same cover photo + overlay +
   centered name + hover-zoom treatment as the Portfolio landing grid) --
   only the grid CONTAINER differs, because this list grows over time
   (one tile per Travel/ subfolder, added as trips happen) rather than
   being a fixed set of exactly 4 categories. Portfolio's grid is
   deliberately locked to fill exactly 100vh in a single row; forcing an
   open-ended, growing list into that same fixed-height/single-row shape
   would make tiles keep shrinking as more cities get added, so this is a
   normal-flow grid that just wraps to as many rows as it needs, scrolling
   like every other gallery page. */

.travel-landing-section {
  padding-bottom: 96px;
}

.travel-landing-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 2px;
  background: #000;
}

/* .portfolio-tile has no aspect-ratio of its own (removed deliberately on
   the Portfolio page so its viewport-fill layout could stretch tiles) --
   scoped here instead of changed at the source, so this page's tiles get
   a sensible fixed shape without affecting the Portfolio page at all. */
.travel-landing-grid .portfolio-tile {
  aspect-ratio: 4 / 5;
}

/* Portfolio's .portfolio-tile-name is nowrap + sized for short single-word
   category names ("Editorial", "Portrait") -- city names run much longer
   ("Reykjavik, Iceland") and were visually overflowing past the tile edge
   on narrow tiles (confirmed via screenshot, not just computed values).
   Scoped here rather than changing the shared rule, so Portfolio's tiles
   are unaffected. */
.travel-landing-grid .portfolio-tile-name {
  white-space: normal;
  text-align: center;
  padding: 0 16px;
  font-size: clamp(1.1rem, 2.2vw, 2rem);
  line-height: 1.3;
}

.travel-placeholder-tile {
  position: relative;
  aspect-ratio: 4 / 5;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
}

.travel-placeholder-tile svg {
  width: 30px;
  height: 30px;
  color: var(--text-faint);
}

.travel-placeholder-tile span {
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-faint);
}

@media (max-width: 1024px) {
  .travel-landing-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 640px) {
  .travel-landing-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 1px;
  }

  .travel-landing-grid .portfolio-tile-name {
    font-size: 1rem;
    letter-spacing: 0.1em;
    padding: 0 10px;
  }
}

/* ---- Fade-in on scroll utility (reusable on later pages, e.g. About) ---- */

.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.8s var(--ease), transform 0.8s var(--ease);
}

.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* ---- Cookie notice banner (js/cookie-notice.js, every public page) ----
   Small corner/bottom bar, not a full-screen takeover. z-index sits below
   .site-nav (1000) and .nav-toggle (1001) on purpose -- the fullscreen
   mobile nav panel is meant to cover this whenever it's open, so there's
   never a case where both are visible/interactive at once. Reuses the
   same solid, near-opaque dark panel treatment as the mobile nav
   (rgba(8, 8, 8, 0.98)) and the same secondary-button look already used
   for .contact-submit / .client-download-all-btn, rather than
   introducing a new button style for one element. */

.cookie-notice {
  position: fixed;
  left: 24px;
  right: 24px;
  bottom: 24px;
  z-index: 950;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  max-width: 640px;
  margin: 0 auto;
  padding: 16px 20px;
  background: rgba(8, 8, 8, 0.98);
  border: 1px solid var(--border);
  border-radius: 4px;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
}

.cookie-notice-text {
  color: var(--text-dim);
  font-size: 0.85rem;
  line-height: 1.5;
  margin: 0;
}

.cookie-notice-text a {
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.cookie-notice-dismiss {
  flex-shrink: 0;
  border: 1px solid var(--text);
  background: transparent;
  color: var(--text);
  padding: 10px 20px;
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.2s var(--ease), color 0.2s var(--ease);
}

.cookie-notice-dismiss:hover {
  background: var(--text);
  color: #000;
}

@media (max-width: 640px) {
  .cookie-notice {
    left: 12px;
    right: 12px;
    bottom: 12px;
    flex-direction: column;
    align-items: stretch;
    padding: 16px;
  }

  .cookie-notice-dismiss {
    align-self: stretch;
  }
}
