/* ---- Design tokens (derived from the 1440px Figma frame) ---- */
:root {
  --bg: #e9e9e9;
  --ink: #282828;
  --ink-strong: #000000;
  --accent: #860d0d;
  /* Outline weight for the accent-ruled controls: the contact fields, Submit
     and Cool Beans' screen buttons. Half a pixel over hairline; on a 1x
     screen browsers may still draw it at 1px. */
  --line-w: 1.5px;
  --placeholder: #d9d9d9;
  --field-bg: #e2e2e2;
  --overlay: rgba(0, 0, 0, 0.68);

  /* Fluid sizes: scale with viewport width, never shrinking below a readable mobile floor,
     never capped going up. */
  --fs-nav: max(10px, 0.84vw);
  --fs-hero: max(22px, 2.35vw);
  --fs-body: clamp(15px, 1.1vw, 19px); /* ~16px at 1440 */
  --fs-form: clamp(15px, 1.11vw, 19px); /* 16px at 1440, per the Figma frame */
  --fs-project-name: max(14px, 1.25vw);
  --pad-x: max(56px, 15.833vw); /* ~228px at 1440 */
  --pad-y: max(18px, 3.194vw);
  --nav-pad-y: max(18px, 2.2vw);
  --nav-gap: max(18px, 2.8vw);
  --logo-w: max(14px, 1.2vw);
  --gap-grid: max(10px, 1.667vw);
  --gap-hero: max(24px, 4vw);

  /* The block rhythm inside a case study, and the air around a caption's text.
     40px at the 1440 frames; it scales with the page rather than sitting at a
     flat 40 so a caption never spaces differently from the blocks either side
     of it. Every case study reads this, so the four stay in step. */
  --case-gap: clamp(18px, 2.778vw, 56px);

  /* Side inset of a case study's column. Matches the nav's content box, so the
     blocks line up with the logo and the last nav link; on phones it drops to
     0 (see the Mobile block) so the work gets the full screen width. */
  --case-pad-x: var(--pad-x);
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: "Poppins", "Segoe UI", Arial, sans-serif;
  font-weight: 400;
  line-height: 1.4;
}

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

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

.skip-link {
  position: absolute;
  left: 1rem;
  top: -3rem;
  background: var(--ink-strong);
  color: #fff;
  padding: 0.75rem 1.25rem;
  border-radius: 4px;
  z-index: 100;
  transition: top 0.2s ease;
}

.skip-link:focus {
  top: 1rem;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ---- Header / Nav ---- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  background: rgba(233, 233, 233, 0.9);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--nav-gap);
  padding: var(--nav-pad-y) var(--pad-x);
}

.logo {
  display: inline-flex;
  align-items: center;
  width: var(--logo-w);
  flex-shrink: 0;
}

.logo svg {
  width: 100%;
  height: auto;
  display: block;
}

.logo path {
  fill: var(--accent);
}

/* Hamburger: hidden until the nav collapses (see the 900px block). */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: space-between;
  width: 30px;
  height: 20px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

.nav-toggle span {
  display: block;
  width: 100%;
  height: 2px;
  border-radius: 2px;
  background: var(--accent); /* same colour as the logo */
  transition: transform 0.25s ease, opacity 0.2s ease;
}

/* Bars are at y = 0 / 9 / 18, so ±9px brings the outer two to the centre. */
.nav-toggle[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(9px) rotate(45deg);
}

.nav-toggle[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
}

.nav-toggle[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-9px) rotate(-45deg);
}

.nav-links {
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--nav-gap);
  margin: 0;
  padding: 0;
}

.nav-links a {
  font-size: var(--fs-nav);
  text-transform: capitalize;
  padding-bottom: 4px;
  border-bottom: 2px solid transparent;
  transition: border-color 0.2s ease;
}

.nav-links a:hover,
.nav-links a.active {
  border-color: var(--ink-strong);
}

/* ---- Hero ---- */
.hero {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-hero);
  padding: max(56px, 7vw) var(--pad-x) max(116px, 12.389vw);
}

.hero-text {
  flex: 0 1 55%;
  max-width: 55%;
}

.hero-text p {
  margin: 0;
  max-width: 100%;
  font-size: var(--fs-hero);
  font-weight: 300; /* Poppins Light */
  color: var(--ink);
}

.hero-illustration {
  flex: 0 0 auto;
  width: min(45%, max(160px, 26.32vw));
  aspect-ratio: 379 / 223;
  position: relative;
  left: 10px;
  top: -1px; /* was 2px */
}

.hero-illustration canvas {
  width: 100%;
  height: 100%;
  display: block;
  cursor: pointer; /* the cat responds to clicks — signal it */
}

/* ---- Projects ---- */
.projects {
  padding: 0 var(--pad-x) max(48px, 6vw);
}

.project-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap-grid);
}

.project-card {
  position: relative;
  aspect-ratio: 400 / 303.5;
  border-radius: 6px;
  overflow: hidden;
  background: var(--placeholder);
}

.project-card a {
  display: block;
  position: relative;
  width: 100%;
  height: 100%;
}

.project-card img,
.project-card video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.project-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--overlay);
  opacity: 0;
  transition: opacity 0.25s ease;
}

.project-card a:hover .project-overlay,
.project-card a:focus-visible .project-overlay {
  opacity: 1;
}

.project-card a:focus-visible {
  outline: 2px solid #fff;
  outline-offset: -4px;
}

.project-name {
  color: #fff;
  font-size: var(--fs-project-name);
  font-weight: 400;
  letter-spacing: 0.02em;
  text-align: center;
  padding: 0 1rem;
}

/* ---- About ---- */
/* Centered container, sized as a unit. The two terms give the two behaviours:
   - calc(100% - 2*--pad-x) is exactly the nav bar's content width, so the group
     can never grow past the nav — it aligns to it on medium screens.
   - 1180px caps it on wide screens, where the nav content is wider than that,
     so the group sits visibly inset from the nav.
   Crossover is ~1725px. Proportions held by the flex-basis values below
   (38 image / 8 gap / 54 text). */
.about {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: min(calc(100% - 2 * var(--pad-x)), 1180px);
  margin-inline: auto;
  /* Raise the centred block off dead-centre by --raise. Doubled on the bottom
     because trimming the box from below moves its centre by half the amount,
     so --raise reads as real pixels of travel. Set it to 0 for dead-centre. */
  --raise: 50px;
  padding: clamp(24px, 4vw, 72px) 0
    calc(clamp(24px, 4vw, 72px) + var(--raise) * 2);
}

.about-media {
  flex: 0 0 38%;
  align-self: center;
  aspect-ratio: 424 / 422;
  background: var(--placeholder);
  border-radius: 6px;
}

.about-text {
  flex: 0 0 54%;
  align-self: center;
}

.about-text p {
  margin: 0 0 1em;
  font-size: var(--fs-body);
  color: var(--ink);
}

.about-text p:last-child {
  margin-bottom: 0;
}

/* Pin the CTA to the bottom of the viewport on any screen. dvh so mobile
   browser chrome doesn't push it out of view. */
body.page-about {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}

body.page-about #main {
  flex: 1;
  display: flex;
  flex-direction: column;
}

/* Grow to absorb the leftover height, so align-items:center leaves equal space
   above and below the picture + text. 'flex: 1 0 auto' (not '1 1 0') keeps the
   content's own height as the floor: a tall stacked layout on a short screen
   scrolls instead of squashing. */
body.page-about .about {
  flex: 1 0 auto;
}

body.page-about .about-cta-wrap {
  margin-top: auto; /* takes up the slack, pushing the CTA down */
}

.about-cta-wrap {
  display: flex;
  justify-content: center;
  /* Bottom space matches the nav's top padding. Top padding is the minimum
     gap when the viewport is too short for the slack above to exist. */
  padding: clamp(32px, 5vw, 80px) var(--pad-x) var(--nav-pad-y);
}

.about-cta {
  font-size: var(--fs-nav);
  text-transform: capitalize;
  padding-bottom: 4px;
  border-bottom: 2px solid var(--ink-strong);
  transition: opacity 0.2s ease;
}

.about-cta:hover {
  opacity: 0.6;
}

/* ---- Back to projects ----
   Shared by every case study page, so the two can never drift apart again.
   Sized and underlined like the nav links, because this is site furniture
   rather than case-study content - the same reasoning as .about-cta above.

   Two knobs for the pages that need them:
     --back-ink      recolours text and rule together, for a dark ground.
     --back-pad-top  set to 0 where the page's own column already supplies the
                     space above this block. */
.project-back {
  /* 40px at 1440: the block rhythm both case studies are built on. */
  --back-pad: clamp(18px, 2.778vw, 56px);

  display: flex;
  justify-content: center;
  padding-top: var(--back-pad-top, var(--back-pad));
  padding-bottom: var(--back-pad);
}

.project-back-link {
  /* inline-block so the underline and the tap target below sit on a
     predictable box rather than on inline fragments. */
  display: inline-block;
  position: relative;
  font-family: "Poppins", "Segoe UI", Arial, sans-serif;
  font-size: var(--fs-nav);
  font-weight: 400;
  text-transform: capitalize;
  color: var(--back-ink, var(--ink-strong));
  /* The nav links' hover underline: same 2px weight, same ink, same 4px drop,
     so the two read as the same piece of furniture. */
  padding-bottom: 4px;
  border-bottom: 2px solid var(--back-ink, var(--ink-strong));
  transition: opacity 0.2s ease;
}

.project-back-link:hover {
  opacity: 0.6;
}

@media (max-width: 1100px) {
  /* --fs-nav bottoms out at its 10px floor here, and the nav bar it was
     matched to has collapsed to a hamburger anyway, so there is nothing left
     on screen to match. Same value .about-cta takes at this breakpoint. */
  .project-back-link {
    font-size: max(16px, 1.6vw);
  }

  /* Opens the tap target to roughly 44px without moving the underline. Padding
     would push the border away from the text, so the hit area is an invisible
     box laid over the link instead - it is inside the <a>, so it takes the
     press. */
  .project-back-link::before {
    content: "";
    position: absolute;
    inset: -12px -20px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .project-back-link {
    transition: none;
  }
}

/* ---- Video sitting on the page's flat ground ----
   A <video> whose artwork is painted onto the page colour rather than cut out
   will otherwise draw a faint line around itself, and it takes three separate
   measures to stop it. Kept here because every case study that places a clip
   on flat ground needs all three, and they are not obvious enough to rediscover.

     1. The wrapper clips, and the clip bleeds 3px past the box on every side.
        The browser scales the clip down to fit and the filter it uses samples
        past the outermost row and column; the overhang puts those samples
        outside the visible area. The 3px given up is flat ground.
     2. Width and height are explicit. A replaced element with `width: auto`
        takes its INTRINSIC size instead of stretching between the offsets, so
        `inset` alone renders the clip full size and lets the wrapper crop it.
     3. ::after repaints the outermost 3px in the ground colour, in the
        ORDINARY paint layer. These boxes are percentages of percentages and
        never land on a whole device pixel, so the video's own compositing
        layer rounds its edges independently of the clip rectangle and two
        sides end up half a pixel out. Covering them sidesteps the rounding
        rather than chasing it.

   The wrapper carries the size and ratio; set --plate-ground on a page whose
   ground is not the site's default. */
.video-plate {
  position: relative;
  overflow: hidden;
  background: var(--plate-ground, var(--bg));
}

.video-plate > video,
.video-plate > img {
  position: absolute;
  top: -3px;
  left: -3px;
  width: calc(100% + 6px);
  height: calc(100% + 6px);
  display: block;
  object-fit: cover;
  /* A video element with no poster can paint black for a frame before the
     first decode. Grounding it makes that gap invisible. */
  background: var(--plate-ground, var(--bg));
}

.video-plate::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  box-shadow: inset 0 0 0 3px var(--plate-ground, var(--bg));
}

/* ---- Case study caption ----
   The same block in every light-ground case study, because it is the same
   Figma component in every frame: a heading and a paragraph starting on the
   container's halfway line. A 1fr 1fr grid with the text in column 2 puts it
   exactly on the midpoint and keeps it there as the window resizes, so it
   lines up with the right-hand half of anything above it.
   (Inflex Trace keeps its own .ix-caption: white on dark, Outfit body copy and
   a reserved min-height make it a genuinely different block.) */
.case-caption {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.case-caption-text {
  grid-column: 2; /* the halfway line; no left padding, so it sits exactly on it */
  /* 64px at 1440, which is where the frames end the 656px measure. */
  padding-right: clamp(16px, 4.44vw, 72px);
}

.case-caption h2 {
  margin: 0 0 0.7em;
  font-family: "Outfit", "Poppins", "Segoe UI", Arial, sans-serif;
  font-size: clamp(20px, 1.736vw, 30px); /* 25px at 1440 */
  font-weight: 400;
  color: var(--ink-strong);
}

.case-caption p {
  margin: 0;
  font-family: "Nunito Sans", "Poppins", "Segoe UI", Arial, sans-serif;
  font-size: clamp(15px, 1.25vw, 22px); /* 18px at 1440 */
  font-weight: 400;
  line-height: 1.5;
  color: var(--case-muted, #78748a);
}

@media (max-width: 1100px) {
  .case-caption {
    grid-template-columns: 1fr;
  }

  .case-caption-text {
    grid-column: 1;
    /* Still left aligned, just not flush against the column edge. */
    padding-inline: clamp(8px, 2vw, 24px);
  }
}

/* ---- Contact ----
   Same centred-container recipe as .about, so the two pages line up.
   Figma proportions at 1440: form 612 / gap 33 / illustration 594. The gap is
   opened to 5% here because 2.7% reads as crowded once it is fluid. */
.contact {
  display: grid;
  /* No max-width cap: the container is exactly the nav's content box, so the
     form's left edge lands on the logo and the illustration's right edge on
     "Contact". Template proportions at 1440 are 612 / 33 / 594. */
  grid-template-columns: 46% 52%;
  column-gap: 2%;
  row-gap: clamp(16px, 1.6vw, 23px);
  align-items: start;
  align-content: center;
  width: calc(100% - 2 * var(--pad-x));
  margin-inline: auto;
  /* Same --raise knob as .about: lifts the centred block by that many real
     pixels. Doubled on the bottom because trimming the box from below moves
     its centre by half the amount. 0 = dead-centre. */
  --raise: 30px;
  padding: clamp(24px, 4vw, 72px) 0
    calc(clamp(24px, 4vw, 72px) + var(--raise) * 2);
}

/* display:contents lifts .contact-fields and .submit out as grid items of
   .contact, so the illustration can share row 1 with the fields and sit on
   their baseline. Form submission and validation are unaffected. */
.contact-form {
  display: contents;
}

.contact-fields {
  grid-column: 1;
  grid-row: 1;
  display: flex;
  flex-direction: column;
  gap: clamp(18px, 2.08vw, 30px);
}

.field {
  display: flex;
  flex-direction: column;
  gap: clamp(7px, 0.76vw, 11px);
}

.field label {
  font-size: var(--fs-form);
  text-transform: capitalize;
  color: var(--ink-strong);
}

.field input,
.field textarea {
  width: 100%;
  font-family: inherit;
  font-size: var(--fs-form);
  font-weight: 400;
  color: var(--ink-strong);
  background: var(--field-bg);
  border: var(--line-w) solid var(--accent);
  border-radius: 5px;
  padding: 0 clamp(10px, 0.97vw, 14px);
}

.field input {
  height: clamp(42px, 3.125vw, 52px); /* 45px at 1440 */
}

.field textarea {
  height: clamp(140px, 12.15vw, 200px); /* 175px at 1440 */
  padding-block: clamp(10px, 0.9vw, 13px);
  resize: vertical;
  line-height: 1.4;
}

/* Submit button plus the line that reports how sending went. */
.contact-actions {
  grid-column: 1;
  grid-row: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px clamp(14px, 1.4vw, 20px);
}

.form-status {
  margin: 0;
  font-size: var(--fs-form);
  color: var(--ink);
}

.form-status[data-state="error"] {
  color: var(--accent);
}

.submit {
  font-family: inherit;
  font-size: var(--fs-form);
  font-weight: 400;
  text-transform: capitalize;
  color: var(--ink-strong);
  background: none;
  border: var(--line-w) solid var(--accent);
  border-radius: 5px;
  padding: clamp(6px, 0.56vw, 8px) clamp(20px, 2.08vw, 30px);
  cursor: pointer;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.submit:hover {
  background: var(--accent);
  color: #fff;
}

.submit:disabled {
  opacity: 0.6;
  cursor: progress;
}

/* Honeypot field: kept out of view without display:none, which some bots
   check for before filling a field in. */
.field-trap {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Turnstile only takes up room when it has something to show. */
.cf-turnstile:empty {
  display: none;
}

.contact-illustration {
  grid-column: 2;
  grid-row: 1;
  align-self: end; /* bottom edge lands on the message field's bottom */
  /* The artboard's own ratio, so Fit.Contain has no letterbox to centre the
     cat in. main.js re-reads it from the file on load; this is the fallback. */
  aspect-ratio: 612 / 291;
}

.contact-illustration canvas {
  width: 100%;
  height: 100%;
  display: block;
  /* The artboard keeps 8 of its 291px empty below the floor line; dropping
     the canvas by that share puts the floor itself on the field's bottom. */
  transform: translateY(calc(8 / 291 * 100%));
  /* Cursor is driven from main.js: pointer over the cat only, not the laptop
     it shares the canvas with. */
  cursor: default;
}

/* Centre the row between nav and the bottom of the viewport, as in the Figma. */
body.page-contact {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}

body.page-contact #main {
  flex: 1;
  display: flex;
  flex-direction: column;
}

body.page-contact .contact {
  flex: 1 0 auto;
}

/* ---- Simple page shell (About / Contact stubs) ---- */
.page-content {
  padding: max(32px, 8vw) var(--pad-x) max(64px, 10vw);
  min-height: 40vh;
}

.page-content h1 {
  font-size: var(--fs-hero);
  font-weight: 400;
  margin: 0 0 1rem;
}

.page-content p {
  max-width: 560px;
  font-size: var(--fs-nav);
  color: var(--ink);
}

/* ---- Nav: collapse to hamburger ----
   1100px is deliberately the same breakpoint as the About stack below, so the
   nav collapses on the same resize that centres the layout. Keep the two in
   sync, along with NAV_BREAKPOINT in js/main.js. */
@media (max-width: 1100px) {
  /* The hamburger is a fixed 30x20px, but --logo-w bottoms out at its 14px
     floor here (1.2vw stops winning below ~1167px), leaving the logo tiny
     next to it. Give the logo a fixed size too, so the pair stays balanced
     all the way down: 20px wide x the SVG's 50/35.31 ratio = ~28px tall. */
  :root {
    --logo-w: 20px;
  }

  .nav {
    position: relative; /* containing block for the panel */
    flex-wrap: nowrap;
  }

  .nav-toggle {
    display: flex;
  }

  /* Panel drops out of the header, spanning its full width. visibility (not
     display) so it can transition and stays out of the tab order when closed. */
  .nav-links {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: flex-end;
    gap: clamp(14px, 3vw, 22px);
    padding: clamp(18px, 4vw, 30px) var(--pad-x) clamp(24px, 5vw, 38px);
    background: rgba(233, 233, 233, 0.98);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
  }

  .nav-links.open {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
  }

  /* --fs-nav bottoms out around 10px here, too small for a touch target. */
  .nav-links a {
    font-size: max(16px, 2.2vw);
  }
}

/* ---- Tablet ---- */
@media (max-width: 1000px) {
  /* Straight from three columns to one: the projects run one by one on
     anything narrower, rather than as a 2-up grid of smaller cards. */
  .project-grid {
    grid-template-columns: 1fr;
  }

  .hero {
    flex-direction: column;
    align-items: flex-start;
  }

  .hero-text {
    max-width: 100%;
  }

  .hero-illustration {
    width: min(70%, 320px);
    align-self: center;
  }
}

/* ---- About: stack ----
   Stacks at 1100px, not 1000px: below ~1100 the text column gets narrow enough
   that it wraps taller than the picture, breaking the "text stays within the
   picture's top and bottom" rule. Width still comes from the base rule, so the
   stacked group stays aligned to the nav bar. */
@media (max-width: 1100px) {
  .about {
    flex-direction: column;
    align-items: center;
    /* Base rule says space-between; once .about grows, that would fling the
       picture and text to opposite ends of the box in column direction. */
    justify-content: center;
    gap: clamp(28px, 5vw, 48px);
    /* --raise is a wide-screen nudge off centre. Stacked, the content nearly
       fills the viewport and its doubled bottom padding just shoves the CTA
       off-screen, so spend that height on the content instead. */
    --raise: 0px;
  }

  .about-media {
    flex: 0 0 auto;
    /* min(440px, 36vh) keeps "Go To Projects" above the fold on short screens
       (at a flat 440px the picture ate ~47% of the viewport), but 36vh on its
       own shrinks without limit — max(200px, ...) is the floor. The outer
       min(100%) stops that floor overflowing a narrow container. */
    width: min(100%, max(200px, min(440px, 36vh)));
    align-self: center;
  }

  .about-text {
    flex: 0 0 auto;
    width: 100%;
  }

  /* --fs-nav bottoms out at 10px here, and the nav bar this was matched to has
     collapsed to a hamburger anyway, so there is no longer anything on screen
     to match. Size it to stay readable instead. */
  .about-cta {
    font-size: max(16px, 1.6vw);
  }
}

/* ---- Contact: stack ---- */
@media (max-width: 1100px) {
  .contact {
    grid-template-columns: 1fr;
    /* Stacked, the content nearly fills the viewport; the lift would only push
       it toward the fold. */
    --raise: 0px;
  }

  .contact-fields {
    grid-column: 1;
    grid-row: 2;
  }

  .submit {
    grid-column: 1;
    grid-row: 3;
  }

  /* Illustration on top, matching the picture-over-text About stack. Its own
     margin rather than a bigger row-gap, so the fields-to-Submit gap stays put. */
  .contact-illustration {
    grid-column: 1;
    grid-row: 1;
    align-self: center;
    width: min(100%, 480px);
    margin-inline: auto;
    margin-bottom: clamp(12px, 3vw, 28px);
  }
}

/* ---- Mobile ---- */
@media (max-width: 600px) {
  /* The old centred/wrapped nav rules lived here; the hamburger owns this
     range now, so the logo stays left and the toggle right. */

  .hero-illustration {
    width: min(85%, 280px);
  }
}

/* ---- Phone gutters ----
   One 16px gutter for the whole site on phones: header, hero, project grid,
   About, Contact and caption text all line up on it. At the desktop formula it
   came to ~62px a side, a third of a phone's width. Case-study media goes
   further and runs edge to edge.
   The second query catches phones held sideways (~830px wide but under 500px
   tall), which would otherwise get the desktop's ~130px margins. No laptop or
   tablet viewport is that short, so they are unaffected. */
@media (max-width: 600px), (max-height: 500px) and (orientation: landscape) {
  :root {
    --pad-x: 16px;
    --case-pad-x: 0px;
  }

  /* With no column inset, caption text needs its own gutter. */
  .case-caption-text {
    padding-inline: 16px;
  }
}

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

  .nav-links,
  .nav-links a,
  .nav-toggle span,
  .about-cta,
  .project-overlay,
  .skip-link {
    transition: none;
  }
}

/* ---- Project film ----
   A Cloudflare Stream film played through Video.js (js/film.js). The wrapper
   carries the size and ratio, exactly as the placeholders did, and the player
   fills it. Before Video.js has loaded, the custom elements are unknown and
   render as plain boxes, so the poster <img> is positioned to fill the frame
   itself; once the skin upgrades it moves the same image into its poster
   slot, which uses the same fit, so the swap is invisible. */
.film {
  position: relative;
  overflow: hidden;
  background: #000;
}

.film video-player,
.film video-neutral-skin {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  --media-accent-color: var(--accent);
  --media-accent-text-color: #ffffff;
  --media-border-color: transparent;
  --media-border-radius: 0;
  --media-font-family: "Poppins", "Segoe UI", Arial, sans-serif;
  --media-object-fit: cover;
}

.film img[slot="poster"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: var(--media-object-fit, cover);
}
