/*
  style.css
  ---------
  The whole visual system for the site lives in this one file, organised
  top to bottom as:
    1. Design tokens (colour, type, spacing) — change the look here.
    2. Base element resets.
    3. Layout shell (nav, footer, page container).
    4. Components (project grid, project tiles, galleries, etc).
    5. Small utilities and responsive tweaks.

  The look: the work is the hero — full-bleed imagery, near-zero gaps,
  quiet chrome. Poppins (bold, rounded, geometric) carries the wordmark
  and headings; Inter carries anything meant to be read.
*/

/* 1. Design tokens ------------------------------------------------------ */

:root {
  /* Colour. The work itself is colourful — magazine covers, renders,
     motion stills — so the site chrome stays quiet: near-white, near-
     black, one muted accent reserved for interactive states only. */
  --color-paper: #ffffff;
  --color-ink: #121212;
  --color-ink-soft: #6b6b6b;
  --color-line: #e4e4e4;
  --color-accent: #2c4c7c;
  --color-accent-soft: #eef1f6;

  /* Shared by the galleries and the About page so their left and right
     edges line up, and so every photo has the same softly rounded
     corners. */
  --page-inset: 0.875rem;
  --radius-image: 6px;

  /* Type. Poppins (rounded, geometric, bold) carries the wordmark and
     headings; Inter (neutral, highly legible) carries everything
     that's meant to be read. */
  --font-display: "Poppins", "Helvetica Neue", Arial, sans-serif;
  --font-body: "Inter", "Helvetica Neue", Arial, sans-serif;

  /* Only used for the huge scrolling greeting on the Contact page. */
  --font-marquee: "Source Serif 4", Georgia, "Times New Roman", serif;

  /* A simple, consistent scale (roughly a 1.25 ratio). */
  --step-0: 1rem;
  --step-1: 1.25rem;
  --step-2: 1.563rem;
  --step-3: 1.953rem;
  --step-4: 2.441rem;
  --step-5: 3.052rem;

  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
  --space-4: 2.5rem;
  --space-5: 4rem;
  --space-6: 6rem;

  --content-width: 72rem;
  --measure: 62ch; /* comfortable reading line length for body copy */
}

/* 2. Base ---------------------------------------------------------------- */

* {
  box-sizing: border-box;
}

html {
  color-scheme: light;
}

body {
  margin: 0;
  background: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

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

a {
  color: inherit;
  text-decoration-color: var(--color-line);
  text-underline-offset: 0.2em;
}

a:hover {
  text-decoration-color: currentColor;
}

/* Visible, deliberate focus state for keyboard users. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.1;
  margin: 0;
}

p {
  margin: 0 0 1em;
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }

  /* An endless loop can't be "sped up to instant", so the scrolling
     greeting simply stays still for people who've asked for less motion. */
  .marquee__track {
    animation: none !important;
  }
}

/* 3. Layout shell --------------------------------------------------------- */

.wrap {
  max-width: var(--content-width);
  margin: 0 auto;
  padding: 0 var(--space-3);
}

/* Header: logo on the left, menu on the right, both lined up with the
   edges of the galleries below. It stays fixed to the top of the window
   as you scroll, on a white background so content slides underneath it. */
.site-nav {
  position: sticky;
  top: 0;
  z-index: 5; /* above the page, below the full-screen image viewer */
  background: var(--color-paper);
  padding: var(--space-2) 0;
}

.site-nav__row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
  padding: 0 var(--page-inset);
}

.site-nav__brand {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-3);
  letter-spacing: -0.01em;
  text-decoration: none;
  color: var(--color-ink);
  display: flex;
  align-items: center;
}

/* Adjust --logo-height to make the logo bigger or smaller. */
.site-nav__logo {
  --logo-height: 2.75rem;
  height: var(--logo-height);
  width: auto;
}

.site-nav__links {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.site-nav__links a {
  text-decoration: none;
  font-size: var(--step-1);
  font-weight: 500;
  color: var(--color-ink);
}

.site-nav__links a[aria-current="page"] {
  font-weight: 700;
}

main {
  min-height: 60vh;
}

.site-footer {
  border-top: 1px solid var(--color-line);
  margin-top: var(--space-6);
  padding: var(--space-3) 0 var(--space-4);
  font-family: var(--font-display);
  font-size: var(--step-0);
  color: var(--color-ink-soft);
}

.site-footer .wrap {
  max-width: none;
  padding: 0 var(--page-inset);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.site-footer a {
  color: var(--color-ink);
  text-decoration: none;
}

/* 4. Components ------------------------------------------------------------ */

/* -- Justified gallery ----------------------------------------------------
   Used for both the homepage work grid and the galleries inside each
   project page. Every row is a different height, chosen so the images in
   it share one height and together fill the full width — with no image
   ever cropped or distorted.

   How it works: each item is given, inline in the HTML, a flex-grow and
   a flex-basis proportional to that image's real width-to-height ratio
   (measured at build time — see the aspectRatio filter in .eleventy.js),
   plus an aspect-ratio so its height follows from its width. Because
   every item in a row ends up with a width proportional to its ratio,
   their heights all come out equal. The browser handles the wrapping.

   Every row, including the last, stretches to fill the full width — so
   a final row with very few images (or one very tall/narrow one) can
   come out taller than the rows above it. If that ever looks odd with
   your real photos, the fix is usually just adding one more image so
   the last row has enough items to stretch sensibly.

   --gallery-gap is the space between images; the container's side
   padding is deliberately a little larger so the outer margin reads as
   intentional rather than cramped. -- */

.justified-gallery {
  --gallery-gap: 0.5rem;
  --gallery-inset: var(--page-inset);

  display: flex;
  flex-wrap: wrap;
  gap: var(--gallery-gap);
  padding: 0 var(--gallery-inset) var(--space-5);
}

.justified-gallery__item {
  position: relative;
  display: block;
  margin: 0;
  overflow: hidden;
  border-radius: var(--radius-image);
  background: var(--color-accent-soft);
  text-decoration: none;
  color: inherit;
  /* flex-grow, flex-basis and aspect-ratio are set inline per item. */
}

.justified-gallery__item img,
.justified-gallery__item video {
  width: 100%;
  height: 100%;
  display: block;
  /* The item's aspect-ratio matches the image exactly, so this scales
     the image to fill without actually cropping anything. */
  object-fit: cover;
}

/* Project pages stack one or more gallery rows (see galleryRows in
   .eleventy.js). The stack owns the page inset and bottom space, so rows
   inside it need none of their own, just the same gap between them. */
.gallery-stack {
  --gallery-gap: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: var(--gallery-gap);
  padding: 0 var(--page-inset) var(--space-5);
}

.gallery-stack > .justified-gallery {
  padding: 0;
}

/* Rows whose photo count was chosen in the CMS never wrap: everything in
   the row shares one height and fills the full width. */
.justified-gallery--manual {
  flex-wrap: nowrap;
}

.justified-gallery--manual .justified-gallery__item {
  min-width: 0;
}

/* Videos play inline; a click pauses or resumes them. */
.justified-gallery__item--video {
  cursor: pointer;
}

/* Sound on/off button in the corner of videos that have an audio track. */
.video-sound {
  position: absolute;
  right: 0.6rem;
  bottom: 0.6rem;
  width: 2.25rem;
  height: 2.25rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(18, 18, 18, 0.6);
  color: #fff;
  cursor: pointer;
  transition: background 0.2s ease;
}

.video-sound:hover,
.video-sound:focus-visible {
  background: rgba(18, 18, 18, 0.85);
}

.video-sound svg {
  width: 1.15rem;
  height: 1.15rem;
  fill: currentColor;
}

/* Show the "muted" icon by default, the "sound on" icon once switched on. */
.video-sound__on,
.video-sound.is-on .video-sound__off {
  display: none;
}

.video-sound.is-on .video-sound__on {
  display: block;
}

/* Captions: hidden until hover/focus, so the grid reads as pure imagery
   at rest. Only used on the homepage. */
.justified-gallery__caption {
  position: absolute;
  inset: auto 0 0 0;
  padding: var(--space-2);
  background: linear-gradient(to top, rgba(18, 18, 18, 0.75), transparent);
  color: #fff;
  opacity: 0;
  transform: translateY(0.5rem);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.justified-gallery__item:hover .justified-gallery__caption,
.justified-gallery__item:focus-visible .justified-gallery__caption {
  opacity: 1;
  transform: translateY(0);
}

.justified-gallery__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--step-1);
  margin: 0;
}

.justified-gallery__meta {
  font-size: var(--step-0);
  opacity: 0.85;
  margin: 0.2em 0 0;
}

/* Gallery images on a project page open full-screen when clicked. */
.justified-gallery__item--zoom {
  cursor: zoom-in;
}

.work-empty {
  padding: var(--space-4) var(--space-2) var(--space-6);
  color: var(--color-ink-soft);
  max-width: var(--measure);
}

/* -- Project detail page -- */

/* Title and category on the left, description on the right, no dividing
   line. Uses the same side inset as the gallery below so their left edges
   line up. Text is deliberately small and tight, like the About page. */
.project-header {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: var(--space-3);
  align-items: start;
  padding: var(--space-4) var(--page-inset);
  font-size: 1.0625rem;
  line-height: 1.25;
}

.project-header h1 {
  font-family: var(--font-body);
  font-size: 1.4rem;
  font-weight: 600;
  line-height: 1.2;
  margin: 0;
}

.project-header__category {
  margin: 0.2em 0 0;
}

.project-header__description {
  padding-top: 0.15rem; /* lines the first line up with the title */
}

.project-header__description > :last-child {
  margin-bottom: 0;
}

/* Line breaks typed into the Description box in /admin are kept. */
.project-header__text {
  white-space: pre-line;
}

.project-footer {
  padding: 0 var(--page-inset);
}

.back-link {
  display: inline-block;
  margin-bottom: var(--space-6);
  text-decoration: none;
  font-family: var(--font-display);
}

/* -- Lightweight full-screen image viewer (no library, just this) -- */
.lightbox {
  position: fixed;
  inset: 0;
  background: rgba(22, 24, 28, 0.92);
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--space-3);
  z-index: 10;
}

.lightbox.is-open {
  display: flex;
}

.lightbox img {
  max-width: 100%;
  max-height: 100%;
}

/* -- Simple page content (About / Contact) -- */
.page-header {
  padding: var(--space-5) 0 var(--space-3);
}

.page-header h1 {
  font-size: var(--step-4);
}

.page-body {
  padding-bottom: var(--space-5);
  max-width: var(--measure);
  font-size: var(--step-1);
}

.page-body :first-child {
  margin-top: 0;
}

/* -- About page: four columns across the full page width, using the
      same side inset as the galleries so the left edges line up. Text is
      deliberately small with tight line spacing; headings are the same
      size as the text, just bolder. -- */
.about {
  display: grid;
  grid-template-columns: 1.5fr 1.15fr 0.85fr 1.15fr;
  column-gap: var(--space-4);
  align-items: start;
  padding: var(--space-5) var(--page-inset) var(--space-6);
  font-size: 1.0625rem;
  line-height: 1.2;
}

.about__col {
  min-width: 0; /* lets long words and URLs wrap instead of overflowing */
}

/* The first paragraph of the bio is the bold intro. */
.about__bio p {
  margin: 0 0 1.2em;
}

.about__bio p:first-child {
  font-weight: 600;
}

.about__block + .about__block {
  margin-top: 2.4em;
}

.about h2 {
  font-family: inherit;
  font-size: inherit;
  font-weight: 600;
  line-height: inherit;
  margin: 0 0 1.2em;
}

.about__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.about__list li + li {
  margin-top: 1.2em;
}

/* Skills sit one per line with no gaps between them. */
.about__list--tight li + li {
  margin-top: 0;
}

.about__link {
  display: block;
  overflow-wrap: anywhere;
}

.about__photo img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-image);
}

/* Hidden on screen but still read by screen readers and search engines. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* -- Contact page --------------------------------------------------------
   A huge endlessly scrolling greeting across the full window width, then
   the intro text, then two small blocks of contact details. */

.marquee {
  overflow: hidden;
  padding: 0.5rem 0 0;
}

/* The track holds two identical groups side by side and slides left by
   exactly one group's width, then restarts: since the second group is by
   then where the first one began, the jump is invisible and the text
   appears to scroll forever. The duration is set from main.js (so the
   speed stays the same whatever the text length); 60s is the fallback. */
.marquee__track {
  display: flex;
  width: max-content;
  animation: marquee-scroll var(--marquee-duration, 60s) linear infinite;
  will-change: transform;
}

.marquee__group {
  flex: none;
  /* Never narrower than the window, so even a very short greeting can't
     leave a gap at the end of the loop. */
  min-width: 100vw;
  padding-right: 0.3em;
  font-family: var(--font-marquee);
  font-weight: 700;
  font-size: clamp(5rem, 17vw, 16rem);
  line-height: 1.1;
  letter-spacing: -0.02em;
  white-space: nowrap;
}

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

.contact {
  --contact-inset: clamp(var(--page-inset), 3.3vw, 2.75rem);
  display: grid;
  gap: clamp(3rem, 7.5vw, 6rem);
  padding: clamp(2.5rem, 7.5vw, 6rem) var(--contact-inset) var(--space-6);
}

/* First paragraph bold, the rest lighter - same idea as the About bio. */
.contact__text {
  font-size: clamp(1.5rem, 2.6vw, 2.25rem);
  line-height: 1.4;
}

.contact__text p {
  margin: 0;
}

.contact__text p:first-child {
  font-weight: 600;
}

.contact__details {
  display: grid;
  gap: 2.75rem;
  justify-items: start;
}

/* Each block starts with a short thin rule, then a light label above
   bolder details. */
.contact__block {
  display: grid;
  width: max-content;
  min-width: 12rem;
  max-width: 100%;
  padding-top: 0.75rem;
  border-top: 1px solid var(--color-ink);
  font-size: 1.125rem;
  line-height: 1.35;
}

.contact__label {
  margin: 0;
  font-weight: 400;
}

.contact__block a {
  font-weight: 600;
  text-decoration: none;
  overflow-wrap: anywhere;
}

.contact__block a:hover,
.contact__block a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

/* 5. Responsive ----------------------------------------------------------- */

@media (max-width: 1024px) {
  .about {
    grid-template-columns: repeat(2, 1fr);
    row-gap: var(--space-5);
  }
}

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

  /* Smaller logo and menu so both fit on one line on a phone. */
  .site-nav__logo {
    --logo-height: 1.75rem;
  }

  .site-nav__links {
    gap: var(--space-2);
  }

  .site-nav__links a {
    font-size: var(--step-0);
  }

  /* On a narrow screen, justified rows would squeeze images down to
     stamp size, so each one gets its own full-width row instead. Their
     real aspect ratios (set inline) still apply, so nothing is cropped. */
  .justified-gallery--manual {
    flex-wrap: wrap;
  }

  .justified-gallery__item {
    flex-basis: 100% !important;
    flex-grow: 1 !important;
  }

  /* Captions can't be hovered on touch screens, so show them always. */
  .justified-gallery__caption {
    opacity: 1;
    transform: none;
  }

  /* Title and description stack on a phone. */
  .project-header {
    grid-template-columns: 1fr;
    row-gap: var(--space-2);
  }
}
