/* ========================================================================
   ZAYCO.

   Contents
   01  Tokens
   02  Reset and base
   03  Typography primitives
   04  Buttons and links
   05  Site header and navigation
   06  Hero
   07  Section shell
   08  Concept gallery
   09  Concept world (the miniature inside each gallery frame)
   10  Client work (styles live, markup still commented in index.html)
   11  What we do
   12  Process
   13  About
   14  Contact
   15  Footer
   16  Responsive
   17  Reduced motion
   ======================================================================== */


/* ========================================================================
   01  TOKENS

   The palette is cool neutral: graphite through to bone, with a single
   restrained iris accent. Colour is deliberately almost absent from the
   site itself so that the concept gallery, where every miniature carries
   its own palette, is the one place colour actually lives.

   Surface colours are semantic (--surface, --fg, --rule) and get
   redefined inside .section-dark, so a component written once works on
   both light and dark ground without a single override.
   ======================================================================== */
:root {
  /* Raw palette. Nothing outside this block should use these directly. */
  --ink-900: #0B0D12;
  /* Hero only. A cooler, deeper near black than --ink-800 so the indigo
     glow has somewhere dark to sit. Never pure black. */
  --hero-ink: #060810;
  --ink-800: #0E1116;
  --ink-700: #141922;
  --ink-600: #1C222D;
  --ink-500: #2A323F;
  --bone-050: #FFFFFF;
  --bone-100: #F5F5F3;
  --bone-200: #F0F0ED;
  --bone-300: #E7E7E3;
  --slate-400: #8A93A3;   /* muted text on dark, 6.2:1 on --ink-800 */
  --slate-600: #5A6373;   /* muted text on light, 5.3:1 on --bone-200 */
  --iris-500: #3E4A6B;    /* accent on light, 7.8:1 on --bone-200 */
  --iris-300: #A8B4D8;    /* accent on dark, 9.2:1 on --ink-800 */

  /* Semantic surface. Light ground is the default. */
  --surface: var(--bone-200);
  --surface-raised: var(--bone-050);
  --fg: #14181F;
  --fg-mute: var(--slate-600);
  --accent: var(--iris-500);
  --rule: rgba(14, 17, 22, 0.13);
  --rule-strong: rgba(14, 17, 22, 0.24);
  --shadow-tint: 20, 26, 38;

  /* Type */
  /* One face across the whole site: Helvetica Neue, with a system
     fallback chain for platforms that lack it. Display, body, and the
     wordmark all resolve to the same family now; hierarchy comes from
     size, weight, and spacing rather than from contrasting typefaces. */
  --font-display: "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-body: "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-mark: "Helvetica Neue", Helvetica, Arial, sans-serif;

  --t-micro: 0.75rem;
  --t-small: 0.875rem;
  --t-body: 1.0625rem;
  --t-lede: clamp(1.0625rem, 0.95rem + 0.5vw, 1.3125rem);
  --t-h3: clamp(1.125rem, 1rem + 0.5vw, 1.375rem);
  --t-h2: clamp(2rem, 1.3rem + 2.9vw, 3.5rem);
  /* Hero headline only. Large and dominant: roughly 42 pixels on a
     phone up to 84 on a wide desktop. Paired with weight 600 and a
     tight 1.04 line height so the two lines read as one block. */
  --t-h1: clamp(2.4rem, 1rem + 5.7vw, 4.85rem);

  /* Space */
  --sp-1: 0.5rem;
  --sp-2: 0.75rem;
  --sp-3: 1rem;
  --sp-4: 1.5rem;
  --sp-5: 2.5rem;
  --sp-6: 4rem;
  --sp-7: 6rem;

  --shell-w: 1240px;
  --gutter: clamp(1.25rem, 5vw, 3.5rem);
  --section-y: clamp(5rem, 9vw, 8.5rem);
  --header-h: 84px;

  /* Corners stay small on purpose. Depth comes from shadow, not rounding. */
  --r-1: 3px;
  --r-2: 6px;
  --r-3: 12px;

  /* Multi layer shadows, tinted cool so they read as shade rather than
     as a grey smudge. Three or four stacked layers per level to imitate
     real light falloff. */
  --shadow-1:
    0 1px 2px rgba(var(--shadow-tint), 0.06),
    0 3px 8px rgba(var(--shadow-tint), 0.05);
  --shadow-2:
    0 1px 2px rgba(var(--shadow-tint), 0.05),
    0 8px 20px rgba(var(--shadow-tint), 0.08),
    0 20px 44px rgba(var(--shadow-tint), 0.06);
  --shadow-3:
    0 2px 4px rgba(var(--shadow-tint), 0.06),
    0 14px 30px rgba(var(--shadow-tint), 0.12),
    0 38px 76px rgba(var(--shadow-tint), 0.12),
    0 70px 120px rgba(var(--shadow-tint), 0.08);

  --ease: cubic-bezier(0.22, 0.68, 0.24, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

/* Dark ground. Every semantic token flips, nothing else has to know. */
.section-dark,
.site-footer {
  --surface: var(--ink-800);
  --surface-raised: var(--ink-700);
  --fg: var(--bone-200);
  --fg-mute: var(--slate-400);
  --accent: var(--iris-300);
  --rule: rgba(240, 240, 237, 0.12);
  --rule-strong: rgba(240, 240, 237, 0.24);
  --shadow-tint: 0, 0, 0;
}


/* ========================================================================
   02  RESET AND BASE
   ======================================================================== */
*,
*::before,
*::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* Anchored sections clear the fixed header instead of hiding under it. */
  scroll-padding-top: calc(var(--header-h) + 1.5rem);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bone-200);
  color: #14181F;
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

body.nav-open { overflow: hidden; }

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: -0.028em;
  text-wrap: balance;
}
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, svg, canvas { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
input, select, textarea { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }

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

/* One focus treatment for the whole site. Never removed, always visible
   against whichever ground it lands on. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}

.skip-link {
  position: fixed;
  top: 0;
  left: 50%;
  transform: translate(-50%, -120%);
  z-index: 200;
  background: var(--ink-800);
  color: var(--bone-200);
  padding: 0.85rem 1.5rem;
  font-size: var(--t-small);
  font-weight: 600;
  border-radius: 0 0 var(--r-2) var(--r-2);
  transition: transform 0.2s var(--ease-out);
}
.skip-link:focus { transform: translate(-50%, 0); }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}


/* ========================================================================
   03  TYPOGRAPHY PRIMITIVES
   ======================================================================== */

/* Small caps style label. Used only where the text is genuinely a label
   for the thing beside it, never as decoration above a heading. */
.label {
  font-family: var(--font-body);
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-mute);
  line-height: 1.4;
}

/* Line mask used by the hero load sequence. The inner span starts at
   its resting position in CSS, so if Motion never loads the headline is
   simply there, unanimated. */
.mask { display: block; overflow: hidden; padding-bottom: 0.06em; }
.mask__i { display: block; }

.edit-note {
  color: var(--fg-mute);
  font-size: var(--t-small);
  font-style: italic;
  border-left: 2px solid var(--rule-strong);
  padding-left: var(--sp-3);
}


/* ========================================================================
   04  BUTTONS AND LINKS

   Small radius rectangles rather than pills. Hover lifts and softly
   illuminates through shadow and a light inner edge, never by swapping
   to a flat different colour.
   ======================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 48px;
  padding: 0.85rem 1.6rem;
  border-radius: var(--r-1);
  font-family: var(--font-body);
  font-size: var(--t-small);
  font-weight: 600;
  letter-spacing: 0.01em;
  text-align: center;
  white-space: nowrap;
  cursor: pointer;
  transition:
    box-shadow 0.3s var(--ease),
    background-color 0.3s var(--ease),
    border-color 0.3s var(--ease),
    color 0.3s var(--ease);
}

.btn-primary {
  background: var(--fg);
  color: var(--surface);
  box-shadow: var(--shadow-1);
}
.btn-primary:hover {
  box-shadow:
    var(--shadow-2),
    inset 0 1px 0 rgba(255, 255, 255, 0.18);
}

.btn-ghost {
  background: transparent;
  color: var(--fg);
  box-shadow: inset 0 0 0 1px var(--rule-strong);
}
.btn-ghost:hover {
  box-shadow:
    inset 0 0 0 1px var(--fg),
    var(--shadow-1);
}

.btn-sm { min-height: 40px; padding: 0.55rem 1.15rem; }
.btn-block { width: 100%; }

.link {
  color: inherit;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-size: 100% 1px;
  background-position: 0 100%;
  padding-bottom: 1px;
  transition: color 0.25s var(--ease), background-size 0.3s var(--ease);
}
.link:hover {
  color: var(--accent);
  background-size: 0% 1px;
}


/* ========================================================================
   05  SITE HEADER AND NAVIGATION

   Sits over the hero with no fill. js/nav.js adds .is-stuck once the
   page scrolls, which brings in the blurred surface and the hairline.
   ======================================================================== */
.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  height: var(--header-h);
  display: flex;
  align-items: center;
  color: var(--bone-200);
  /* A soft cross fade between transparent and bordered, in both
     directions. Long enough to read as a considered fade rather than a
     hard cut, short enough that it never feels slow. The ease-out
     curve trails off, which is what makes the change feel like it
     settles rather than snaps. */
  transition:
    background-color 0.4s var(--ease-out),
    box-shadow 0.4s var(--ease-out),
    color 0.4s var(--ease-out),
    backdrop-filter 0.4s var(--ease-out),
    -webkit-backdrop-filter 0.4s var(--ease-out);
}

/* The header escapes the shell's 1240px max width so the wordmark and
   the CTA sit against the viewport edges (with only a small breathing
   gutter). Every other section still uses the shell for its own
   comfortable measure. */
.site-header .shell {
  max-width: none;
  padding-inline: clamp(1rem, 2vw, 2rem);
}

/* The fill is dark rather than light because the page alternates
   grounds. One dark bar reads correctly over the bone sections and the
   ink ones, and the wordmark never has to change colour mid scroll. */
.site-header.is-stuck,
.site-header.is-open {
  background: rgba(11, 13, 18, 0.82);
  -webkit-backdrop-filter: saturate(150%) blur(16px);
  backdrop-filter: saturate(150%) blur(16px);
  box-shadow:
    inset 0 -1px 0 rgba(240, 240, 237, 0.1),
    0 10px 30px rgba(0, 0, 0, 0.18);
}

/* Mobile lays this out as a flex row: wordmark left, toggle right. The
   fixed panel opens beneath. Desktop overrides to a three column grid
   further down so the nav links can sit optically centred between the
   wordmark and the CTA. */
.header__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
}

@media (min-width: 881px) {
  .site-header .shell {
    padding-inline: clamp(2rem, 14vw, 16rem);
  }
  .header__row {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: var(--sp-4);
  }
  .header__row > .wordmark { justify-self: start; }
  /* The nav itself has no box: its two children (list and CTA) drop
     straight into columns 2 and 3 of the header grid. This keeps the
     mobile DOM untouched and needs no duplicate elements. */
  .header__row > .nav { display: contents; }
  .header__row > .nav > .nav__list { justify-self: center; }
  .header__row > .nav > .nav__cta { justify-self: end; }
}

/* Wordmark. Poppins is kept from the previous build so the mark itself
   is unchanged. It is small and set left, and it is a link, not the H1.
   A touch bigger than the nav links so it holds the left of the bar
   without shouting. */
.wordmark {
  font-family: var(--font-mark);
  font-weight: 700;
  font-size: 1.4rem;
  letter-spacing: -0.015em;
  line-height: 1;
  display: inline-flex;
  align-items: baseline;
  gap: 0.14em;
  color: currentColor;
  transition: opacity 0.25s var(--ease);
}
.wordmark:hover { opacity: 0.65; }
.wordmark__dot {
  width: 0.3em;
  height: 0.3em;
  border-radius: 50%;
  background: currentColor;
  transform: translateY(-0.02em);
}
.wordmark-footer { font-size: 1.375rem; }

.nav {
  display: flex;
  align-items: center;
  gap: clamp(1.25rem, 2.5vw, 2.5rem);
}
.nav__list {
  display: flex;
  align-items: center;
  gap: clamp(1.25rem, 2.5vw, 2.25rem);
}
.nav__list a {
  position: relative;
  display: inline-block;
  padding-block: 0.4rem;
  font-size: 0.9375rem;
  font-weight: 500;
  color: currentColor;
  opacity: 0.82;
  transition: opacity 0.25s var(--ease);
}
.nav__list a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.35s var(--ease-out);
}
.nav__list a:hover { opacity: 1; }
.nav__list a:hover::after { transform: scaleX(1); }

/* The nav CTA is pinned to the inverse pair because the header ground
   is always dark. Its sizing is handled by the shared PRIMARY CTA
   block below so it matches the hero button exactly. */
/* The two primary CTAs are deliberately different colours. The nav one
   is a vibrant indigo so it pops off the dark bar; the hero one stays
   light on the dark hero. They still share their sizing, below.
   White on this indigo measures 6.3:1, comfortably past AA. */
.nav__cta {
  background: #5A47D6;
  color: #FFFFFF;
}
.nav__cta:hover {
  background: #6A58E6;
  box-shadow: 0 2px 12px rgba(90, 71, 214, 0.45);
}

/* PRIMARY CTAs. The nav "Get Started" and the hero "Get Started" are
   deliberately the same object shown at two points on the page, so they
   share one spec. Slightly larger than the other buttons on the page so
   they read as the primary action. Everything else about the base
   .btn (family, weight, radius, hover shadow) is inherited. */
.nav__cta {
  min-height: 42px;
  padding: 0.65rem 1.3rem;
  font-size: 0.875rem;
  border-radius: 7px;
  box-shadow: 0 1px 2px rgba(6, 8, 16, 0.28);
}
.hero__actions .btn-primary {
  min-height: 52px;
  padding: 0.9rem 1.75rem;
  font-size: 1.0625rem;
  border-radius: 8px;
  box-shadow: 0 1px 2px rgba(6, 8, 16, 0.28);
}
.hero__actions .btn-primary:hover {
  background: var(--bone-050);
  box-shadow: 0 2px 10px rgba(6, 8, 16, 0.34);
}

/* Arrow hover on the hero Get Started button only. The nav CTA stays a
   clean label with no arrow. Arrow always points east, no rotation.
   Heavier stroke than the default unicode glyph. */
.hero__actions .btn-primary::after {
  content: "\2192";
  display: inline-block;
  font-size: 1.15em;
  font-weight: 700;
  -webkit-text-stroke: 0.4px currentColor;
  line-height: 1;
  opacity: 0;
  width: 0;
  overflow: hidden;
  transition:
    opacity 0.3s var(--ease),
    width 0.35s var(--ease);
}
.hero__actions .btn-primary:hover::after {
  opacity: 1;
  width: 1.2em;
}

.nav-toggle {
  display: none;
  align-items: center;
  gap: 0.6rem;
  min-height: 44px;
  padding: 0.4rem 0.2rem;
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: currentColor;
}
.nav-toggle__bars {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 22px;
  height: 14px;
}
.nav-toggle__bars i {
  display: block;
  height: 1.5px;
  background: currentColor;
  transition: transform 0.3s var(--ease-out), opacity 0.2s var(--ease);
}
.nav-toggle[aria-expanded="true"] .nav-toggle__bars i:first-child { transform: translateY(3.25px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars i:last-child  { transform: translateY(-3.25px) rotate(-45deg); }


/* ========================================================================
   06  HERO

   The canvas is decorative and sits behind a scrim that guarantees text
   contrast whatever the shader happens to be doing at that moment.
   The static gradient on .hero__bg is what shows when WebGL is
   unavailable or reduced motion is on, so the hero is never blank.
   ======================================================================== */
/* Centred both ways. The top padding carries the fixed header's height
   plus the same breathing room as the bottom, so the content block is
   optically centred in the space actually visible below the header
   rather than in the raw section box. */
/* Full viewport, edge to edge. The asymmetric padding is the whole
   composition: a large reserve of empty space under the fixed header,
   and much less beneath, which lands the content cluster a little below
   the vertical centre and well above the densest part of the orbital
   system drawn underneath it. */
.hero {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100svh;
  padding-top: calc(var(--header-h) + clamp(4rem, 13vh, 9.5rem));
  padding-bottom: clamp(2.5rem, 7vh, 5rem);
  background: var(--hero-ink);
  color: var(--bone-200);
  overflow: hidden;
  isolation: isolate;
}

/* Static fallback, and the first paint before the canvas is sized. If
   the canvas never starts this is what the visitor sees: the same
   near black ground with the same indigo glow low and centred, so the
   hero degrades to a still version of itself rather than to nothing. */
.hero__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  /* Three-tone glow: cyan left, indigo centre, purple right —
     matches the three-layer canvas radial gradients. */
  background:
    radial-gradient(88% 80% at 70% 145%, rgba(139, 92, 246, 0.28) 0%, rgba(124, 58, 237, 0.18) 16%, rgba(90, 50, 200, 0.09) 34%, rgba(55, 35, 140, 0.04) 56%, rgba(8, 12, 34, 0) 100%),
    radial-gradient(80% 72% at 50% 145%, rgba(99, 102, 241, 0.22) 0%, rgba(85, 90, 220, 0.14) 18%, rgba(65, 70, 185, 0.07) 38%, rgba(40, 45, 130, 0.03) 58%, rgba(8, 12, 34, 0) 100%),
    radial-gradient(84% 74% at 30% 145%, rgba(34, 211, 238, 0.30) 0%, rgba(56, 189, 248, 0.20) 18%, rgba(45, 140, 220, 0.09) 38%, rgba(30, 80, 160, 0.04) 58%, rgba(8, 12, 40, 0) 100%),
    linear-gradient(180deg, #050710 0%, #060810 48%, #080A16 100%);
}
.hero__canvas {
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 1.1s var(--ease-out);
}
.hero__canvas.is-live { opacity: 1; }

.hero__inner {
  position: relative;
  text-align: center;
}

/* Large, bold and tight, so the two lines read as one statement rather
   than as a stacked pair. The line break is explicit in the markup
   (the .mask spans) rather than left to max-width, which guarantees the
   same break at every viewport and lets the load sequence lift each
   line independently. */
.hero__title {
  font-family: "Poppins", "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: var(--t-h1);
  font-weight: 800;
  line-height: 1.04;
  letter-spacing: -0.038em;
  margin-inline: auto;
  text-wrap: initial;
  filter: drop-shadow(0 0.06em 0.5em rgba(80, 150, 235, 0.13)) drop-shadow(0 0.10em 0.7em rgba(130, 100, 230, 0.15));
}

/* A barely perceptible white to lavender wash across each line. The
   point is a little dimension on the strokes, not a visible gradient
   effect. Applied per line rather than to the whole heading because the
   lines are transformed during the load sequence, and a background clip
   on a parent does not follow transformed children reliably.

   Guarded by @supports and preceded by a solid colour, so a browser
   without background-clip: text gets near white text rather than
   invisible text. */
.mask__i { color: #F7F6FB; }

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .hero__title .mask__i {
    background-image: linear-gradient(168deg, #FFFFFF 0%, #F4F2FC 48%, #DED8F4 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

/* Two deliberate lines, well under the headline in weight and size.
   Each line is its own block so the break survives, and so the longer
   second line can still wrap on its own on narrow screens. */
.hero__lede {
  margin-top: clamp(1.1rem, 2vw, 1.75rem);
  margin-inline: auto;
  max-width: 58ch;
  font-size: clamp(0.875rem, 0.82rem + 0.3vw, 0.9375rem);
  font-weight: 400;
  line-height: 1.6;
  letter-spacing: 0.004em;
  color: rgba(232, 232, 244, 0.62);
  text-shadow: 0 0.05em 0.45em rgba(80, 150, 235, 0.08), 0 0.07em 0.55em rgba(126, 108, 220, 0.10);
}
.hero__lede-line { display: block; text-wrap: balance; }
.hero__lede-line:first-child { color: rgba(240, 240, 250, 0.8); }

/* One button, stacked with its hint. Column flex so the hint sits
   directly beneath the button, both centred. */
.hero__actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.85rem;
  margin-top: clamp(1.75rem, 3.2vw, 2.75rem);
}
/* On the hero the ground is always dark, so the primary button is
   pinned to the inverse pair rather than relying on the surrounding
   surface tokens. */
.hero__actions .btn-primary { background: var(--bone-200); color: var(--ink-800); }

/* Deliberately understated. Small, low weight, low contrast, no all
   caps: it is a soft nudge below the button, not a second call to
   action. Contrast still clears AA on the ink ground. */
.hero__cta-hint {
  font-family: var(--font-body);
  font-size: 0.8125rem;
  font-weight: 400;
  letter-spacing: 0.01em;
  color: rgba(240, 240, 237, 0.55);
  text-shadow: 0 0.03em 0.3em rgba(80, 150, 235, 0.05), 0 0.04em 0.4em rgba(126, 108, 220, 0.06);
}


/* ========================================================================
   07  SECTION SHELL
   ======================================================================== */
.section {
  position: relative;
  padding-block: var(--section-y);
  background: var(--surface);
  color: var(--fg);
}
.section-alt { --surface: var(--bone-300); }
.section-dark { background: var(--surface); color: var(--fg); }

.section__head { max-width: 62ch; }
.section__title {
  font-size: var(--t-h2);
  font-weight: 600;
  letter-spacing: -0.032em;
}
.section__lede {
  margin-top: var(--sp-3);
  max-width: 54ch;
  font-size: var(--t-lede);
  line-height: 1.6;
  color: var(--fg-mute);
}

/* Reveal targets are visible by default. Motion animates from hidden to
   this state, so a failed CDN import costs the animation, never the
   content. */
[data-reveal] { opacity: 1; }


/* ========================================================================
   08  CONCEPT GALLERY

   Markup is generated by js/concept-gallery.js. Nothing in here is tied
   to a specific number of concepts, so adding one to the CONCEPTS array
   needs no change in this file.
   ======================================================================== */
/* Pulled up close to the hero. The section itself keeps its lower
   padding, but the top is tightened so the first concept sits just
   under the hero call to action rather than a screen away. */
.gallery { padding-top: clamp(2rem, 4.5vw, 3.75rem); }
.gallery__mount { margin-top: clamp(1.5rem, 3.5vw, 2.75rem); }

.cg { position: relative; }

.cg__stage {
  position: relative;
  overflow: hidden;
  padding-block: clamp(1.5rem, 3vw, 3rem) clamp(2rem, 4vw, 3.5rem);
  /* Vertical panning still works on touch. Horizontal is ours. */
  touch-action: pan-y;
  cursor: grab;

  /* Three fixed positions: left, centre, right. js/concept-gallery.js
     reads these to place every slide, so the same code works at any
     width and the values below are the single source of truth for the
     layout.

       --cg-slide-w   a slide's base width, as a fraction of the stage
       --cg-side-x    how far the side slides sit from centre, as a
                      fraction of a slide's OWN width (Motion translates
                      in element-relative percentages)
       --cg-far-x     where off stage slides wait, same units
       --cg-side-*    the scale and opacity of the two side slides

     At 40% slide width and 0.7 side scale, a side translate of 87%
     lands each side centre near 85% of the stage, leaving a clear gap
     on both edges of the centre slide. They are sized so the three
     never touch, at rest or mid transition. */
  --cg-slide-w: min(40%, 460px);
  --cg-side-x: 87%;
  --cg-far-x: 190%;
  --cg-side-scale: 0.7;
  --cg-side-opacity: 0.4;
}
.cg__stage.is-dragging { cursor: grabbing; }
.cg__stage:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: var(--r-2);
}

/* All slides share one grid cell; position comes entirely from their
   transform, so DOM order never decides what sits where. */
.cg__track {
  display: grid;
  will-change: transform;
}

.cg__slide {
  grid-area: 1 / 1;
  justify-self: center;
  width: var(--cg-slide-w);
  aspect-ratio: 16 / 10;
  will-change: transform, opacity;
}

.cg__frame {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: var(--r-3);
  box-shadow:
    var(--shadow-3),
    inset 0 0 0 1px rgba(255, 255, 255, 0.08);
  /* Lets the miniature inside size itself against the frame rather than
     against the viewport. */
  container-type: inline-size;
}

/* A discreet corner tag, rendered only while a concept is still a
   placeholder. Set placeholder: false in js/concepts.js and it goes.
   Bottom right because every layout variant keeps its own navigation
   top right and its footer notes to the left. */
.cg__edit {
  position: absolute;
  bottom: 0.75rem;
  right: 0.75rem;
  z-index: 3;
  padding: 0.25rem 0.5rem;
  border-radius: var(--r-1);
  background: rgba(11, 13, 18, 0.72);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: #F0F0ED;
  font-family: var(--font-body);
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.16em;
}

/* Caption under the stage: counter, name, then industry and the honesty
   label. Purely informational now, since navigation is drag, swipe, or
   arrow keys, so it stacks in a simple column with no controls beside
   it. */
.cg__bar {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding-top: var(--sp-4);
  border-top: 1px solid var(--rule);
}

.cg__count {
  font-family: var(--font-body);
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: 0.16em;
  font-variant-numeric: tabular-nums;
  color: var(--fg-mute);
}
.cg__count b { color: var(--fg); font-weight: 600; }

.cg__name {
  font-size: clamp(1.375rem, 1rem + 1.4vw, 2rem);
  font-weight: 600;
  letter-spacing: -0.03em;
}

.cg__sub {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 0.85rem;
  margin-top: 0.1rem;
}
.cg__industry { font-size: var(--t-small); color: var(--fg-mute); }

/* The honesty label. Small and typographically calm, but it is always
   present and never abbreviated away. */
.cg__label {
  display: inline-block;
  padding: 0.22rem 0.55rem;
  border-radius: var(--r-1);
  box-shadow: inset 0 0 0 1px var(--rule-strong);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-mute);
  white-space: nowrap;
}

/* Position indicator. One mark per concept, the active one filled. Not
   interactive: there are no navigation buttons anywhere in the gallery,
   so these are a read only sense of where you are, generated so the
   count always matches the number of concepts. */
.cg__ticks {
  display: flex;
  gap: 6px;
  margin-top: var(--sp-4);
}
.cg__tick {
  display: block;
  position: relative;
  flex: 1 1 0;
  height: 2px;
  border-radius: 1px;
  background: var(--rule);
  overflow: hidden;
}
.cg__tick::after {
  content: "";
  display: block;
  height: 100%;
  width: 100%;
  background: var(--fg);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.5s var(--ease-out);
}
.cg__tick.is-active::after { transform: scaleX(1); }

/* Fallback driver. Used only when Motion is unavailable, in which case
   js/concept-gallery.js writes transforms to style and these ease
   them. The two drivers are never both active. */
.cg-css .cg__slide {
  transition:
    transform 0.75s var(--ease-out),
    opacity 0.5s var(--ease);
}


/* ========================================================================
   09  CONCEPT WORLD

   The miniature site inside a gallery frame. Everything that varies
   between concepts arrives as a custom property set inline from the
   data object, so a new concept needs no new CSS. Three layout variants
   exist; an unknown one falls back to editorial.

   Sizing is in container query units against the frame, with a viewport
   clamp underneath for browsers without container query support, so the
   miniature stays proportional at every size.
   ======================================================================== */
.cw {
  --cw-bg: #101318;
  --cw-surface: #171B22;
  --cw-ink: #F2F2EF;
  --cw-mute: #9AA1AD;
  --cw-accent: #A8B4D8;
  --cw-display: var(--font-display);
  --cw-body: var(--font-body);

  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  padding: 3.6cqw 4.2cqw;
  gap: 2cqw;
  background: var(--cw-bg);
  color: var(--cw-ink);
  font-family: var(--cw-body);
  /* First line is the fallback for browsers without container queries.
     The second scales against the frame, but clamped: a purely
     proportional miniature turns into illegible grey mush on a phone,
     so below a certain frame width it stops shrinking and simply reads
     as a narrow site instead of a shrunken wide one. */
  font-size: clamp(9px, 1.15vw, 15px);
  font-size: clamp(9.5px, 1.55cqw, 15px);
  line-height: 1.5;
  user-select: none;
}

/* A soft light source in the corner. Gives each world its own sense of
   depth without adding an image. */
.cw::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(100% 80% at 78% 6%, rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0) 62%);
  pointer-events: none;
}

.cw__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2cqw;
  padding-bottom: 2.4cqw;
  border-bottom: 1px solid color-mix(in srgb, var(--cw-ink) 14%, transparent);
}
.cw__mark {
  font-family: var(--cw-display);
  font-weight: 700;
  font-size: 1.5em;
  letter-spacing: -0.02em;
  line-height: 1;
}
.cw__nav { display: flex; gap: 1.6em; }
.cw__nav span {
  font-size: 0.8em;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--cw-mute);
}

.cw__stage {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1.1em;
  min-height: 0;
}

.cw__eyebrow {
  font-size: 0.75em;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--cw-accent);
}

.cw__headline {
  font-family: var(--cw-display);
  font-size: 3.4em;
  font-weight: 600;
  line-height: 1.02;
  letter-spacing: -0.035em;
  max-width: 16ch;
  text-wrap: balance;
}

.cw__body {
  font-size: 1em;
  line-height: 1.6;
  max-width: 40ch;
  color: var(--cw-mute);
}

.cw__cta {
  align-self: flex-start;
  margin-top: 0.4em;
  padding: 0.7em 1.4em;
  border-radius: 2px;
  background: var(--cw-accent);
  color: var(--cw-bg);
  font-size: 0.85em;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.cw__foot {
  display: flex;
  gap: 2em;
  padding-top: 2.2cqw;
  border-top: 1px solid color-mix(in srgb, var(--cw-ink) 12%, transparent);
}
.cw__foot span {
  font-size: 0.75em;
  letter-spacing: 0.06em;
  color: var(--cw-mute);
}

/* Layout variants ---------------------------------------------------- */

/* editorial: centred, calm, serif friendly. The default. */
.cw-editorial .cw__stage { align-items: center; text-align: center; }
.cw-editorial .cw__headline { max-width: 18ch; }
.cw-editorial .cw__body { max-width: 44ch; }
.cw-editorial .cw__cta { align-self: center; }
.cw-editorial .cw__foot { justify-content: center; }

/* split: copy left, a tinted plate right. Reads as a services or
   product page rather than a landing page. */
.cw-split .cw__stage {
  display: grid;
  grid-template-columns: 1.25fr 1fr;
  align-items: center;
  gap: 3cqw;
}
.cw-split .cw__plate {
  align-self: stretch;
  border-radius: 3px;
  background:
    linear-gradient(155deg,
      color-mix(in srgb, var(--cw-accent) 32%, transparent),
      color-mix(in srgb, var(--cw-accent) 8%, transparent)),
    var(--cw-surface);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--cw-ink) 10%, transparent);
}
.cw-split .cw__copy { display: flex; flex-direction: column; gap: 1.1em; }
.cw-split .cw__headline { font-size: 2.9em; max-width: 12ch; }

/* stack: type led, headline anchored to the bottom. The loudest of the
   three, for concepts where the name is the whole idea. */
.cw-stack .cw__stage { justify-content: flex-end; gap: 0.9em; }
.cw-stack .cw__headline {
  font-size: 4.4em;
  line-height: 0.94;
  letter-spacing: -0.05em;
  max-width: 13ch;
}
.cw-stack .cw__body { max-width: 34ch; }

/* Concepts that ship with a real image instead of a generated world. */
.cw-image { padding: 0; }
.cw-image img { width: 100%; height: 100%; object-fit: cover; }


/* ========================================================================
   10  CLIENT WORK

   Live styles for the section that is still commented out in
   index.html. Deliberately quiet and evidence led so it never reads as
   a second concept gallery. Class names avoid double hyphens because
   the markup lives inside an HTML comment until it ships.
   ======================================================================== */
.work .section_kicker {
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg-mute);
  margin-bottom: var(--sp-2);
}
.work .section_title { font-size: var(--t-h2); font-weight: 600; letter-spacing: -0.032em; }
.work .section_lede { margin-top: var(--sp-3); max-width: 52ch; color: var(--fg-mute); }

.work_list {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  display: grid;
  gap: clamp(2rem, 4vw, 3.5rem);
}
.work_link {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 3vw, 3rem);
  align-items: center;
  padding-top: clamp(2rem, 4vw, 3rem);
  border-top: 1px solid var(--rule);
}
.work_thumb {
  overflow: hidden;
  border-radius: var(--r-2);
  aspect-ratio: 3 / 2;
  background: var(--surface-raised);
  box-shadow: var(--shadow-2);
}
.work_thumb img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.7s var(--ease-out);
}
.work_link:hover .work_thumb img { transform: scale(1.03); }
.work_name { font-size: var(--t-h3); font-weight: 600; }
.work_industry { margin-top: 0.35rem; font-size: var(--t-small); color: var(--fg-mute); }
.work_note { margin-top: var(--sp-3); color: var(--fg-mute); }
.work_cta {
  display: inline-block;
  margin-top: var(--sp-3);
  font-size: var(--t-small);
  font-weight: 600;
  box-shadow: inset 0 -1px 0 currentColor;
}


/* ========================================================================
   11  WHAT WE DO
   ======================================================================== */
.offer {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--rule);
  border-block: 1px solid var(--rule);
}

.offer__item {
  display: flex;
  flex-direction: column;
  padding: clamp(1.75rem, 3vw, 2.75rem) clamp(1.5rem, 2.5vw, 2.25rem);
  background: var(--surface);
  transition: background-color 0.4s var(--ease);
}
.offer__item:hover { background: var(--surface-raised); }

.offer__title {
  font-size: var(--t-h3);
  font-weight: 600;
  letter-spacing: -0.022em;
  max-width: 20ch;
}
.offer__body {
  margin-top: var(--sp-3);
  color: var(--fg-mute);
  font-size: 1rem;
  max-width: 34ch;
}
.offer__label { margin-top: auto; padding-top: var(--sp-5); }


/* ========================================================================
   12  PROCESS

   The numbers are real sequence information, not decoration, so they
   get typographic weight rather than a badge.
   ======================================================================== */
.steps {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  display: grid;
  gap: 0;
  counter-reset: step;
}

.step {
  display: grid;
  grid-template-columns: minmax(88px, 12%) 1fr;
  gap: clamp(1rem, 3vw, 2.5rem);
  align-items: start;
  padding-block: clamp(1.5rem, 3vw, 2.5rem);
  border-top: 1px solid var(--rule);
  transition: border-color 0.4s var(--ease);
}
.steps .step:last-child { border-bottom: 1px solid var(--rule); }
.step:hover { border-top-color: var(--rule-strong); }

.step__num {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 1rem + 1.6vw, 2.25rem);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--fg-mute);
  transition: color 0.4s var(--ease);
}
.step:hover .step__num { color: var(--accent); }

.step__title { font-size: var(--t-h3); font-weight: 600; letter-spacing: -0.022em; }
.step__body p {
  margin-top: var(--sp-2);
  max-width: 56ch;
  color: var(--fg-mute);
}

.cta-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-top: clamp(2.5rem, 5vw, 4rem);
}
.cta-line p {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 1rem + 1vw, 1.75rem);
  font-weight: 500;
  letter-spacing: -0.025em;
}


/* ========================================================================
   13  ABOUT
   ======================================================================== */
.about {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: start;
}
.about__copy { display: grid; gap: var(--sp-3); max-width: 58ch; }
.about__copy p { color: var(--fg-mute); }
.about__copy p:first-child { color: var(--fg); font-size: var(--t-lede); line-height: 1.6; }


/* ========================================================================
   14  CONTACT
   ======================================================================== */
.contact {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2.5rem, 5vw, 5rem);
  align-items: start;
}
.contact__intro p { margin-top: var(--sp-3); color: var(--fg-mute); max-width: 46ch; }
.contact__alt { font-weight: 600; color: var(--fg) !important; }

.form { display: grid; gap: var(--sp-4); }
.form__hp { position: absolute; left: -9999px; }

.field { display: grid; gap: 0.5rem; }
.field label {
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-mute);
}
.field input,
.field select,
.field textarea {
  width: 100%;
  min-height: 48px;
  padding: 0.8rem 0.9rem;
  border: 0;
  border-radius: var(--r-1);
  background: var(--surface-raised);
  color: var(--fg);
  box-shadow: inset 0 0 0 1px var(--rule-strong);
  transition: box-shadow 0.25s var(--ease);
}
.field textarea { min-height: 132px; resize: vertical; line-height: 1.6; }
.field select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%235A6373' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1rem center;
  padding-right: 2.5rem;
}
.field input:hover,
.field select:hover,
.field textarea:hover { box-shadow: inset 0 0 0 1px var(--fg-mute); }
.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--accent);
}


/* ========================================================================
   15  FOOTER
   ======================================================================== */
.site-footer {
  background: var(--surface);
  color: var(--fg);
  padding-block: clamp(2.5rem, 5vw, 4rem);
}
.footer__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
}
.footer__email { font-size: var(--t-small); color: var(--fg-mute); }
.footer__email:hover { color: var(--accent); }
.footer__end { display: flex; align-items: center; gap: var(--sp-4); }
.footer__social {
  display: grid;
  place-items: center;
  width: 44px; height: 44px;
  margin: -12px;
  color: var(--fg-mute);
  transition: color 0.25s var(--ease);
}
.footer__social:hover { color: var(--fg); }
.footer__legal {
  font-size: var(--t-micro);
  letter-spacing: 0.04em;
  color: var(--fg-mute);
}


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

   Mobile is laid out for mobile rather than compressed from desktop:
   the nav becomes a full panel, the gallery frame widens to the gutter,
   and the offer grid becomes a stacked list with real rules between.
   ======================================================================== */

@media (max-width: 1040px) {
  .about { grid-template-columns: 1fr; }
  .contact { grid-template-columns: 1fr; }
}

@media (max-width: 900px) {
  .offer { grid-template-columns: 1fr; }
  .offer__label { padding-top: var(--sp-4); }
  .work_link { grid-template-columns: 1fr; }
}

@media (max-width: 880px) {
  .nav-toggle { display: inline-flex; }

  .nav {
    position: fixed;
    inset: var(--header-h) 0 auto 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: var(--sp-3) var(--gutter) var(--sp-5);
    background: rgba(14, 17, 22, 0.94);
    -webkit-backdrop-filter: blur(18px);
    backdrop-filter: blur(18px);
    color: var(--bone-200);
    box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    transition:
      opacity 0.3s var(--ease),
      transform 0.4s var(--ease-out),
      visibility 0s linear 0.35s;
  }
  .nav.is-open {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    transition-delay: 0s;
  }

  .nav__list { flex-direction: column; align-items: stretch; gap: 0; }
  .nav__list li { border-bottom: 1px solid rgba(240, 240, 237, 0.1); }
  .nav__list a {
    display: block;
    padding-block: 1.05rem;
    font-family: var(--font-display);
    font-size: 1.375rem;
    font-weight: 500;
    letter-spacing: -0.02em;
    opacity: 1;
  }
  .nav__list a::after { display: none; }

  .nav__cta,
  .site-header.is-stuck .nav__cta {
    margin-top: var(--sp-4);
    width: 100%;
    min-height: 52px;
    padding: 0.9rem 1.75rem;
    font-size: 1.0625rem;
    border-radius: 8px;
    background: var(--bone-200);
    color: var(--ink-800);
  }

  /* Below the desktop breakpoint the centre slide widens and the side
     slides move out further, so the three stay proportional and clearly
     separated as the stage narrows. Their outer edges run under the
     stage's overflow, which reads as more waiting on either side. */
  .cg__stage {
    padding-inline: 0;
    --cg-slide-w: 50%;
    --cg-side-x: 90%;
    --cg-side-scale: 0.68;
  }
}

@media (max-width: 720px) {
  :root { --header-h: 64px; }

  /* Phones keep the full viewport and the same composition: a generous
     gap under the header, content a little below centre, orbital system
     below that. The top reserve is proportionally smaller than desktop
     because a phone viewport is tall and narrow, and the desktop value
     would push the cluster off the bottom. */
  .hero {
    min-height: 100svh;
    padding-top: calc(var(--header-h) + clamp(2.5rem, 9vh, 5rem));
    padding-bottom: clamp(2rem, 6vh, 3.5rem);
  }
  .hero__title { letter-spacing: -0.028em; }
  .hero__lede { max-width: 38ch; }
  /* Comfortable but not full bleed. A full width primary reads as a
     form submit rather than a considered call to action, so it holds
     a fixed minimum instead. */
  .hero__actions .btn { min-width: 210px; }

  /* On phones the centre slide takes most of the stage and the sides
     become slim edge peeks: still three distinct, non-overlapping
     positions, just weighted hard toward the middle where a thumb can
     read it. */
  .cg__stage {
    --cg-slide-w: 62%;
    --cg-side-x: 92%;
    --cg-side-scale: 0.72;
    --cg-side-opacity: 0.32;
  }
  .cg__slide { aspect-ratio: 4 / 3; }

  .step { grid-template-columns: 1fr; gap: var(--sp-2); }
  .step__num { font-size: 1.25rem; }

  .cta-line { flex-direction: column; align-items: flex-start; }
  .cta-line .btn { width: 100%; }

  .footer__row { flex-direction: column; align-items: flex-start; gap: var(--sp-3); }
}

/* At phone widths the frame is about the width of a phone, so each
   miniature is allowed to become the narrow layout it would really be,
   rather than a squeezed wide one. The footer notes and the split
   plate are the first things a narrow site would drop, so they go. */
@media (max-width: 480px) {
  .cw { padding: 5cqw 5.5cqw; gap: 3cqw; }
  .cw__headline { font-size: 2.9em; }
  .cw-stack .cw__headline { font-size: 3.2em; }
  .cw-split .cw__stage { grid-template-columns: 1fr; }
  .cw-split .cw__plate { display: none; }
  .cw__foot { display: none; }
  .cw__nav span:nth-child(n + 3) { display: none; }
}


/* ========================================================================
   17  REDUCED MOTION

   Nothing on the page depends on movement to be understood. When a
   visitor asks for less, transitions collapse, the shader is never
   painted as a still composition (js/orbital-hero.js), and the gallery changes slides
   instantly instead of springing.
   ======================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  /* The orbital canvas stays visible. js/orbital-hero.js paints the
     orbits, the glow and the points of light once and never starts the
     loop, so a visitor who asked for less motion still gets the full
     composition, just held still. Only the fade in is dropped. */
  .hero__canvas { opacity: 1 !important; transition: none !important; }
  .work_link:hover .work_thumb img { transform: none; }
}
