/* Fonts are served from the site itself (League Spartan, DM Sans — SIL OFL,
   from Google Fonts). Both are variable fonts, so one file per script covers
   every weight in use; unicode-range means only the scripts a page actually
   uses are downloaded. */
/* latin-ext */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(../assets/fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(../assets/fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(../assets/fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(../assets/fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(../assets/fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(../assets/fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
@font-face {
  font-family: 'League Spartan';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(../assets/fonts/league-spartan-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'League Spartan';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(../assets/fonts/league-spartan-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'League Spartan';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(../assets/fonts/league-spartan-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
@font-face {
  font-family: 'League Spartan';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(../assets/fonts/league-spartan-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'League Spartan';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(../assets/fonts/league-spartan-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'League Spartan';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(../assets/fonts/league-spartan-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
@font-face {
  font-family: 'League Spartan';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url(../assets/fonts/league-spartan-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'League Spartan';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url(../assets/fonts/league-spartan-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'League Spartan';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url(../assets/fonts/league-spartan-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
@font-face {
  font-family: 'League Spartan';
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url(../assets/fonts/league-spartan-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'League Spartan';
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url(../assets/fonts/league-spartan-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'League Spartan';
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url(../assets/fonts/league-spartan-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ============================================================
   KAZI NETWORK — Design System
   ============================================================ */
:root {
  --color-bg:      #FFFFF0;
  --color-surface: #F0EBE0;
  --color-border:  #E0D8D0;
  --color-accent:  #D74E31;
  --color-dark:    #111111;
  --color-muted:   #999080;
  --color-dim:     #555555;

  /* Fallbacks matched to the real fonts' proportions so the swap-in causes
     no visible reflow (League Spartan is condensed -> Arial Narrow). */
  --font-display: 'League Spartan', 'Arial Narrow', 'Helvetica Neue', sans-serif;
  --font-body:    'DM Sans', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;

  --space-xs: 8px;  --space-sm: 16px; --space-md: 24px;
  --space-lg: 40px; --space-xl: 80px; --space-2xl: 120px; --space-3xl: 160px;

  --maxw: 1440px;
  --pad: 80px;
  --radius: 0px;
  --nav-h: 72px;
}

/* One focus ring for the whole site. The browser's default is a blue that
   belongs to nothing else in the palette, and it turns up on any button the
   visitor clicked as soon as they touch a key — the arrow keys included.
   currentColor keeps it legible on cream and on orange alike. */
:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }

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

html { scroll-behavior: smooth; -webkit-font-smoothing: antialiased; overflow-x: clip; }

body {
  font-family: var(--font-body);
  background: var(--color-bg);
  color: var(--color-dark);
  overflow-x: clip;
}
body.is-locked { overflow: hidden; height: 100vh; }

/* height:auto lets the intrinsic width/height attributes on every <img>
   act purely as an aspect-ratio hint (reserving space to stop layout
   shift) instead of forcing a fixed pixel height when the image is
   scaled down by max-width. More specific rules still override it. */
img, video { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; }

.container { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 var(--pad); }

/* Headings */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 0.82;
  letter-spacing: -0.04em;
  text-transform: uppercase;
}

/* Visually hidden, still read out. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

.eyebrow {
  font-family: var(--font-body);
  font-size: 13px; font-weight: 500;
  letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--color-accent);
  display: inline-block;
}
.eyebrow--light { color: var(--color-bg); opacity: 0.85; }
.accent { color: var(--color-accent); }

/* Buttons
   Hover fills the button like water: the fill rises from below with a soft,
   rippling surface — a sine wave that keeps drifting sideways as it climbs —
   and settles as it reaches the top (the wave crest ends just above the edge,
   so the full state is flat). ~1s in on a steady rise with a gentle landing;
   it drains in 0.5s, so a button never hangs in its hover state after the
   pointer has left. The label rolls with it: the word slides up and out as
   its copy rises into place. The fill is a layer under the label (isolation
   keeps its z-index:-1 above the button's own ground), clipped by the padding
   box so the outline stays visible around it. */
.btn {
  --btn-ease: cubic-bezier(0.45, 0.05, 0.35, 1);
  --btn-in: 1s;                /* hover on: the fill */
  --btn-out: .5s;              /* hover off: it drains */
  --btn-fill: var(--color-bg);
  --btn-wave: 9px;             /* height of the water's surface */
  position: relative; isolation: isolate; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-body); font-size: 14px; font-weight: 500;
  letter-spacing: 0.02em;
  padding: 14px 30px; border: 1px solid transparent; border-radius: 7px; cursor: pointer;
  transition: transform var(--btn-out) var(--btn-ease), color var(--btn-out) var(--btn-ease), border-color var(--btn-out) var(--btn-ease);
}
/* The water: a block as tall as the button plus its surface. Two mask
   layers shape it — a repeating wave strip for the surface, solid below it.
   Sliding the wave layer sideways is the ripple. */
.btn::before {
  --wave: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 10' preserveAspectRatio='none'%3E%3Cpath d='M0 5 Q15 0 30 5 T60 5 V10 H0 Z'/%3E%3C/svg%3E");
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; z-index: -1;
  height: calc(100% + var(--btn-wave));
  background: var(--btn-fill);
  -webkit-mask-image: var(--wave), linear-gradient(#000, #000);
          mask-image: var(--wave), linear-gradient(#000, #000);
  -webkit-mask-size: 60px var(--btn-wave), 100% calc(100% - var(--btn-wave) + 1px);
          mask-size: 60px var(--btn-wave), 100% calc(100% - var(--btn-wave) + 1px);
  -webkit-mask-position: 0 0, 0 100%;
          mask-position: 0 0, 0 100%;
  -webkit-mask-repeat: repeat-x, no-repeat;
          mask-repeat: repeat-x, no-repeat;
  transform: translateY(100%);
  transition: transform var(--btn-out) var(--btn-ease);
  animation: btn-ripple 1.1s linear infinite paused;
}
@keyframes btn-ripple {
  from { -webkit-mask-position: 0 0, 0 100%;     mask-position: 0 0, 0 100%; }
  to   { -webkit-mask-position: -60px 0, 0 100%; mask-position: -60px 0, 0 100%; }
}
/* the label is two stacked copies in a one-line window */
.btn__label { display: block; position: relative; overflow: hidden; line-height: 1.3; text-align: center; }
.btn__t { display: block; transition: transform calc(var(--btn-out) * .9) var(--btn-ease); }
.btn__t + .btn__t { position: absolute; left: 0; right: 0; top: 100%; }

.btn--filled { background: var(--color-accent); color: var(--color-bg); --btn-fill: var(--color-bg); }
.btn--ghost { background: transparent; color: var(--color-dark); border-color: var(--color-accent); --btn-fill: var(--color-accent); }
.btn--ghost-light { background: var(--color-bg); color: var(--color-accent); --btn-fill: var(--color-dark); }

/* Only where a pointer can actually hover: on a touchscreen a tap would
   leave the button stuck mid-state. Keyboard focus gets the same motion. */
@media (hover: hover) {
  .btn:hover, .btn:hover::before { transition-duration: var(--btn-in); }
  .btn:hover .btn__t { transition-duration: calc(var(--btn-in) * .9); }
  .btn:hover { transform: translateY(-2px); }
  .btn:hover::before { transform: translateY(0); animation-play-state: running; }
  .btn:hover .btn__t { transform: translateY(-100%); }
  /* filled inverts to cream, with orange type and outline, so it still
     reads as a button on the cream page and on the nav's dark glass */
  .btn--filled:hover { color: var(--color-accent); border-color: var(--color-accent); }
  .btn--ghost:hover { color: var(--color-bg); }
  .btn--ghost-light:hover { color: var(--color-bg); }
}
/* keyboard focus, and a tap on a touchscreen (.is-pressed, from js/press.js,
   which holds the page change for the fill to be seen — so a tap fills at
   double speed) */
.btn.is-pressed { --btn-in: .5s; }
.btn:focus-visible, .btn:focus-visible::before,
.btn.is-pressed, .btn.is-pressed::before { transition-duration: var(--btn-in); }
.btn:focus-visible .btn__t, .btn.is-pressed .btn__t { transition-duration: calc(var(--btn-in) * .9); }
.btn:focus-visible, .btn.is-pressed { transform: translateY(-2px); }
.btn:focus-visible::before, .btn.is-pressed::before { transform: translateY(0); animation-play-state: running; }
.btn:focus-visible .btn__t, .btn.is-pressed .btn__t { transform: translateY(-100%); }
.btn--filled:focus-visible, .btn--filled.is-pressed { color: var(--color-accent); border-color: var(--color-accent); }
.btn--ghost:focus-visible, .btn--ghost-light:focus-visible,
.btn--ghost.is-pressed, .btn--ghost-light.is-pressed { color: var(--color-bg); }

/* reduced motion: the colours still change, nothing travels or ripples */
@media (prefers-reduced-motion: reduce) {
  .btn { --btn-in: .01s; --btn-out: .01s; }
  .btn::before, .btn:hover::before, .btn:focus-visible::before { animation: none; }
  .btn:hover, .btn:focus-visible, .btn.is-pressed { transform: none; }
  .btn:hover .btn__t, .btn:focus-visible .btn__t, .btn.is-pressed .btn__t { transform: none; }
  .btn.is-pressed { --btn-in: .01s; }
}

/* ============================================================
   "GROW MY BRAND" FORM (js/lead.js)
   A cream sheet on a dark ground, the palette the rest of the site uses. On
   a phone it takes the width and scrolls inside itself, so a keyboard coming
   up never pushes the send button off the screen.
   ============================================================ */
.lead {
  /* the site's reset zeroes every margin, which takes out the centring a
     modal dialog gets from the browser */
  margin: auto;
  width: min(560px, calc(100% - 32px));
  max-height: min(86vh, 760px);
  padding: 0; border: none; border-radius: 14px;
  background: var(--color-bg); color: var(--color-dark);
  overflow: hidden;
}
.lead::backdrop { background: rgba(17, 17, 17, 0.62); }
.lead__panel {
  position: relative;
  display: block;
  max-height: inherit; overflow-y: auto;
  padding: clamp(26px, 4vw, 44px) clamp(20px, 3.4vw, 40px) clamp(22px, 3vw, 36px);
}
.lead__close {
  position: absolute; top: 12px; right: 12px;
  width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center;
  padding: 0; border: 1px solid rgba(17, 17, 17, 0.25);
  background: none; color: var(--color-dark);
  font-family: var(--font-body); font-size: 16px; line-height: 1; cursor: pointer;
  transition: background .25s ease;
}
@media (hover: hover) { .lead__close:hover { background: rgba(17, 17, 17, 0.08); } }
.lead__eyebrow { margin-bottom: 10px; }
.lead__title {
  font-family: var(--font-display); font-weight: 800; text-transform: uppercase;
  font-size: clamp(30px, 5vw, 46px); line-height: .9; letter-spacing: -0.03em;
  color: var(--color-dark);
}
.lead__sub {
  margin-top: 10px; max-width: 44ch;
  font-family: var(--font-body); font-size: clamp(14px, 1.1vw, 16px);
  line-height: 1.5; color: var(--color-dim);
}
.lead__grid {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(14px, 2vw, 22px);
  margin-top: clamp(20px, 3vw, 30px);
}
.lead__field { display: flex; flex-direction: column; gap: 6px; }
.lead__field--full { grid-column: 1 / -1; }
.lead__label {
  font-family: var(--font-body); font-size: 12px; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--color-dim);
}
.lead__label em { font-style: normal; opacity: .7; letter-spacing: .04em; }
.lead__input {
  width: 100%; padding: 10px 2px;
  font-family: var(--font-body); font-size: 16px;   /* 16px: iOS zooms anything smaller */
  color: var(--color-dark); background: none;
  border: none; border-bottom: 1px solid rgba(17, 17, 17, 0.25);
  border-radius: 0;                                  /* iOS rounds inputs by default */
  transition: border-color .25s ease;
}
.lead__input::placeholder { color: rgba(17, 17, 17, 0.35); }
.lead__input:focus { outline: none; border-bottom-color: var(--color-accent); }
.lead__input.is-wrong { border-bottom-color: var(--color-accent); }
.lead__error {
  font-family: var(--font-body); font-size: 12.5px; line-height: 1.4;
  color: var(--color-accent);
}
textarea.lead__input { resize: vertical; min-height: 76px; }
.lead__note {
  margin-top: 16px;
  font-family: var(--font-body); font-size: 14px; line-height: 1.5;
  color: var(--color-accent);
}
.lead__actions { display: flex; justify-content: flex-end; margin-top: clamp(20px, 3vw, 28px); }
/* the sent state hides the form parts; flex/grid rules would otherwise win */
.lead [hidden] { display: none !important; }
.lead__thanks:focus { outline: none; }
.lead__send:disabled { opacity: .6; pointer-events: none; }

@media (max-width: 560px) {
  .lead__grid { grid-template-columns: 1fr; }
  .lead__actions { justify-content: stretch; }
  .lead__send, .lead__done { width: 100%; }
}

/* ============================================================
   PRELOADER
   ============================================================ */
.preloader {
  position: fixed; inset: 0; z-index: 9999;
  background: var(--color-accent);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.preloader__logo {
  position: relative; z-index: 2;
  width: min(46vw, 560px);
  transform: scale(.86);
  opacity: 0;
}
.preloader__logo img { width: 100%; height: auto; }
/* The pop-in runs in CSS so the wordmark paints as soon as the page does,
   without waiting for GSAP to arrive from the CDN; js/main.js picks the
   sequence up from there. */
.preloader__logo { animation: preloaderPop .7s cubic-bezier(.215, .61, .355, 1) both; }
@keyframes preloaderPop {
  from { opacity: 0; transform: scale(.86); }
  to   { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) { .preloader__logo { animation-duration: .01s; } }
.preloader__blinds {
  position: absolute; inset: 0; z-index: 3; display: flex;
  pointer-events: none;
}
.preloader__blinds span {
  flex: 1; height: 100%; background: var(--color-bg);
  transform: scaleY(0); transform-origin: top;
}
.preloader.is-done { pointer-events: none; }

/* ============================================================
   NAVBAR
   ============================================================ */
/* Floating, rounded, frosted-glass pill — inset from the edges (not full width) */
.nav {
  position: fixed; top: 0; left: 50%;
  width: min(calc(100% - 2 * clamp(16px, 2.5vw, 32px)), var(--maxw));
  height: var(--nav-h);
  z-index: 1000;
  background: rgba(20, 18, 16, 0.34);
  backdrop-filter: blur(18px) saturate(1.3); -webkit-backdrop-filter: blur(18px) saturate(1.3);
  border: 1px solid rgba(255, 255, 240, 0.14);
  border-radius: 16px;
  overflow: hidden;
  opacity: 0;
  transform: translate(-50%, -135%);
  transition: transform .45s cubic-bezier(.16,1,.3,1), opacity .45s ease;
  will-change: transform;
}
/* Once revealed, the nav stays in place — no auto-hide on scroll. */
.nav.is-visible { transform: translate(-50%, 0); opacity: 1; }

/* On desktop the bar was running to within 32px of both edges (1376px of a
   1440px viewport), which read as a full-width bar rather than the floating
   pill it is drawn as. Narrower here only — the gutter-based width above is
   already right on phones and tablets, so they are left alone. Change the
   860px to taste; nothing else depends on it. The content (links + CTA
   + 36px padding a side) needs about 550px, so that is the floor before
   anything starts to crowd. */
@media (min-width: 900px) {
  .nav { width: min(calc(100% - 2 * clamp(16px, 2.5vw, 32px)), 860px); }
}
/* grainy texture over the frosted glass */
.nav::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  border-radius: inherit; opacity: .14; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
.nav__inner {
  position: relative; z-index: 2;
  height: 100%; padding: 0 clamp(20px, 2.6vw, 36px);
  /* Centred, now that the brand mark that used to hold the left side is gone
     and the links have the whole pill to sit in.

     The gap has to be stated explicitly. nav__menu is display:contents, so the
     links and the CTA are siblings here, and it was the old space-between that
     held them apart; without it the button closes right up against the last
     link and sits on top of "Contact". */
  display: flex; align-items: center; justify-content: center;
  gap: clamp(22px, 2.8vw, 40px);
}
.nav__links { display: flex; gap: 36px; }
.nav__links a {
  font-family: var(--font-body); font-size: 14px; color: var(--color-bg);
  position: relative;
}
/* The same roll as the buttons: the word slides up and out as an orange
   copy rises into place, and an underline draws in beneath it. On a phone or
   tablet the tap plays it (js/press.js adds .is-pressed and holds the page
   change until it has been seen). */
/* inline-block so it — and the underline — is as wide as the word, even in
   the phone menu where the link itself spans the whole row */
.nav__label { display: inline-block; vertical-align: top; position: relative; overflow: hidden; line-height: 1.3; }
.nav__t { display: block; transition: transform .5s cubic-bezier(0.45, 0.05, 0.35, 1); }
.nav__t + .nav__t { position: absolute; left: 0; right: 0; top: 100%; color: var(--color-accent); }
.nav__label::after {
  content: ''; position: absolute; left: 0; bottom: 0; width: 0; height: 1px;
  background: var(--color-accent); transition: width .45s cubic-bezier(0.45, 0.05, 0.35, 1);
}
@media (hover: hover) {
  .nav__links a:hover .nav__t { transform: translateY(-100%); }
  .nav__links a:hover .nav__label::after { width: 100%; }
}
.nav__links a:focus-visible .nav__t, .nav__links a.is-pressed .nav__t { transform: translateY(-100%); }
.nav__links a:focus-visible .nav__label::after, .nav__links a.is-pressed .nav__label::after { width: 100%; }
@media (prefers-reduced-motion: reduce) {
  .nav__t, .nav__label::after { transition-duration: .01s; }
}
.nav__cta { padding: 10px 22px; }

/* ---- Hamburger button (hidden on desktop) ---- */
.nav__burger {
  display: none;
  flex-direction: column; justify-content: center; align-items: center;
  gap: 5px; width: 42px; height: 42px;
  background: none; border: none; cursor: pointer;
  padding: 0; z-index: 2; flex-shrink: 0;
}
.nav__burger span {
  display: block; width: 22px; height: 2px;
  background: var(--color-bg);
  border-radius: 2px;
  transition: transform 0.35s cubic-bezier(.16,1,.3,1),
              opacity 0.2s ease,
              width 0.3s ease;
  transform-origin: center;
}
/* X animation when open */
.nav__burger.is-open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav__burger.is-open span:nth-child(2) { opacity: 0; width: 0; }
.nav__burger.is-open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ---- Desktop nav__menu: transparent passthrough ---- */
.nav__menu { display: contents; }

/* The brand mark is for phones only; the desktop pill is the links alone */
.nav__brand { display: none; }

/* ============================================================
   HERO
   ============================================================ */
.hero {
  position: relative; height: 100vh; width: 100%;
  background: var(--color-dark); overflow: hidden;
}
.hero__media { position: absolute; inset: 0; transform: scale(1.18); opacity: 0; }
.hero__video, .hero__img, .hero__poster { width: 100%; height: 100%; object-fit: cover; }

/* The clip stays fully visible at all times. It must: Safari only autoplays
   a video it considers visible on screen, so hiding the clip until it plays
   deadlocks it — invisible, therefore never started, therefore never
   visible — and it would only begin on a tap.

   So the still is layered ON TOP of the clip instead, and fades away once
   playback is under way. That covers Safari's start-playback button (it is
   painted by the video, which is underneath) without ever making the video
   itself invisible to the autoplay policy. */
.hero__video { position: absolute; inset: 0; }
.hero__poster {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  transition: opacity 0.45s ease;
}
.hero__media.is-playing .hero__poster { opacity: 0; }

/* Without scripting nothing adds .is-playing, so the still would never lift.
   The browser autoplays the clip natively regardless, so drop the still and
   let the video show. */
@media (scripting: none) {
  .hero__poster { opacity: 0; }
}

/* The hero plays itself and must never offer a transport control. No
   `controls` attribute is set, but a tap can still pause a video on some
   platforms, and Safari paints its own start-playback button over a clip
   until it actually begins — so the element is made untouchable and every
   native control surface is hidden. js/main.js resumes anything that still
   manages to pause. The scroll cue is a sibling, not a child, so it stays
   clickable. */
.hero__video { pointer-events: none; }

/* Each pseudo-element gets its OWN rule on purpose. These are vendor
   pseudo-elements that differ between engines: ...-start-playback-button is
   Safari's, ...-overlay-play-button is Blink's, and neither engine parses the
   other's. A selector a browser cannot parse invalidates the WHOLE rule it
   sits in, so grouping them with commas meant Safari threw all of them away
   and kept showing its play button. Split, an unknown selector costs only
   its own line. Do not merge these back together. */
.hero__video::-webkit-media-controls { display: none !important; -webkit-appearance: none; }
.hero__video::-webkit-media-controls-panel { display: none !important; -webkit-appearance: none; }
.hero__video::-webkit-media-controls-start-playback-button { display: none !important; -webkit-appearance: none; opacity: 0; }
.hero__video::-webkit-media-controls-play-button { display: none !important; -webkit-appearance: none; }
.hero__video::-webkit-media-controls-overlay-play-button { display: none !important; -webkit-appearance: none; }
.hero__video::-webkit-media-controls-container { display: none !important; -webkit-appearance: none; }
.hero__scroll {
  position: absolute; left: 50%; bottom: clamp(20px, 4vh, 44px); transform: translateX(-50%);
  z-index: 3; display: flex; flex-direction: column; align-items: center; gap: 8px;
  color: var(--color-bg); font-family: var(--font-body);
  font-size: 12px; font-weight: 500; letter-spacing: 0.22em; text-transform: uppercase;
}
.hero__scroll-arrow { font-size: 18px; line-height: 1; animation: hero-bounce 1.6s ease-in-out infinite; }
@keyframes hero-bounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(7px); } }

/* ============================================================
   SUB-HERO
   ============================================================ */
.subhero { padding: var(--space-3xl) 0 var(--space-2xl); }
.subhero__grid {
  display: grid; grid-template-columns: 1fr; gap: var(--space-xl);
  margin-bottom: var(--space-xl);
}
.subhero__title {
  font-size: clamp(48px, 8vw, 120px); line-height: 0.82;
}
/* body sits below the title, aligned to the right */
.subhero__lead { justify-self: end; }
.subhero__lead p {
  font-size: 20px; line-height: 1.5; color: var(--color-dim); max-width: 46ch;
}
/* Logo marquee — a full-bleed accent band of client marks. Several of the
   files carry their own background (Relax and Orient are white, RAB and Rini
   dark), which is how the Figma shows them, so they are left as they come. */
.logos {
  width: 100%;
  background: var(--color-accent);
  overflow: hidden;
  padding: clamp(26px, 3.6vw, 54px) 0;
}
.logos__track { display: flex; width: max-content; will-change: transform; }
.logos__set { display: flex; align-items: center; }
.logos__set img {
  flex: 0 0 auto;
  height: clamp(44px, 5.2vw, 76px);
  width: auto;
  object-fit: contain;
  margin: 0 clamp(24px, 3.2vw, 60px);
}
.subhero__actions {
  display: flex; gap: var(--space-md); justify-content: center;
  padding: clamp(32px, 4.4vw, 64px) var(--pad) 0;
}

/* ============================================================
   SERVICES — WHAT KAZI DOES (interactive columns + slider)
   Full-bleed cream panel. Hovering a column expands that service:
   the SAME image and name morph larger (no crossfade), while the
   other columns collapse into accent number rails.
   ============================================================ */
.ks {
  /* Fast out of the gate, then a long slow settle — the panel should read as
     arriving rather than winding up. The old curve (0.66, 0, 0.15, 1) started
     slow, which on top of a 1.15s run made the whole thing feel heavy.
     Fast then slow, but it has to start from rest. Every easeOut curve
     (expo, quint, quart) sets its first control point at y=1, which means
     the panel leaves the gate at full speed — mathematically fast-then-slow,
     and read by the eye as a snap. This one pulls that point down to 0.6, so
     velocity climbs over the first few frames instead of arriving at its
     peak: about a third of the travel in the first fifth of the run, then a
     long tail settling to a dead stop. The initial speed is roughly half
     that of the expo-out it replaces, which is the whole difference between
     "it snapped open" and "it moved".

     Duration and responsiveness are separate knobs. How quickly the panel
     answers the cursor is the 45ms intent delay in main.js, not this; 0.55s
     here only cut the tail short without making anything feel quicker.
     Every other timing in this section is scaled to this one — changing it
     alone leaves the numbers and rails trailing the panel. */
  --ks-ease: cubic-bezier(0.2, 0.6, 0.2, 1);
  --ks-dur: 0.8s;
  background: var(--color-bg);
}
.ks__frame {
  display: flex; flex-direction: column;
  height: 100svh; min-height: 620px;
}
.ks__head {
  text-align: center;
  border-bottom: 1px solid var(--color-accent);
  padding: clamp(18px, 2.4vw, 34px) 16px;
}
.ks__head h2 {
  font-family: var(--font-display); font-weight: 800; text-transform: uppercase;
  letter-spacing: -0.02em; line-height: 1;
  font-size: clamp(26px, 3.2vw, 52px); color: var(--color-accent);
}
.ks__viewport { position: relative; flex: 1 1 auto; min-height: 0; overflow: hidden; }
.ks__track { display: flex; width: 300%; height: 100%; will-change: transform; }
.ks__slide { width: 33.3333%; height: 100%; display: flex; min-width: 0; }

/* ---- columns ---- */
.ks__col {
  position: relative; overflow: hidden; min-width: 0;
  flex: 1 1 0%;
  /* The expansion animates flex-basis, widths and heights, so every frame
     costs a layout. Containment scopes that work to the column instead of
     letting it invalidate the rest of the page — the single biggest win for
     how smooth this reads on a phone. */
  contain: layout paint;
  transition: flex-grow var(--ks-dur) var(--ks-ease),
              flex-basis var(--ks-dur) var(--ks-ease),
              background-color var(--ks-dur) var(--ks-ease);
  background: var(--color-bg);
}
.ks__col + .ks__col { border-left: 1px solid var(--color-accent); }
/* a mouse opens a service on hover, a touch screen on a press, so the
   column has to look interactive everywhere — not just on the phone */
.ks__col { cursor: pointer; -webkit-tap-highlight-color: transparent; }
/* A collapsing column holds its cream until it is nearly a rail. The accent
   is the rail's colour, not a state the card passes through: on the shared
   ease-out curve the blend arrived while the column was still full width, so
   the card you were just reading spent a third of a second as pale salmon
   with its own image and type washed out underneath. This ease-IN keeps the
   colour flat for most of the run and lands it as the width does. */
.ks__slide.has-active .ks__col {
  flex-grow: 0; flex-basis: clamp(44px, 4.2vw, 68px); background: var(--color-accent);
  transition: flex-grow var(--ks-dur) var(--ks-ease),
              flex-basis var(--ks-dur) var(--ks-ease),
              background-color var(--ks-dur) cubic-bezier(0.7, 0, 0.9, 0.3);
}
/* The opening card takes its cream back outright, with no blend at all.
   Running the colour over the full duration put a salmon tint across the
   whole panel, image included, for most of the expansion; even a fast blend
   left a wash sweeping over it. There is nothing to ease here — at the moment
   this column opens it is still a 60px rail, so the switch happens in a strip
   too narrow to register as a flash, and the card is cream from the first
   frame it has any width. Collapsing columns keep the slow colour change
   above: they are turning INTO the accent rails, and there the blend is the
   point. */
.ks__slide.has-active .ks__col.is-active {
  flex-grow: 1; flex-basis: 60%; background: var(--color-bg);
  transition: flex-grow var(--ks-dur) var(--ks-ease),
              flex-basis var(--ks-dur) var(--ks-ease),
              background-color 0s;
}

/* ---- single morphing layer ---- */
.ks__inner {
  position: absolute; inset: 0 auto 0 0;
  width: var(--ks-colw, 100%);
  display: flex; flex-direction: column;
  padding: clamp(16px, 1.8vw, 30px) clamp(18px, 2vw, 34px);
  /* No fade IN, at all. Coming from another open service this content starts
     at opacity 0 (it was a rail), and any ramp at all — even a tenth of a
     second — is a visible wash across the image while the column is still
     narrow. The widening IS the reveal: the content is already solid inside
     the column and gets uncovered, the way a curtain opens, and the overflow
     clip on the column is what hides the part not yet reached. The fade OUT,
     on the rule below, keeps its timing — a column collapsing to a rail has
     somewhere to go, so it can afford to be gentle. */
  transition: width var(--ks-dur) var(--ks-ease), opacity 0s;
}
.ks__col.is-active .ks__inner { width: var(--ks-expw, 100%); }
/* The closing card holds its content solid and loses it late, on the same
   ease-IN as the background above. A plain fade — even a fast one — emptied
   the card while it was still full width, so the service you had just been
   reading hung there as a ghost of itself: image and type at a fraction of
   their opacity across the whole panel. That ghost is the fade people see,
   and it is on the card leaving, not the one arriving. Held this way the
   content stays whole while the column narrows, and only goes as the column
   becomes a rail with no room left to show it. The hold is a delay rather
   than a slow curve: a curve that trails to zero only at the very end left a
   ghost of the closing title legible inside the finished rail. By 280ms the
   column is down to about 250px, narrow enough that clearing it there is not
   the full-width ghost, and it is gone well before the rail settles. */
.ks__slide.has-active .ks__col:not(.is-active) .ks__inner {
  opacity: 0;
  transition: width var(--ks-dur) var(--ks-ease),
              opacity 0.3s ease 0.28s;
}

/* image wrapper: contains both static and hovered images stacked */
.ks__imgwrap {
  position: relative;
  width: 82%; height: 36%;
  overflow: hidden;
  /* no will-change here: width and height cannot be composited, so the hint
     bought nothing and cost a layer */
  transition: width var(--ks-dur) var(--ks-ease),
              height var(--ks-dur) var(--ks-ease);
}
.ks__col.is-active .ks__imgwrap { width: 100%; height: 56%; }
/* middle column keeps its image wrapper low & centred when idle */
.ks__col--b .ks__imgwrap { align-self: center; width: 62%; height: 44%; order: 3; margin-top: auto; }
.ks__col--b.is-active .ks__imgwrap { width: 100%; height: 54%; }

/* A screenshot has to be shown whole — cover would crop away the navigation
   and the wordmark, which is most of what makes it read as a web page. The
   frame stays exactly the size and position it is for every other service;
   only what happens inside it changes, so the row still lines up. The image
   is limited by the frame's height and centred, and because the page it shows
   sits on its own near-white ground it meets the cream panel without a seam. */
.ks__col--whole .ks__img { object-fit: contain; object-position: center; }
/* Contained inside the standard frame, the page is limited by that frame's
   height and ends up a small picture adrift in a wide panel. Opened, this one
   card takes the height back instead: 73% of the content box, measured
   against what is actually spare once the name line and the copy have taken
   theirs — about 180px at 1440, and the copy's own cap is allowed for, so
   nothing is pushed out at any width. */
.ks__col--whole.is-active .ks__imgwrap { height: 73%; }
/* At rest the frame is narrow enough that the page is limited by its width,
   not its height — so width is the only lever that changes anything here, and
   the 44% height it inherits already leaves the picture room to grow into.
   72% rather than the 62% the other middle cards use: a screenshot has to
   carry a whole layout at this size, where a photograph only has to carry a
   subject, so it can stand to be a little larger before it reads as too much. */
.ks__col--whole .ks__imgwrap { width: 72%; }


/* both images fill the wrapper — Figma exports are the source of truth */
.ks__img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  /* No dissolve between the two stills. They are the same frame at different
     crops, so blending them is not a crossfade between two pictures — it is
     the same picture briefly ghosted over itself, which is exactly the
     softness that kept reading as a fade. The swap is instant; the static
     underneath stays fully opaque, so nothing dips and the cream panel never
     washes through. */
  transition: none;
}
/* static image visible by default; hovered image hidden */
.ks__img--hover { opacity: 0; }
/* Brand Identity carries a clip instead of the two stills; it fills the same
   wrapper and re-crops as the column expands, exactly as the images do. */
.ks__vid { opacity: 1; }
/* on active: the hovered image fades in ON TOP of the static one, which stays
   fully opaque — a true crossfade would dip both below 1 and let the cream
   panel wash through as a whitish flash mid-transition */
.ks__col.is-active .ks__img--hover  { opacity: 1; }

/* kicker + description: revealed by the expansion */
.ks__info {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: clamp(20px, 4vw, 80px); overflow: hidden;
  max-height: 0; opacity: 0;
  margin-top: 0;
  transition: max-height var(--ks-dur) var(--ks-ease), opacity 0.34s ease,
              margin-top var(--ks-dur) var(--ks-ease);
}
/* 130px, not the 180px this used to carry. max-height caps the box, so the
   reveal is visually over the moment it passes the real content height —
   measured at 94px at its tallest, 2560px down to 1024px wide. Against 180px
   the text finished in the first tenth of a second and then sat still while
   the panel went on expanding, which is its own kind of abrupt regardless of
   the curve. 130px keeps a line and a half of headroom for a fallback font
   while letting the reveal track most of the panel's own movement. */
.ks__col.is-active .ks__info {
  max-height: 130px; opacity: 1; margin-top: clamp(12px, 1.4vw, 22px);
  transition: max-height var(--ks-dur) var(--ks-ease), opacity 0.18s ease 0.05s,
              margin-top var(--ks-dur) var(--ks-ease);
}
.ks__kicker {
  font-family: var(--font-display); font-weight: 800; text-transform: uppercase;
  font-size: clamp(15px, 1.5vw, 24px); letter-spacing: -0.01em;
  color: var(--color-accent); white-space: nowrap;
}
.ks__desc {
  max-width: 30ch; text-align: right;
  font-size: clamp(13px, 1.15vw, 18px); line-height: 1.3;
  color: var(--color-dark);
}

/* name line: the type itself scales up with the column */
.ks__nameline {
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: 24px; margin-top: auto;
}
.ks__name {
  font-family: var(--font-display); font-weight: 800; text-transform: uppercase;
  letter-spacing: -0.02em; line-height: 0.92;
  font-size: clamp(22px, 2.6vw, 44px); color: var(--color-accent);
  transition: font-size var(--ks-dur) var(--ks-ease);
}
.ks__col.is-active .ks__name { font-size: clamp(30px, 4vw, 66px); }
.ks__num {
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(28px, 3.4vw, 56px); line-height: 0.9; color: var(--color-accent);
  opacity: 0; transform: translateY(8px);
  transition: opacity 0.40s ease, transform 0.50s var(--ks-ease);
}
.ks__col.is-active .ks__num { opacity: 1; transform: translateY(0); transition-delay: 0.18s; }

/* middle column: name row on top (as in the Figma) */
.ks__col--b .ks__nameline { order: 1; margin-top: 0; align-items: flex-start; }
.ks__col--b .ks__info { order: 2; }
.ks__col--b .ks__imgwrap { order: 3; }

/* ---- collapsed rail ---- */
.ks__rail {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  /* gone outright when this column opens: the card is cream from that same
     frame, and a white number lingering over it is its own little fade */
  opacity: 0; transition: opacity 0s;
  pointer-events: none;
}
/* arriving is the other way round — the number fades up once the column it
   belongs to has actually become a rail */
.ks__slide.has-active .ks__col:not(.is-active) .ks__rail {
  opacity: 1; transition: opacity 0.3s ease 0.22s;
}
.ks__rail span {
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(22px, 2.6vw, 44px); color: var(--color-bg); line-height: 1;
}

/* ---- footer / slider ---- */
.ks__foot {
  display: flex; align-items: center; gap: clamp(16px, 2vw, 32px);
  border-top: 1px solid var(--color-accent);
  padding: clamp(12px, 1.4vw, 20px) clamp(16px, 2vw, 32px);
}
.ks__hint {
  font-family: var(--font-body); font-weight: 500; font-size: 12px;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--color-accent);
  white-space: nowrap;
}
.ks__segs { display: flex; gap: 8px; flex: 1 1 auto; max-width: 340px; }
.ks__segs button {
  flex: 1 1 0%; height: 4px; border: none; padding: 0; cursor: pointer;
  background: rgba(215, 78, 49, 0.25); transition: background 0.3s ease;
}
.ks__segs button.is-active { background: var(--color-accent); }
.ks__arrow {
  margin-left: auto; background: none; border: none; cursor: pointer;
  font-size: clamp(28px, 2.8vw, 44px); line-height: 1; color: var(--color-accent);
  transition: transform 0.25s ease;
}
.ks__arrow:hover { transform: translateX(5px); }

/* ============================================================
   PORTFOLIO
   ============================================================ */
.work { padding: var(--space-3xl) 0; background: var(--color-bg); }
.work__head { margin-bottom: var(--space-xl); }
.work__head .eyebrow { margin-bottom: var(--space-md); }
.work__title { font-size: clamp(48px, 8vw, 110px); }
.work__cta { display: flex; justify-content: center; margin-top: var(--space-2xl); }
/* Masonry grid — fixed asymmetric layout matching the Figma. A 20-row track
   grid lets the left and right columns split at different points (the masonry
   look) while both still bottom-align. aspect-ratio gives the grid its height
   so the 1fr rows have something to divide. */
.work__grid {
  display: grid;
  grid-template-columns: 27fr 27fr 44fr;
  grid-template-rows: repeat(20, 1fr);
  gap: clamp(12px, 1.2vw, 20px);
  aspect-ratio: 1090 / 908;
}
.card--rab    { grid-column: 1 / 3; grid-row: 1 / 11; }
.card--rs     { grid-column: 1 / 2; grid-row: 11 / 21; }
.card--eye    { grid-column: 2 / 3; grid-row: 11 / 21; }
.card--orient { grid-column: 3 / 4; grid-row: 1 / 7; }
.card--jorhat { grid-column: 3 / 4; grid-row: 7 / 21; }

.card {
  position: relative; overflow: hidden; height: 100%;
  background: var(--color-dark); cursor: pointer;
}
.card__video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transition: transform .6s ease;
}
/* Hover wash: blurs + dims the video behind the text for legibility. Hidden
   by default so the resting card is just the playing clip. */
.card::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: rgba(17,17,17,.42);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  opacity: 0; transition: opacity .45s ease;
}
.card__meta {
  position: absolute; top: 0; left: 0; z-index: 3;
  padding: clamp(16px, 1.8vw, 30px);
  display: flex; flex-direction: column; gap: 6px;
}
.card__name { font-size: clamp(24px, 2.9vw, 54px); line-height: .88; color: var(--color-bg); }
.card__tag {
  font-family: var(--font-body); font-size: clamp(11px, .9vw, 14px);
  letter-spacing: .03em; text-transform: none; color: rgba(255,255,240,.9);
}
.card__line {
  position: absolute; bottom: 0; right: 0; z-index: 3;
  max-width: 16ch; padding: clamp(16px, 1.8vw, 30px); text-align: right;
  font-family: var(--font-display); font-weight: 700; text-transform: uppercase;
  font-size: clamp(17px, 1.85vw, 30px); line-height: .98; letter-spacing: -.02em;
  color: var(--color-bg);
}
/* arrows removed on all breakpoints */
.card__arrow { display: none; }

/* Text is hidden until the card is hovered */
.card__meta, .card__line, .card__arrow {
  opacity: 0; transition: opacity .4s ease, transform .3s ease;
}

/* The reveal belongs to a pointer on a screen wide enough for the collage.
   Below 1025px, or on a touch screen, there is no hover to trigger it and
   nothing stands in for one — the cards stay as their clips, nothing else. */
@media (min-width: 1025px) and (hover: hover) {
  .card:hover::after,
  .card:hover .card__meta,
  .card:hover .card__line,
  .card:hover .card__arrow { opacity: 1; }
  .card:hover .card__video { transform: scale(1.05); }
}

/* per-card text + arrow placement to match the Figma */
.card--rs   .card__line,
.card--eye  .card__line  { right: auto; left: 0; text-align: left; }
.card--rs   .card__arrow,
.card--eye  .card__arrow { left: auto; right: clamp(16px, 1.8vw, 30px); }

/* ============================================================
   LEMON REVEAL
   ============================================================ */
.lemon { background: var(--color-bg); padding: var(--space-3xl) 0 0; }
.lemon__text {
  font-size: clamp(40px, 6.5vw, 96px); line-height: 0.92; text-align: center;
  max-width: 16ch; margin: 0 auto;
}
.lemon__text .word { display: inline-block; opacity: .12; transition: opacity .2s ease; }
.lemon__text .accent.word { color: var(--color-accent); }
.lemon__strip {
  margin-top: var(--space-3xl); background: var(--color-accent); color: var(--color-bg);
  padding: var(--space-lg) 0; overflow: hidden;
}
.lemon__track { display: flex; width: max-content; animation: lemon-marquee 22s linear infinite; }
.lemon__seq {
  flex: 0 0 auto; white-space: nowrap;
  font-family: var(--font-display); font-weight: 800; text-transform: uppercase;
  font-size: clamp(22px, 3vw, 40px); letter-spacing: -0.02em;
}
.lemon__seq i { font-style: normal; opacity: .6; padding: 0 0.15em; }
@keyframes lemon-marquee { to { transform: translateX(-50%); } }

/* ============================================================
   TESTIMONIALS
   Ivory full-width section, centred heading. Wide content row:
   left image-card slider + a wider orange panel, both exactly the
   same height. Active card centred (full colour, 5px accent border);
   neighbours grayscale & peeking.
   ============================================================ */
.ts { background: var(--color-bg); color: var(--color-dark); padding: clamp(56px, 8vw, 120px) 0; overflow: hidden; }

.ts__head { text-align: center; max-width: 900px; margin: 0 auto clamp(56px, 8vw, 120px); padding: 0 var(--pad); }
.ts__title { font-size: clamp(44px, 6vw, 92px); color: var(--color-dark); }
.ts__sub {
  margin: clamp(12px, 1.4vw, 20px) auto 0; max-width: 60ch;
  color: var(--color-accent); font-size: clamp(15px, 1.2vw, 19px); font-weight: 500; letter-spacing: 0.01em;
}

.ts__grid {
  --ts-h: clamp(500px, 62vh, 600px);
  width: 100%; max-width: 1520px; margin: 0 auto; padding: 0 clamp(20px, 3vw, 44px);
  display: grid; grid-template-columns: min(480px, 40%) 1fr; column-gap: clamp(28px, 3vw, 44px);
  /* The row has to be pinned to the grid's height. Left implicit it was
     auto-sized to its content, so the panel's height: 100% grew with a long
     quote instead of holding at --ts-h — it ran past the bottom of the card
     strip and carried the arrows out of reach, and because the panel itself
     never overflowed, nothing measuring it could tell. */
  grid-template-rows: minmax(0, 1fr);
  height: var(--ts-h);
}
/* fitQuote()'s last resort: a quote that still will not fit at 16px on a very
   short window lets the row grow with it instead of clipping the controls */
.ts__grid.ts__grid--grow { height: auto; grid-template-rows: auto; }

/* ---- left: card slider ---- */
.ts__cards { position: relative; height: 100%; overflow: hidden; touch-action: pan-y; }
.ts__strip { position: absolute; left: 0; right: 0; top: 0; will-change: transform; }
.ts__card {
  position: absolute; left: 0; right: 0; overflow: hidden;
  border: 5px solid var(--color-accent); background: var(--color-bg);
  cursor: pointer;
}
.ts__card img {
  display: block; width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(1) brightness(0.7);
  transition: filter 0.6s ease, transform 0.6s ease;
}
.ts__card.is-active img { filter: grayscale(0) brightness(1); }
.ts__card:not(.is-active):hover img { filter: grayscale(1) brightness(0.85); }

/* ---- right: orange panel ---- */
.ts__panel {
  position: relative; height: 100%; background: var(--color-accent); color: var(--color-bg);
  /* flex-start, not center. The quote's auto bottom margin already puts the
     mark at the top and the foot at the bottom whenever there is room, so
     this changes nothing in the normal case. It matters when a quote runs
     long: centred, the overflow spilled off both ends at once and scrollHeight
     under-reported it, which is what fitQuote() in main.js measures. */
  display: flex; flex-direction: column; justify-content: flex-start;
  padding: clamp(30px, 3.4vw, 60px) clamp(32px, 3.8vw, 66px) clamp(26px, 2.8vw, 46px);
}
.ts__quote {
  font-family: Georgia, 'Times New Roman', serif; font-weight: 700;
  font-size: clamp(70px, 8vw, 130px); line-height: 0.7; height: 0.45em;
  color: var(--color-bg); user-select: none;
}
.ts__text {
  margin: clamp(18px, 2vw, 34px) 0 auto; max-width: 30ch;
  font-family: var(--font-body); font-weight: 400;
  font-size: clamp(20px, 1.9vw, 32px); line-height: 1.3; color: var(--color-bg);
}
.ts__text, .ts__name, .ts__role { will-change: transform, opacity; }
.ts__foot {
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: 24px; margin-top: clamp(28px, 3.4vw, 56px);
}
.ts__name {
  font-family: var(--font-display); font-weight: 800; text-transform: uppercase;
  letter-spacing: -0.02em; line-height: 0.95;
  font-size: clamp(24px, 2.4vw, 38px); color: var(--color-bg);
}
.ts__role { margin-top: 6px; font-size: clamp(12px, 1vw, 15px); opacity: 0.9; }
/* Slider controls. Three 3px dashes were the only thing offering to move the
   slider, which read as a progress indicator rather than a control — the
   arrows say plainly that there is a next one, and the counter says how many. */
.ts__nav {
  display: flex; align-items: center; gap: clamp(8px, 0.9vw, 14px);
  margin-top: clamp(14px, 1.6vw, 22px); flex-wrap: wrap;
}
.ts__arrow {
  flex: 0 0 auto; width: 38px; height: 38px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  background: none; color: var(--color-bg);
  border: 1px solid rgba(255, 255, 240, 0.45);
  transition: background 0.25s ease, color 0.25s ease, border-color 0.25s ease;
}
.ts__arrow svg { width: 18px; height: 18px; display: block; }
.ts__arrow:hover, .ts__arrow:focus-visible {
  background: var(--color-bg); color: var(--color-accent); border-color: var(--color-bg);
}
.ts__dots { display: flex; align-items: center; gap: 10px; margin-left: 4px; }
.ts__dots button {
  width: 26px; height: 3px; border: none; cursor: pointer;
  /* a 3px-tall target is unusable; the padding grows the hit area to 21px
     while background-clip keeps the dash itself thin. content-box is required
     — under the page's border-box default the padding would swallow the 3px
     height and leave nothing to paint. */
  box-sizing: content-box; padding: 9px 0; background-clip: content-box;
  background-color: rgba(255, 255, 240, 0.4);
  transition: background-color 0.3s ease, width 0.3s ease;
}
.ts__dots button:hover { background-color: rgba(255, 255, 240, 0.75); }
.ts__dots button.is-active { background-color: var(--color-bg); width: 34px; }
.ts__count {
  margin-left: auto; font-family: var(--font-body); font-weight: 500;
  font-size: clamp(11px, 0.85vw, 13px); letter-spacing: 0.1em;
  color: var(--color-bg); opacity: 0.75; white-space: nowrap;
}
/* the strip is draggable — say so */
.ts__cards { cursor: grab; }
.ts__cards:active { cursor: grabbing; }
.ts__stars { font-size: clamp(14px, 1.2vw, 19px); letter-spacing: 4px; color: var(--color-bg); white-space: nowrap; }

@media (max-width: 920px) {
  .ts__grid { grid-template-columns: 1fr; grid-template-rows: none; height: auto; padding: 0 clamp(16px, 4vw, 32px); }
  .ts__cards { height: clamp(300px, 48vh, 420px); }
  .ts__panel { min-height: clamp(380px, 55vh, 500px); }
  .ts__text { max-width: none; }
  .ts__quote { font-size: clamp(50px, 10vw, 90px); }
  /* the stars would squeeze the controls on a narrow panel */
  .ts__foot { flex-direction: column; align-items: flex-start; gap: 14px; }
}

/* ============================================================
   CTA + MARQUEE
   ============================================================ */
.cta { background: var(--color-bg); padding: var(--space-3xl) 0 0; text-align: center; overflow: hidden; }
.cta__title { font-size: clamp(44px, 7vw, 96px); color: var(--color-accent); }
.cta__actions { margin-top: var(--space-xl); display: flex; gap: var(--space-md); justify-content: center; }

.marquee { margin-top: var(--space-3xl); width: 100%; overflow: hidden; }
.marquee__track { display: flex; width: max-content; will-change: transform; }
/* bottom-align so the staggered heights sit flush on the footer */
.marquee__set { display: flex; align-items: flex-end; gap: 0; }
.marquee__set img {
  width: clamp(190px, 23vw, 340px); object-fit: cover; flex: 0 0 auto;
}
/* staggered card heights — matches the Figma */
.marquee__set img:nth-child(1) { height: clamp(200px, 26vw, 320px); }
.marquee__set img:nth-child(2) { height: clamp(255px, 34vw, 420px); }
.marquee__set img:nth-child(3) { height: clamp(235px, 31vw, 380px); }
.marquee__set img:nth-child(4) { height: clamp(190px, 24vw, 300px); }
.marquee__set img:nth-child(5) { height: clamp(270px, 36vw, 440px); }
.marquee__set img:nth-child(6) { height: clamp(210px, 27vw, 330px); }
.marquee__set img:nth-child(7) { height: clamp(250px, 33vw, 400px); }

/* ============================================================
   ROLLING LINK LABEL
   The nav links' roll, for links set on the orange ground — the footer, the
   contact page's email and phone, [LET'S TALK]. The copy that rises in, and
   the underline, keep the label's own colour — only the motion changes. A link or
   button opts in with data-roll, and wraps its label in .lnk with two
   .lnk__t copies (the second aria-hidden). A tap on a touchscreen plays it
   too (js/press.js).
   ============================================================ */
.lnk {
  --lnk-copy: currentColor;
  --lnk-ease: cubic-bezier(0.45, 0.05, 0.35, 1);
  display: inline-block; vertical-align: top; position: relative; overflow: hidden; line-height: 1.3;
}
.lnk__t { display: block; transition: transform .5s var(--lnk-ease); }
.lnk__t + .lnk__t { position: absolute; left: 0; right: 0; top: 100%; color: var(--lnk-copy); }
.lnk::after {
  content: ''; position: absolute; left: 0; bottom: 0; width: 0; height: 1px;
  background: var(--lnk-copy); transition: width .45s var(--lnk-ease);
}
/* an icon rolls the same way, without the underline */
.lnk--ico { line-height: 0; }
.lnk--ico::after { display: none; }
@media (hover: hover) {
  [data-roll]:hover .lnk__t { transform: translateY(-100%); }
  [data-roll]:hover .lnk::after { width: 100%; }
}
[data-roll]:focus-visible .lnk__t, [data-roll].is-pressed .lnk__t { transform: translateY(-100%); }
[data-roll]:focus-visible .lnk::after, [data-roll].is-pressed .lnk::after { width: 100%; }
@media (prefers-reduced-motion: reduce) {
  .lnk__t, .lnk::after { transition-duration: .01s; }
}

/* ============================================================
   FOOTER
   ============================================================ */
.footer { background: var(--color-accent); color: var(--color-bg); padding-top: var(--space-lg); overflow: hidden; }
.footer__bar {
  display: flex; align-items: center; justify-content: space-between;
  padding-top: var(--space-md); padding-bottom: var(--space-md);
  border-bottom: 1px solid rgba(255,255,240,.3);
}
.footer__nav { display: flex; gap: var(--space-lg); }
.footer__nav a, .footer__contact a { font-size: 14px; }
.footer__contact { display: flex; align-items: center; gap: var(--space-md); }
.footer__ig { display: inline-flex; }
.footer__wordmark { padding: var(--space-lg) var(--pad) 0; }
.footer__wordmark img { width: 100%; height: auto; }
.footer__fine {
  display: flex; justify-content: space-between; gap: var(--space-md);
  padding-top: var(--space-md); padding-bottom: var(--space-lg);
  font-size: 12px; opacity: .8;
}

/* ============================================================
   SCROLL REVEAL
   ============================================================ */
.reveal { opacity: 0; transform: translateY(22px); transition: opacity .5s ease, transform .55s cubic-bezier(.16,1,.3,1); }
.reveal.visible { opacity: 1; transform: translateY(0); }

/* ============================================================
   RESPONSIVE
   ============================================================ */

/* ---- 1024px ---- */
@media (max-width: 1024px) {
  :root { --pad: 40px; }
  .subhero__grid { grid-template-columns: 1fr; gap: var(--space-lg); }
}

/* ---- 768px: Mobile ---- */
@media (max-width: 768px) {
  :root { --pad: 20px; --nav-h: 60px; }

  /* --- Hamburger visible, desktop links hidden ---
     margin-left:auto pins it right on its own terms. Leaving that to the
     pill's space-between put it on the LEFT whenever it was the only item in
     flow (the logo missing, or a cached page without it). */
  .nav__burger { display: flex; margin-left: auto; }
  /* the dropdown is taken out of flow here, so the pill holds just the brand
     mark on the left and the burger on the right */
  .nav__inner { justify-content: space-between; }

  .nav__brand {
    display: block; flex: 0 0 auto;
    height: 34px; border-radius: 6px; overflow: hidden;
  }
  .nav__brand img { display: block; height: 100%; width: auto; object-fit: contain; }

  /* The pill must not clip the dropdown (it is the fixed/abs containing
     block because of its transform + backdrop-filter) */
  .nav { overflow: visible; }
  .nav::after { overflow: hidden; }

  /* Mobile menu: dropdown card under the pill */
  .nav__menu {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
    position: absolute;
    left: 0; right: 0; top: calc(100% + 10px);
    background: rgba(20, 18, 16, 0.96);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border: 1px solid rgba(255, 255, 240, 0.14);
    border-radius: 16px;
    padding: 18px;
    opacity: 0; visibility: hidden; transform: translateY(-10px);
    transition: opacity 0.3s ease, transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), visibility 0.3s;
  }
  .nav__menu.is-open { opacity: 1; visibility: visible; transform: translateY(0); }

  .nav__links { flex-direction: column; align-items: flex-start; gap: 4px; }
  .nav__links a {
    display: block; width: 100%;
    padding: 10px 6px;
    font-size: 17px; color: var(--color-bg);
  }
  .nav__cta { margin-top: 8px; width: 100%; text-align: center; padding: 14px 24px; font-size: 15px; }

  /* --- Hero --- */
  .hero { height: 100svh; }
  .hero__scroll { font-size: 10px; letter-spacing: 0.16em; }

  /* --- Sub-hero --- */
  .subhero { padding-top: var(--space-2xl); padding-bottom: var(--space-xl); }
  .subhero__title { font-size: clamp(34px, 11vw, 54px); }
  .subhero__lead { justify-self: start; }
  .subhero__lead p { max-width: none; font-size: 16px; }
  .subhero__actions {
    flex-direction: column;
    align-items: center;
    gap: 10px;
  }
  .subhero__actions .btn {
    width: 100%; max-width: 220px; justify-content: center;
    padding: 12px 18px; font-size: 13px;
  }

  /* --- Portfolio grid --- */
  /* Bottom trimmed only: the 120px here met the lemon section's 120px
     top, so the [See Portfolio] button and the lemon line were 240px
     apart on a phone. The top is left alone — that is a different gap. */
  .work { padding: var(--space-2xl) 0 var(--space-xl); }
  .work__head { margin-bottom: var(--space-xl); }
  .work__title { font-size: clamp(36px, 11vw, 60px); }
  .work__grid {
    grid-template-columns: 1fr;
    grid-template-rows: none;
    aspect-ratio: auto;
    gap: 12px;
  }
  .work__grid .card { grid-column: 1 / -1; grid-row: auto; aspect-ratio: 16 / 10; }
  .work__cta { margin-top: var(--space-xl); }

  /* --- Lemon --- */
  .lemon { padding-top: var(--space-xl); }
  .lemon__text { font-size: clamp(28px, 8vw, 52px); }
  .lemon__strip { margin-top: var(--space-2xl); }

  /* --- Testimonials --- */
  .ts { padding: clamp(40px, 8vw, 80px) 0; }
  .ts__title { font-size: clamp(32px, 9vw, 56px); }
  .ts__sub { font-size: clamp(14px, 4vw, 17px); }
  .ts__text { font-size: clamp(16px, 4.5vw, 22px); }

  /* --- CTA --- */
  .cta { padding-top: var(--space-2xl); }
  .cta__title { font-size: clamp(34px, 10vw, 60px); }
  .cta__actions {
    margin-top: var(--space-lg);
    flex-direction: column;
    align-items: center;
    gap: var(--space-sm);
  }
  .cta__actions .btn { width: 100%; max-width: 300px; justify-content: center; }

  /* --- Marquee --- */
  .marquee__set img { width: clamp(130px, 36vw, 220px); }
  .marquee__set img:nth-child(1) { height: clamp(130px, 18vw, 200px); }
  .marquee__set img:nth-child(2) { height: clamp(160px, 22vw, 260px); }
  .marquee__set img:nth-child(3) { height: clamp(150px, 20vw, 240px); }
  .marquee__set img:nth-child(4) { height: clamp(120px, 16vw, 180px); }
  .marquee__set img:nth-child(5) { height: clamp(170px, 24vw, 280px); }
  .marquee__set img:nth-child(6) { height: clamp(135px, 18vw, 210px); }
  .marquee__set img:nth-child(7) { height: clamp(155px, 21vw, 250px); }

  /* --- Footer --- */
  .footer__bar { flex-direction: column; align-items: flex-start; gap: var(--space-md); }
  .footer__nav { gap: var(--space-md); flex-wrap: wrap; }
  .footer__fine { flex-direction: column; gap: var(--space-xs); text-align: left; }
  .footer__wordmark { padding: var(--space-md) var(--pad) 0; }
}

/* ---- 480px: Very small phones ---- */
@media (max-width: 480px) {
  :root { --pad: 16px; }
  .subhero__title { font-size: clamp(30px, 12vw, 46px); }
  .work__title { font-size: clamp(30px, 12vw, 46px); }
  .cta__title { font-size: clamp(28px, 11vw, 48px); }
  .lemon__text { font-size: clamp(24px, 9vw, 38px); }
  .ts__title { font-size: clamp(28px, 10vw, 44px); }
  .ts__text { font-size: clamp(15px, 4.5vw, 20px); }
  .btn { padding: 12px 20px; font-size: 13px; }
  .nav__links a { font-size: clamp(24px, 9vw, 36px); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
  .reveal { opacity: 1; transform: none; }
  .lemon__text .word { opacity: 1; }
}

/* ============================================================
   CASE STUDY — floating back button (replaces the nav on
   case-study pages; frosted circle works over the dark hero
   and the accent canvas alike)
   ============================================================ */
.cs__back {
  position: fixed; top: clamp(16px, 2vw, 28px); left: clamp(16px, 2vw, 28px);
  z-index: 1000;
  width: 46px; height: 46px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(20, 18, 16, 0.34);
  backdrop-filter: blur(18px) saturate(1.3); -webkit-backdrop-filter: blur(18px) saturate(1.3);
  border: 1px solid rgba(255, 255, 240, 0.22);
  color: var(--color-bg);
  transition: background .25s ease, transform .25s ease;
}
.cs__back:hover { background: var(--color-dark); transform: translateX(-3px); }
.cs__back svg { width: 20px; height: 20px; display: block; }

/* services — small screens: stack the three services vertically */
@media (max-width: 768px) {
  /* a stacked card opens over a shorter distance than a column sliding across
     the full width, so the same duration would feel slow here */
  .ks { --ks-dur: 0.5s; }
  .ks__frame { height: auto; }
  .ks__head { padding: 16px; }
  /* auto-height chain: the desktop 100% heights would squeeze the
     expanded card and clip its image */
  .ks__viewport { flex: none; }
  .ks__track { height: auto; align-items: flex-start; }
  .ks__slide { flex-direction: column; height: auto; width: 33.3333%; }
  /* Accordion on a phone. The three services stack as tappable headers and one
     is open at a time (the script guarantees one via ensureMobileActive), so
     the whole set stays reachable without scrolling past three full cards.
     The desktop model expands sideways off a flex-basis; here the column is
     always full width and the *contents* open instead, so every rule below
     neutralises the horizontal geometry and drives height off .is-active. */
  .ks__col,
  .ks__slide.has-active .ks__col,
  .ks__slide.has-active .ks__col.is-active {
    flex: 0 0 auto; flex-basis: auto; min-height: 0;
    background: var(--color-bg);
  }
  .ks__col + .ks__col { border-left: none; border-top: 1px solid var(--color-accent); }
  .ks__rail { display: none; }
  .ks__inner,
  .ks__slide.has-active .ks__col:not(.is-active) .ks__inner {
    width: 100% !important; position: relative; inset: auto;
    display: flex; flex-direction: column;
    padding: 16px 16px 18px;
    opacity: 1;
  }
  /* header first, then the artwork and copy that open beneath it */
  .ks__col .ks__nameline,
  .ks__col--b .ks__nameline {
    order: 1; margin-top: 0; align-items: center;
  }
  .ks__col .ks__imgwrap,
  .ks__col--b .ks__imgwrap,
  .ks__slide.has-active .ks__col:not(.is-active) .ks__imgwrap {
    order: 2; align-self: stretch; margin: 0; display: block;
    width: 100% !important;
    /* closed: collapsed to nothing but still in flow, so the open/close is a
       height transition rather than a reflow jump */
    height: 0 !important; opacity: 0;
    transition: height var(--ks-dur) var(--ks-ease), opacity 0.26s ease,
                margin var(--ks-dur) var(--ks-ease);
  }
  .ks__slide .ks__col.is-active .ks__imgwrap {
    height: 56vw !important; opacity: 1; margin-top: 14px;
  }
  /* show the expanded artwork, not the idle crop */
  .ks__img--hover { opacity: 1; }
  .ks__info,
  .ks__col.is-active .ks__info {
    display: block; order: 3;
    position: static; opacity: 0; transform: none;
    max-height: 0; overflow: hidden; padding: 0;
    transition: max-height var(--ks-dur) var(--ks-ease), opacity 0.26s ease,
                margin var(--ks-dur) var(--ks-ease);
  }
  /* 60vh was far taller than this copy ever gets, so most of the animation ran
     past the end of the content: the text finished moving almost at once and
     the rest of the duration was spent on empty space, which read as a stall.
     The tallest of these blocks measures 78px, from a 320px screen up, so this
     cap keeps the motion and the content together with room to spare. */
  .ks__col.is-active .ks__info { max-height: 130px; opacity: 1; margin-top: 14px; }
  /* desktop sets these side by side with the copy flush right; stacked on a
     phone they both read from the left edge */
  .ks__desc { text-align: left; max-width: none; margin-top: 6px; }
  .ks__name,
  .ks__slide.has-active .ks__col:not(.is-active) .ks__name {
    color: var(--color-accent); opacity: 1; transform: none;
  }
  .ks__name { font-size: clamp(20px, 5.8vw, 28px); }
  /* the number doubles as the open/closed affordance */
  .ks__num,
  .ks__col.is-active .ks__num {
    display: block; opacity: 0.5; transform: none; transition: opacity 0.3s ease;
    font-size: clamp(13px, 3.6vw, 16px);
  }
  .ks__col.is-active .ks__num { opacity: 1; }
  .ks__foot { flex-wrap: wrap; row-gap: 10px; }
  /* phone-range vw so the arrow scales between small and large phones */
  .ks__arrow { font-size: clamp(26px, 7vw, 40px); }
}
