/* ==========================================================================
   CraftCam.tv — Design system
   Tokens transcribed directly from the build brief. Change values here,
   not in individual components, so the whole site stays consistent.
   ========================================================================== */

@font-face {
  font-family: "Sohne";
  src: url("/assets/fonts/soehne-buch.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Sohne";
  src: url("/assets/fonts/soehne-kraftig.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Sohne Mono";
  src: url("/assets/fonts/soehne-mono-buch.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
/* If you don't have a Sohne license, delete the three @font-face blocks
   above and the fallback stack below will quietly use Inter instead —
   nothing else needs to change. */

:root{
  /* Colour */
  --bg: #0A0A0A;
  --surface: #141414;
  --surface-hover: #1C1C1C;
  --text: #F2F2F2;
  --text-muted: #8A8A8A;
  --line: rgba(255,255,255,0.12);
  --accent: #FF3B30;
  --scrim: rgba(0,0,0,0.72);
  --hero-scrim: rgba(0,0,0,0.35);

  /* Type */
  --font-sans: "Sohne", "Inter", Verdana, -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: "Sohne Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;

  --size-hero-name: 104px;
  --size-hero-role: 24px;
  --size-section-heading: 72px;
  --size-item-title: 28px;
  --size-body: 18px;
  --size-caption: 15px;
  --size-timecode: 15px;
  --size-nav: 16px;

  /* Layout */
  --max-width: 1440px;
  --gutter: 80px;
  --section-gap: 160px;

  /* Motion */
  --ease-standard: cubic-bezier(0.22, 1, 0.36, 1);
  --dur-hover: 200ms;
  --dur-expand: 450ms;
  --dur-scrim: 350ms;
  --dur-entry: 900ms;
}

@media (max-width: 810px){
  :root{
    --size-hero-name: 46px;
    --size-hero-role: 16px;
    --size-section-heading: 38px;
    --size-item-title: 22px;
    --size-body: 17px;
    --size-caption: 13px;
    --size-timecode: 13px;
    --size-nav: 15px;
    --gutter: 24px;
    --section-gap: 80px;
  }
}

/* ---------- Reset ---------- */
*, *::before, *::after{ box-sizing: border-box; }
html{
  background: var(--bg);
  overflow-x: hidden;
  /* clip, not hidden, is what keeps position:sticky working — `hidden`
     makes this a scroll container and sticky elements stop sticking. The
     hidden line above stays as a fallback for browsers without clip. */
  overflow-x: clip;
}
body{
  margin: 0;
  /* Transparent on purpose: the .ambient-bg layer below supplies the black,
     so the red glows sit *in* the background instead of behind it. */
  background: transparent;
  overflow-x: hidden;
  overflow-x: clip;
  width: 100%;
  max-width: 100vw;
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--size-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
img, video{ max-width: 100%; display: block; }
a{ color: inherit; text-decoration: none; }
button{ font-family: inherit; color: inherit; background: none; border: none; cursor: pointer; }
ul{ margin: 0; padding: 0; list-style: none; }
h1,h2,h3,p{ margin: 0; }
h1, h2, h3,
.hero-name, .section-heading, .skill-title, .item-title,
.detail-header h1, .about-text h3{
  font-weight: 700;
}

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

.measure{ max-width: 65ch; }

/* ==words== in any content field. Underline rather than a highlight block, so
   it works on the dark background without a box behind the text. */
.mark{
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 2px;
  text-underline-offset: 5px;
  /* Don't let descenders break the line into segments. */
  text-decoration-skip-ink: none;
}

.container{
  max-width: var(--max-width);
  margin: 0 auto;
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}

section{ padding-top: var(--section-gap); padding-bottom: 0; }
section:last-of-type{ padding-bottom: var(--section-gap); }

.caption{
  font-size: var(--size-caption);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
}

/* ---------- Section entry animation ---------- */
.reveal{
  opacity: 0;
  transform: translateY(48px);
  transition: opacity var(--dur-entry) var(--ease-standard), transform var(--dur-entry) var(--ease-standard);
}
.reveal.is-visible{ opacity: 1; transform: translateY(0); }
@media (prefers-reduced-motion: reduce){
  .reveal{ opacity: 1; transform: none; transition: none; }
}

/* ==========================================================================
   Header
   ========================================================================== */
.site-header{
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Slim bar: half the previous 24px vertical padding. */
  padding: 12px var(--gutter);
  background: transparent;
  transition: background-color var(--dur-hover) var(--ease-standard), backdrop-filter var(--dur-hover) var(--ease-standard);
}
.site-header.is-scrolled{
  background: rgba(10,10,10,0.8);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
.site-header .logo{
  display: block;
}
.site-header .logo img{
  height: 60px;
  width: auto;
  display: block;
}
.site-nav{
  display: flex;
  gap: 36px;
}
.site-nav a{
  font-size: var(--size-nav);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  position: relative;
  padding-bottom: 4px;
  opacity: 0.75;
}
.site-nav a::after{
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-hover) var(--ease-standard);
}
.site-nav a:hover{ opacity: 1; }
.site-nav a:hover::after{ transform: scaleX(1); }
.site-nav a.is-active{
  opacity: 1;
}
.site-nav a.is-active::after{ transform: scaleX(1); background: var(--text); }

.timecode{
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: var(--size-timecode);
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  min-width: 118px;
  justify-content: flex-end;
}
.timecode .rec-dot{
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
}
.timecode .rec-dot.blink{ animation: blink 1s steps(1) infinite; }
.timecode .rec-dot.pulse{ animation: pulse 1s ease-in-out infinite; }
@keyframes blink{ 0%, 49%{ opacity: 1; } 50%, 100%{ opacity: 0; } }
@keyframes pulse{ 0%, 100%{ opacity: 1; } 50%{ opacity: 0.25; } }
@media (prefers-reduced-motion: reduce){
  .timecode .rec-dot{ animation: none !important; opacity: 1; }
}

/* ---------- Hamburger + mobile dropdown ----------
   Base state first, then the breakpoint that turns it on. Declaring the
   base rule after the media query would silently win at equal specificity
   and leave narrow screens with no navigation at all. */
.hamburger{
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  padding: 10px 6px;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
}
.hamburger span{
  width: 22px; height: 1.5px;
  background: var(--text);
  transition: transform var(--dur-hover) var(--ease-standard),
              opacity var(--dur-hover) var(--ease-standard);
}
/* Open: top and bottom bars cross, middle bar fades — so the same control
   clearly closes the menu it opened. */
.hamburger[aria-expanded="true"] span:nth-child(1){ transform: translateY(6.5px) rotate(45deg); }
.hamburger[aria-expanded="true"] span:nth-child(2){ opacity: 0; }
.hamburger[aria-expanded="true"] span:nth-child(3){ transform: translateY(-6.5px) rotate(-45deg); }

.mobile-nav{
  /* Anchored to the header itself rather than a hard-coded offset, so it
     always hangs directly below it whatever the logo size. */
  position: absolute;
  top: 100%;
  right: var(--gutter);
  min-width: 200px;
  max-width: calc(100vw - (var(--gutter) * 2));
  display: flex;
  flex-direction: column;
  padding: 8px 0;
  background: rgba(14,14,14,0.98);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 18px 40px rgba(0,0,0,0.5);
  opacity: 0;
  transform: translateY(-8px);
  pointer-events: none;
  transition: opacity 200ms var(--ease-standard), transform 200ms var(--ease-standard);
}
.mobile-nav.is-open{
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.mobile-nav a{
  padding: 13px 22px;
  font-size: 15px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap;
}
.mobile-nav a + a{ border-top: 1px solid var(--line); }
.mobile-nav a:hover{ color: var(--text); }
.mobile-nav a.is-active{
  color: var(--text);
  /* Same red marker the rest of the site uses for "you are here". */
  box-shadow: inset 2px 0 0 var(--accent);
}

@media (max-width: 810px){
  .timecode, .site-nav{ display: none; }
  .hamburger{ display: flex; }
  /* The bar is slim now, so the full-size logo would fill it end to end. */
  .site-header .logo img{ height: 44px; }
}
@media (min-width: 811px){
  /* Belt and braces: never leave the dropdown reachable on desktop. */
  .mobile-nav{ display: none; }
}
@media (prefers-reduced-motion: reduce){
  .mobile-nav{ transition: opacity 200ms linear; transform: none; }
  .mobile-nav.is-open{ transform: none; }
  .hamburger span{ transition: none; }
}

/* ==========================================================================
   Footer
   ========================================================================== */
.site-footer{
  border-top: 1px solid var(--line);
  padding: 72px var(--gutter) 48px;
}
.footer-heading{
  font-size: var(--size-section-heading);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin-bottom: 48px;
}
.footer-heading::before{
  content: "";
  display: inline-block;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--accent);
  margin-right: 20px;
  vertical-align: middle;
  margin-top: -6px;
}
.footer-cols{
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 32px;
}
.site-footer .footer-name{ font-size: 20px; margin-bottom: 8px; }
.site-footer .footer-contact-line{
  font-size: var(--size-body);
  color: var(--text-muted);
  max-width: 46ch;
}
.site-footer .footer-contact{
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
}
/* Same size, weight and colour as the name/role line opposite. */
.site-footer .footer-contact a{
  display: block;
  font-size: 20px;
  color: var(--text);
  margin-bottom: 4px;
}
.site-footer .footer-contact a:hover{ color: var(--accent); }
.site-footer .footer-socials{ display: flex; gap: 24px; align-items: center; margin-top: 16px; justify-content: flex-end; }
.site-footer .footer-socials a{ opacity: 1; transition: opacity var(--dur-hover) var(--ease-standard), transform var(--dur-hover) var(--ease-standard); display: inline-block; }
.site-footer .footer-socials a:hover{ opacity: 1; transform: translateY(-2px); }
.site-footer .footer-socials a:hover svg{ fill: var(--accent); }
.site-footer .footer-socials svg{ width: 26px; height: 26px; fill: var(--text); }
@media (max-width: 810px){
  /* Columns stack here, so a right-aligned contact block would read as
     stray text floating against the opposite edge. */
  .site-footer .footer-contact{ align-items: flex-start; text-align: left; }
  .site-footer .footer-socials{ justify-content: flex-start; }
  .site-footer .footer-contact a{ font-size: 17px; }
}
/* Same pill as the hero's unmute control and the portfolio filters. */
.footer-cta{
  display: inline-block;
  margin-top: 40px;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 12px 22px;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text);
  background: rgba(10,10,10,0.4);
  transition: border-color var(--dur-hover) var(--ease-standard),
              color var(--dur-hover) var(--ease-standard);
}
.footer-cta:hover{ border-color: var(--accent); color: var(--accent); }
.footer-bottom{
  width: 100%;
  margin-top: 40px;
  display: flex;
  justify-content: space-between;
  border-top: 1px solid var(--line);
  padding-top: 20px;
  font-size: var(--size-caption);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* ==========================================================================
   Loading screen
   ========================================================================== */
#loading-screen{
  position: fixed; inset: 0;
  /* Transparent so the site's own background layer — the black plus the red
     ambient glows — stays put between pages, with only the iris on top of
     it. Page content is hidden separately, below. */
  background: transparent;
  z-index: 999;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 400ms var(--ease-standard);
}
#loading-screen .loading-animation{
  /* The file is 1000x1000; without an explicit size it would render huge. */
  width: 128px;
  height: 128px;
}
@media (max-width: 810px){
  #loading-screen .loading-animation{ width: 96px; height: 96px; }
}
#loading-screen .loading-dot{
  width: 28px; height: 28px;
  border-radius: 50%;
  background: var(--accent);
  /* Hard on/off, like a record light — not a soft pulse. */
  animation: loading-blink 0.8s steps(1) infinite;
}
@keyframes loading-blink{
  0%, 49%{ opacity: 1; }
  50%, 100%{ opacity: 0; }
}
#loading-screen.is-hidden{ opacity: 0; pointer-events: none; }
/* Hidden while the loading screen is up — but with a CSS-only failsafe, so a
   JavaScript failure or an odd back/forward-cache restore can never leave the
   whole page invisible. The animation forces visibility back after 2.5s
   whatever happens, and once it has run the state survives a bfcache restore. */
body.is-loading > *:not(#loading-screen):not(.ambient-bg){
  visibility: hidden;
  animation: loading-failsafe 0s linear 2.5s forwards;
}
@keyframes loading-failsafe{ to{ visibility: visible; } }
/* Everything except the background layer and the loading screen itself is
   hidden while loading. visibility (rather than display) keeps the layout
   intact, so nothing reflows when it comes back. */
body.is-loading > *:not(#loading-screen):not(.ambient-bg){ visibility: hidden; }
@media (prefers-reduced-motion: reduce){
  #loading-screen{ display: none; }
  #loading-screen .loading-dot{ animation: none; }
}

/* ==========================================================================
   Page transitions
   ========================================================================== */
body{ opacity: 1; transition: opacity 250ms var(--ease-standard); position: relative; }
body.is-transitioning{ opacity: 0; }

/* ---------- Ambient background texture ---------- */
/* Heavily-blurred, low-opacity red dots that scroll with the page,
   scattered by percentage so they land sensibly regardless of a given
   page's actual length. Purely decorative — sits behind everything and
   never intercepts clicks. */
.ambient-bg{
  position: fixed;
  inset: 0;
  z-index: -1;
  /* The page's black lives here, with the glows layered on top of it, so the
     two read as one background. overflow:hidden keeps the blurred circles
     from pushing the document wider than the viewport. */
  background: var(--bg);
  overflow: hidden;
  pointer-events: none;
}
.ambient-glow{
  position: absolute;
  border-radius: 50%;
  background: var(--accent);
  filter: blur(120px);
  opacity: 0.16;
}
.ambient-glow-a{ width: 480px; height: 480px; top: -6%; right: -120px; }
.ambient-glow-b{ width: 420px; height: 420px; top: 42%; left: -120px; }
.ambient-glow-c{ width: 380px; height: 380px; bottom: -8%; right: -100px; }
@media (max-width: 810px){
  .ambient-glow{ opacity: 0.12; }
  .ambient-glow-a, .ambient-glow-b, .ambient-glow-c{ width: 260px; height: 260px; }
}
@media (prefers-reduced-motion: reduce){
  .ambient-glow{ opacity: 0.1; }
}

/* ==========================================================================
   Hero
   ========================================================================== */
.hero{
  position: relative;
  height: 100vh;
  min-height: 560px;
  width: 100%;
  overflow: hidden;
  display: flex;
}
.hero-media{
  position: absolute; inset: 0;
  width: 100%; height: 100%;
}
.hero-media iframe,
.hero-media video{
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  border: none;
}
.hero-scrim{
  position: absolute; inset: 0;
  background: var(--hero-scrim);
}
.hero-content{
  position: relative;
  z-index: 2;
  width: 100%;
  display: flex;
  padding: 0 var(--gutter) 96px;
}
.hero-content.placement-center{
  align-items: center;
  justify-content: center;
  text-align: center;
  padding-bottom: 0;
}
.hero-content.placement-bottom-left{
  align-items: flex-end;
  justify-content: flex-start;
}
.hero-name{
  font-size: var(--size-hero-name);
  text-transform: uppercase;
  letter-spacing: -0.02em;
  line-height: 1.02;
}
.hero-role{
  margin-top: 14px;
  font-size: var(--size-hero-role);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
}
/* The note and the pill share one bottom-right anchor, so the note always
   sits immediately left of the button however wide its label is — the label
   changes between "Unmute" and "Mute". */
.hero-utility{
  position: absolute;
  bottom: 32px; right: var(--gutter);
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 16px;
}
.hero-note{
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  text-align: right;
  text-transform: uppercase;
}
/* The hero is already crowded on a phone — the note would run into the scroll
   cue and the role line, so it drops out there. */
@media (max-width: 810px){
  .hero-note{ display: none; }
}
.hero-mute-toggle{
  border: 1px solid var(--line);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 10px 18px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  background: rgba(10,10,10,0.4);
  backdrop-filter: blur(6px);
}
.hero-mute-toggle:hover{ border-color: var(--text); }
.scroll-cue{
  position: absolute;
  bottom: 32px; left: 50%;
  transform: translateX(-50%);
  z-index: 3;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  transition: opacity 400ms var(--ease-standard);
}
.scroll-cue::after{
  content: "";
  width: 1px; height: 28px;
  background: var(--line);
}
.scroll-cue.is-hidden{ opacity: 0; }
@media (max-width: 810px){
  .hero-content.placement-bottom-left{ padding-bottom: 64px; }
}

/* ==========================================================================
   Two-column intro
   ========================================================================== */
.intro-grid{
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 64px;
  /* Centred rather than top-aligned: the intro copy is a few lines against a
     square portrait, so top-aligning left the text stranded at the top with
     dead space beneath it. Change to `start` to top-align again. */
  align-items: center;
}
.intro-grid .intro-portrait{ order: 2; }
/* The paragraphs are wrapped in .intro-text so the grid always has exactly
   two children. Without the wrapper, a second paragraph took the portrait's
   column and bumped the portrait to a row of its own, where it stretched to
   the full width of the text column. */
.intro-text{ min-width: 0; }
/* Matches the heading size used inside the Key Skills tabs. */
.intro-heading{
  font-size: 34px;
  line-height: 1.2;
  margin: 0 0 0.6em;
}
.intro-copy{ font-size: 20px; line-height: 1.55; margin: 0; }
.intro-copy + .intro-copy{ margin-top: 1.1em; }
.intro-bullets{ margin-top: 1.4em; }
/* The shared tick rule uses --size-body (18px) and sits further down the
   file, so this needs the extra .intro-text to outrank it rather than relying
   on source order. */
.intro-text .intro-bullets li{ font-size: 20px; line-height: 1.55; }
/* text-wrap: balance evens the line lengths of a short paragraph, which stops
   awkward breaks like "or / browse". Ignored by older browsers, which simply
   wrap as before. */
/* Opening paragraphs on Bio and Portfolio. No .measure cap — these run the
   full width of the container, so they read as a statement rather than a
   column of body copy. text-wrap: pretty stops the last line being left with
   a single stranded word; older browsers just wrap as before. */
.page-intro{
  font-size: 28px;
  line-height: 1.45;
  margin: 0;
  max-width: none;
  text-wrap: pretty;
}
.page-intro + .page-intro{ margin-top: 0.7em; }
/* The paragraph is the only thing above the filter row on Portfolio, so it
   carries the gap down to it. */
.portfolio-intro-gap{ margin-bottom: 56px; }
@media (max-width: 810px){
  .page-intro{ font-size: 20px; line-height: 1.5; }
}
@media (max-width: 810px){
  /* Block rather than grid, so the portrait can float and the copy wraps
     around it — grid items can't float. Half width keeps it from swallowing
     the screen as it did at full width. */
  .intro-grid{ display: block; }
  .intro-grid .intro-portrait{
    float: right;
    width: 50%;
    margin: 4px 0 16px 20px;
  }
  /* The float is taller than the copy on a narrow screen, so clear it or the
     section below rides up alongside it. */
  .intro-grid::after{ content: ""; display: block; clear: both; }
}

/* ==========================================================================
   Section headings
   ========================================================================== */
.section-heading{
  font-size: var(--size-section-heading);
  letter-spacing: -0.02em;
  line-height: 1.05;
  margin-bottom: 56px;
}

/* ==========================================================================
   Key Skills
   ========================================================================== */
.skill-row{
  display: flex;
  align-items: center;
  gap: 48px;
  padding: 32px 0;
  border-top: 1px solid var(--line);
}
/* Generous breathing room between skills on desktop, so you aren't looking at
   two or three autoplaying clips at once. */
@media (min-width: 811px){
  .key-skills-list .skill-row{ padding: 160px 0; }
    /* The big padding is for the gaps between skills. At the top and bottom of
     the list it would also push the heading and the next section away, so
     those two outer edges go back to the original 32px. */
  .key-skills-list .skill-row:first-child{ padding-top: 32px; }
  .key-skills-list .skill-row:last-child{ padding-bottom: 32px; }
}
/* Lines between the skills only — the row border-top above provides them, so
   the first row's is suppressed and the last row gets none added. */
.skills-list .skill-row:first-child{ border-top: none; }
.skill-row.align-right{ flex-direction: row-reverse; }
.skill-media{
  position: relative;
  flex: 0 0 45%;
  aspect-ratio: 16/9;
  overflow: hidden;
  background: var(--surface);
  transition: transform var(--dur-hover) var(--ease-standard);
}
.key-skills-list .skill-media{
  /* Slightly narrower than the media elsewhere so the enlarged titles
     ("Live Broadcast" and friends) don't wrap awkwardly beside it. */
  flex-basis: 58%;
}
/* Red corner-bracket frame, viewfinder-style — reused across Key Skills,
   Selected Work thumbnails, and the About/intro portrait for a consistent
   accent throughout the site. */
.key-skills-list .skill-media::before,
.key-skills-list .skill-media::after,
.work-thumb::before,
.work-thumb::after,
.intro-portrait::before,
.intro-portrait::after{
  content: "";
  position: absolute;
  width: 28px; height: 28px;
  z-index: 2;
  pointer-events: none;
}
.key-skills-list .skill-media::before,
.work-thumb::before,
.intro-portrait::before{
  top: 14px; left: 14px;
  border-top: 3px solid var(--accent);
  border-left: 3px solid var(--accent);
}
.key-skills-list .skill-media::after,
.work-thumb::after,
.intro-portrait::after{
  bottom: 14px; right: 14px;
  border-bottom: 3px solid var(--accent);
  border-right: 3px solid var(--accent);
}
/* Now that the full body copy sits beside the video rather than a one-line
   tagline, give the text more room as the window narrows — at 58% the column
   fell to about 34 characters a line by 1000px. */
@media (max-width: 1200px){
  .key-skills-list .skill-media{ flex-basis: 50%; }
}
/* This section runs wider than the rest of the site so the videos have more
   room, with the video taking a larger share of that extra width. Capped
   rather than full-bleed, so the rows don't touch the screen edges. */
@media (min-width: 1321px){
  .container.key-skills-wide{ max-width: 1720px; }
  .key-skills-list .skill-media{ flex-basis: 62%; }
}
.intro-portrait{ position: relative; }
.intro-portrait img{
  /* display:block matters: as an inline image it sat on a text baseline,
     leaving a few px of gap below it inside the container, so the bottom
     bracket hung off the edge of the photo rather than hugging its corner.
     No fixed aspect ratio, so the frame takes the shape of whatever you
     supply — square, portrait or landscape — and the brackets always land
     on the actual corners. */
  display: block;
  width: 100%;
  height: auto;
}
@media (max-width: 810px){
  .key-skills-list .skill-media::before,
  .key-skills-list .skill-media::after,
  .work-thumb::before,
  .work-thumb::after,
  .intro-portrait::before,
  .intro-portrait::after{ width: 20px; height: 20px; }
}
.skill-media video, .skill-media img{ width: 100%; height: 100%; object-fit: cover; }
/* Stream-hosted loop: poster as the background until the iframe is inserted. */
.skill-loop{
  width: 100%; height: 100%;
  background-size: cover;
  background-position: center;
}
.skill-loop iframe{ width: 100%; height: 100%; border: 0; display: block; }
.skill-info{ flex: 1; display: flex; align-items: center; }
.skill-text{ min-width: 0; }
.skill-title{ font-size: var(--size-item-title); }
.key-skills-list .skill-title{ font-size: 40px; }
.skill-tagline{
  margin-top: 8px;
  font-size: 16px;
  color: var(--text-muted);
  max-width: 60ch;
}
/* Body copy now sits on the page rather than behind a click. */
.skill-body{ margin-top: 16px; }
@media (max-width: 810px){
  .key-skills-list .skill-title{ font-size: 26px; }
}
.skill-marker{
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  opacity: 0;
  transition: opacity var(--dur-hover) var(--ease-standard);
  white-space: nowrap;
}
.skill-row:hover .skill-marker{ opacity: 1; }

.expand-scrim{
  position: fixed; inset: 0;
  background: var(--scrim);
  z-index: 300;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-scrim) var(--ease-standard);
}
.expand-scrim.is-active{ opacity: 1; pointer-events: auto; }

.expand-panel{
  position: fixed;
  top: 50%; left: 50%;
  transform: translate(-50%,-50%) scale(0.94);
  z-index: 301;
  width: 90vw;
  max-width: 1100px;
  max-height: 86vh;
  overflow-y: auto;
  display: flex;
  gap: 40px;
  background: var(--bg);
  padding: 40px;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-expand) var(--ease-standard), transform var(--dur-expand) var(--ease-standard);
}
.expand-panel.is-active{ opacity: 1; transform: translate(-50%,-50%) scale(1); pointer-events: auto; }
.expand-panel .expand-media{ flex: 0 0 60%; aspect-ratio: 16/9; background: #000; }
.expand-panel .expand-media video, .expand-panel .expand-media img{ width: 100%; height: 100%; object-fit: cover; }
.expand-panel .expand-body{
  flex: 1;
  opacity: 0;
  transition: opacity var(--dur-hover) var(--ease-standard) 150ms;
}
.expand-panel.is-active .expand-body{ opacity: 1; }
.expand-close{
  position: absolute; top: 20px; right: 20px;
  font-size: 24px;
  width: 40px; height: 40px;
  display: flex; align-items: center; justify-content: center;
}
.expand-panel .view-full-link{
  display: inline-block;
  margin-top: 20px;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border-bottom: 1px solid var(--line);
  padding-bottom: 2px;
}
@media (max-width: 810px){
  .skill-row, .skill-row.align-right{ flex-direction: column; align-items: flex-start; gap: 16px; }
  .skill-media{ flex-basis: 100%; width: 100%; }
  .expand-panel{ flex-direction: column; width: 92vw; padding: 24px; }
  .expand-panel .expand-media{ flex-basis: auto; }
}

/* ==========================================================================
   Selected Work — two-column cards, caption box attached under each thumb
   ========================================================================== */
.work-grid{
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 40px 32px;
}
.work-card{
  cursor: pointer;
  display: flex;
  flex-direction: column;
}
.work-thumb{
  position: relative;
  aspect-ratio: 16/9;
  overflow: hidden;
  background: var(--surface);
}
.work-thumb img, .work-thumb video{
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--dur-hover) var(--ease-standard);
}
.work-card:hover .work-thumb img,
.work-card:hover .work-thumb video{ transform: scale(1.04); }
/* No gap and no top border — the caption reads as one piece with the image. */
.work-caption{
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 24px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-top: none;
  transition: background var(--dur-hover) var(--ease-standard);
}
.work-card:hover .work-caption{ background: var(--surface-hover); }
.work-caption-text{ min-width: 0; }
.work-title{
  font-size: var(--size-item-title);
  line-height: 1.2;
  margin-bottom: 6px;
}
.work-caption .skill-marker{ margin-top: 4px; }
.work-card:hover .skill-marker{ opacity: 1; }
@media (max-width: 810px){
  .work-grid{ grid-template-columns: 1fr; gap: 32px; }
  .work-caption{ padding: 16px 18px; }
}

/* This section runs wider than the rest of the site so the video has more
   room. Capped rather than full-bleed, so the tab strip still lines up with
   something and the panel doesn't touch the screen edges. */
@media (min-width: 1321px){
  .skill-tabs-section .container{ max-width: 1720px; }
}
/* ---------- Scroll-driven tab cycling ----------
   The scroller is given an explicit height by skill-tabs.js — tall enough
   for one step of scrolling per tab — and the section sticks inside it, so
   scrolling advances the tabs instead of moving the page. Everything here
   is inert until that height is set, so with JS off (or on a phone, or a
   short screen) the section is simply a normal block of clickable tabs. */
.skill-tabs-sticky{
  position: sticky;
  /* Clears the fixed 84px header. Kept tight so the section still fits —
     and so still pins — on shorter laptop screens. */
  top: 96px;
}
.skill-tabs-scroller.is-pinned{ position: relative; }

/* ==========================================================================
   Key Skills — tabbed variant
   The browser feel comes from two quiet cues: a favicon-style dot on each
   tab, and the active tab losing its bottom border so it joins the panel
   below into one continuous surface.
   ========================================================================== */
.skill-tabs-bar{
  display: flex;
  align-items: flex-end;
  gap: 16px;
  border-bottom: 1px solid var(--line);
  /* Tabs overlap this border by 1px so the active one can break through it. */
  padding-bottom: 0;
}
.skill-tabs-strip{
  display: flex;
  align-items: flex-end;
  gap: 2px;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  margin-bottom: -1px;
}
.skill-tabs-strip::-webkit-scrollbar{ display: none; }

.skill-tab{
  display: flex;
  align-items: center;
  gap: 9px;
  flex-shrink: 0;
  padding: 11px 18px;
  font-size: 14px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 9px 9px 0 0;
  transition: background var(--dur-hover) var(--ease-standard),
              color var(--dur-hover) var(--ease-standard);
}
.skill-tab:hover{ color: var(--text); background: rgba(255,255,255,0.04); }
.skill-tab .tab-favicon{
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--text-muted);
  opacity: 0.5;
  flex-shrink: 0;
  transition: background var(--dur-hover) var(--ease-standard),
              opacity var(--dur-hover) var(--ease-standard);
}
.skill-tab.is-active{
  color: var(--text);
  background: var(--surface);
  border-color: var(--line);
  /* Break the strip's bottom line so tab and panel read as one surface. */
  border-bottom-color: var(--surface);
}
.skill-tab.is-active .tab-favicon{ background: var(--accent); opacity: 1; }

.skill-tabpanels{
  background: var(--surface);
  border: 1px solid var(--line);
  border-top: none;
  border-radius: 0 0 9px 9px;
  padding: 40px;
}
.skill-tabpanel{
  display: none;
  gap: 48px;
  align-items: center;
    /* Lets the text drop below the video when there isn't room beside it —
     see the min-width on .tab-text. */
  flex-wrap: wrap;
}
.skill-tabpanel.is-active{ display: flex; }
.skill-tabpanel.align-right{ flex-direction: row-reverse; }
.skill-tabpanel .tab-media{
  position: relative;
  flex: 0 0 55%;
  aspect-ratio: 16/9;
  overflow: hidden;
  background: var(--bg);
}
/* Vertical work is shown portrait (562x1000) rather than letterboxed into a
   16:9 frame. Height-led so the panel keeps roughly the same overall depth
   as the horizontal skills and the tabs don't jump as you switch. */
.skill-tabpanel .tab-media.is-vertical{
  flex: 0 0 auto;
  width: auto;
  height: 460px;
  aspect-ratio: 562/1000;
}
.skill-media.is-vertical,
.expand-media.is-vertical{ aspect-ratio: 562/1000; }
/* A portrait frame is much narrower than a 16:9 one, so let the text and
   media sit together in the middle instead of being flung to opposite
   edges with a void between them. */
.skill-tabpanel.has-vertical-media{
  justify-content: center;
  gap: 64px;
}
.skill-tabpanel.has-vertical-media .tab-text{ flex: 0 1 520px; }

@media (min-width: 811px){
  /* Media is height-led rather than width-led here, and every panel uses the
     same height, so the panel area never changes size as you move between
     tabs — portrait and 16:9 alike. Tying it to viewport height also keeps
     the whole section short enough to pin on a laptop screen, not just a
     big monitor. */
  .skill-tabpanel .tab-media{
    flex: 0 0 auto;
    /* Width derived from the target height, but never wider than the panel.
       Set as a height it could overflow the box on a short, narrow window;
       capping the width and letting aspect-ratio supply the height keeps it
       inside on every size. */
    height: auto;
    width: min(66%, calc(clamp(240px, 50vh, 520px) * 16 / 9));
  }
  .skill-tabpanel .tab-media.is-vertical{
    height: auto;
    width: min(100%, calc(clamp(240px, 50vh, 520px) * 562 / 1000));
  }
  .skill-tabpanels{ min-height: calc(clamp(240px, 50vh, 520px) + 80px); }
  /* The heading sits inside the pinned area, so it's kept tight. */
  .skill-tabs-section .section-heading{ margin-bottom: 32px; }
}
/* Between the phone breakpoint and 1280px there isn't room for the video and
   a readable column of text side by side, so the panel becomes a stack: video
   across the full width of the panel, text below it at full width, left
   aligned. Laid out explicitly rather than left to flex-wrap, because a
   wrapped item keeps its own width and would sit narrow against the left
   edge instead of filling the panel. */
@media (min-width: 811px) and (max-width: 1320px){
  .skill-tabpanel,
  .skill-tabpanel.align-right,
  .skill-tabpanel.has-vertical-media{
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    gap: 32px;
  }
  .skill-tabpanel .tab-media{
    width: 100%;
    align-self: center;
  }
  /* A portrait video stretched to the panel width would be absurdly tall, so
     it stays its own size and centres instead. */
  .skill-tabpanel .tab-media.is-vertical{
    width: min(100%, 300px);
    align-self: center;
  }
  .skill-tabpanel .tab-text,
  .skill-tabpanel.has-vertical-media .tab-text{
    flex: 0 0 auto;
    width: 100%;
    min-width: 0;
    text-align: left;
  }
    /* The paragraph itself carries .measure (65ch), so the column filled the
     panel while the text inside it still stopped short. Lift the cap when
     stacked. */
  .skill-tabpanel .tab-text .measure{ max-width: none; }
}
.skill-tabpanel .tab-media video,
.skill-tabpanel .tab-media img{ width: 100%; height: 100%; object-fit: cover; }
/* Same viewfinder brackets used elsewhere on the page. */
.skill-tabpanel .tab-media::before,
.skill-tabpanel .tab-media::after{
  content: "";
  position: absolute;
  width: 28px; height: 28px;
  z-index: 2;
  pointer-events: none;
}
.skill-tabpanel .tab-media::before{
  top: 14px; left: 14px;
  border-top: 3px solid var(--accent);
  border-left: 3px solid var(--accent);
}
.skill-tabpanel .tab-media::after{
  bottom: 14px; right: 14px;
  border-bottom: 3px solid var(--accent);
  border-right: 3px solid var(--accent);
}
/* The video keeps its size, so as the window narrows the text column is what
   gets squeezed. Below about 27 characters a line it turns to soup, so give
   it a floor: once it can't fit, flex-wrap drops it onto its own row under
   the video instead of crushing it. */
.skill-tabpanel .tab-text{ flex: 1 1 27ch; min-width: 27ch; }
.skill-tabpanel .tab-text .skill-title{ font-size: 34px; }
.skill-tabpanel .tab-text .skill-tagline{ margin-bottom: 16px; max-width: none; }
/* Panels fade in rather than snapping, so switching tabs feels deliberate. */
@media (prefers-reduced-motion: no-preference){
  .skill-tabpanel.is-active{ animation: tab-fade 320ms var(--ease-standard); }
  @keyframes tab-fade{
    from{ opacity: 0; transform: translateY(8px); }
    to{ opacity: 1; transform: translateY(0); }
  }
}
@media (max-width: 810px){
  .skill-tabs-bar{ gap: 8px; }
  .skill-tab{ padding: 10px 14px; font-size: 13px; }
  /* Same fade the portfolio filter row uses, to hint there's more to scroll. */
  .skill-tabs-strip{ mask-image: linear-gradient(to right, black 92%, transparent 100%); }
  .skill-tabpanels{ padding: 20px; }
  .skill-tabpanel, .skill-tabpanel.align-right{
    flex-direction: column;
    align-items: stretch;
    gap: 20px;
  }
  .skill-tabpanel .tab-media{ flex-basis: auto; }
  .skill-tabpanel .tab-media.is-vertical{
    height: auto;
    width: min(260px, 70%);
    align-self: center;
  }
    /* Same as the stacked desktop case: don't let .measure hold the paragraph
     short of the panel edge. */
  .skill-tabpanel .tab-text .measure{ max-width: none; }
    /* In a column, flex-basis sets HEIGHT. The side-by-side rules give the text
     a basis of 27ch (and 520px on the portrait panel), which here became a
     fixed tall box padded out with empty space below the copy. Let the
     content decide instead. */
  .skill-tabpanel .tab-text,
  .skill-tabpanel.has-vertical-media .tab-text{
    flex: 0 0 auto;
    width: 100%;
    min-width: 0;
  }
  /* This panel's desktop centring and 64px gap aren't scoped, so undo them. */
  .skill-tabpanel.has-vertical-media{
    justify-content: flex-start;
    gap: 20px;
  }
  .skill-tabpanel .tab-text .skill-title{ font-size: 24px; }
  .intro-heading{ font-size: 24px; }
  .skill-tabpanel .tab-media::before,
  .skill-tabpanel .tab-media::after{ width: 20px; height: 20px; }
}

/* ---------- Variant preview switcher (remove with BUILD_BOTH_HOME_VARIANTS) ---------- */
.variant-switcher{
  position: fixed;
  left: 20px; bottom: 20px;
  z-index: 150;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 16px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(10,10,10,0.82);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  font-size: 12px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  transition: border-color var(--dur-hover) var(--ease-standard);
}
.variant-switcher:hover{ border-color: var(--text); }
.variant-switcher-eyebrow{ color: var(--accent); font-size: 10px; letter-spacing: 0.12em; }
@media (max-width: 810px){
  .variant-switcher{ left: 12px; bottom: 12px; padding: 8px 12px; font-size: 11px; }
}

/* ==========================================================================
   What I deliver
   ========================================================================== */
.deliver-grid{
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 64px;
}
.deliver-col h3{
  font-size: 16px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: 20px;
}
.deliver-col li,
.intro-bullets li{
  position: relative;
  padding-left: 28px;
  margin-bottom: 16px;
  font-size: var(--size-body);
}
.deliver-col li::before,
.intro-bullets li::before{
  content: "\2713";
  position: absolute;
  left: 0; top: 0;
  color: var(--accent);
  font-weight: 700;
  font-size: 16px;
}
@media (max-width: 810px){
  .deliver-grid{ grid-template-columns: 1fr; gap: 40px; }
}

/* ==========================================================================
   Trusted by
   ========================================================================== */
.trusted-marquee{
  overflow: hidden;
  position: relative;
  width: 100%;
  /* Full bleed, so the logos fade out at the screen edges rather than being
     sliced off mid-logo. Delete this line for a hard cut. */
  mask-image: linear-gradient(to right, transparent 0, black 6%, black 94%, transparent 100%);
}
.trusted-marquee .track{
  display: flex;
  gap: 64px;
  align-items: center;
  width: max-content;
  animation: marquee 42s linear infinite;
}
.trusted-marquee:hover img{ opacity: 0.85; }
.trusted-marquee img{ height: 40px; width: auto; opacity: 0.62; transition: opacity var(--dur-hover) var(--ease-standard); }
@keyframes marquee{
  from{ transform: translateX(0); }
  to{ transform: translateX(-50%); }
}
@media (max-width: 810px){
  .trusted-marquee img{ height: 32px; }
}

/* ==========================================================================
   Portfolio filters + grid
   ========================================================================== */
.filter-row{
  display: flex;
  align-items: center;
  margin-bottom: 56px;
}
.filter-all-col{
  flex-shrink: 0;
  margin-right: 20px;
  padding-right: 20px;
  border-right: 1px solid var(--line);
}
.filter-tags{
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
@media (max-width: 810px){
  .filter-row{
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 8px;
    mask-image: linear-gradient(to right, black 90%, transparent 100%);
  }
  .filter-all-col{
    margin-right: 10px;
    padding-right: 10px;
  }
  .filter-tags{
    flex-wrap: nowrap;
  }
}
/* Filter pills, matching the hero's unmute control. */
.filter-btn{
  flex-shrink: 0;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 10px 18px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  background: rgba(10,10,10,0.4);
  transition: border-color var(--dur-hover) var(--ease-standard),
              color var(--dur-hover) var(--ease-standard);
  white-space: nowrap;
}
.filter-btn:hover{ border-color: var(--text); color: var(--text); }
.filter-btn.is-active{ border-color: var(--accent); color: var(--text); }
.filter-btn.filter-all.is-active{ border-color: var(--accent); }

.portfolio-grid{
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}
@media (max-width: 1100px){ .portfolio-grid{ grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px){ .portfolio-grid{ grid-template-columns: 1fr; } }

.grid-item{
  transition: opacity 300ms var(--ease-standard), transform 300ms var(--ease-standard);
}
.grid-item.is-hidden{
  opacity: 0;
  transform: scale(0.92);
  pointer-events: none;
  position: absolute;
  visibility: hidden;
}
.grid-item a{ display: block; }
.grid-item .thumb{
  position: relative;
  aspect-ratio: 16/9;
  overflow: hidden;
  background: var(--surface);
  margin-bottom: 14px;
}
.grid-item .thumb img, .grid-item .thumb video{
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--dur-hover) var(--ease-standard);
}
.grid-item a:hover .thumb img,
.grid-item a:hover .thumb video{ transform: scale(1.04); }
.grid-item .item-title{ font-size: var(--size-item-title); margin-bottom: 4px; }
.grid-item .item-meta{ font-size: var(--size-caption); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em; }

.empty-state{
  padding: 80px 0;
  text-align: center;
  color: var(--text-muted);
  font-size: 15px;
}

/* ==========================================================================
   Portfolio detail
   ========================================================================== */
.detail-header{
  position: relative;
  height: 70vh;
  min-height: 420px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.detail-header .header-image{
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  will-change: transform;
}
.detail-header .header-scrim{ position: absolute; inset: 0; background: rgba(0,0,0,0.45); }
.detail-header .header-content{ position: relative; z-index: 2; }
.detail-header h1{ font-size: var(--size-section-heading); text-transform: uppercase; letter-spacing: -0.01em; }
.detail-header .meta{ margin-top: 12px; }

.detail-main{
  display: grid;
  grid-template-columns: 66% 1fr;
  gap: 56px;
  align-items: start;
}
.detail-copy .credits-block{ margin-top: 28px; }
.detail-main .media-col{ position: relative; aspect-ratio: 16/9; background: #000; }
.detail-main .media-col iframe, .detail-main .media-col video, .detail-main .media-col img{
  width: 100%; height: 100%; object-fit: cover; border: none;
  /* Portrait players (TikTok, Reels) — a 16:9 frame would letterbox them. */
.detail-main .media-col.is-vertical{ aspect-ratio: 9/16; width: 100%; max-width: 420px; justify-self: center; }
}
/* No video on this project: single column, description centred, and the
   contact sheet below carries the images on its own. */
.detail-main.no-media{
  grid-template-columns: 1fr;
  justify-items: left;
  text-align: left;
}
.detail-main.no-media .measure{ margin-inline: auto; }
.detail-gallery-grid{
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
@media (max-width: 900px){
  .detail-main{ grid-template-columns: 1fr; }
  .detail-gallery-grid{ grid-template-columns: repeat(2, 1fr); }
}

.play-facade{
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  background-size: cover; background-position: center;
}
.play-facade::before{ content: ""; position: absolute; inset: 0; background: rgba(0,0,0,0.35); }
.play-facade .play-icon{
  position: relative; z-index: 2;
  width: 64px; height: 64px;
  border: 1px solid rgba(255,255,255,0.6);
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
}
.play-facade .play-icon::after{
  content: "";
  border-style: solid;
  border-width: 8px 0 8px 14px;
  border-color: transparent transparent transparent #fff;
  margin-left: 4px;
}

.contact-sheet{
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
.contact-sheet button{ display: block; padding: 0; aspect-ratio: 16/9; overflow: hidden; background: var(--surface); }
.contact-sheet img{ width: 100%; height: 100%; object-fit: cover; transition: transform var(--dur-hover) var(--ease-standard); }
.contact-sheet button:hover img{ transform: scale(1.05); }
@media (max-width: 640px){ .contact-sheet{ grid-template-columns: repeat(2, 1fr); } }

.lightbox{
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.92);
  z-index: 400;
  display: none;
  align-items: center;
  justify-content: center;
}
.lightbox.is-open{ display: flex; }
.lightbox img{ max-width: 88vw; max-height: 84vh; object-fit: contain; }
.lightbox .lightbox-close{ position: absolute; top: 24px; right: 32px; font-size: 28px; }
.lightbox .lightbox-arrow{ position: absolute; top: 50%; transform: translateY(-50%); font-size: 32px; padding: 20px; }
.lightbox .lightbox-prev{ left: 8px; }
.lightbox .lightbox-next{ right: 8px; }

.credits-block{
  color: var(--text-muted);
  font-size: 14px;
  text-transform: uppercase;
  line-height: 1.8;
  /* Mono at this size reads better with a little extra tracking. */
  letter-spacing: 0.05em;
}

.prev-next{
  display: flex;
  justify-content: space-between;
  border-top: 1px solid var(--line);
  padding-top: 32px;
  font-size: 14px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* ==========================================================================
   About — alternating blocks + scrapbook
   ========================================================================== */
.about-block{
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 64px;
  align-items: stretch;
}
.about-block.align-right{ direction: rtl; }
.about-block.align-right > *{ direction: ltr; }
.about-block .about-text{ display: flex; flex-direction: column; justify-content: center; }
.about-block .about-text h3{ font-size: var(--size-item-title); margin-bottom: 16px; }
.about-gallery{
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  height: 100%;
  min-height: 320px;
}
.about-gallery img{ width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 810px){
  .about-block, .about-block.align-right{ grid-template-columns: 1fr; direction: ltr; }
  .about-gallery{ min-height: 220px; }
}

.scrapbook-strip{
  overflow: hidden;
  position: relative;
  padding: 40px 0;
  width: 100%;
}
.scrapbook-track{
  display: flex;
  gap: 20px;
  width: max-content;
  animation: scrapbook-scroll 34s linear infinite;
}

.scrapbook-track img{
  height: 180px; width: auto;
  box-shadow: 0 12px 30px rgba(0,0,0,0.5);
  flex-shrink: 0;
}

@keyframes scrapbook-scroll{
  from{ transform: translateX(0); }
  to{ transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce){
  .scrapbook-track{ animation: none; }
}

.cv-button{
  display: inline-block;
  margin-top: 24px;
  padding: 14px 28px;
  border: 1px solid var(--line);
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  transition: background var(--dur-hover) var(--ease-standard), color var(--dur-hover) var(--ease-standard);
}
.cv-button:hover{ background: #fff; color: #000; }

/* ---------- Map ---------- */
.map-count-line{ margin-bottom: 20px; color: var(--text-muted); font-size: 14px; }
.map-wrap{
  position: relative;
  height: 520px;
  border: 1px solid var(--line);
}
#about-map{ width: 100%; height: 100%; }
.map-zoom-controls{
  position: absolute;
  bottom: 16px; right: 16px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  z-index: 5;
}
.map-zoom-controls button{
  width: 36px; height: 36px;
  background: var(--surface);
  border: 1px solid var(--line);
  font-size: 16px;
}
.map-zoom-controls button:hover{ background: var(--surface-hover); }
.mapboxgl-popup-content{
  background: var(--surface) !important;
  color: var(--text) !important;
  font-family: var(--font-sans) !important;
  border: 1px solid var(--line);
  box-shadow: none !important;
}
.mapboxgl-popup-tip{ border-top-color: var(--surface) !important; border-bottom-color: var(--surface) !important; }
.map-marker{
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--bg);
  transition: transform var(--dur-hover) var(--ease-standard);
  cursor: pointer;
}
.map-marker:hover{ transform: scale(1.3); }
.map-not-configured{
  display: flex; align-items: center; justify-content: center;
  height: 100%; color: var(--text-muted); font-size: 14px; text-align: center; padding: 40px;
}

/* ==========================================================================
   Kit accordion
   ========================================================================== */
.kit-accordion .accordion-bar{
  width: 100%;
  height: 72px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 24px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  font-size: 16px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  transition: background var(--dur-hover) var(--ease-standard);
}
.kit-accordion .accordion-bar:hover{ background: var(--surface-hover); }
.kit-accordion .accordion-plus{
  font-size: 22px;
  font-weight: 300;
  transition: transform var(--dur-expand) var(--ease-standard);
  display: inline-block;
}
.kit-accordion .accordion-item.is-open .accordion-plus{ transform: rotate(45deg); }
.kit-accordion .accordion-panel{
  max-height: 0;
  overflow: hidden;
  background: var(--surface);
  transition: max-height var(--dur-expand) var(--ease-standard);
}
.kit-accordion .accordion-panel-inner{ padding: 24px; }
.kit-accordion .accordion-panel-inner li{
  padding: 6px 0;
  font-size: 15px;
  color: var(--text-muted);
}
.kit-intro{ margin-bottom: 24px; }
/* Line of copy under the kit heading. */
.kit-intro-body{
  font-size: var(--size-body);
  margin: 0 0 56px;
  max-width: none;
}
.kit-intro-body + .kit-intro-body{ margin-top: -40px; }
.kit-terms{
  margin-top: 64px;
  font-style: italic;
  color: var(--text-muted);
  font-size: 14px;
  max-width: none;
  /* Centred on the page: auto side margins centre the block itself, and
     text-align centres the lines within it. */
  margin-inline: auto;
  text-align: center;
}
/* Only the first terms paragraph needs the big gap above it. */
.kit-terms + .kit-terms{ margin-top: 1.2em; }

/* ==========================================================================
   Misc
   ========================================================================== */
.back-to-top{ cursor: pointer; }
.no-scroll{ overflow: hidden; }

/* Homepage intro portrait — square frame, two corners */
/* No forced square and no cropping: the frame takes the shape of whatever
   image you supply, so nothing is clipped and the red corners sit the same
   distance from the artwork on every side. A fixed 1/1 frame with
   object-fit: cover trimmed the top and bottom off anything not square. */
.intro-portrait{ aspect-ratio: auto; }
.intro-portrait img{ display: block; width: 100%; height: auto; }
.intro-portrait::before, .intro-portrait::after{ width: 40px; height: 40px; }
.intro-portrait::before{ top: 10px; left: 10px; }
.intro-portrait::after{ bottom: 10px; right: 10px; }

.accordion-panel-inner li.is-sub{ padding-left: 20px; opacity: 0.62; }
.accordion-panel-inner li.is-sub::before{ content: "└ "; opacity: 0.5; }