/* =============================================================================
   LIGHT ALASADY — PORTFOLIO
   base.css · the shared design system (loaded on every page)
   -----------------------------------------------------------------------------
   Built from the LIGHT brand kit:
     • Palette   Void · Sumi · Ink-wash · Haze · Bone · Light, and gold for the
                 spark alone (Filament · Filament Lit · Hot Core).
     • Type      Cormorant Garamond. SemiBold caps for the wordmark and labels,
                 Medium for reading, Medium Italic for descriptors and the
                 lowercase roman numerals that mark every sequence (i. ii. iii.).
     • The Flip  "Ink on paper becomes light in the void." Sections alternate
                 between the void (dark, light text) and paper (bone, ink text).
     • Never     gold anywhere but the spark · a stretched or rotated mark ·
                 a flat void (it always holds a little light).

   TABLE OF CONTENTS
     00. Fonts
     01. Design tokens
     02. Reset & base elements
     03. Typography
     04. Layout primitives
     05. Surfaces — void & paper (the flip) and brushed edges
     06. Atmosphere — the painted field, glints, near stars, shooting stars, the comet
     07. The living bulb
     08. Header & navigation
     09. Components
           09a kicker & numerals   09b buttons & links   09c tags
           09d proof (statistics)   09e attribution legend
           09f media frames         09g video facade      09h audio player
           09i organisation plate   09j fact list         09k next project
     10. Footer
     11. Reveal (painted-in entrances)
     12. Utilities
     13. Calm motion (reduced-motion + the on-site toggle)
     14. Page-to-page navigation
   ============================================================================= */


/* =============================================================================
   00. FONTS
   Self-hosted (no third-party request, no render-blocking stylesheet).
   font-display: swap shows text immediately in the metric-matched fallback.
   ============================================================================= */
@font-face {
  font-family: "Cormorant Garamond";
  src: url("/assets/fonts/cormorant-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("/assets/fonts/cormorant-500-italic.woff2") format("woff2");
  font-weight: 500; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("/assets/fonts/cormorant-600.woff2") format("woff2");
  font-weight: 600 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("/assets/fonts/cormorant-600-italic.woff2") format("woff2");
  font-weight: 600 700; font-style: italic; font-display: swap;
}
/* Fallback tuned to Cormorant's proportions so the swap barely moves the layout */
@font-face {
  font-family: "Cormorant Fallback";
  src: local("Times New Roman"), local("Times"), local("Liberation Serif"), local("Tinos");
  size-adjust: 94%;
  ascent-override: 98%;
  descent-override: 31%;
  line-gap-override: 0%;
}


/* =============================================================================
   01. DESIGN TOKENS
   ============================================================================= */
:root {
  /* — Brand palette (from the LIGHT brand kit, page iv) — */
  --void: #07060D;        /* the dark the void is made of */
  --sumi: #121015;        /* ink on paper */
  --ink-wash: #1B1636;    /* tone inside the void */
  --haze: #3A2A8C;        /* a glow at 10–25%, never solid */
  --bone: #EFE8DC;        /* paper */
  --light: #F3EDE2;       /* what ink becomes in the void */
  --filament: #EDA63A;    /* the spark, on paper */
  --filament-lit: #FFC970;/* the spark's glow */
  --hot-core: #FFF2D6;    /* the spark, lit */

  /* — Contextual colours. Surfaces (section 05) swap these. — */
  --surface: var(--void);
  --text: var(--light);
  /* Paper grain: a tiny SVG noise tile — "Add Noise 2%" from the brand kit */
  --paper-grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .07 0 0 0 0 .06 0 0 0 0 .08 0 0 0 .07 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  --text-soft: rgba(243, 237, 226, .76);
  --text-faint: rgba(243, 237, 226, .58);
  --rule: rgba(243, 237, 226, .16);
  --rule-strong: rgba(243, 237, 226, .32);
  --paint: var(--light);          /* brush fill for buttons/marks on this surface */
  --paint-ink: var(--sumi);       /* text that sits on top of that paint */

  /* — Type — */
  --serif: "Cormorant Garamond", "Cormorant Fallback", "Times New Roman", Times, serif;
  --fs-body: clamp(1.18rem, 1.06rem + .38vw, 1.34rem);    /* ~19–21.5px: Cormorant reads small */
  --fs-small: clamp(1.02rem, .98rem + .18vw, 1.1rem);
  --fs-lead: clamp(1.42rem, 1.2rem + .8vw, 1.86rem);
  --fs-h3: clamp(1.6rem, 1.3rem + 1.1vw, 2.3rem);
  --fs-h2: clamp(2.3rem, 1.6rem + 2.8vw, 4.4rem);
  --fs-h1: clamp(3rem, 1.8rem + 5vw, 7.2rem);
  --fs-label: .74rem;                                      /* caps labels & kickers */
  --track-label: .22em;                                    /* brand: +200 to +220 */
  --track-wordmark: .32em;                                 /* brand: +320 */

  /* — Space & layout — */
  --page-max: 1320px;
  --gutter: clamp(18px, 4.2vw, 64px);
  --measure: 62ch;
  --section-pad: clamp(84px, 11vw, 176px);
  --header-h: 72px;

  /* — Motion — */
  --ease-brush: cubic-bezier(.17, .82, .18, 1);   /* a stroke that lands and drags */
  --ease-soft: cubic-bezier(.4, 0, .2, 1);
  --dur-reveal: .6s;

  /* — Live values written by JavaScript (intro flicker) — */
  --light-level: 1;          /* 0 = lights off, 1 = lights on */
}
@media (max-width: 760px) {
  :root { --header-h: 62px; }
}


/* =============================================================================
   02. RESET & BASE ELEMENTS
   ============================================================================= */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-padding-top: calc(var(--header-h) + 24px);
  background: var(--void);
}
@media (prefers-reduced-motion: no-preference) {
  html:not(.calm) { scroll-behavior: smooth; }
}
body {
  margin: 0;
  min-height: 100vh;
  min-height: 100svh;
  /* Transparent on purpose: the void colour lives on <html>, so the fixed
     atmosphere layer (§06, z-index -1) can show between the page and the void.
     A painted body would sit on top of it and hide the whole sky. */
  background: transparent;
  color: var(--text);
  font-family: var(--serif);
  font-size: var(--fs-body);
  font-weight: 500;
  line-height: 1.55;
  font-kerning: normal;
  font-variant-numeric: lining-nums proportional-nums; /* Cormorant defaults to old-style figures */
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}
img, picture, video, svg, canvas, iframe { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; }
p, h1, h2, h3, h4, figure, blockquote, dl, dd, ol, ul { margin: 0; }
ol[role="list"], ul[role="list"] { list-style: none; padding: 0; }
::selection { background: var(--haze); color: var(--light); }

/* A single, clearly visible focus style everywhere (keyboard users) */
:focus-visible {
  outline: 2px solid var(--light);     /* Light on the void; Sumi on paper (section 05) */
  outline-offset: 4px;
  border-radius: 2px;
}


/* =============================================================================
   03. TYPOGRAPHY
   ============================================================================= */
h1, h2, h3, h4 {
  font-weight: 500;
  line-height: .98;
  letter-spacing: -.012em;
  text-wrap: balance;
  font-feature-settings: "liga" 1, "lnum" 1;
}
.t-display { font-size: var(--fs-h1); line-height: .9; letter-spacing: -.02em; }
.t-h2 { font-size: var(--fs-h2); }
.t-h3 { font-size: var(--fs-h3); line-height: 1.05; }
.t-lead { font-size: var(--fs-lead); line-height: 1.38; color: var(--text); }
.t-small { font-size: var(--fs-small); }
.t-soft { color: var(--text-soft); }
.t-italic, em, .descriptor { font-style: italic; }

/* Brand voice: italic lowercase descriptors under names and titles */
.descriptor {
  font-weight: 500;
  letter-spacing: .02em;
  color: var(--text-soft);
}

/* Caps label — SemiBold, tracked +220 (kickers, metadata, small headings) */
.label {
  font-size: var(--fs-label);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  line-height: 1.35;
  color: var(--text-soft);
}

/* Wordmark — SemiBold caps, tracked +320 */
.wordmark {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--track-wordmark);
  line-height: 1;
}

/* Running text */
.prose { max-width: var(--measure); }
.prose > * + * { margin-top: 1em; }
.prose p { color: var(--text-soft); }
.prose strong { color: var(--text); font-weight: 600; }
.prose a { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .22em; }
.prose a:hover { text-decoration-thickness: 2px; }


/* =============================================================================
   04. LAYOUT PRIMITIVES
   ============================================================================= */
.wrap {
  width: min(100% - (var(--gutter) * 2), var(--page-max));
  margin-inline: auto;
}
.wrap--narrow { width: min(100% - (var(--gutter) * 2), 920px); }
.section { position: relative; padding-block: var(--section-pad); }
.section--tight { padding-block: calc(var(--section-pad) * .55); }

/* A 12-column grid used by case-study layouts */
.grid-12 {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: clamp(16px, 2.4vw, 40px);
  row-gap: clamp(28px, 4vw, 56px);
}
.stack > * + * { margin-top: var(--stack-gap, 1.2rem); }

/* Section heading block: numeral + label + heading */
.section-head { display: grid; gap: .9rem; margin-bottom: clamp(36px, 5vw, 72px); max-width: 980px; }
.section-head .t-h2 { max-width: 18ch; }
.section-head p { max-width: var(--measure); color: var(--text-soft); }


/* =============================================================================
   05. SURFACES — the void and the paper (the flip)
   -----------------------------------------------------------------------------
   .surface-void  : transparent, so the sky canvas (section 06) shows through.
   .surface-paper : bone paper with a faint grain; ink-coloured text.
   .edge-top / .edge-bottom : brushed "flying white" edges where paper meets void
                              (the edge shape is traced from the brand kit).
   ============================================================================= */
.surface-void {
  --surface: var(--void);
  color: var(--text);
}
.surface-paper {
  --surface: var(--bone);
  --text: var(--sumi);
  --text-soft: rgba(18, 16, 21, .78);
  --text-faint: rgba(18, 16, 21, .62);
  --rule: rgba(18, 16, 21, .16);
  --rule-strong: rgba(18, 16, 21, .34);
  --paint: var(--sumi);
  --paint-ink: var(--bone);
  position: relative;
  color: var(--text);
  background-color: var(--bone);
  /* Paper grain: a tiny SVG noise tile, multiplied — "Add Noise 2%" from the kit */
  background-image: var(--paper-grain);
  background-size: 220px 220px;
}
.surface-paper ::selection { background: var(--sumi); color: var(--bone); }
.surface-paper :focus-visible { outline-color: var(--sumi); }

/* Brushed edges. The pseudo-element is painted in the paper colour and masked
   by the traced flying-white stroke, so the paper appears to be dragged on. */
.edge-top::before,
.edge-bottom::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  height: clamp(48px, 7vw, 120px);
  background: var(--bone) var(--paper-grain) 0 0 / 220px 220px;   /* same grain as the sheet, so the seam disappears */
  -webkit-mask: url("/assets/brand/edge-flyingwhite.svg") 0 0 / 100% 100% no-repeat;
          mask: url("/assets/brand/edge-flyingwhite.svg") 0 0 / 100% 100% no-repeat;
  pointer-events: none;
}
.edge-top::before {
  bottom: 100%;
  transform: scaleY(-1) translateY(-1px);   /* paper rises up into the void */
}
.edge-bottom::after {
  top: 100%;
  transform: scaleX(-1) translateY(-1px);   /* mirrored so the two edges never match */
}


/* =============================================================================
   06. ATMOSPHERE — the painting that lives behind the page
   -----------------------------------------------------------------------------
   z-order (back → front):
     <html> background     the void colour
     .atmosphere           fixed, z -1 : haze + stars + brush sweeps, one layer
     #comet (pointer)      fixed, z -1 : after .atmosphere in the DOM, so it sits
                                         on the sky but BEHIND all content: the
                                         comet flies through the void and never
                                         crosses an image or a word.
     #pencil (pointer)     fixed, z 40 : graphite on the paper sections, and the
                                         comet over the solid brand worlds.
                                         Never over an image, a poster or a logo
                                         (modules/comet.js decides per surface).
     page content          z auto
     .lamp                 fixed, z 50 : the living bulb
     .site-header          fixed, z 60
     .switch-stage         fixed, z 100 (home only)

   Built for smoothness
   • .atmosphere__field holds everything (haze, stars, sweeps). It is painted
     ONCE into a single GPU layer; scrolling only slides that layer
     (transform), so nothing behind the page is repainted while you scroll.
   • No blend modes, no filters, no canvas. (A canvas here would be a layer of
     its own, and everything painted after it would be pushed onto extra
     layers too. One layer is far cheaper for the GPU to draw every frame.)
   • The continuous motion is a few tiny glints (opacity + transform), and the
     near stars, which are tied to the scroll position; the compositor runs
     both without touching the main thread.
   ============================================================================= */
.atmosphere {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
  contain: strict;                 /* nothing inside can affect page layout */
  opacity: calc(.35 + var(--light-level) * .65);
}
/* The painted field: taller than the screen so it has room to drift */
.atmosphere__field {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 132vh;
  height: 132lvh;                  /* the large viewport: steady while a phone's address bar moves */
  /* The void is never flat: stars (a seamless tile), then ink-wash and haze
     glows fading to 0% */
  background:
    url("/assets/brand/stars.svg") 0 0 / 900px 900px repeat,
    radial-gradient(60vmax 42vmax at 84% 4%, rgba(58, 42, 140, .24), transparent 70%),
    radial-gradient(52vmax 40vmax at 8% 72%, rgba(27, 22, 54, .95), transparent 72%),
    radial-gradient(44vmax 32vmax at 46% 38%, rgba(58, 42, 140, .1), transparent 70%);
  will-change: transform;          /* its own layer: painted once, then only moved */
}

/* Brush sweeps — three enormous strokes from the house brush, in Light at very
   low opacity. Static: they ride along with the field when it drifts. */
.sweep {
  position: absolute;
  display: block;
  width: 130%;
  left: -15%;
  /* Solid fill + two mask layers (the brush shape, intersected with a fade).
     The fade lives in the mask, not a background-image, so the browser never
     mistakes a decorative sweep for the page's main content (LCP). */
  background: rgba(243, 237, 226, .1);
  -webkit-mask: url("/assets/brand/brush-house.svg") center / 100% 100% no-repeat,
                linear-gradient(90deg, #000, rgba(0, 0, 0, .35) 55%, transparent);
  -webkit-mask-composite: source-in;
          mask: url("/assets/brand/brush-house.svg") center / 100% 100% no-repeat,
                linear-gradient(90deg, #000, rgba(0, 0, 0, .35) 55%, transparent);
          mask-composite: intersect;
}
.sweep--a { top: 8%; height: 16%; transform: rotate(-7deg); }
.sweep--b {
  top: 42%; height: 11%; left: -5%;
  -webkit-mask-image: url("/assets/brand/brush-dry.svg"), linear-gradient(90deg, #000, rgba(0, 0, 0, .35) 55%, transparent);
          mask-image: url("/assets/brand/brush-dry.svg"), linear-gradient(90deg, #000, rgba(0, 0, 0, .35) 55%, transparent);
  transform: rotate(4deg) scaleX(-1);
  opacity: .8;
}
.sweep--c { top: 76%; height: 19%; transform: rotate(-3deg); opacity: .65; }

/* Glints — four-point sparks that breathe in the void (brand: "four-point
   glints"). Each is a tiny layer; the compositor animates them on its own. */
.glint {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: calc(22px * var(--s, 1));
  aspect-ratio: 1;
  margin: calc(-11px * var(--s, 1)) 0 0 calc(-11px * var(--s, 1));
  background: url("/assets/brand/glint.svg") center / contain no-repeat;
  opacity: .5;
}
html:not(.calm) .glint { animation: twinkle var(--d, 5.5s) ease-in-out var(--o, 0s) infinite; }
@keyframes twinkle {
  0%, 100% { opacity: .12; transform: scale(.55) rotate(0deg); }
  50%      { opacity: 1;   transform: scale(1) rotate(45deg); }
}

/* Scroll-linked drift, with no JavaScript, where the browser supports it.
   (Runs on the compositor.) Other browsers: modules/sweeps.js. */
@supports (animation-timeline: scroll()) {
  html:not(.calm) .atmosphere__field { animation: field-drift linear both; animation-timeline: scroll(root); }
}
@keyframes field-drift { to { transform: translate3d(0, -24vh, 0); } }

/* The near stars: a sparse layer of brighter stars that streams past faster
   than the far field while you scroll, so the void has depth. Scroll-linked (it runs on the
   compositor, locked to the scroll position). The tile repeats every 900px and
   the layer slides one tile per iteration; stars.js sets how many iterations
   fit the page (--near-iter), so the stars always travel at about half the
   speed of the page, on a short case study or the long home page alike. */
.atmosphere__near {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: calc(100lvh + 900px);
  background: url("/assets/brand/stars-near.svg") 0 0 / 1200px 900px repeat;
  will-change: transform;
  /* They appear while you scroll and fade when you stop: the sky streams past
     as you move, and a still page draws nothing extra. */
  opacity: 0;
  transition: opacity 1.1s var(--ease-soft) .15s;
}
.is-scrolling .atmosphere__near { opacity: 1; transition-duration: .3s; transition-delay: 0s; }
html.calm .atmosphere__near { display: none; }
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    html:not(.calm) .atmosphere__near {
      animation: near-drift linear both;
      animation-timeline: scroll(root);
      animation-iteration-count: var(--near-iter, 6);
    }
  }
}
@keyframes near-drift { to { transform: translate3d(0, -900px, 0); } }

/* Shooting stars: while you scroll, now and then one streaks past, away from
   the direction you're travelling (modules/stars.js fires them; three reused
   elements, transform + opacity only). */
.shooting-star {
  position: absolute;
  left: var(--x, 50%);
  top: var(--y, 30%);
  width: 190px;
  height: 2px;
  margin-left: -190px;
  border-radius: 2px;
  background: linear-gradient(90deg, rgba(243, 237, 226, 0), rgba(243, 237, 226, .7) 72%, var(--hot-core));
  transform-origin: 100% 50%;
  rotate: var(--a, 210deg);
  opacity: 0;
}
.shooting-star::after {                     /* its head: the brand's spark */
  content: "";
  position: absolute;
  right: -5px;
  top: 50%;
  width: 11px;
  height: 11px;
  margin-top: -5.5px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--hot-core), rgba(255, 201, 112, .55) 38%, rgba(255, 201, 112, 0) 70%);
}
.shooting-star.is-shooting { animation: shoot var(--dur, .9s) cubic-bezier(.25, .1, .35, 1) both; }
@keyframes shoot {
  0%   { opacity: 0; transform: translate3d(0, 0, 0) scaleX(.15); }
  14%  { opacity: 1; }
  70%  { opacity: .9; }
  100% { opacity: 0; transform: translate3d(var(--len, 420px), 0, 0) scaleX(1); }
}

/* The comet that follows your pointer (created by modules/comet.js): a bright
   head and a tapering tail of light that sheds a little star dust and leaves
   nothing behind. On phones a tap sends a small one off from your fingertip.
   Rendered at reduced resolution and scaled up: soft light doesn't need more
   pixels. */
#comet {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  pointer-events: none;
}
/* …and its paper twin: the pencil (graphite on the paper sections) and the
   comet over the brand worlds, just above the content but under the bulb and
   the header. Hidden whenever nothing is fading on it. */
#pencil {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 40;
  pointer-events: none;
}


/* =============================================================================
   07. THE LIVING BULB
   -----------------------------------------------------------------------------
   A pendant that hangs from the top of the screen. Its light is the brand's
   spark — the only gold on the site. lamp.js swings it (transform only) when
   the pointer or scroll nudges it, then lets it sleep; the breathing glow is a
   pure CSS animation that runs on the compositor.
   Structure:
     .lamp > .lamp__swing > .lamp__cord + .lamp__bulb > (.lamp__aura .lamp__socket
                                                         .lamp__glass .lamp__spark)
   ============================================================================= */
.lamp {
  --lamp-x: 50vw;            /* anchor (set by lamp.js from [data-lamp-anchor]) */
  --lamp-drop: 0px;          /* how far the cord has lowered */
  --lamp-cord: 128px;        /* cord length */
  position: fixed;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  z-index: 50;
  pointer-events: none;
  transform: translate3d(var(--lamp-x), 0, 0);
}
.lamp__swing {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: 0 0;
  transform: translate3d(0, var(--lamp-drop), 0) rotate(0deg);   /* lamp.js writes this */
  will-change: transform;          /* one small layer: swinging never repaints */
  transition: opacity .5s var(--ease-soft);
}
.lamp__cord {
  position: absolute;
  left: -1px;
  top: -40px;                                  /* starts above the viewport */
  width: 2px;
  height: calc(var(--lamp-cord) + 40px);
  background: linear-gradient(rgba(243, 237, 226, .25), rgba(160, 154, 146, .85));
}
.lamp__bulb {
  position: absolute;
  left: -40px;
  top: var(--lamp-cord);
  width: 80px;
  height: 120px;
}
.lamp__socket {
  position: absolute;
  left: 50%;
  top: -6px;
  width: 30px;
  height: 22px;
  transform: translateX(-50%);
  border-radius: 7px 7px 5px 5px;
  background:
    repeating-linear-gradient(180deg, rgba(243, 237, 226, .14) 0 2px, transparent 2px 5px),
    linear-gradient(180deg, #2a2730, #121015);
  box-shadow: 0 4px 10px rgba(0, 0, 0, .35);
}
.lamp__glass {
  position: absolute;
  left: 50%;
  top: 12px;
  width: 68px;
  height: 86px;
  transform: translateX(-50%);
  border-radius: 46% 46% 44% 44% / 40% 40% 60% 60%;
  /* Light in the void; the faint sumi rim keeps it drawn when it hangs over paper */
  background:
    radial-gradient(circle at 34% 26%, rgba(255, 255, 255, .85), rgba(255, 255, 255, .1) 16%, transparent 22%),
    radial-gradient(circle at 50% 46%, rgba(255, 242, 214, calc(.18 + var(--light-level) * .4)), rgba(243, 237, 226, .08) 62%, rgba(243, 237, 226, .03));
  border: 1.5px solid rgba(243, 237, 226, .5);
  box-shadow:
    0 0 0 1px rgba(18, 16, 21, .32),
    inset 0 0 22px rgba(255, 242, 214, calc(.06 + var(--light-level) * .16));
}
/* The spark — a four-point glint, the brand's only gold */
.lamp__spark {
  position: absolute;
  left: 50%;
  top: 46px;
  width: 22px;
  height: 22px;
  margin-left: -11px;
  color: var(--hot-core);
  opacity: calc(.25 + var(--light-level) * .75);
  filter: drop-shadow(0 0 6px rgba(255, 201, 112, .9));
}
.lamp__spark svg { width: 100%; height: 100%; }
/* The breathing glow — Hot Core over Filament Lit, 0% at the edge.
   .lamp__glow carries the light level (it only changes during the intro);
   .lamp__aura breathes with fixed keyframes, so the compositor runs it alone. */
.lamp__glow {
  position: absolute;
  left: 50%;
  top: 57px;
  width: 0;
  height: 0;
  opacity: var(--light-level);
}
.lamp__aura {
  position: absolute;
  left: -170px;
  top: -170px;
  width: 340px;
  height: 340px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 242, 214, .55) 0%, rgba(255, 201, 112, .2) 22%, rgba(255, 201, 112, .06) 46%, rgba(255, 201, 112, 0) 70%);
  opacity: .8;
  animation: lamp-breathe 3.5s ease-in-out infinite;   /* the original "pulseGlow" rhythm */
}
@keyframes lamp-breathe {
  0%, 100% { transform: scale(.86); opacity: .5; }
  60%      { transform: scale(1.08); opacity: .95; }
}

/* Cosmic tentacles — every so often the bulb's own filament (the tentacle from
   the LIGHT mark) reaches out around the glass, curls, and slips back.
   lamp.js adds .is-reaching for one cycle; CSS does the motion.
   Structure: .lamp__reach > .lamp__nebula + 3 × (.lamp__tentacle > i)
   The outer element holds each tentacle's fixed angle; the inner <i> animates
   (scale, a small curl and opacity only), all on the compositor. */
.lamp__reach { position: absolute; inset: 0; display: none; }
.lamp.has-reach .lamp__reach { display: block; }       /* images load only once the bulb has settled */
.lamp__nebula {
  position: absolute;
  left: 50%;
  top: 55px;
  width: 260px;
  height: 260px;
  margin: -130px 0 0 -130px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(58, 42, 140, .55), rgba(27, 22, 54, .3) 40%, transparent 70%);
  opacity: 0;
}
.lamp__tentacle {
  position: absolute;
  width: 46px;
  height: 60px;
  transform-origin: 52% 96%;                      /* the tentacle's root */
  transform: rotate(var(--r, 0deg)) scaleX(var(--flip, 1)) scale(var(--k, 1));
}
.lamp__tentacle i {
  position: absolute;
  inset: 0;
  background: url("/assets/brand/tentacle.svg") center / contain no-repeat;
  transform-origin: 52% 96%;
  opacity: 0;
  transform: scale(.15);
}
/* Roots sit on the glass: right, left (mirrored) and under it */
.lamp__tentacle--a { left: 42px; top: 2px;  --r: 38deg; }
.lamp__tentacle--b { left: -8px; top: 4px;  --r: -38deg; --flip: -1; }
.lamp__tentacle--c { left: 16px; top: 38px; --r: 168deg; --k: .78; }
.lamp.is-reaching .lamp__tentacle i { animation: tentacle-reach 4.4s var(--ease-soft) both; }
.lamp.is-reaching .lamp__tentacle--b i { animation-delay: .25s; }
.lamp.is-reaching .lamp__tentacle--c i { animation-delay: .5s; }
.lamp.is-reaching .lamp__nebula { animation: nebula-bloom 4.9s var(--ease-soft) both; }
@keyframes tentacle-reach {
  0%   { opacity: 0;   transform: scale(.15) rotate(-18deg); }
  24%  { opacity: .92; transform: scale(1) rotate(6deg); }
  44%  { opacity: .92; transform: scale(.94) rotate(-5deg); }
  64%  { opacity: .92; transform: scale(1.02) rotate(3deg); }
  100% { opacity: 0;   transform: scale(.15) rotate(-18deg); }
}
@keyframes nebula-bloom {
  0%, 100% { opacity: 0; transform: scale(.6); }
  35%, 65% { opacity: 1; transform: scale(1); }
}

@media (max-width: 760px) {
  .lamp { --lamp-cord: 96px; }
  .lamp__bulb { transform: scale(.82); transform-origin: 50% 0; }
  .lamp__aura { width: 260px; height: 260px; left: -130px; top: -130px; }
}
/* Lights off: the bulb waits above the screen */
.lights-off:not(.lights-warming) .lamp__swing { opacity: 0; }


/* =============================================================================
   08. HEADER & NAVIGATION
   ============================================================================= */
.skip-link {
  position: absolute;
  left: 12px;
  top: -100px;
  z-index: 200;
  padding: .7em 1.1em;
  background: var(--light);
  color: var(--sumi);
  font-weight: 600;
}
.skip-link:focus { top: 12px; }

.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;
  height: var(--header-h);
  color: var(--light);
  /* A dark fade at the top; becomes a solid ink bar once scrolled */
  background: linear-gradient(180deg, rgba(7, 6, 13, .9), rgba(7, 6, 13, 0));
  transition: background-color .35s var(--ease-soft), box-shadow .35s var(--ease-soft);
}
.site-header.is-scrolled {
  background-color: rgba(7, 6, 13, .94);
  box-shadow: 0 1px 0 rgba(243, 237, 226, .08);
}
/* Pages that open on paper get a solid ink bar from the start (no muddy fade) */
.page-case:has(main > article.surface-paper:first-child) .site-header {
  background: rgba(7, 6, 13, .96);
}
.site-header__inner {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
}
.brand__icon { position: relative; flex: none; width: 30px; }
.brand__icon .mark-live { width: 30px; }
/* Idle life for the mark: its spark breathes in step with the bulb, and every
   so often the filament flickers (the glow below). The mark's own parts move
   too, very slightly (see 07b: .mark-live). */
.brand__icon::after {
  content: "";
  position: absolute;
  left: 53.6%;                 /* the spark's centre within the mark */
  top: 38.9%;
  width: 30px;
  height: 30px;
  margin: -15px 0 0 -15px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 242, 214, .95) 0, rgba(255, 201, 112, .55) 22%, rgba(255, 201, 112, 0) 68%);
  opacity: .35;
  pointer-events: none;
}
html:not(.calm) .brand__icon::after { animation: mark-idle 7s ease-in-out infinite; }
.brand:hover .brand__icon::after { opacity: 1; }
@keyframes mark-idle {
  0%, 100% { opacity: .3;  transform: scale(.8); }
  25%      { opacity: .85; transform: scale(1.08); }
  50%      { opacity: .3;  transform: scale(.8); }
  72%      { opacity: .8;  transform: scale(1.05); }
  86%      { opacity: .35; transform: scale(.9); }
  88%      { opacity: .95; transform: scale(1.12); }   /* the flicker */
  90%      { opacity: .25; transform: scale(.85); }
  92%      { opacity: .8;  transform: scale(1); }
}

/* Where to find an organisation: its website and socials, under its project
   (home stream and case studies). Each opens in a new tab. */
.org-links { display: grid; gap: 7px; width: 100%; padding-top: 12px; border-top: 1px solid var(--rule); }
.org-links > div { display: grid; grid-template-columns: 6.4rem minmax(0, 1fr); gap: 12px; align-items: baseline; }
.org-links dt { font-size: var(--fs-label); font-weight: 600; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--text-faint); }
.org-links dd { display: flex; flex-wrap: wrap; gap: 2px 16px; font-size: 1.05rem; line-height: 1.35; }
.org-links a {
  color: var(--text);
  text-decoration: underline;
  text-decoration-color: var(--rule-strong);
  text-decoration-thickness: 1px;
  text-underline-offset: .26em;
  transition: text-decoration-color .3s;
}
.org-links a:hover { text-decoration-color: currentColor; }
.org-links a::after { content: "↗"; display: inline-block; margin-left: .22em; font-size: .72em; opacity: .55; }
/* In a case study it reads as the end of the facts list above it */
.chapter__body .org-links > div { grid-template-columns: minmax(110px, 30%) 1fr; gap: 16px; }
.facts + .org-links { margin-top: 18px; padding-top: 0; border-top: 0; }

/* -----------------------------------------------------------------------------
   07b. THE LIVING MARK (.mark-live) — the LIGHT bulb, alive
   The mark is split into its parts (assets/brand/mark/*.svg, one shared
   viewBox, stacked exactly), so each can move on its own, subtly:
     • the tentacle sways from its root and its suckers glow and dim with the
       bulb's breathing;
     • the brush stroke leans a fraction the other way, and now and then a
       glint of light runs along it;
     • the glow around the spark breathes.
   Every movement is a transform or opacity change on its own small layer, so
   the GPU runs it without repainting. Calm motion: the mark is still.
     .mark-live > .mark-live__glow
                + .mark-live__stroke > img + .mark-live__sheen
                + .mark-live__arm    > img + .mark-live__suckers
                + .mark-live__socket
   ----------------------------------------------------------------------------- */
.mark-live { position: relative; display: block; aspect-ratio: 428 / 620; }
.mark-live img,
.mark-live__stroke,
.mark-live__arm { position: absolute; inset: 0; width: 100%; height: 100%; }
.mark-live__stroke { transform-origin: 39% 75%; }      /* the brush stroke's root, by the socket */
.mark-live__arm { transform-origin: 67% 75%; }         /* the tentacle's root */
.mark-live__suckers { opacity: .2; }
.mark-live__sheen {
  position: absolute;
  inset: 0;
  overflow: hidden;
  -webkit-mask: url("/assets/brand/mark/stroke.svg") center / 100% 100% no-repeat;
          mask: url("/assets/brand/mark/stroke.svg") center / 100% 100% no-repeat;
}
.mark-live__sheen::before {
  content: "";
  position: absolute;
  inset: -10% -70%;
  background: linear-gradient(118deg, transparent 43%, rgba(255, 242, 214, .95) 50%, transparent 57%);
  transform: translate3d(-55%, 0, 0);
  opacity: 0;
}
@media (prefers-reduced-motion: no-preference) {
  html:not(.calm) .mark-live__arm { animation: mark-arm 5.8s ease-in-out infinite alternate; }
  html:not(.calm) .mark-live__stroke { animation: mark-stroke 7.4s ease-in-out -2s infinite alternate; }
  html:not(.calm) .mark-live__suckers { animation: mark-suckers 3.5s ease-in-out infinite; }
  html:not(.calm) .mark-live__sheen::before { animation: mark-sheen 7s ease-in-out 1.2s infinite; }
  html:not(.calm) .mark-live__glow { animation: mark-glow 3.5s ease-in-out infinite; }
}
@keyframes mark-arm    { from { transform: rotate(-2.4deg); } to { transform: rotate(2.8deg); } }
@keyframes mark-stroke { from { transform: rotate(1.1deg); }  to { transform: rotate(-1.3deg); } }
@keyframes mark-suckers {
  0%, 100% { opacity: .18; }
  50%      { opacity: .95; }
}
@keyframes mark-sheen {
  0%, 62%  { opacity: 0; transform: translate3d(-55%, 0, 0); }
  66%      { opacity: 1; }
  86%      { opacity: .9; }
  92%, 100% { opacity: 0; transform: translate3d(55%, 0, 0); }
}
@keyframes mark-glow {
  0%, 100% { opacity: .8; transform: scale(.97); }
  50%      { opacity: 1;  transform: scale(1.03); }
}
.brand__name { font-size: .86rem; }
.brand__name .descriptor { display: block; margin-top: 4px; font-size: .86rem; letter-spacing: .02em; text-transform: none; }

.nav { display: flex; align-items: center; gap: clamp(14px, 2.4vw, 34px); }
.nav__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: rgba(243, 237, 226, .8);
  transition: color .25s var(--ease-soft);
}
.nav__link .numeral { margin-right: .5em; font-size: .9rem; letter-spacing: 0; text-transform: none; opacity: .7; }
/* A dry-brush stroke paints under the hovered / current link */
.nav__link::after {
  content: "";
  position: absolute;
  left: -6px; right: -6px;
  bottom: 6px;
  height: 7px;
  background: currentColor;
  -webkit-mask: url("/assets/brand/brush-dry.svg") center / 100% 100% no-repeat;
          mask: url("/assets/brand/brush-dry.svg") center / 100% 100% no-repeat;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .45s var(--ease-brush);
  opacity: .9;
}
.nav__link:hover, .nav__link[aria-current="true"], .nav__link[aria-current="page"] { color: var(--light); }
.nav__link:hover::after, .nav__link[aria-current="true"]::after, .nav__link[aria-current="page"]::after { transform: scaleX(1); }
.nav__cv { gap: .45em; }

.menu-toggle {
  display: none;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 4px;
  border: 0;
  background: none;
  cursor: pointer;
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
.menu-toggle__lines { display: grid; gap: 5px; width: 22px; }
.menu-toggle__lines span { height: 2px; background: currentColor; border-radius: 2px; }
.menu-toggle__lines span:last-child { width: 70%; justify-self: end; }

@media (max-width: 860px) {
  .nav { display: none; }
  .menu-toggle { display: inline-flex; }
  .brand__name .descriptor { display: none; }
}

/* Mobile menu sheet — a full-screen void with the links set large */
.menu-sheet {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  align-content: center;
  padding: calc(var(--header-h) + 24px) var(--gutter) 48px;
  background:
    radial-gradient(70vmax 50vmax at 80% 0%, rgba(58, 42, 140, .28), transparent 70%),
    var(--void);
  color: var(--light);
  visibility: hidden;
  opacity: 0;
  transition: opacity .35s var(--ease-soft), visibility 0s linear .35s;
}
.menu-sheet.is-open { visibility: visible; opacity: 1; transition: opacity .35s var(--ease-soft); }
.menu-sheet__list { display: grid; gap: 6px; }
.menu-sheet__list a {
  display: flex;
  align-items: baseline;
  gap: 18px;
  padding: 8px 0;
  font-size: clamp(2.4rem, 11vw, 3.6rem);
  line-height: 1;
  border-bottom: 1px solid var(--rule);
}
.menu-sheet__list .numeral { font-size: 1.1rem; opacity: .6; }
.menu-sheet__close {
  position: absolute;
  top: 10px;
  right: var(--gutter);
  min-height: 44px;
  padding: 0 4px;
  border: 0;
  background: none;
  cursor: pointer;
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
.menu-sheet__foot { margin-top: 36px; display: flex; flex-wrap: wrap; gap: 14px 26px; }


/* =============================================================================
   09. COMPONENTS
   ============================================================================= */

/* 09a — Kicker & numerals ---------------------------------------------------- */
.kicker {
  display: inline-flex;
  align-items: baseline;
  gap: .8em;
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-soft);
}
/* Lowercase italic roman numerals — the brand's sequence marks (i. ii. iii.) */
.numeral {
  font-style: italic;
  font-weight: 500;
  text-transform: none;
  letter-spacing: .02em;
  font-size: 1.05rem;
  color: var(--text-faint);
}
.kicker .numeral { font-size: 1.15rem; }
/* The seed — a small four-point spark used as a separator (light, never gold) */
.seed {
  display: inline-block;
  width: .62em;
  height: .62em;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 0C10.8 6 14 9.2 20 10C14 10.8 10.8 14 10 20C9.2 14 6 10.8 0 10C6 9.2 9.2 6 10 0Z'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 0C10.8 6 14 9.2 20 10C14 10.8 10.8 14 10 20C9.2 14 6 10.8 0 10C6 9.2 9.2 6 10 0Z'/%3E%3C/svg%3E") center / contain no-repeat;
  opacity: .7;
  vertical-align: .05em;
}

/* 09b — Buttons & links ------------------------------------------------------ */
/* .btn is painted with the house brush: press, body, release (flying white). */
.btn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .7em;
  min-height: 54px;
  padding: 0 1.2em 0 1.3em;
  margin-right: 2.4em;          /* room for the tail */
  border: 0;
  background: none;
  cursor: pointer;
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--paint-ink);
  transition: transform .35s var(--ease-brush);
}
.btn::before {
  content: "";
  position: absolute;
  inset: 3px -24% 3px -10px;   /* the brush's dry tail runs past the label */
  z-index: -1;
  background: var(--paint);
  /* brush-plate.svg = the house brush with its dry tail shortened, so the
     label always sits on solid ink and only the overhang breaks into streaks */
  -webkit-mask: url("/assets/brand/brush-plate.svg") center / 100% 100% no-repeat;
          mask: url("/assets/brand/brush-plate.svg") center / 100% 100% no-repeat;
  transition: transform .45s var(--ease-brush);
  transform-origin: left center;
}
.btn:hover { transform: translateX(3px); }
.btn:hover::before { transform: scaleX(1.05); }
/* Ghost button: text with a dry-brush underline */
.btn--ghost { color: var(--text); padding-inline: .2em; min-height: 48px; margin-right: 0; }
.btn--ghost::before {
  inset: auto -4px 6px -4px;
  height: 7px;
  -webkit-mask-image: url("/assets/brand/brush-dry.svg");
          mask-image: url("/assets/brand/brush-dry.svg");
  background: currentColor;
  opacity: .85;
}
.btn .arrow { width: 1.05em; height: 1.05em; flex: none; }
.btn-row { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 26px; }

/* Text link with a fine underline that thickens on hover */
.link {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .24em;
  text-decoration-color: var(--rule-strong);
  transition: text-decoration-color .25s, text-decoration-thickness .25s;
}
.link:hover { text-decoration-color: currentColor; text-decoration-thickness: 2px; }

/* 09c — Tags (categories) ---------------------------------------------------- */
.tags { display: flex; flex-wrap: wrap; gap: 6px 14px; }
.tag {
  display: inline-flex;
  align-items: center;
  gap: .55em;
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--text-soft);
}
.tag::before {
  content: "";
  width: .5em;
  height: .5em;
  border-radius: 50%;
  border: 1px solid currentColor;
  opacity: .8;
}

/* 09d — Proof (statistics with context) --------------------------------------
   Every number carries: what it counts, where it came from, and how much of it
   was mine (the attribution chip — see 09e). */
.proof-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  gap: clamp(28px, 3.5vw, 48px) clamp(20px, 3vw, 44px);
}
.proof { display: grid; align-content: start; gap: .5rem; padding-top: 1rem; border-top: 1px solid var(--rule); }
.proof__figure {
  font-size: clamp(2.8rem, 2rem + 3.2vw, 5rem);
  font-weight: 500;
  line-height: .9;
  letter-spacing: -.02em;
  font-variant-numeric: lining-nums tabular-nums;
}
.proof__figure small { font-size: .5em; margin-left: .06em; letter-spacing: 0; }
.proof__label { font-size: var(--fs-small); line-height: 1.3; color: var(--text); max-width: 24ch; }
.proof__source { font-size: .98rem; font-style: italic; color: var(--text-faint); line-height: 1.35; }

/* 09e — Attribution chips + legend ----------------------------------------- */
.attr {
  display: inline-flex;
  align-items: center;
  gap: .45em;
  font-style: normal;
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--text-soft);
  white-space: nowrap;
}
.attr::before { content: ""; width: .62em; height: .62em; border-radius: 50%; border: 1.5px solid currentColor; }
.attr--delivered::before { background: currentColor; }                         /* ● made it myself */
.attr--led::before { background: linear-gradient(90deg, currentColor 50%, transparent 50%); } /* ◐ led the team that achieved it */
.attr--contributed::before { background: transparent; }                       /* ○ contributed to a team result */
.attr--context::before { border-style: dashed; }                              /* ◌ scale / context only */
.legend { display: flex; flex-wrap: wrap; gap: 10px 24px; padding-top: 18px; border-top: 1px dashed var(--rule); }
.legend .attr { color: var(--text-faint); white-space: normal; }   /* long legend labels wrap on a phone */
.legend__note { flex-basis: 100%; font-size: .98rem; font-style: italic; color: var(--text-faint); }

/* 09f — Media frames ---------------------------------------------------------
   Images keep their intrinsic aspect ratio. A frame never grows taller than
   most of the viewport, and shrinks its width to stay in proportion.
   • .frame          → object-fit: contain (artwork, screens, logos, diagrams)
   • .frame--photo   → object-fit: cover   (only for photographs)
   Set --ar on the frame (width ÷ height) — the markup does this for you. */
.frame {
  --ar: 1.6;
  --frame-max-h: min(78vh, 880px);
  position: relative;
  width: 100%;
  /* never taller than --frame-max-h, never wider than the image itself (--w) */
  max-width: min(calc(var(--frame-max-h) * var(--ar)), var(--w, 100%));
  aspect-ratio: var(--ar);
  margin-inline: auto;
}
.frame > picture, .frame > img, .frame > picture > img, .frame > .video-facade {
  width: 100%;
  height: 100%;
}
.frame img { object-fit: contain; object-position: center; }
.frame--photo img { object-fit: cover; object-position: var(--focus, 50% 35%); }
.frame--matte {                            /* artwork on its own small matte */
  padding: clamp(10px, 1.6vw, 22px);
  background: rgba(243, 237, 226, .04);
  outline: 1px solid var(--rule);
  outline-offset: -1px;
}
.surface-paper .frame--matte { background: rgba(18, 16, 21, .04); }
figure figcaption, .caption {
  margin-top: .8rem;
  font-size: .98rem;
  font-style: italic;
  line-height: 1.4;
  color: var(--text-faint);
}
figcaption .numeral { margin-right: .4em; }

/* 09g — Video facade (YouTube loads only when asked) ------------------------- */
.video-facade {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: var(--sumi);
  cursor: pointer;
  overflow: hidden;
}
.video-facade img { width: 100%; height: 100%; object-fit: cover; transition: transform .8s var(--ease-brush), opacity .4s; opacity: .86; }
.video-facade:hover img { transform: scale(1.02); opacity: 1; }
.video-facade__play {
  position: absolute;
  left: 50%;
  top: 50%;
  display: grid;
  place-items: center;
  width: 88px;
  height: 88px;
  margin: -44px 0 0 -44px;
  border-radius: 50%;
  border: 1.5px solid rgba(243, 237, 226, .85);
  background: rgba(7, 6, 13, .55);
  color: var(--light);
  transition: transform .4s var(--ease-brush), background-color .3s;
}
.video-facade__play svg { width: 28px; height: 28px; margin-left: 4px; }
.video-facade:hover .video-facade__play { transform: scale(1.06); background: rgba(7, 6, 13, .75); }
.video-facade__label {
  position: absolute;
  left: 18px;
  bottom: 16px;
  color: var(--light);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  text-shadow: 0 1px 12px rgba(0, 0, 0, .7);
}
.video-embed { width: 100%; height: 100%; border: 0; background: #000; }
/* Corner play button, for stills whose centre carries a title */
.video-facade--corner .video-facade__play { left: auto; top: auto; right: 22px; bottom: 18px; margin: 0; }

/* 09h — Audio player ---------------------------------------------------------
   Nothing loads until the visitor presses play (preload="none"). */
.audio-list { display: grid; gap: 0; border-top: 1px solid var(--rule); }
.track {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 22px;
  align-items: center;
  padding: 22px 0;
  border-bottom: 1px solid var(--rule);
}
.track__play {
  grid-row: span 2;
  position: relative;
  display: grid;
  place-items: center;
  width: 58px;
  height: 58px;
  border-radius: 50%;
  border: 1.5px solid var(--rule-strong);
  background: transparent;
  color: var(--text);
  cursor: pointer;
  transition: border-color .25s, background-color .25s;
}
.track__play:hover { border-color: currentColor; }
.track__play svg { width: 20px; height: 20px; }
.track__play .icon-pause { display: none; }
.track.is-playing .track__play .icon-play { display: none; }
.track.is-playing .track__play .icon-pause { display: block; }
/* When a track plays, its spark lights — the "on air" lamp */
.track__play::after {
  content: "";
  position: absolute;
  right: -2px;
  top: -2px;
  width: 12px;
  height: 12px;
  background: var(--filament-lit);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 0C10.8 6 14 9.2 20 10C14 10.8 10.8 14 10 20C9.2 14 6 10.8 0 10C6 9.2 9.2 6 10 0Z'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 0C10.8 6 14 9.2 20 10C14 10.8 10.8 14 10 20C9.2 14 6 10.8 0 10C6 9.2 9.2 6 10 0Z'/%3E%3C/svg%3E") center / contain no-repeat;
  filter: drop-shadow(0 0 5px var(--filament-lit));
  opacity: 0;
  transform: scale(.4);
  transition: opacity .3s, transform .4s var(--ease-brush);
}
.track.is-playing .track__play::after { opacity: 1; transform: scale(1); }
.track__play[disabled] { cursor: not-allowed; opacity: .45; }
.track__title { font-size: var(--fs-h3); line-height: 1.05; }
.track__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 16px; font-size: 1rem; color: var(--text-faint); font-style: italic; }
.track__progress {
  grid-column: 2;
  display: none;
  align-items: center;
  gap: 14px;
  margin-top: 10px;
}
.track.is-loaded .track__progress { display: flex; }
.track__progress input[type="range"] { flex: 1; accent-color: var(--text); height: 24px; }
.track__time { font-size: .95rem; font-variant-numeric: tabular-nums; color: var(--text-faint); min-width: 7ch; text-align: right; }
.track__note { grid-column: 2; font-size: 1rem; color: var(--text-soft); max-width: 60ch; }

/* 09i — Organisation logos ------------------------------------------------
   Client and employer logos are shown EXACTLY as supplied: their own colours,
   no filters, no blending. Each sits on a white card, the same white as the
   ground of the supplied file, so nothing about the logo changes.
   (media/<slug>-exact.webp|png are lossless crops of the original files,
   made by tools/make-logo-trims.js.) */
.org-plate,
.logo-card {
  display: inline-grid;
  place-items: center;
  background: #fff;
}
.org-plate {
  width: clamp(150px, 16vw, 210px);
  padding: 12px 16px;
}
.org-plate img,
.logo-card img {
  display: block;
  width: 100%;
  height: auto;
}

/* 09j — Fact list (role, timeframe, team…) -------------------------------- */
.facts { display: grid; gap: 0; border-top: 1px solid var(--rule); }
.facts > div { display: grid; grid-template-columns: minmax(110px, 30%) 1fr; gap: 16px; padding: 14px 0; border-bottom: 1px solid var(--rule); }
.facts dt { font-size: var(--fs-label); font-weight: 600; letter-spacing: var(--track-label); text-transform: uppercase; color: var(--text-faint); padding-top: .35em; }
.facts dd { color: var(--text); line-height: 1.4; }
.facts dd small { display: block; margin-top: 4px; font-size: .98rem; font-style: italic; color: var(--text-faint); }

/* 09k — Next project (the stream continues) -------------------------------- */
.next-project {
  position: relative;
  display: grid;
  gap: 18px;
  padding-block: clamp(80px, 12vw, 180px);
  border-top: 1px solid var(--rule);
  overflow: hidden;
}
.next-project__link { display: grid; gap: 14px; max-width: 1100px; }
.next-project__title {
  font-size: clamp(3rem, 2rem + 6vw, 8.4rem);
  line-height: .9;
  letter-spacing: -.02em;
  transition: transform .7s var(--ease-brush);
}
.next-project__link:hover .next-project__title { transform: translateX(14px); }
.next-project__stroke {
  position: absolute;
  left: -6vw;
  right: -6vw;
  bottom: 18%;
  height: 34%;
  z-index: -1;
  background: linear-gradient(90deg, rgba(243, 237, 226, .1), rgba(243, 237, 226, .02));
  -webkit-mask: url("/assets/brand/brush-house.svg") center / 100% 100% no-repeat;
          mask: url("/assets/brand/brush-house.svg") center / 100% 100% no-repeat;
  transform: scaleX(.2);
  transform-origin: left center;
  transition: transform 1.2s var(--ease-brush);
}
.next-project.is-visible .next-project__stroke { transform: scaleX(1); }


/* =============================================================================
   10. FOOTER
   ============================================================================= */
.site-footer {
  position: relative;
  padding: 56px 0 44px;
  border-top: 1px solid var(--rule);
  color: var(--text-soft);
  font-size: 1rem;
}
.site-footer__inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 18px 32px; }
.site-footer__links { display: flex; flex-wrap: wrap; gap: 8px 22px; }
.site-footer a, .site-footer button { min-height: 44px; display: inline-flex; align-items: center; }
.toggle-calm {
  gap: .6em;
  border: 0;
  background: none;
  cursor: pointer;
  padding: 0;
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--text-soft);
}
.toggle-calm__dot { width: 30px; height: 16px; border-radius: 10px; border: 1.5px solid currentColor; position: relative; }
.toggle-calm__dot::after { content: ""; position: absolute; top: 2px; left: 2px; width: 9px; height: 9px; border-radius: 50%; background: currentColor; transition: transform .25s; }
.toggle-calm[aria-pressed="true"] .toggle-calm__dot::after { transform: translateX(13px); }


/* =============================================================================
   11. REVEAL — content paints in as it enters the viewport (reveal.js)
   Only opacity and transform animate, so it stays on the compositor.
   ============================================================================= */
.js .reveal {
  opacity: 0;
  transform: translate3d(0, 14px, 0);
  transition: opacity var(--dur-reveal) var(--ease-soft), transform var(--dur-reveal) var(--ease-brush);
  transition-delay: var(--delay, 0s);
}
.js .reveal.is-visible { opacity: 1; transform: none; }

/* Images are uncovered by a sumi brush that drags off to the right */
.js .reveal-ink { position: relative; overflow: hidden; }
.js .reveal-ink::after {
  content: "";
  position: absolute;
  inset: -2px -30% -2px -2px;
  z-index: 2;
  background: var(--surface);
  -webkit-mask: linear-gradient(90deg, #000 72%, transparent 100%);
          mask: linear-gradient(90deg, #000 72%, transparent 100%);
  transform: translate3d(0, 0, 0);
  transition: transform 1.25s var(--ease-brush);
  transition-delay: var(--delay, .05s);
  pointer-events: none;
}
.js .reveal-ink.is-visible::after { transform: translate3d(101%, 0, 0); }

/* While the page is scrolling (header.js adds .is-scrolling), every decorative
   loop pauses, so the GPU spends each frame on the scroll alone. */
.is-scrolling .glint,
.is-scrolling .lamp__aura,
.is-scrolling .brand__icon::after,
.is-scrolling .mark-live *, .is-scrolling .mark-live__sheen::before,
.is-scrolling [data-live] *,
.is-scrolling [data-live] *::before,
.is-scrolling [data-live] *::after { animation-play-state: paused !important; }

/* Looping animations inside a [data-live] block run only while it is on screen
   (reveal.js toggles .is-live). Off-screen motion would cost frames for nothing. */
.js [data-live]:not(.is-live) *,
.js [data-live]:not(.is-live) *::before,
.js [data-live]:not(.is-live) *::after { animation-play-state: paused !important; }


/* =============================================================================
   12. UTILITIES
   ============================================================================= */
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}
.nowrap { white-space: nowrap; }
.center { text-align: center; }
[hidden] { display: none !important; }


/* =============================================================================
   13. CALM MOTION
   Applies when the visitor's system asks for reduced motion OR when they
   choose "Calm motion" in the footer (html.calm). The painting, switch and
   bulb all stay — they simply stop drifting, swinging and sweeping.
   ============================================================================= */
@media (prefers-reduced-motion: reduce) {
  html { --dur-reveal: 0s; }
  .atmosphere__field, .glint, .brand__icon::after { animation: none !important; }
  .lamp__aura { animation-duration: 7s; }
  .lamp__reach { display: none !important; }
  .js .reveal { opacity: 1; transform: none; }
  .js .reveal-ink::after { display: none; }
}
html.calm { --dur-reveal: 0s; }
html.calm .atmosphere__field, html.calm .glint, html.calm .brand__icon::after { animation: none !important; }
html.calm .lamp__aura { animation-duration: 7s; }
html.calm .lamp__reach { display: none !important; }
html.calm .js .reveal, html.calm.js .reveal { opacity: 1; transform: none; }
html.calm.js .reveal-ink::after { display: none; }


/* =============================================================================
   14. PAGE-TO-PAGE NAVIGATION
   Pages switch instantly. (An earlier cross-fade between pages was removed: it
   held every click for most of a second and made the site feel slower.)
   ============================================================================= */
