/* ==========================================================
   Design tokens
   Text colours: Ink or Olive ONLY. Sand and Khaki fail
   contrast on Cream (1.24:1 and 1.83:1), so they are used
   for surfaces, borders and rules, never text.
   Checked: Ink/Cream 14.0:1 · Olive/Cream 6.67:1 · Olive/Sand 5.39:1
   ========================================================== */
:root {
  --cream: #E6DFD5;  /* page background */
  --sand:  #DBC8A7;  /* secondary surfaces, tags */
  --khaki: #B1A68A;  /* borders, dividers */
  --olive: #4F4A3D;  /* secondary text, meta */
  --ink:   #141316;  /* primary text */

  --display: "Archivo", "Helvetica Neue", sans-serif;
  --serif:   "Newsreader", Georgia, "Times New Roman", serif;
  --mono:    "IBM Plex Mono", ui-monospace, Menlo, monospace;

  --gutter: clamp(1rem, 4vw, 3rem);   /* 16px on phones, up to 48px */
  --max:    1440px;
  --rule:   1px solid var(--khaki);

  /* Thumbnail treatment: desaturate, then warm-tint toward the palette */
  --thumb-filter: grayscale(1) sepia(0.3) contrast(0.95) brightness(0.97);
}

/* ==========================================================
   Base
   ========================================================== */
*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--display);
  font-size: 1.0625rem;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p, ul, figure { margin: 0; }
ul { padding: 0; list-style: none; }
img { display: block; max-width: 100%; height: auto; }

a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 0.2em; }

/* Visible focus for every interactive element (keyboard users) */
:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

.wrap {
  width: 100%;
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Monospace meta style: dates, tags, disciplines */
.meta {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--olive);
}

.section-label {
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
}

.skip-link {
  position: absolute;
  left: var(--gutter);
  top: -100px;
  padding: 0.5rem 0.75rem;
  background: var(--ink);
  color: var(--cream);
  font-family: var(--mono);
  font-size: 0.8125rem;
  z-index: 10;
}
.skip-link:focus { top: 0.5rem; }

/* ==========================================================
   Header / nav
   ========================================================== */
.site-header { border-bottom: var(--rule); }

.site-header__inner {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding-block: 1rem;
}

.wordmark {
  font-family: var(--display);
  font-weight: 800;
  font-stretch: 85%;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  text-decoration: none;
}

.nav-list { display: flex; gap: clamp(1rem, 3vw, 2rem); }

.nav-list a {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
}
.nav-list a:hover { text-decoration: underline; }

/* ==========================================================
   Hero: huge all-caps grotesk + large classic serif beneath
   ========================================================== */
.hero {
  padding-block: clamp(3rem, 10vw, 8rem) clamp(3rem, 8vw, 6rem);
}

.hero__title {
  font-family: var(--display);
  font-weight: 800;
  font-stretch: 80%;            /* uses Archivo's width axis: tighter, more editorial */
  text-transform: uppercase;
  font-size: clamp(3.25rem, 13vw, 12rem);
  line-height: 0.86;
  letter-spacing: -0.02em;
}

.hero__lede {
  text-wrap: balance;            /* evens out line lengths so no single word is stranded */
  margin-top: clamp(1rem, 2.5vw, 2rem);
  max-width: 24ch;
  font-family: var(--serif);
  font-weight: 350;
  font-size: clamp(1.75rem, 4.5vw, 4rem);
  line-height: 1.08;
  letter-spacing: -0.01em;
}

.hero__disciplines { margin-top: clamp(1.25rem, 3vw, 2rem); }

/* Small outlined button (a link that looks like a button: it navigates) */
.button {
  display: inline-block;
  margin-top: 1.75rem;
  padding: 0.7rem 1.1rem;
  border: 1px solid var(--ink);
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background-color 0.2s ease, color 0.2s ease;
}
.button:hover { background: var(--ink); color: var(--cream); }

/* ==========================================================
   Selected work: two-column list, thin rules between rows
   ========================================================== */
.work { padding-block: clamp(2rem, 6vw, 5rem); }

.section-head {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
  justify-content: space-between;
  align-items: baseline;
  padding-bottom: 1rem;
}

.project-list {
  display: grid;
  grid-template-columns: 1fr;       /* phone: one column */
  column-gap: clamp(1.5rem, 4vw, 3.5rem);
}

@media (min-width: 900px) {
  .project-list { grid-template-columns: 1fr 1fr; }  /* desktop: two columns */
}

.project-list > li { border-top: var(--rule); }

.project {
  position: relative;               /* anchor for the stretched link below */
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
  gap: clamp(1rem, 2.5vw, 1.5rem);
  padding-block: clamp(1.25rem, 3vw, 2rem);
}

.project__thumb {
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border: var(--rule);
}

.project__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: var(--thumb-filter);
  transition: filter 0.45s ease;
}

/* Full colour when the row is hovered OR its link has keyboard focus */
.project:hover .project__thumb img,
.project:focus-within .project__thumb img {
  filter: none;
}

/* Labelled placeholder box until real images arrive */
.placeholder {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  background: var(--sand);
  padding: 0.5rem;
  text-align: center;
}
.placeholder span {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);                /* Ink on Sand: 11.3:1 */
}

.project__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
}

.tag {
  padding: 0.15rem 0.45rem;
  background: var(--sand);
  color: var(--ink);
}

.project__title {
  margin-top: 0.6rem;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(1.375rem, 2.2vw, 1.875rem);
  line-height: 1.15;
}

.project__link { text-decoration: none; }

/* "Stretched link": the ::after pseudo-element covers the whole card,
   so the entire row is clickable while there is still only ONE real
   link inside it (screen readers hear the title once, not twice). */
.project__link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.project:hover .project__link { text-decoration: underline; }

/* Draw the focus ring around the whole card instead of just the title */
.project__link:focus-visible { outline: none; }
.project__link:focus-visible::after {
  outline: 2px solid var(--ink);
  outline-offset: 4px;
}

.project__desc {
  margin-top: 0.5rem;
  max-width: 42ch;
  font-size: 0.9375rem;
  color: var(--olive);
}

/* ==========================================================
   About
   ========================================================== */
.about {
  display: grid;
  gap: 1.25rem;
  padding-block: clamp(3rem, 8vw, 6rem);
  border-top: var(--rule);
}

@media (min-width: 900px) {
  .about { grid-template-columns: 1fr 2fr; }
}

.about__body { display: grid; gap: 1.25rem; max-width: 60ch; }

.about__lead {
  font-family: var(--serif);
  font-size: clamp(1.5rem, 2.8vw, 2.25rem);
  line-height: 1.2;
}

/* ==========================================================
   Contact / footer
   ========================================================== */
.site-footer {
  border-top: var(--rule);
  padding-block: clamp(3rem, 8vw, 6rem) 2rem;
}

.contact__cta {
  margin-top: 1.25rem;
  font-family: var(--serif);
  font-size: clamp(1.75rem, 5vw, 4rem);
  line-height: 1.1;
  overflow-wrap: anywhere;          /* long emails wrap on small phones */
}

.contact__links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.5rem;
  margin-top: 2rem;
}

.site-footer__small { margin-top: clamp(3rem, 8vw, 5rem); }

/* ==========================================================
   Case study pages
   Screenshots here are NEVER filtered: full colour, hung
   "gallery wall" style on a cream mat with a thin Khaki border.
   ========================================================== */
.case-hero {
  display: grid;
  justify-items: start;
  gap: clamp(1rem, 2.5vw, 1.75rem);
  padding-block: clamp(3rem, 8vw, 6rem) clamp(2rem, 5vw, 4rem);
}
.case-hero .meta a { text-decoration: none; }
.case-hero .meta a:hover { text-decoration: underline; }

.case-hero__title {
  font-family: var(--display);
  font-weight: 800;
  font-stretch: 80%;
  text-transform: uppercase;
  font-size: clamp(3.5rem, 15vw, 13rem);
  line-height: 0.86;
  letter-spacing: -0.02em;
}

.case-hero__lede {
  max-width: 26ch;
  font-family: var(--serif);
  font-weight: 350;
  font-size: clamp(1.5rem, 3.6vw, 3rem);
  line-height: 1.12;
  text-wrap: balance;
}

.facts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem 2rem;
  width: 100%;
  max-width: 48rem;
  margin: 0;
  padding-top: 1.25rem;
  border-top: var(--rule);
}
@media (min-width: 700px) { .facts { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.facts dt, .stats dt {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--olive);
}
.facts dd, .stats dd { margin: 0.25rem 0 0; }
.facts .button { margin-top: 0; }

/* The frame: cream mat + thin border. Used for every piece of work. */
.frame {
  margin: 0;
  padding: clamp(0.75rem, 2.5vw, 1.5rem);
  border: var(--rule);
  background: var(--cream);
}
.frame img { width: 100%; height: auto; }
.frame--wide { max-width: 960px; }

.plate { display: grid; gap: 0.75rem; padding-bottom: clamp(2rem, 6vw, 5rem); }

.case-section {
  display: grid;
  gap: 1.25rem;
  padding-block: clamp(3rem, 7vw, 5rem);
  border-top: var(--rule);
}
@media (min-width: 900px) { .case-section { grid-template-columns: 1fr 2fr; } }
.case-section__body { display: grid; gap: 1.25rem; max-width: 60ch; justify-items: start; }
.case-section__body .button { margin-top: 0.5rem; }

.case-lead {
  font-family: var(--serif);
  font-size: clamp(1.5rem, 2.8vw, 2.25rem);
  line-height: 1.2;
  text-wrap: balance;
}

.stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
  width: 100%;
  margin: 0.5rem 0 0;
}
.stats > div { padding-top: 0.75rem; border-top: var(--rule); }
.stats dd {
  font-family: var(--serif);
  font-size: clamp(1.75rem, 4vw, 2.75rem);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* ---- Walkthrough steps ----
   Two versions of every screen: Sinkless's phone layout below 1200px, its
   desktop layout from 1200px (the width where Sinkless itself switches).
   The same 1200px is in sinkless.html (<source media>) and case-study.js. */
.walkthrough { padding-block: clamp(2rem, 5vw, 4rem); border-top: var(--rule); }

.step {
  display: grid;
  gap: clamp(1.5rem, 4vw, 4rem);
  padding-block: clamp(2rem, 5vw, 4rem);
  border-top: var(--rule);
}
.step > .frame { max-width: 380px; width: 100%; justify-self: center; }
@media (min-width: 800px) {
  .step { grid-template-columns: minmax(0, 380px) minmax(0, 1fr); align-items: center; }
  .step > .frame { justify-self: start; }
}
@media (min-width: 1200px) {
  .step { grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); }
  .step > .frame { max-width: none; }
}

.step .frame, .live__frame { padding: clamp(0.75rem, 2vw, 1.25rem); }
.shot { position: relative; display: block; }
.shot img { width: 100%; height: auto; }

.step__text { display: grid; gap: 0.75rem; align-content: start; min-width: 0; }
.step__title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(1.625rem, 3vw, 2.5rem);
  line-height: 1.1;
  text-wrap: balance;
}

.notes { display: grid; gap: 0; padding: 0; list-style: none; }
.note {
  display: grid;
  grid-template-columns: 1.375rem minmax(0, 1fr);
  gap: 0.875rem;
  align-items: start;
  padding-block: 0.875rem;
  border-top: var(--rule);
  color: var(--olive);
}
.note p { max-width: 48ch; padding-top: 0.1rem; }
.note.is-active p { color: var(--ink); }

.note__num {
  display: grid;
  place-items: center;
  width: 1.375rem;
  height: 1.375rem;
  border-radius: 50%;
  border: 1px solid var(--ink);
  background: var(--cream);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 500;
  line-height: 1;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.note.is-active .note__num { background: var(--ink); color: var(--cream); }

/* ---- Connector lines ----
   One SVG over the window; case-study.js draws a line from each note's number
   to a box around what it describes, and keeps them attached while scrolling.
   Every mark is drawn twice: a wide cream casing under a thin ink line, so it
   reads on the cream page and on the dark app alike. */
.connectors {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 3;
  pointer-events: none;
  overflow: visible;
}
.conn { transition: opacity 0.2s ease; }
.conn .casing { fill: none; stroke: var(--cream); stroke-width: 4; stroke-linecap: round; stroke-linejoin: round; }
.conn .line   { fill: none; stroke: var(--ink);   stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; }
.conn .dot    { fill: var(--ink); stroke: var(--cream); stroke-width: 1.5; }
.conn .badge__bg  { fill: var(--cream); stroke: var(--ink); stroke-width: 1; }
.conn .badge__num { fill: var(--ink); font: 500 9px var(--mono); }
.conn.is-active .badge__bg { fill: var(--ink); }
.conn.is-active .badge__num { fill: var(--cream); }
@media (max-width: 799px) {                /* narrow margin: thinner lines so a bundle stays legible */
  .conn .casing { stroke-width: 3; }
  .conn .line { stroke-width: 1; }
}
.connectors.has-active .conn:not(.is-active) { opacity: 0.25; }
.conn.is-active .line { stroke-width: 2; }
.conn.is-active .casing { stroke-width: 5; }

/* Draw-in: paths have pathLength="1", so a dash offset of 1 hides the whole
   stroke and 0 shows it. The line runs note -> target, then the box traces. */
.conn path, .conn rect { stroke-dasharray: 1; stroke-dashoffset: 0; }
.conn path { transition: stroke-dashoffset 0.6s cubic-bezier(0.4, 0, 0.2, 1) calc(var(--i, 0) * 140ms); }
.conn rect { transition: stroke-dashoffset 0.5s cubic-bezier(0.4, 0, 0.2, 1) calc(var(--i, 0) * 140ms + 450ms); }
.conn .dot, .conn .badge { transition: opacity 0.2s ease calc(var(--i, 0) * 140ms + 450ms); }
.conn.is-new path, .conn.is-new rect { stroke-dashoffset: 1; }
.conn.is-new .dot, .conn.is-new .badge { opacity: 0; }

/* ---- Pinned walkthrough ("scrolly") ----
   case-study.js adds .is-scrolly. The stage is one window tall and sticks
   while the tall .scrolly track scrolls past; every step sits in the same
   grid cell and only the current one shows, so there's never a previous or
   next screen in view. Each scroll moves to the next note. Without JS the
   steps stay a plain list.

   --shot-h is the height the app gets; --ar its aspect ratio (phone app
   below 1200px, desktop app from 1200px). The screenshot frame and the live
   frame both size from these, so the live app lands exactly over them. */
.is-scrolly .scrolly { position: relative; height: var(--track, 400vh); }
.is-scrolly .stage {
  --gap: clamp(1.5rem, 4vw, 4rem);
  --pad: 1rem;
  --ar: calc(390 / 844);
  --shot-h: 46svh;
  position: sticky;
  top: 0;
  height: 100svh;
  display: grid;
  grid-template-columns: minmax(0, 1fr);   /* one column that never grows past the page */
}
@media (min-width: 800px) { .is-scrolly .stage { --shot-h: calc(100svh - 7rem); } }
@media (min-width: 1200px) { .is-scrolly .stage { --ar: 1.5; } }
.is-scrolly .stage > * { grid-area: 1 / 1; min-width: 0; min-height: 0; }

/* Steps and the live app share one layout: app on the left (on top on phones), notes beside it. */
.is-scrolly .step, .is-scrolly .live {
  display: grid;
  gap: 1rem;
  align-content: start;
  padding-block: 1rem;
  border-top: 0;
}
@media (min-width: 800px) {
  .is-scrolly .step, .is-scrolly .live {
    grid-template-columns: auto minmax(0, 1fr);   /* phone app: sized by height */
    column-gap: var(--gap);
    align-content: center;
    align-items: center;
    padding-block: 0;
  }
}
@media (min-width: 1200px) {                  /* desktop app: its column caps the width */
  .is-scrolly .step, .is-scrolly .live { grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); }
}
.is-scrolly .step {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.45s ease, visibility 0s linear 0.45s;
}
.is-scrolly .step.is-current { opacity: 1; visibility: visible; transition: opacity 0.45s ease; }

.is-scrolly .step > .frame,
.is-scrolly .live__frame {
  justify-self: center;
  width: min(100%, calc(var(--shot-h) * var(--ar) + 2 * var(--pad) + 2px));
  max-width: none;
  padding: var(--pad);
}
@media (min-width: 800px) {
  .is-scrolly .step > .frame, .is-scrolly .live__frame { justify-self: start; }
}
@media (min-width: 800px) and (max-width: 1199px) {
  /* The phone app's column is sized by its content, so the frame needs a
     definite width (a percentage there is circular and falls back to the
     screenshot file's own 780px). */
  .is-scrolly .step > .frame, .is-scrolly .live__frame {
    width: calc(var(--shot-h) * var(--ar) + 2 * var(--pad) + 2px);
  }
}
.is-scrolly .step > .frame .shot img { width: 100%; }

/* Notes: every note of the step is listed, the one being explained is lit.
   On phones only that one shows, to fit under the app. */
.is-scrolly .note { opacity: 0.4; transition: opacity 0.3s ease; }
.is-scrolly .note.is-on { opacity: 1; color: var(--ink); }
.is-scrolly .note.is-on .note__num { background: var(--ink); color: var(--cream); }
@media (max-width: 799px) {
  .is-scrolly .note:not(.is-on) { display: none; }
  .is-scrolly .note { border-top: 0; padding-top: 0; }
  .is-scrolly .step__title { font-size: 1.5rem; }
}

.stage__progress { display: none; }
.is-scrolly .stage__progress {
  display: block;
  align-self: end;
  justify-self: end;
  padding-bottom: 1rem;
  pointer-events: none;
  font-variant-numeric: tabular-nums;
}

/* ---- Live app in the stage ----
   Hidden until the Sinkless embed answers; then it covers each step's
   screenshot (which keeps its space) and clicks go through to the app. */
.live[hidden] { display: none; }       /* .live's display: grid would otherwise override [hidden] */
.live { pointer-events: none; }
.live__frame { pointer-events: auto; display: grid; gap: 0.5rem; justify-items: center; }
.is-live .step > .frame { visibility: hidden; }

/* The app renders at a real device size (--vw x --vh: 390 x 844 phone,
   1200 x 800 desktop) and is scaled down as a whole, so it lays out exactly
   as it would on that device. --s is set by JS. */
.live__shot {
  --vw: 390;
  --vh: 844;
  width: 100%;
  aspect-ratio: var(--vw) / var(--vh);
  overflow: hidden;
  background: #14120e;                     /* the app's own ground while it loads */
}
@media (min-width: 1200px) { .live__shot { --vw: 1200; --vh: 800; } }

.live__shot iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: calc(var(--vw) * 1px);
  height: calc(var(--vh) * 1px);
  border: 0;
  transform: scale(var(--s, 0.5));
  transform-origin: 0 0;
  transition: opacity 0.2s ease;
}
.live__shot.is-switching iframe { opacity: 0; }

/* ---- Phones: a plain page ----
   Below 800px there's no pinned stage and no live app: each step is its
   screenshot and notes, scrolled freely. The note nearest the middle of the
   screen is lit and gets the line (case-study.js adds .is-list). */
.walk-cta { display: none; }
@media (max-width: 799px) {
  .walk-cta { display: block; margin-bottom: 0.5rem; }
  .walk-cta--end { margin-top: 1.5rem; margin-bottom: 0; }
  .walk-cta .button { margin-top: 0; }
  /* Full width, but never taller than the screen. */
  .step > .frame { max-width: min(100%, calc(88svh * 390 / 844 + 2 * clamp(0.75rem, 2vw, 1.25rem) + 2px)); }
}
@media (max-width: 799px) {
  /* With JS: each step's screenshot sticks to the top while its notes scroll
     past underneath (free scrolling, nothing locked), so the line from the
     note in view always has its target on screen. */
  .is-list .step { align-items: start; padding-bottom: 20svh; }
  .is-list .step > .frame {
    position: sticky;
    top: 0;
    z-index: 1;
    max-width: min(100%, calc(55svh * 390 / 844 + 2 * clamp(0.75rem, 2vw, 1.25rem) + 2px));
    /* a cream band the full width of the screen, so notes passing underneath
       don't show at the screenshot's sides (clipped to the frame's height) */
    box-shadow: 0 0 0 100vmax var(--cream);
    clip-path: inset(0 -100vmax);
  }
}
.is-list .live { display: none; }
.is-list.is-live .step > .frame { visibility: visible; }
.is-list .note { opacity: 0.4; transition: opacity 0.3s ease; }
.is-list .note.is-on { opacity: 1; color: var(--ink); }
.is-list .note.is-on .note__num { background: var(--ink); color: var(--cream); }
.live__caption { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; justify-content: center; }
.live__dot { width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--olive); }

/* ---- Motion ----
   Content is visible by default. case-study.js adds .is-pending only to
   sections that start below the fold, then removes it as they scroll in. */
.reveal { transition: opacity 0.8s ease, transform 0.8s cubic-bezier(0.2, 0.8, 0.2, 1); }
.reveal.is-pending { opacity: 0; transform: translateY(40px); }

/* Page-load entrance: title rises out of a mask, then the rest follows */
@keyframes rise    { from { transform: translateY(105%); } to { transform: none; } }
@keyframes fade-up { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }

.mask { display: block; overflow: hidden; }
.mask > span { display: block; animation: rise 0.9s cubic-bezier(0.2, 0.8, 0.2, 1) both; }
.enter { animation: fade-up 0.7s ease both; animation-delay: calc(var(--d, 0) * 110ms + 300ms); }

/* ==========================================================
   Respect reduced-motion preferences
   ========================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    transition-delay: 0s !important;
    animation: none !important;
  }
}
